Merge remote branch 'upstream/master'
authorLars Op den Kamp <lars@opdenkamp.eu>
Wed, 12 Jan 2011 10:44:20 +0000 (11:44 +0100)
committerLars Op den Kamp <lars@opdenkamp.eu>
Wed, 12 Jan 2011 10:44:20 +0000 (11:44 +0100)
535 files changed:
.cproject
.gitignore
.project
Makefile.in
XBMC.xcodeproj/project.pbxproj
addons/library.xbmc.addon/dlfcn-win32.cpp [new file with mode: 0644]
addons/library.xbmc.addon/dlfcn-win32.h [new file with mode: 0644]
addons/library.xbmc.addon/libXBMC_addon.h [new file with mode: 0644]
addons/library.xbmc.gui/libXBMC_gui.h [new file with mode: 0644]
addons/library.xbmc.pvr/libXBMC_pvr.h [new file with mode: 0644]
addons/pvr.hts/addon.xml [new file with mode: 0644]
addons/pvr.hts/icon.png [new file with mode: 0644]
addons/pvr.hts/pthreadVC2.dll [new file with mode: 0644]
addons/pvr.hts/pthreadVC2d.dll [new file with mode: 0644]
addons/pvr.hts/resources/language/Dutch/strings.xml [new file with mode: 0644]
addons/pvr.hts/resources/language/English/strings.xml [new file with mode: 0644]
addons/pvr.hts/resources/language/German/strings.xml [new file with mode: 0644]
addons/pvr.hts/resources/settings.xml [new file with mode: 0644]
addons/pvr.mythtv/addon.xml [new file with mode: 0644]
addons/pvr.mythtv/resources/language/English/strings.xml [new file with mode: 0644]
addons/pvr.mythtv/resources/settings.xml [new file with mode: 0644]
addons/pvr.team-mediaportal.tvserver/addon.xml [new file with mode: 0644]
addons/pvr.team-mediaportal.tvserver/icon.jpg [new file with mode: 0644]
addons/pvr.team-mediaportal.tvserver/resources/language/Dutch/strings.xml [new file with mode: 0644]
addons/pvr.team-mediaportal.tvserver/resources/language/English/strings.xml [new file with mode: 0644]
addons/pvr.team-mediaportal.tvserver/resources/language/German/strings.xml [new file with mode: 0644]
addons/pvr.team-mediaportal.tvserver/resources/settings.xml [new file with mode: 0644]
addons/pvr.vdr.streamdev/addon.xml [new file with mode: 0644]
addons/pvr.vdr.streamdev/icon.jpg [new file with mode: 0644]
addons/pvr.vdr.streamdev/pthreadVC2.dll [new file with mode: 0644]
addons/pvr.vdr.streamdev/pthreadVC2d.dll [new file with mode: 0644]
addons/pvr.vdr.streamdev/resources/data/noSignal.mpg [new file with mode: 0644]
addons/pvr.vdr.streamdev/resources/language/Dutch/strings.xml [new file with mode: 0644]
addons/pvr.vdr.streamdev/resources/language/English/strings.xml [new file with mode: 0644]
addons/pvr.vdr.streamdev/resources/language/German/strings.xml [new file with mode: 0644]
addons/pvr.vdr.streamdev/resources/settings.xml [new file with mode: 0644]
addons/pvr.vdr.vnsi/addon.xml [new file with mode: 0644]
addons/pvr.vdr.vnsi/icon.jpg [new file with mode: 0644]
addons/pvr.vdr.vnsi/pthreadVC2.dll [new file with mode: 0644]
addons/pvr.vdr.vnsi/pthreadVC2d.dll [new file with mode: 0644]
addons/pvr.vdr.vnsi/resources/language/Dutch/strings.xml [new file with mode: 0644]
addons/pvr.vdr.vnsi/resources/language/English/strings.xml [new file with mode: 0644]
addons/pvr.vdr.vnsi/resources/language/German/strings.xml [new file with mode: 0644]
addons/pvr.vdr.vnsi/resources/settings.xml [new file with mode: 0644]
addons/pvr.vdr.vnsi/resources/skins/Confluence/720p/ChannelScan.xml [new file with mode: 0644]
addons/skin.confluence/720p/DialogFullScreenInfo.xml
addons/skin.confluence/720p/DialogPVRChannelManager.xml [new file with mode: 0644]
addons/skin.confluence/720p/DialogPVRChannelsOSD.xml [new file with mode: 0644]
addons/skin.confluence/720p/DialogPVRGroupManager.xml [new file with mode: 0644]
addons/skin.confluence/720p/DialogPVRGuideInfo.xml [new file with mode: 0644]
addons/skin.confluence/720p/DialogPVRGuideOSD.xml [new file with mode: 0644]
addons/skin.confluence/720p/DialogPVRGuideSearch.xml [new file with mode: 0644]
addons/skin.confluence/720p/DialogPVRRecordingInfo.xml [new file with mode: 0644]
addons/skin.confluence/720p/DialogPVRTimerSettings.xml [new file with mode: 0644]
addons/skin.confluence/720p/DialogPVRUpdateProgressBar.xml [new file with mode: 0644]
addons/skin.confluence/720p/Home.xml
addons/skin.confluence/720p/MyTV.xml [new file with mode: 0644]
addons/skin.confluence/720p/PlayerControls.xml
addons/skin.confluence/720p/Settings.xml
addons/skin.confluence/720p/SettingsSystemInfo.xml
addons/skin.confluence/720p/VideoFullScreen.xml
addons/skin.confluence/720p/VideoOSD.xml
addons/skin.confluence/720p/custom_SkinSetting_1111.xml
addons/skin.confluence/720p/defaults.xml
addons/skin.confluence/720p/includes.xml
addons/skin.confluence/backgrounds/tv.jpg [new file with mode: 0644]
addons/skin.confluence/language/English/strings.xml
addons/skin.confluence/language/German/strings.xml
addons/skin.confluence/media/Makefile
addons/skin.confluence/media/OSDChannelDownFO.png [new file with mode: 0644]
addons/skin.confluence/media/OSDChannelDownNF.png [new file with mode: 0644]
addons/skin.confluence/media/OSDChannelListFO.png [new file with mode: 0644]
addons/skin.confluence/media/OSDChannelListNF.png [new file with mode: 0644]
addons/skin.confluence/media/OSDChannelUPFO.png [new file with mode: 0644]
addons/skin.confluence/media/OSDChannelUPNF.png [new file with mode: 0644]
addons/skin.confluence/media/OSDRecordNF2.png [new file with mode: 0644]
addons/skin.confluence/media/OSDTeleTextFO.png [new file with mode: 0644]
addons/skin.confluence/media/OSDTeleTextNF.png [new file with mode: 0644]
addons/skin.confluence/media/OSDepgFO.png [new file with mode: 0644]
addons/skin.confluence/media/OSDepgNF.png [new file with mode: 0644]
addons/skin.confluence/media/PVR-HasTimer.png [new file with mode: 0644]
addons/skin.confluence/media/PVR-IsRecording.png [new file with mode: 0644]
addons/skin.confluence/media/StackNF.png
addons/skin.confluence/media/epg-genres/0.png [new file with mode: 0644]
addons/skin.confluence/media/epg-genres/112.png [new file with mode: 0644]
addons/skin.confluence/media/epg-genres/128.png [new file with mode: 0644]
addons/skin.confluence/media/epg-genres/144.png [new file with mode: 0644]
addons/skin.confluence/media/epg-genres/16.png [new file with mode: 0644]
addons/skin.confluence/media/epg-genres/160.png [new file with mode: 0644]
addons/skin.confluence/media/epg-genres/176.png [new file with mode: 0644]
addons/skin.confluence/media/epg-genres/192.png [new file with mode: 0644]
addons/skin.confluence/media/epg-genres/208.png [new file with mode: 0644]
addons/skin.confluence/media/epg-genres/224.png [new file with mode: 0644]
addons/skin.confluence/media/epg-genres/240.png [new file with mode: 0644]
addons/skin.confluence/media/epg-genres/32.png [new file with mode: 0644]
addons/skin.confluence/media/epg-genres/48.png [new file with mode: 0644]
addons/skin.confluence/media/epg-genres/64.png [new file with mode: 0644]
addons/skin.confluence/media/epg-genres/80.png [new file with mode: 0644]
addons/skin.confluence/media/epg-genres/96.png [new file with mode: 0644]
addons/skin.confluence/media/epg-genres/genre-numbers.txt [new file with mode: 0644]
configure.in
guilib/GUIControl.h
guilib/GUIControlFactory.cpp
guilib/GUIEPGGridContainer.cpp [new file with mode: 0644]
guilib/GUIEPGGridContainer.h [new file with mode: 0644]
guilib/GUIEditControl.cpp
guilib/GUIEditControl.h
guilib/GUILabelControl.cpp
guilib/GUILabelControl.h
guilib/GUIListGroup.cpp
guilib/GUIListGroup.h
guilib/GUIListItemLayout.cpp
guilib/GUIListItemLayout.h
guilib/GUIListLabel.cpp
guilib/GUIListLabel.h
guilib/Key.h
guilib/Makefile.in
guilib/system.h
language/Dutch/strings.xml
language/English/strings.xml
language/German/strings.xml
lib/addons/library.xbmc.addon/Makefile.in [new file with mode: 0644]
lib/addons/library.xbmc.addon/libXBMC_addon.cpp [new file with mode: 0644]
lib/addons/library.xbmc.addon/project/VS2008Express/libXBMC_addon.sln [new file with mode: 0644]
lib/addons/library.xbmc.addon/project/VS2008Express/libXBMC_addon.vcproj [new file with mode: 0644]
lib/addons/library.xbmc.addon/project/VS2010Express/libXBMC_addon.vcxproj [new file with mode: 0644]
lib/addons/library.xbmc.addon/project/VS2010Express/libXBMC_addon.vcxproj.filters [new file with mode: 0644]
lib/addons/library.xbmc.gui/Makefile.in [new file with mode: 0644]
lib/addons/library.xbmc.gui/libXBMC_gui.cpp [new file with mode: 0644]
lib/addons/library.xbmc.gui/project/VS2008Express/libXBMC_gui.sln [new file with mode: 0644]
lib/addons/library.xbmc.gui/project/VS2008Express/libXBMC_gui.vcproj [new file with mode: 0644]
lib/addons/library.xbmc.gui/project/VS2010Express/libXBMC_gui.vcxproj [new file with mode: 0644]
lib/addons/library.xbmc.gui/project/VS2010Express/libXBMC_gui.vcxproj.filters [new file with mode: 0644]
lib/addons/library.xbmc.pvr/Makefile.in [new file with mode: 0644]
lib/addons/library.xbmc.pvr/libXBMC_pvr.cpp [new file with mode: 0644]
lib/addons/library.xbmc.pvr/project/VS2008Express/libXBMC_pvr.sln [new file with mode: 0644]
lib/addons/library.xbmc.pvr/project/VS2008Express/libXBMC_pvr.vcproj [new file with mode: 0644]
lib/addons/library.xbmc.pvr/project/VS2010Express/libXBMC_pvr.vcxproj [new file with mode: 0644]
lib/addons/library.xbmc.pvr/project/VS2010Express/libXBMC_pvr.vcxproj.filters [new file with mode: 0644]
project/VS2010Express/XBMC for Windows.sln
project/VS2010Express/XBMC.vcxproj
project/VS2010Express/XBMC.vcxproj.filters
project/VS2010Express/guilib.vcxproj
project/VS2010Express/guilib.vcxproj.filters
system/keymaps/keyboard.xml
system/keymaps/remote.xml
system/playercorefactory.xml
tools/Linux/packaging/debian/xbmc-bin.install
xbmc/AddonDatabase.cpp
xbmc/AddonDatabase.h
xbmc/AdvancedSettings.cpp
xbmc/AdvancedSettings.h
xbmc/Application.cpp
xbmc/Application.h
xbmc/ApplicationMessenger.cpp
xbmc/ApplicationMessenger.h
xbmc/ButtonTranslator.cpp
xbmc/Database.cpp
xbmc/Database.h
xbmc/FileItem.cpp
xbmc/FileItem.h
xbmc/FileSystem/AddonsDirectory.cpp
xbmc/FileSystem/DllLibCMyth.h
xbmc/FileSystem/FactoryDirectory.cpp
xbmc/FileSystem/FileFactory.cpp
xbmc/FileSystem/ILiveTV.h
xbmc/FileSystem/Makefile.in
xbmc/FileSystem/MythFile.cpp
xbmc/FileSystem/MythFile.h
xbmc/FileSystem/PVRDirectory.cpp [new file with mode: 0644]
xbmc/FileSystem/PVRDirectory.h [new file with mode: 0644]
xbmc/FileSystem/PVRFile.cpp [new file with mode: 0644]
xbmc/FileSystem/PVRFile.h [new file with mode: 0644]
xbmc/FileSystem/VTPFile.cpp
xbmc/FileSystem/VTPFile.h
xbmc/GUIDialogAddonInfo.cpp
xbmc/GUIDialogContextMenu.h
xbmc/GUIDialogMediaSource.cpp
xbmc/GUIDialogNumeric.cpp
xbmc/GUIDialogNumeric.h
xbmc/GUIDialogPVRChannelManager.cpp [new file with mode: 0644]
xbmc/GUIDialogPVRChannelManager.h [new file with mode: 0644]
xbmc/GUIDialogPVRChannelsOSD.cpp [new file with mode: 0644]
xbmc/GUIDialogPVRChannelsOSD.h [new file with mode: 0644]
xbmc/GUIDialogPVRCutterOSD.cpp [new file with mode: 0644]
xbmc/GUIDialogPVRCutterOSD.h [new file with mode: 0644]
xbmc/GUIDialogPVRDirectorOSD.cpp [new file with mode: 0644]
xbmc/GUIDialogPVRDirectorOSD.h [new file with mode: 0644]
xbmc/GUIDialogPVRGroupManager.cpp [new file with mode: 0644]
xbmc/GUIDialogPVRGroupManager.h [new file with mode: 0644]
xbmc/GUIDialogPVRGuideInfo.cpp [new file with mode: 0644]
xbmc/GUIDialogPVRGuideInfo.h [new file with mode: 0644]
xbmc/GUIDialogPVRGuideOSD.cpp [new file with mode: 0644]
xbmc/GUIDialogPVRGuideOSD.h [new file with mode: 0644]
xbmc/GUIDialogPVRGuideSearch.cpp [new file with mode: 0644]
xbmc/GUIDialogPVRGuideSearch.h [new file with mode: 0644]
xbmc/GUIDialogPVRRecordingInfo.cpp [new file with mode: 0644]
xbmc/GUIDialogPVRRecordingInfo.h [new file with mode: 0644]
xbmc/GUIDialogPVRTimerSettings.cpp [new file with mode: 0644]
xbmc/GUIDialogPVRTimerSettings.h [new file with mode: 0644]
xbmc/GUIDialogPVRUpdateProgressBar.cpp [new file with mode: 0644]
xbmc/GUIDialogPVRUpdateProgressBar.h [new file with mode: 0644]
xbmc/GUIDialogSeekBar.cpp
xbmc/GUIDialogSettings.cpp
xbmc/GUIDialogSettings.h
xbmc/GUIDialogVideoSettings.cpp
xbmc/GUIMediaWindow.cpp
xbmc/GUISettings.cpp
xbmc/GUISettings.h
xbmc/GUIViewControl.cpp
xbmc/GUIViewState.cpp
xbmc/GUIViewStateTV.cpp [new file with mode: 0644]
xbmc/GUIViewStateTV.h [new file with mode: 0644]
xbmc/GUIViewStateVideo.cpp
xbmc/GUIWindowFullScreen.cpp
xbmc/GUIWindowFullScreen.h
xbmc/GUIWindowOSD.cpp
xbmc/GUIWindowSettingsCategory.cpp
xbmc/GUIWindowSystemInfo.cpp
xbmc/GUIWindowTV.cpp [new file with mode: 0644]
xbmc/GUIWindowTV.h [new file with mode: 0644]
xbmc/GUIWindowVideoBase.cpp
xbmc/Makefile.in
xbmc/Settings.cpp
xbmc/SortFileItem.cpp
xbmc/SortFileItem.h
xbmc/URL.cpp
xbmc/Util.cpp
xbmc/Util.h
xbmc/XBIRRemote.h
xbmc/addons/Addon.cpp
xbmc/addons/Addon.h
xbmc/addons/AddonDll.h
xbmc/addons/AddonHelpers_Addon.cpp [new file with mode: 0644]
xbmc/addons/AddonHelpers_Addon.h [new file with mode: 0644]
xbmc/addons/AddonHelpers_GUI.cpp [new file with mode: 0644]
xbmc/addons/AddonHelpers_GUI.h [new file with mode: 0644]
xbmc/addons/AddonHelpers_PVR.cpp [new file with mode: 0644]
xbmc/addons/AddonHelpers_PVR.h [new file with mode: 0644]
xbmc/addons/AddonHelpers_local.cpp [new file with mode: 0644]
xbmc/addons/AddonHelpers_local.h [new file with mode: 0644]
xbmc/addons/AddonManager.cpp
xbmc/addons/AddonManager.h
xbmc/addons/DllPVRClient.h [new file with mode: 0644]
xbmc/addons/Makefile
xbmc/addons/PVRClient.cpp [new file with mode: 0644]
xbmc/addons/PVRClient.h [new file with mode: 0644]
xbmc/addons/Skin.cpp
xbmc/addons/include/xbmc_pvr_dll.h [new file with mode: 0644]
xbmc/addons/include/xbmc_pvr_types.h [new file with mode: 0644]
xbmc/cores/DllLoader/DllLoaderContainer.cpp
xbmc/cores/DllLoader/Win32DllLoader.cpp
xbmc/cores/dvdplayer/DVDCodecs/Video/VDPAU.cpp
xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.h
xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp
xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPVRClient.cpp [new file with mode: 0644]
xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPVRClient.h [new file with mode: 0644]
xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPacket.h [new file with mode: 0644]
xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxUtils.h
xbmc/cores/dvdplayer/DVDDemuxers/DVDFactoryDemuxer.cpp
xbmc/cores/dvdplayer/DVDDemuxers/Makefile.in
xbmc/cores/dvdplayer/DVDInputStreams/DVDFactoryInputStream.cpp
xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStream.h
xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamHTSP.cpp
xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamHTSP.h
xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamMMS.cpp
xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamPVRManager.cpp [new file with mode: 0644]
xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamPVRManager.h [new file with mode: 0644]
xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamTV.cpp
xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamTV.h
xbmc/cores/dvdplayer/DVDInputStreams/Makefile
xbmc/cores/dvdplayer/DVDPlayer.cpp
xbmc/cores/dvdplayer/DVDPlayer.h
xbmc/cores/dvdplayer/DVDSubtitles/SamiTagConvertor.cpp [new file with mode: 0644]
xbmc/cores/dvdplayer/DVDSubtitles/SamiTagConvertor.h [new file with mode: 0644]
xbmc/cores/paplayer/CodecFactory.cpp
xbmc/lib/libhts/Win32/include/stdint.h
xbmc/lib/libhts/Win32/libhts_2010.vcxproj
xbmc/pvr/Makefile [new file with mode: 0644]
xbmc/pvr/PVRChannel.cpp [new file with mode: 0644]
xbmc/pvr/PVRChannel.h [new file with mode: 0644]
xbmc/pvr/PVRChannelGroup.cpp [new file with mode: 0644]
xbmc/pvr/PVRChannelGroup.h [new file with mode: 0644]
xbmc/pvr/PVRChannelGroups.cpp [new file with mode: 0644]
xbmc/pvr/PVRChannelGroups.h [new file with mode: 0644]
xbmc/pvr/PVRChannels.cpp [new file with mode: 0644]
xbmc/pvr/PVRChannels.h [new file with mode: 0644]
xbmc/pvr/PVRChannelsContainer.cpp [new file with mode: 0644]
xbmc/pvr/PVRChannelsContainer.h [new file with mode: 0644]
xbmc/pvr/PVRDatabase.cpp [new file with mode: 0644]
xbmc/pvr/PVRDatabase.h [new file with mode: 0644]
xbmc/pvr/PVREpg.cpp [new file with mode: 0644]
xbmc/pvr/PVREpg.h [new file with mode: 0644]
xbmc/pvr/PVREpgInfoTag.cpp [new file with mode: 0644]
xbmc/pvr/PVREpgInfoTag.h [new file with mode: 0644]
xbmc/pvr/PVREpgSearchFilter.cpp [new file with mode: 0644]
xbmc/pvr/PVREpgSearchFilter.h [new file with mode: 0644]
xbmc/pvr/PVREpgs.cpp [new file with mode: 0644]
xbmc/pvr/PVREpgs.h [new file with mode: 0644]
xbmc/pvr/PVRManager.cpp [new file with mode: 0644]
xbmc/pvr/PVRManager.h [new file with mode: 0644]
xbmc/pvr/PVRRecordings.cpp [new file with mode: 0644]
xbmc/pvr/PVRRecordings.h [new file with mode: 0644]
xbmc/pvr/PVRTimerInfoTag.cpp [new file with mode: 0644]
xbmc/pvr/PVRTimerInfoTag.h [new file with mode: 0644]
xbmc/pvr/PVRTimers.cpp [new file with mode: 0644]
xbmc/pvr/PVRTimers.h [new file with mode: 0644]
xbmc/pvrclients/Makefile.include.in [new file with mode: 0644]
xbmc/pvrclients/MediaPortal/Makefile.in [new file with mode: 0644]
xbmc/pvrclients/MediaPortal/README [new file with mode: 0644]
xbmc/pvrclients/MediaPortal/Socket.cpp [new file with mode: 0644]
xbmc/pvrclients/MediaPortal/Socket.h [new file with mode: 0644]
xbmc/pvrclients/MediaPortal/StdString.h [new file with mode: 0644]
xbmc/pvrclients/MediaPortal/channels.cpp [new file with mode: 0644]
xbmc/pvrclients/MediaPortal/channels.h [new file with mode: 0644]
xbmc/pvrclients/MediaPortal/client.cpp [new file with mode: 0644]
xbmc/pvrclients/MediaPortal/client.h [new file with mode: 0644]
xbmc/pvrclients/MediaPortal/epg.cpp [new file with mode: 0644]
xbmc/pvrclients/MediaPortal/epg.h [new file with mode: 0644]
xbmc/pvrclients/MediaPortal/linux/pvrclient-mediaportal_os_posix.h [new file with mode: 0644]
xbmc/pvrclients/MediaPortal/project/VS2008Express/XBMC_MPTV.sln [new file with mode: 0644]
xbmc/pvrclients/MediaPortal/project/VS2008Express/XBMC_MPTV.vcproj [new file with mode: 0644]
xbmc/pvrclients/MediaPortal/project/VS2010Express/XBMC_MPTV.vcxproj [new file with mode: 0644]
xbmc/pvrclients/MediaPortal/project/VS2010Express/XBMC_MPTV.vcxproj.filters [new file with mode: 0644]
xbmc/pvrclients/MediaPortal/pvrclient-mediaportal.cpp [new file with mode: 0644]
xbmc/pvrclients/MediaPortal/pvrclient-mediaportal.h [new file with mode: 0644]
xbmc/pvrclients/MediaPortal/pvrclient-mediaportal_os.h [new file with mode: 0644]
xbmc/pvrclients/MediaPortal/recordings.cpp [new file with mode: 0644]
xbmc/pvrclients/MediaPortal/recordings.h [new file with mode: 0644]
xbmc/pvrclients/MediaPortal/timers.cpp [new file with mode: 0644]
xbmc/pvrclients/MediaPortal/timers.h [new file with mode: 0644]
xbmc/pvrclients/MediaPortal/utils.cpp [new file with mode: 0644]
xbmc/pvrclients/MediaPortal/utils.h [new file with mode: 0644]
xbmc/pvrclients/MediaPortal/windows/pvrclient-mediaportal_os_windows.h [new file with mode: 0644]
xbmc/pvrclients/mythtv/Makefile.in [new file with mode: 0644]
xbmc/pvrclients/mythtv/MythXml.cpp [new file with mode: 0644]
xbmc/pvrclients/mythtv/MythXml.h [new file with mode: 0644]
xbmc/pvrclients/mythtv/StdString.h [new file with mode: 0644]
xbmc/pvrclients/mythtv/client.cpp [new file with mode: 0644]
xbmc/pvrclients/mythtv/client.h [new file with mode: 0644]
xbmc/pvrclients/mythtv/libmythxml/GetChannelListCommand.h [new file with mode: 0644]
xbmc/pvrclients/mythtv/libmythxml/GetChannelListParameters.h [new file with mode: 0644]
xbmc/pvrclients/mythtv/libmythxml/GetChannelListResult.cpp [new file with mode: 0644]
xbmc/pvrclients/mythtv/libmythxml/GetChannelListResult.h [new file with mode: 0644]
xbmc/pvrclients/mythtv/libmythxml/GetNumChannelsCommand.h [new file with mode: 0644]
xbmc/pvrclients/mythtv/libmythxml/GetNumChannelsParameters.h [new file with mode: 0644]
xbmc/pvrclients/mythtv/libmythxml/GetNumChannelsResult.cpp [new file with mode: 0644]
xbmc/pvrclients/mythtv/libmythxml/GetNumChannelsResult.h [new file with mode: 0644]
xbmc/pvrclients/mythtv/libmythxml/GetProgramGuideCommand.h [new file with mode: 0644]
xbmc/pvrclients/mythtv/libmythxml/GetProgramGuideParameters.cpp [new file with mode: 0644]
xbmc/pvrclients/mythtv/libmythxml/GetProgramGuideParameters.h [new file with mode: 0644]
xbmc/pvrclients/mythtv/libmythxml/GetProgramGuideResult.cpp [new file with mode: 0644]
xbmc/pvrclients/mythtv/libmythxml/GetProgramGuideResult.h [new file with mode: 0644]
xbmc/pvrclients/mythtv/libmythxml/Makefile [new file with mode: 0644]
xbmc/pvrclients/mythtv/libmythxml/MythXmlCommand.cpp [new file with mode: 0644]
xbmc/pvrclients/mythtv/libmythxml/MythXmlCommand.h [new file with mode: 0644]
xbmc/pvrclients/mythtv/libmythxml/MythXmlCommandParameters.cpp [new file with mode: 0644]
xbmc/pvrclients/mythtv/libmythxml/MythXmlCommandParameters.h [new file with mode: 0644]
xbmc/pvrclients/mythtv/libmythxml/MythXmlCommandResult.h [new file with mode: 0644]
xbmc/pvrclients/mythtv/libmythxml/SChannel.h [new file with mode: 0644]
xbmc/pvrclients/mythtv/libmythxml/SEpg.h [new file with mode: 0644]
xbmc/pvrclients/mythtv/linux/pvrclient-mythtv_os_posix.h [new file with mode: 0644]
xbmc/pvrclients/mythtv/pvrclient-mythtv_os.h [new file with mode: 0644]
xbmc/pvrclients/tvheadend/HTSPData.cpp [new file with mode: 0644]
xbmc/pvrclients/tvheadend/HTSPData.h [new file with mode: 0644]
xbmc/pvrclients/tvheadend/HTSPDemux.cpp [new file with mode: 0644]
xbmc/pvrclients/tvheadend/HTSPDemux.h [new file with mode: 0644]
xbmc/pvrclients/tvheadend/HTSPSession.cpp [new file with mode: 0644]
xbmc/pvrclients/tvheadend/HTSPSession.h [new file with mode: 0644]
xbmc/pvrclients/tvheadend/Makefile.in [new file with mode: 0644]
xbmc/pvrclients/tvheadend/StdString.h [new file with mode: 0644]
xbmc/pvrclients/tvheadend/client.cpp [new file with mode: 0644]
xbmc/pvrclients/tvheadend/client.h [new file with mode: 0644]
xbmc/pvrclients/tvheadend/linux/os_posix.h [new file with mode: 0644]
xbmc/pvrclients/tvheadend/project/VS2008Express/XBMC_hts.vcproj [new file with mode: 0644]
xbmc/pvrclients/tvheadend/project/VS2010Express/XBMC_tvheadend.vcxproj [new file with mode: 0644]
xbmc/pvrclients/tvheadend/project/VS2010Express/XBMC_tvheadend.vcxproj.filters [new file with mode: 0644]
xbmc/pvrclients/tvheadend/pthread_win32/pthread.h [new file with mode: 0644]
xbmc/pvrclients/tvheadend/pthread_win32/pthreadVC2.lib [new file with mode: 0644]
xbmc/pvrclients/tvheadend/pthread_win32/pthreadVC2d.lib [new file with mode: 0644]
xbmc/pvrclients/tvheadend/pthread_win32/sched.h [new file with mode: 0644]
xbmc/pvrclients/tvheadend/pthread_win32/semaphore.h [new file with mode: 0644]
xbmc/pvrclients/tvheadend/pvrclient-tvheadend_os.h [new file with mode: 0644]
xbmc/pvrclients/tvheadend/thread.cpp [new file with mode: 0644]
xbmc/pvrclients/tvheadend/thread.h [new file with mode: 0644]
xbmc/pvrclients/tvheadend/tools.cpp [new file with mode: 0644]
xbmc/pvrclients/tvheadend/tools.h [new file with mode: 0644]
xbmc/pvrclients/tvheadend/windows/dirent.cpp [new file with mode: 0644]
xbmc/pvrclients/tvheadend/windows/dirent.h [new file with mode: 0644]
xbmc/pvrclients/tvheadend/windows/os_windows.cpp [new file with mode: 0644]
xbmc/pvrclients/tvheadend/windows/os_windows.h [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/Makefile.in [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/README [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/StdString.h [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/channels.cpp [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/channels.h [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/channelscan.cpp [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/channelscan.h [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/client.cpp [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/client.h [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/epg.cpp [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/epg.h [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/linux/pvrclient-vdr_os_posix.h [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/patches/streamdev-cvs100210-ReplaceRecordingStreaming.patch [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/patches/streamdev-cvs221109-AddCallbackMsg.diff [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/patches/streamdev-cvs221109-AddFemonV1.diff [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/patches/vdr-1.7.10_extensionsAndXBMC.diff [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/patches/vdr-1.7.7-ExtendetStatusMessage.diff [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/patches/vdr-1.7.7-GenreToFromEpgDat.diff [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/project/VS2008Express/XBMC_VDR.sln [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/project/VS2008Express/XBMC_VDR.vcproj [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/project/VS2008Express/inttypes.h [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/project/VS2008Express/stdint.h [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/project/VS2010Express/XBMC_VDR.vcxproj [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/project/VS2010Express/XBMC_VDR.vcxproj.filters [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/pthread_win32/pthread.h [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/pthread_win32/pthreadVC2.lib [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/pthread_win32/pthreadVC2d.lib [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/pthread_win32/sched.h [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/pthread_win32/semaphore.h [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/pvrclient-vdr_os.h [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/recordings.cpp [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/recordings.h [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/ringbuffer.cpp [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/ringbuffer.h [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/select.cpp [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/select.h [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/thread.cpp [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/thread.h [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/timers.cpp [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/timers.h [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/tools.cpp [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/tools.h [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/vtptransceiver.cpp [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/vtptransceiver.h [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/windows/dirent.cpp [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/windows/dirent.h [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/windows/getline.cpp [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/windows/getline.h [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/windows/pvrclient-vdr_os_windows.cpp [new file with mode: 0644]
xbmc/pvrclients/vdr-streamdev/windows/pvrclient-vdr_os_windows.h [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/COPYING [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/Makefile.in [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/README [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/StdString.h [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/VNSIChannelScan.cpp [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/VNSIChannelScan.h [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/VNSIData.cpp [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/VNSIData.h [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/VNSIDemux.cpp [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/VNSIDemux.h [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/VNSIRecording.cpp [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/VNSIRecording.h [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/VNSISession.cpp [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/VNSISession.h [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/client.cpp [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/client.h [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/linux/os_posix.h [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/pvrclient-vdrVNSI_os.h [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/recordings.cpp [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/recordings.h [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/requestpacket.cpp [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/requestpacket.h [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/responsepacket.cpp [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/responsepacket.h [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/thread.cpp [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/thread.h [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/tools.cpp [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/tools.h [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/COPYING [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/HISTORY [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/Makefile [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/README [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/bitstream.c [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/bitstream.h [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/cmdcontrol.c [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/cmdcontrol.h [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/config.c [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/config.h [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/connection.c [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/connection.h [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/cxsocket.c [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/cxsocket.h [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer.c [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer.h [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_AAC.c [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_AAC.h [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_AC3.c [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_AC3.h [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_DTS.c [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_DTS.h [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_MPEGAudio.c [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_MPEGAudio.h [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_MPEGVideo.c [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_MPEGVideo.h [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_Subtitle.c [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_Subtitle.h [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_Teletext.c [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_Teletext.h [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_h264.c [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_h264.h [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/global.h [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/patches/vdr-wirbelscan-0.0.5-pre11e-AddServiceInterface.diff [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/project/VNSI Server.cbp [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/project/VNSI Server.layout [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/receiver.c [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/receiver.h [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/recplayer.c [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/recplayer.h [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/requestpacket.c [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/requestpacket.h [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/responsepacket.c [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/responsepacket.h [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/server.c [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/server.h [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/suspend.c [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/suspend.dat [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/suspend.h [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/tools.c [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/tools.h [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/vdrcommand.h [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/vnsiserver.c [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/vnsiserver/allowed_hosts.conf [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/vnsiserver/noSignal.mpg [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/wirbelscanservice.h [new file with mode: 0644]
xbmc/pvrclients/vdr-vnsi/vdrcommand.h [new file with mode: 0644]
xbmc/settings/VideoSettings.h
xbmc/utils/GUIInfoManager.cpp
xbmc/utils/GUIInfoManager.h
xbmc/utils/Makefile
xbmc/utils/Observer.cpp [new file with mode: 0644]
xbmc/utils/Observer.h [new file with mode: 0644]
xbmc/utils/TextSearch.cpp [new file with mode: 0644]
xbmc/utils/TextSearch.h [new file with mode: 0644]
xbmc/win32/stdbool.h [new file with mode: 0644]

index 43f2c81..25923fd 100644 (file)
--- a/.cproject
+++ b/.cproject
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="UTF-8"?>
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?fileVersion 4.0.0?>
 
 <cproject>
@@ -7,7 +7,7 @@
 <storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="cdt.managedbuild.config.gnu.macosx.exe.debug.277492429" moduleId="org.eclipse.cdt.core.settings" name="Debug">
 <externalSettings/>
 <extensions>
-<extension id="org.eclipse.cdt.core.MachO" point="org.eclipse.cdt.core.BinaryParser"/>
+<extension id="org.eclipse.cdt.core.MachO64" point="org.eclipse.cdt.core.BinaryParser"/>
 <extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
 <extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
 <extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
@@ -19,7 +19,7 @@
 <folderInfo id="cdt.managedbuild.config.gnu.macosx.exe.debug.277492429." name="/" resourcePath="">
 <toolChain errorParsers="" id="cdt.managedbuild.toolchain.gnu.macosx.exe.debug.181128994" name="MacOSX GCC" nonInternalBuilderId="cdt.managedbuild.target.gnu.builder.macosx.exe.debug" superClass="cdt.managedbuild.toolchain.gnu.macosx.exe.debug">
 <targetPlatform id="cdt.managedbuild.target.gnu.platform.macosx.exe.debug.611397248" name="Debug Platform" superClass="cdt.managedbuild.target.gnu.platform.macosx.exe.debug"/>
-<builder arguments="-configuration Debug" autoBuildTarget="all" buildPath="${workspace_loc:/XBMC/}" cleanBuildTarget="clean" command="xcodebuild" enableAutoBuild="false" enableCleanBuild="false" enabledIncrementalBuild="true" id="cdt.managedbuild.target.gnu.builder.macosx.exe.debug.2011146033" incrementalBuildTarget="" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" parallelBuildOn="false" parallelizationNumber="4" stopOnErr="true" superClass="cdt.managedbuild.target.gnu.builder.macosx.exe.debug">
+<builder arguments="" autoBuildTarget="all" buildPath="${workspace_loc:/XBMC/}" cleanBuildTarget="clean" command="make" enableAutoBuild="false" enableCleanBuild="false" enabledIncrementalBuild="true" id="cdt.managedbuild.target.gnu.builder.macosx.exe.debug.2011146033" incrementalBuildTarget="" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" parallelBuildOn="false" parallelizationNumber="-4" stopOnErr="true" superClass="cdt.managedbuild.target.gnu.builder.macosx.exe.debug">
 <outputEntries>
 <entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="outputPath" name=""/>
 </outputEntries>
 </tool>
 </toolChain>
 </folderInfo>
+<folderInfo id="cdt.managedbuild.config.gnu.macosx.exe.debug.277492429.170851026" name="/" resourcePath="xbmc/lib/UnrarXLib">
+<toolChain errorParsers="" id="cdt.managedbuild.toolchain.gnu.macosx.exe.debug.1271468311" name="MacOSX GCC" superClass="cdt.managedbuild.toolchain.gnu.macosx.exe.debug" unusedChildren="">
+<targetPlatform id="cdt.managedbuild.target.gnu.platform.macosx.exe.debug" name="Debug Platform" superClass="cdt.managedbuild.target.gnu.platform.macosx.exe.debug"/>
+<tool id="cdt.managedbuild.tool.macosx.c.linker.macosx.exe.debug.461961223" name="MacOS X C Linker" superClass="cdt.managedbuild.tool.macosx.c.linker.macosx.exe.debug.1750093173"/>
+<tool id="cdt.managedbuild.tool.macosx.cpp.linker.macosx.exe.debug.464820511" name="MacOS X C++ Linker" superClass="cdt.managedbuild.tool.macosx.cpp.linker.macosx.exe.debug.835214875"/>
+<tool errorParsers="org.eclipse.cdt.core.GASErrorParser" id="cdt.managedbuild.tool.gnu.assembler.macosx.exe.debug.2046303719" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.macosx.exe.debug.1794289768">
+<inputType id="cdt.managedbuild.tool.gnu.assembler.input.1967010553" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
+</tool>
+<tool id="cdt.managedbuild.tool.gnu.archiver.macosx.base.991037695" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.macosx.base.1308334469"/>
+<tool errorParsers="org.eclipse.cdt.core.GCCErrorParser" id="cdt.managedbuild.tool.gnu.cpp.compiler.macosx.exe.debug.864504005" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.macosx.exe.debug.65441666">
+<option id="gnu.cpp.compilermacosx.exe.debug.option.optimization.level.1929724833" name="Optimization Level" superClass="gnu.cpp.compilermacosx.exe.debug.option.optimization.level" value="gnu.cpp.compiler.optimization.level.none" valueType="enumerated"/>
+<option id="gnu.cpp.compiler.macosx.exe.debug.option.debugging.level.1437239494" name="Debug Level" superClass="gnu.cpp.compiler.macosx.exe.debug.option.debugging.level" value="gnu.cpp.compiler.debugging.level.max" valueType="enumerated"/>
+<option id="gnu.cpp.compiler.option.preprocessor.def.930016596" name="Defined symbols (-D)" superClass="gnu.cpp.compiler.option.preprocessor.def" valueType="definedSymbols">
+<listOptionValue builtIn="false" value="_DEBUG"/>
+<listOptionValue builtIn="false" value="SILENT"/>
+<listOptionValue builtIn="false" value="_LINUX"/>
+<listOptionValue builtIn="false" value="_REENTRANT"/>
+<listOptionValue builtIn="false" value="_FILE_DEFINED"/>
+<listOptionValue builtIn="false" value="_FILE_OFFSET_BITS=64"/>
+<listOptionValue builtIn="false" value="_LARGEFILE64_SOURCE"/>
+<listOptionValue builtIn="false" value="HAS_SDL_JOYSTICK"/>
+</option>
+<inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.1310329132" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
+</tool>
+<tool errorParsers="org.eclipse.cdt.core.GCCErrorParser" id="cdt.managedbuild.tool.gnu.c.compiler.macosx.exe.debug.1727159323" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.macosx.exe.debug.2086201526">
+<option defaultValue="gnu.c.optimization.level.none" id="gnu.c.compiler.macosx.exe.debug.option.optimization.level.1047846560" name="Optimization Level" superClass="gnu.c.compiler.macosx.exe.debug.option.optimization.level" valueType="enumerated"/>
+<option id="gnu.c.compiler.macosx.exe.debug.option.debugging.level.1754809956" name="Debug Level" superClass="gnu.c.compiler.macosx.exe.debug.option.debugging.level" value="gnu.c.debugging.level.max" valueType="enumerated"/>
+<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.1950409522" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
+</tool>
+</toolChain>
+</folderInfo>
+<folderInfo id="cdt.managedbuild.config.gnu.macosx.exe.debug.277492429.356548846" name="/" resourcePath="xbmc/lib/libUPnP">
+<toolChain errorParsers="" id="cdt.managedbuild.toolchain.gnu.macosx.exe.debug.2012337279" name="MacOSX GCC" superClass="cdt.managedbuild.toolchain.gnu.macosx.exe.debug" unusedChildren="">
+<targetPlatform id="cdt.managedbuild.target.gnu.platform.macosx.exe.debug" name="Debug Platform" superClass="cdt.managedbuild.target.gnu.platform.macosx.exe.debug"/>
+<tool id="cdt.managedbuild.tool.macosx.c.linker.macosx.exe.debug.1316166915" name="MacOS X C Linker" superClass="cdt.managedbuild.tool.macosx.c.linker.macosx.exe.debug.1750093173"/>
+<tool id="cdt.managedbuild.tool.macosx.cpp.linker.macosx.exe.debug.896232824" name="MacOS X C++ Linker" superClass="cdt.managedbuild.tool.macosx.cpp.linker.macosx.exe.debug.835214875"/>
+<tool errorParsers="org.eclipse.cdt.core.GASErrorParser" id="cdt.managedbuild.tool.gnu.assembler.macosx.exe.debug.1669692608" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.macosx.exe.debug.1794289768">
+<inputType id="cdt.managedbuild.tool.gnu.assembler.input.2031504877" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
+</tool>
+<tool id="cdt.managedbuild.tool.gnu.archiver.macosx.base.1215299429" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.macosx.base.1308334469"/>
+<tool errorParsers="org.eclipse.cdt.core.GCCErrorParser" id="cdt.managedbuild.tool.gnu.cpp.compiler.macosx.exe.debug.919985893" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.macosx.exe.debug.65441666">
+<option id="gnu.cpp.compilermacosx.exe.debug.option.optimization.level.55887864" name="Optimization Level" superClass="gnu.cpp.compilermacosx.exe.debug.option.optimization.level" value="gnu.cpp.compiler.optimization.level.none" valueType="enumerated"/>
+<option id="gnu.cpp.compiler.macosx.exe.debug.option.debugging.level.794874135" name="Debug Level" superClass="gnu.cpp.compiler.macosx.exe.debug.option.debugging.level" value="gnu.cpp.compiler.debugging.level.max" valueType="enumerated"/>
+<option id="gnu.cpp.compiler.option.include.paths.1095698845" name="Include paths (-I)" superClass="gnu.cpp.compiler.option.include.paths" valueType="includePath">
+<listOptionValue builtIn="false" value="&quot;${workspace_loc:/XBMC/xbmc/lib/libportaudio}&quot;"/>
+<listOptionValue builtIn="false" value="&quot;${workspace_loc:/XBMC/xbmc/lib/libUPnP/Platinum/Source/Devices/MediaServer}&quot;"/>
+<listOptionValue builtIn="false" value="&quot;${workspace_loc:/XBMC/xbmc/lib/libUPnP/Platinum/Source/Devices/MediaRenderer}&quot;"/>
+<listOptionValue builtIn="false" value="&quot;${workspace_loc:/XBMC/xbmc/lib/libUPnP/Platinum/Source/Core/}&quot;"/>
+<listOptionValue builtIn="false" value="&quot;${workspace_loc:/XBMC/xbmc/lib/libUPnP/Platinum/ThirdParty/Neptune/Source/Core/}&quot;"/>
+<listOptionValue builtIn="false" value="&quot;${workspace_loc:/XBMC/xbmc/utils}&quot;"/>
+<listOptionValue builtIn="false" value="&quot;${workspace_loc:/XBMC/xbmc/cores}&quot;"/>
+<listOptionValue builtIn="false" value="&quot;${workspace_loc:/XBMC/xbmc/ffmpeg}&quot;"/>
+<listOptionValue builtIn="false" value="&quot;${workspace_loc:/XBMC/xbmc/cores/dvdplayer}&quot;"/>
+<listOptionValue builtIn="false" value="&quot;${workspace_loc:/XBMC/xbmc}&quot;"/>
+<listOptionValue builtIn="false" value="&quot;${workspace_loc:/XBMC/xbmc/linux}&quot;"/>
+<listOptionValue builtIn="false" value="&quot;${workspace_loc:/XBMC/guilib}&quot;"/>
+<listOptionValue builtIn="false" value="/opt/local/include"/>
+<listOptionValue builtIn="false" value="/opt/local/include/freetype2"/>
+</option>
+<inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.1898344904" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
+</tool>
+<tool errorParsers="org.eclipse.cdt.core.GCCErrorParser" id="cdt.managedbuild.tool.gnu.c.compiler.macosx.exe.debug.794980876" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.macosx.exe.debug.2086201526">
+<option defaultValue="gnu.c.optimization.level.none" id="gnu.c.compiler.macosx.exe.debug.option.optimization.level.953270789" name="Optimization Level" superClass="gnu.c.compiler.macosx.exe.debug.option.optimization.level" valueType="enumerated"/>
+<option id="gnu.c.compiler.macosx.exe.debug.option.debugging.level.1774148998" name="Debug Level" superClass="gnu.c.compiler.macosx.exe.debug.option.debugging.level" value="gnu.c.debugging.level.max" valueType="enumerated"/>
+<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.1429314286" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
+</tool>
+</toolChain>
+</folderInfo>
 <folderInfo id="cdt.managedbuild.config.gnu.macosx.exe.debug.277492429.1172416896" name="/" resourcePath="xbmc/linux">
 <toolChain errorParsers="" id="cdt.managedbuild.toolchain.gnu.macosx.exe.debug.1253052677" name="MacOSX GCC" superClass="cdt.managedbuild.toolchain.gnu.macosx.exe.debug" unusedChildren="">
 <targetPlatform id="cdt.managedbuild.target.gnu.platform.macosx.exe.debug" name="Debug Platform" superClass="cdt.managedbuild.target.gnu.platform.macosx.exe.debug"/>
 </tool>
 </toolChain>
 </folderInfo>
-<folderInfo id="cdt.managedbuild.config.gnu.macosx.exe.debug.277492429.643967003" name="/" resourcePath="xbmc/cores/DllLoader">
-<toolChain errorParsers="" id="cdt.managedbuild.toolchain.gnu.macosx.exe.debug.1734765344" name="MacOSX GCC" superClass="cdt.managedbuild.toolchain.gnu.macosx.exe.debug" unusedChildren="">
-<targetPlatform id="cdt.managedbuild.target.gnu.platform.macosx.exe.debug" name="Debug Platform" superClass="cdt.managedbuild.target.gnu.platform.macosx.exe.debug"/>
-<tool id="cdt.managedbuild.tool.macosx.c.linker.macosx.exe.debug.1979556041" name="MacOS X C Linker" superClass="cdt.managedbuild.tool.macosx.c.linker.macosx.exe.debug.1750093173"/>
-<tool id="cdt.managedbuild.tool.macosx.cpp.linker.macosx.exe.debug.986787272" name="MacOS X C++ Linker" superClass="cdt.managedbuild.tool.macosx.cpp.linker.macosx.exe.debug.835214875"/>
-<tool errorParsers="org.eclipse.cdt.core.GASErrorParser" id="cdt.managedbuild.tool.gnu.assembler.macosx.exe.debug.833592423" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.macosx.exe.debug.1794289768">
-<inputType id="cdt.managedbuild.tool.gnu.assembler.input.1164190729" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
-</tool>
-<tool id="cdt.managedbuild.tool.gnu.archiver.macosx.base.1942199706" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.macosx.base.1308334469"/>
-<tool errorParsers="org.eclipse.cdt.core.GCCErrorParser" id="cdt.managedbuild.tool.gnu.cpp.compiler.macosx.exe.debug.591922786" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.macosx.exe.debug.65441666">
-<option id="gnu.cpp.compilermacosx.exe.debug.option.optimization.level.1240764958" name="Optimization Level" superClass="gnu.cpp.compilermacosx.exe.debug.option.optimization.level" value="gnu.cpp.compiler.optimization.level.none" valueType="enumerated"/>
-<option id="gnu.cpp.compiler.macosx.exe.debug.option.debugging.level.83648616" name="Debug Level" superClass="gnu.cpp.compiler.macosx.exe.debug.option.debugging.level" value="gnu.cpp.compiler.debugging.level.max" valueType="enumerated"/>
-<option id="gnu.cpp.compiler.option.preprocessor.def.684325808" name="Defined symbols (-D)" superClass="gnu.cpp.compiler.option.preprocessor.def" valueType="definedSymbols">
-<listOptionValue builtIn="false" value="_DEBUG"/>
-<listOptionValue builtIn="false" value="API_DEBUG"/>
-<listOptionValue builtIn="false" value="_LINUX"/>
-<listOptionValue builtIn="false" value="_REENTRANT"/>
-<listOptionValue builtIn="false" value="_FILE_DEFINED"/>
-<listOptionValue builtIn="false" value="_FILE_OFFSET_BITS=64"/>
-<listOptionValue builtIn="false" value="_LARGEFILE64_SOURCE"/>
-<listOptionValue builtIn="false" value="HAS_SDL_JOYSTICK"/>
-</option>
-<inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.2045638600" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
-</tool>
-<tool errorParsers="org.eclipse.cdt.core.GCCErrorParser" id="cdt.managedbuild.tool.gnu.c.compiler.macosx.exe.debug.907902063" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.macosx.exe.debug.2086201526">
-<option defaultValue="gnu.c.optimization.level.none" id="gnu.c.compiler.macosx.exe.debug.option.optimization.level.124736836" name="Optimization Level" superClass="gnu.c.compiler.macosx.exe.debug.option.optimization.level" valueType="enumerated"/>
-<option id="gnu.c.compiler.macosx.exe.debug.option.debugging.level.1632737144" name="Debug Level" superClass="gnu.c.compiler.macosx.exe.debug.option.debugging.level" value="gnu.c.debugging.level.max" valueType="enumerated"/>
-<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.1699992470" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
-</tool>
-</toolChain>
-</folderInfo>
-<folderInfo id="cdt.managedbuild.config.gnu.macosx.exe.debug.277492429.919238836" name="/" resourcePath="xbmc/cores/DllLoader/exports">
-<toolChain errorParsers="" id="cdt.managedbuild.toolchain.gnu.macosx.exe.debug.1237365591" name="MacOSX GCC" superClass="cdt.managedbuild.toolchain.gnu.macosx.exe.debug" unusedChildren="">
-<targetPlatform id="cdt.managedbuild.target.gnu.platform.macosx.exe.debug" name="Debug Platform" superClass="cdt.managedbuild.target.gnu.platform.macosx.exe.debug"/>
-<tool id="cdt.managedbuild.tool.macosx.c.linker.macosx.exe.debug.1047209358" name="MacOS X C Linker" superClass="cdt.managedbuild.tool.macosx.c.linker.macosx.exe.debug.1979556041"/>
-<tool id="cdt.managedbuild.tool.macosx.cpp.linker.macosx.exe.debug.288617395" name="MacOS X C++ Linker" superClass="cdt.managedbuild.tool.macosx.cpp.linker.macosx.exe.debug.986787272"/>
-<tool errorParsers="org.eclipse.cdt.core.GASErrorParser" id="cdt.managedbuild.tool.gnu.assembler.macosx.exe.debug.1068019392" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.macosx.exe.debug.833592423">
-<inputType id="cdt.managedbuild.tool.gnu.assembler.input.713501646" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
-</tool>
-<tool id="cdt.managedbuild.tool.gnu.archiver.macosx.base.366482976" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.macosx.base.1942199706"/>
-<tool errorParsers="org.eclipse.cdt.core.GCCErrorParser" id="cdt.managedbuild.tool.gnu.cpp.compiler.macosx.exe.debug.1841251975" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.macosx.exe.debug.591922786">
-<option id="gnu.cpp.compilermacosx.exe.debug.option.optimization.level.267213578" name="Optimization Level" superClass="gnu.cpp.compilermacosx.exe.debug.option.optimization.level" value="gnu.cpp.compiler.optimization.level.none" valueType="enumerated"/>
-<option id="gnu.cpp.compiler.macosx.exe.debug.option.debugging.level.1089716219" name="Debug Level" superClass="gnu.cpp.compiler.macosx.exe.debug.option.debugging.level" value="gnu.cpp.compiler.debugging.level.max" valueType="enumerated"/>
-<option id="gnu.cpp.compiler.option.preprocessor.def.1815424215" name="Defined symbols (-D)" superClass="gnu.cpp.compiler.option.preprocessor.def" valueType="definedSymbols">
-<listOptionValue builtIn="false" value="_DEBUG"/>
-<listOptionValue builtIn="false" value="_LINUX"/>
-<listOptionValue builtIn="false" value="_REENTRANT"/>
-<listOptionValue builtIn="false" value="_FILE_DEFINED"/>
-<listOptionValue builtIn="false" value="_FILE_OFFSET_BITS=64"/>
-<listOptionValue builtIn="false" value="_LARGEFILE64_SOURCE"/>
-<listOptionValue builtIn="false" value="HAS_SDL_JOYSTICK"/>
-</option>
-<inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.1642113994" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
-</tool>
-<tool errorParsers="org.eclipse.cdt.core.GCCErrorParser" id="cdt.managedbuild.tool.gnu.c.compiler.macosx.exe.debug.17570680" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.macosx.exe.debug.907902063">
-<option defaultValue="gnu.c.optimization.level.none" id="gnu.c.compiler.macosx.exe.debug.option.optimization.level.1073864500" name="Optimization Level" superClass="gnu.c.compiler.macosx.exe.debug.option.optimization.level" valueType="enumerated"/>
-<option id="gnu.c.compiler.macosx.exe.debug.option.debugging.level.943491841" name="Debug Level" superClass="gnu.c.compiler.macosx.exe.debug.option.debugging.level" value="gnu.c.debugging.level.max" valueType="enumerated"/>
-<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.1349353172" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
-</tool>
-</toolChain>
-</folderInfo>
 <folderInfo id="cdt.managedbuild.config.gnu.macosx.exe.debug.277492429.1334863358" name="/" resourcePath="xbmc/cores/dvdplayer">
 <toolChain errorParsers="" id="cdt.managedbuild.toolchain.gnu.macosx.exe.debug.171733843" name="MacOSX GCC" superClass="cdt.managedbuild.toolchain.gnu.macosx.exe.debug" unusedChildren="">
 <targetPlatform id="cdt.managedbuild.target.gnu.platform.macosx.exe.debug" name="Debug Platform" superClass="cdt.managedbuild.target.gnu.platform.macosx.exe.debug"/>
 </tool>
 </toolChain>
 </folderInfo>
-<folderInfo id="cdt.managedbuild.config.gnu.macosx.exe.debug.277492429.356548846" name="/" resourcePath="xbmc/lib/libUPnP">
-<toolChain errorParsers="" id="cdt.managedbuild.toolchain.gnu.macosx.exe.debug.2012337279" name="MacOSX GCC" superClass="cdt.managedbuild.toolchain.gnu.macosx.exe.debug" unusedChildren="">
+<folderInfo id="cdt.managedbuild.config.gnu.macosx.exe.debug.277492429.643967003" name="/" resourcePath="xbmc/cores/DllLoader">
+<toolChain errorParsers="" id="cdt.managedbuild.toolchain.gnu.macosx.exe.debug.1734765344" name="MacOSX GCC" superClass="cdt.managedbuild.toolchain.gnu.macosx.exe.debug" unusedChildren="">
 <targetPlatform id="cdt.managedbuild.target.gnu.platform.macosx.exe.debug" name="Debug Platform" superClass="cdt.managedbuild.target.gnu.platform.macosx.exe.debug"/>
-<tool id="cdt.managedbuild.tool.macosx.c.linker.macosx.exe.debug.1316166915" name="MacOS X C Linker" superClass="cdt.managedbuild.tool.macosx.c.linker.macosx.exe.debug.1750093173"/>
-<tool id="cdt.managedbuild.tool.macosx.cpp.linker.macosx.exe.debug.896232824" name="MacOS X C++ Linker" superClass="cdt.managedbuild.tool.macosx.cpp.linker.macosx.exe.debug.835214875"/>
-<tool errorParsers="org.eclipse.cdt.core.GASErrorParser" id="cdt.managedbuild.tool.gnu.assembler.macosx.exe.debug.1669692608" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.macosx.exe.debug.1794289768">
-<inputType id="cdt.managedbuild.tool.gnu.assembler.input.2031504877" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
+<tool id="cdt.managedbuild.tool.macosx.c.linker.macosx.exe.debug.1979556041" name="MacOS X C Linker" superClass="cdt.managedbuild.tool.macosx.c.linker.macosx.exe.debug.1750093173"/>
+<tool id="cdt.managedbuild.tool.macosx.cpp.linker.macosx.exe.debug.986787272" name="MacOS X C++ Linker" superClass="cdt.managedbuild.tool.macosx.cpp.linker.macosx.exe.debug.835214875"/>
+<tool errorParsers="org.eclipse.cdt.core.GASErrorParser" id="cdt.managedbuild.tool.gnu.assembler.macosx.exe.debug.833592423" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.macosx.exe.debug.1794289768">
+<inputType id="cdt.managedbuild.tool.gnu.assembler.input.1164190729" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
 </tool>
-<tool id="cdt.managedbuild.tool.gnu.archiver.macosx.base.1215299429" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.macosx.base.1308334469"/>
-<tool errorParsers="org.eclipse.cdt.core.GCCErrorParser" id="cdt.managedbuild.tool.gnu.cpp.compiler.macosx.exe.debug.919985893" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.macosx.exe.debug.65441666">
-<option id="gnu.cpp.compilermacosx.exe.debug.option.optimization.level.55887864" name="Optimization Level" superClass="gnu.cpp.compilermacosx.exe.debug.option.optimization.level" value="gnu.cpp.compiler.optimization.level.none" valueType="enumerated"/>
-<option id="gnu.cpp.compiler.macosx.exe.debug.option.debugging.level.794874135" name="Debug Level" superClass="gnu.cpp.compiler.macosx.exe.debug.option.debugging.level" value="gnu.cpp.compiler.debugging.level.max" valueType="enumerated"/>
-<option id="gnu.cpp.compiler.option.include.paths.1095698845" name="Include paths (-I)" superClass="gnu.cpp.compiler.option.include.paths" valueType="includePath">
-<listOptionValue builtIn="false" value="&quot;${workspace_loc:/XBMC/xbmc/lib/libportaudio}&quot;"/>
-<listOptionValue builtIn="false" value="&quot;${workspace_loc:/XBMC/xbmc/lib/libUPnP/Platinum/Source/Devices/MediaServer}&quot;"/>
-<listOptionValue builtIn="false" value="&quot;${workspace_loc:/XBMC/xbmc/lib/libUPnP/Platinum/Source/Devices/MediaRenderer}&quot;"/>
-<listOptionValue builtIn="false" value="&quot;${workspace_loc:/XBMC/xbmc/lib/libUPnP/Platinum/Source/Core/}&quot;"/>
-<listOptionValue builtIn="false" value="&quot;${workspace_loc:/XBMC/xbmc/lib/libUPnP/Platinum/ThirdParty/Neptune/Source/Core/}&quot;"/>
-<listOptionValue builtIn="false" value="&quot;${workspace_loc:/XBMC/xbmc/utils}&quot;"/>
-<listOptionValue builtIn="false" value="&quot;${workspace_loc:/XBMC/xbmc/cores}&quot;"/>
-<listOptionValue builtIn="false" value="&quot;${workspace_loc:/XBMC/xbmc/ffmpeg}&quot;"/>
-<listOptionValue builtIn="false" value="&quot;${workspace_loc:/XBMC/xbmc/cores/dvdplayer}&quot;"/>
-<listOptionValue builtIn="false" value="&quot;${workspace_loc:/XBMC/xbmc}&quot;"/>
-<listOptionValue builtIn="false" value="&quot;${workspace_loc:/XBMC/xbmc/linux}&quot;"/>
-<listOptionValue builtIn="false" value="&quot;${workspace_loc:/XBMC/guilib}&quot;"/>
-<listOptionValue builtIn="false" value="/opt/local/include"/>
-<listOptionValue builtIn="false" value="/opt/local/include/freetype2"/>
+<tool id="cdt.managedbuild.tool.gnu.archiver.macosx.base.1942199706" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.macosx.base.1308334469"/>
+<tool errorParsers="org.eclipse.cdt.core.GCCErrorParser" id="cdt.managedbuild.tool.gnu.cpp.compiler.macosx.exe.debug.591922786" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.macosx.exe.debug.65441666">
+<option id="gnu.cpp.compilermacosx.exe.debug.option.optimization.level.1240764958" name="Optimization Level" superClass="gnu.cpp.compilermacosx.exe.debug.option.optimization.level" value="gnu.cpp.compiler.optimization.level.none" valueType="enumerated"/>
+<option id="gnu.cpp.compiler.macosx.exe.debug.option.debugging.level.83648616" name="Debug Level" superClass="gnu.cpp.compiler.macosx.exe.debug.option.debugging.level" value="gnu.cpp.compiler.debugging.level.max" valueType="enumerated"/>
+<option id="gnu.cpp.compiler.option.preprocessor.def.684325808" name="Defined symbols (-D)" superClass="gnu.cpp.compiler.option.preprocessor.def" valueType="definedSymbols">
+<listOptionValue builtIn="false" value="_DEBUG"/>
+<listOptionValue builtIn="false" value="API_DEBUG"/>
+<listOptionValue builtIn="false" value="_LINUX"/>
+<listOptionValue builtIn="false" value="_REENTRANT"/>
+<listOptionValue builtIn="false" value="_FILE_DEFINED"/>
+<listOptionValue builtIn="false" value="_FILE_OFFSET_BITS=64"/>
+<listOptionValue builtIn="false" value="_LARGEFILE64_SOURCE"/>
+<listOptionValue builtIn="false" value="HAS_SDL_JOYSTICK"/>
 </option>
-<inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.1898344904" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
+<inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.2045638600" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
 </tool>
-<tool errorParsers="org.eclipse.cdt.core.GCCErrorParser" id="cdt.managedbuild.tool.gnu.c.compiler.macosx.exe.debug.794980876" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.macosx.exe.debug.2086201526">
-<option defaultValue="gnu.c.optimization.level.none" id="gnu.c.compiler.macosx.exe.debug.option.optimization.level.953270789" name="Optimization Level" superClass="gnu.c.compiler.macosx.exe.debug.option.optimization.level" valueType="enumerated"/>
-<option id="gnu.c.compiler.macosx.exe.debug.option.debugging.level.1774148998" name="Debug Level" superClass="gnu.c.compiler.macosx.exe.debug.option.debugging.level" value="gnu.c.debugging.level.max" valueType="enumerated"/>
-<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.1429314286" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
+<tool errorParsers="org.eclipse.cdt.core.GCCErrorParser" id="cdt.managedbuild.tool.gnu.c.compiler.macosx.exe.debug.907902063" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.macosx.exe.debug.2086201526">
+<option defaultValue="gnu.c.optimization.level.none" id="gnu.c.compiler.macosx.exe.debug.option.optimization.level.124736836" name="Optimization Level" superClass="gnu.c.compiler.macosx.exe.debug.option.optimization.level" valueType="enumerated"/>
+<option id="gnu.c.compiler.macosx.exe.debug.option.debugging.level.1632737144" name="Debug Level" superClass="gnu.c.compiler.macosx.exe.debug.option.debugging.level" value="gnu.c.debugging.level.max" valueType="enumerated"/>
+<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.1699992470" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
 </tool>
 </toolChain>
 </folderInfo>
-<folderInfo id="cdt.managedbuild.config.gnu.macosx.exe.debug.277492429.170851026" name="/" resourcePath="xbmc/lib/UnrarXLib">
-<toolChain errorParsers="" id="cdt.managedbuild.toolchain.gnu.macosx.exe.debug.1271468311" name="MacOSX GCC" superClass="cdt.managedbuild.toolchain.gnu.macosx.exe.debug" unusedChildren="">
+<folderInfo id="cdt.managedbuild.config.gnu.macosx.exe.debug.277492429.919238836" name="/" resourcePath="xbmc/cores/DllLoader/exports">
+<toolChain errorParsers="" id="cdt.managedbuild.toolchain.gnu.macosx.exe.debug.1237365591" name="MacOSX GCC" superClass="cdt.managedbuild.toolchain.gnu.macosx.exe.debug" unusedChildren="">
 <targetPlatform id="cdt.managedbuild.target.gnu.platform.macosx.exe.debug" name="Debug Platform" superClass="cdt.managedbuild.target.gnu.platform.macosx.exe.debug"/>
-<tool id="cdt.managedbuild.tool.macosx.c.linker.macosx.exe.debug.461961223" name="MacOS X C Linker" superClass="cdt.managedbuild.tool.macosx.c.linker.macosx.exe.debug.1750093173"/>
-<tool id="cdt.managedbuild.tool.macosx.cpp.linker.macosx.exe.debug.464820511" name="MacOS X C++ Linker" superClass="cdt.managedbuild.tool.macosx.cpp.linker.macosx.exe.debug.835214875"/>
-<tool errorParsers="org.eclipse.cdt.core.GASErrorParser" id="cdt.managedbuild.tool.gnu.assembler.macosx.exe.debug.2046303719" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.macosx.exe.debug.1794289768">
-<inputType id="cdt.managedbuild.tool.gnu.assembler.input.1967010553" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
+<tool id="cdt.managedbuild.tool.macosx.c.linker.macosx.exe.debug.1047209358" name="MacOS X C Linker" superClass="cdt.managedbuild.tool.macosx.c.linker.macosx.exe.debug.1979556041"/>
+<tool id="cdt.managedbuild.tool.macosx.cpp.linker.macosx.exe.debug.288617395" name="MacOS X C++ Linker" superClass="cdt.managedbuild.tool.macosx.cpp.linker.macosx.exe.debug.986787272"/>
+<tool errorParsers="org.eclipse.cdt.core.GASErrorParser" id="cdt.managedbuild.tool.gnu.assembler.macosx.exe.debug.1068019392" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.macosx.exe.debug.833592423">
+<inputType id="cdt.managedbuild.tool.gnu.assembler.input.713501646" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
 </tool>
-<tool id="cdt.managedbuild.tool.gnu.archiver.macosx.base.991037695" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.macosx.base.1308334469"/>
-<tool errorParsers="org.eclipse.cdt.core.GCCErrorParser" id="cdt.managedbuild.tool.gnu.cpp.compiler.macosx.exe.debug.864504005" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.macosx.exe.debug.65441666">
-<option id="gnu.cpp.compilermacosx.exe.debug.option.optimization.level.1929724833" name="Optimization Level" superClass="gnu.cpp.compilermacosx.exe.debug.option.optimization.level" value="gnu.cpp.compiler.optimization.level.none" valueType="enumerated"/>
-<option id="gnu.cpp.compiler.macosx.exe.debug.option.debugging.level.1437239494" name="Debug Level" superClass="gnu.cpp.compiler.macosx.exe.debug.option.debugging.level" value="gnu.cpp.compiler.debugging.level.max" valueType="enumerated"/>
-<option id="gnu.cpp.compiler.option.preprocessor.def.930016596" name="Defined symbols (-D)" superClass="gnu.cpp.compiler.option.preprocessor.def" valueType="definedSymbols">
+<tool id="cdt.managedbuild.tool.gnu.archiver.macosx.base.366482976" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.macosx.base.1942199706"/>
+<tool errorParsers="org.eclipse.cdt.core.GCCErrorParser" id="cdt.managedbuild.tool.gnu.cpp.compiler.macosx.exe.debug.1841251975" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.macosx.exe.debug.591922786">
+<option id="gnu.cpp.compilermacosx.exe.debug.option.optimization.level.267213578" name="Optimization Level" superClass="gnu.cpp.compilermacosx.exe.debug.option.optimization.level" value="gnu.cpp.compiler.optimization.level.none" valueType="enumerated"/>
+<option id="gnu.cpp.compiler.macosx.exe.debug.option.debugging.level.1089716219" name="Debug Level" superClass="gnu.cpp.compiler.macosx.exe.debug.option.debugging.level" value="gnu.cpp.compiler.debugging.level.max" valueType="enumerated"/>
+<option id="gnu.cpp.compiler.option.preprocessor.def.1815424215" name="Defined symbols (-D)" superClass="gnu.cpp.compiler.option.preprocessor.def" valueType="definedSymbols">
 <listOptionValue builtIn="false" value="_DEBUG"/>
-<listOptionValue builtIn="false" value="SILENT"/>
 <listOptionValue builtIn="false" value="_LINUX"/>
 <listOptionValue builtIn="false" value="_REENTRANT"/>
 <listOptionValue builtIn="false" value="_FILE_DEFINED"/>
 <listOptionValue builtIn="false" value="_LARGEFILE64_SOURCE"/>
 <listOptionValue builtIn="false" value="HAS_SDL_JOYSTICK"/>
 </option>
-<inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.1310329132" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
+<inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.1642113994" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
 </tool>
-<tool errorParsers="org.eclipse.cdt.core.GCCErrorParser" id="cdt.managedbuild.tool.gnu.c.compiler.macosx.exe.debug.1727159323" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.macosx.exe.debug.2086201526">
-<option defaultValue="gnu.c.optimization.level.none" id="gnu.c.compiler.macosx.exe.debug.option.optimization.level.1047846560" name="Optimization Level" superClass="gnu.c.compiler.macosx.exe.debug.option.optimization.level" valueType="enumerated"/>
-<option id="gnu.c.compiler.macosx.exe.debug.option.debugging.level.1754809956" name="Debug Level" superClass="gnu.c.compiler.macosx.exe.debug.option.debugging.level" value="gnu.c.debugging.level.max" valueType="enumerated"/>
-<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.1950409522" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
+<tool errorParsers="org.eclipse.cdt.core.GCCErrorParser" id="cdt.managedbuild.tool.gnu.c.compiler.macosx.exe.debug.17570680" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.macosx.exe.debug.907902063">
+<option defaultValue="gnu.c.optimization.level.none" id="gnu.c.compiler.macosx.exe.debug.option.optimization.level.1073864500" name="Optimization Level" superClass="gnu.c.compiler.macosx.exe.debug.option.optimization.level" valueType="enumerated"/>
+<option id="gnu.c.compiler.macosx.exe.debug.option.debugging.level.943491841" name="Debug Level" superClass="gnu.c.compiler.macosx.exe.debug.option.debugging.level" value="gnu.c.debugging.level.max" valueType="enumerated"/>
+<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.1349353172" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
 </tool>
 </toolChain>
 </folderInfo>
 <storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="cdt.managedbuild.config.macosx.exe.release.2032528784" moduleId="org.eclipse.cdt.core.settings" name="Release">
 <externalSettings/>
 <extensions>
-<extension id="org.eclipse.cdt.core.MachO" point="org.eclipse.cdt.core.BinaryParser"/>
+<extension id="org.eclipse.cdt.core.MachO64" point="org.eclipse.cdt.core.BinaryParser"/>
 <extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
 <extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
 <extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
 <storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="cdt.managedbuild.config.gnu.macosx.exe.debug.277492429.675625890" moduleId="org.eclipse.cdt.core.settings" name="OSX Debug">
 <externalSettings/>
 <extensions>
-<extension id="org.eclipse.cdt.core.MachO" point="org.eclipse.cdt.core.BinaryParser"/>
+<extension id="org.eclipse.cdt.core.MachO64" point="org.eclipse.cdt.core.BinaryParser"/>
 <extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
 <extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
 <extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
index 4eb57c5..b59360d 100644 (file)
@@ -48,10 +48,11 @@ config.log
 /Makefile
 /Makefile.include
 /aclocal.m4
-/autom4te.cache/
+autom4te.cache
 /build-aux/config.guess
 /build-aux/config.sub
 /build-aux/install-sh
+/build-aux/ltmain.sh
 /build-aux/missing
 /autotools
 /BUILD
@@ -86,6 +87,7 @@ config.log
 /addons/visualization.waveform/Waveform_win32.vis
 /addons/script.module.pil/
 /addons/script.module.pysqlite/
+/addons/pvr.*/*.pvr
 
 # /guilib/
 /guilib/Makefile
@@ -106,6 +108,11 @@ config.log
 
 # /guilib/tinyXML/
 
+# /lib/addons/
+/lib/addons/library.xbmc.addon/Makefile
+/lib/addons/library.xbmc.gui/Makefile
+/lib/addons/library.xbmc.pvr/Makefile
+
 # /lib/cpluff/
 /lib/cpluff/ABOUT-NLS
 /lib/cpluff/aclocal.m4
@@ -167,6 +174,11 @@ config.log
 /lib/libass/libass/Makefile.in
 /lib/libass/libtool
 /lib/libass/ltmain.sh
+/lib/libass/m4/libtool.m4
+/lib/libass/m4/ltoptions.m4
+/lib/libass/m4/ltsugar.m4
+/lib/libass/m4/ltversion.m4
+/lib/libass/m4/lt~obsolete.m4
 /lib/libass/missing
 /lib/libass/shave/libtool.m4
 /lib/libass/shave/ltoptions.m4
@@ -245,6 +257,9 @@ config.log
 /lib/pcre/libpcre/Debug
 /lib/pcre/libpcre/Release
 
+# /libtool
+/libtool
+
 # /project
 /project/obj
 /project/output
@@ -437,6 +452,9 @@ config.log
 # /xbmc/linux/
 /xbmc/linux/Makefile
 
+# /xbmc/cdrip
+/xbmc/cdrip/Makefile
+
 # /xbmc/cores/AudioRenderers/
 /xbmc/cores/AudioRenderers/Makefile
 
@@ -533,6 +551,7 @@ config.log
 /xbmc/cores/dvdplayer/Codecs/libdvd/libdvdcss/aclocal.m4
 /xbmc/cores/dvdplayer/Codecs/libdvd/libdvdcss/autom4te.cache/
 /xbmc/cores/dvdplayer/Codecs/libdvd/libdvdcss/config.h.in~
+/xbmc/cores/dvdplayer/Codecs/libdvd/libdvdcss/config.h.in
 /xbmc/cores/dvdplayer/Codecs/libdvd/libdvdcss/configure
 /xbmc/cores/dvdplayer/Codecs/libdvd/libdvdcss/doc/Makefile.in
 /xbmc/cores/dvdplayer/Codecs/libdvd/libdvdcss/src/Makefile.in
@@ -759,6 +778,9 @@ config.log
 /xbmc/cores/paplayer/ADPCMCodec/Debug
 /xbmc/cores/paplayer/ADPCMCodec/Release
 
+# /xbmc/cores/paplayer/asap/
+/xbmc/cores/paplayer/asap/Makefile
+
 # /xbmc/cores/paplayer/FLACCodec/flac-1.2.1/
 /xbmc/cores/paplayer/FLACCodec/flac-1.2.1/Makefilee
 /xbmc/cores/paplayer/FLACCodec/flac-1.2.1/config.log
@@ -1158,6 +1180,14 @@ config.log
 /xbmc/lib/libXDAAP/libXDAAP_win32/Debug
 /xbmc/lib/libXDAAP/libXDAAP_win32/Release
 
+# /xbmc/osx/
+/xbmc/osx/Makefile
+
+# /xbmc/pvrclients
+/xbmc/pvrclients/Makefile.include
+/xbmc/pvrclients/*/.dependencies
+/xbmc/pvrclients/*/Makefile
+
 /xbmc/screensavers/Makefile
 
 /xbmc/screensavers/rsxs-0.9/Makefile
@@ -1214,3 +1244,21 @@ config.log
 /xbmc/visualizations/XBMCProjectM/libprojectM/config.inp
 
 /xbmc/win32/git_rev.h
+
+/addons/library.xbmc.addon/libXBMC_addon.dll
+/addons/library.xbmc.addon/libXBMC_addon.lib
+/addons/library.xbmc.gui/libXBMC_gui.dll
+/addons/library.xbmc.gui/libXBMC_gui.lib
+/addons/library.xbmc.pvr/libXBMC_pvr.dll
+/addons/library.xbmc.pvr/libXBMC_pvr.lib
+
+/addons/pvr.team-mediaportal.tvserver/XBMC_MPTV.lib
+/addons/pvr.hts/XBMC_Tvheadend_win32.lib
+/addons/pvr.vdr.streamdev/XBMC_VDR_win32.lib
+
+/xbmc/pvrclients/MediaPortal/project/VS2010Express/Debug
+/xbmc/pvrclients/MediaPortal/project/VS2010Express/Release
+/xbmc/pvrclients/tvheadend/project/VS2010Express/Debug
+/xbmc/pvrclients/tvheadend/project/VS2010Express/Release
+/xbmc/pvrclients/vdr-streamdev/project/VS2010Express/Debug
+/xbmc/pvrclients/vdr-streamdev/project/VS2010Express/Release
index 3eeb326..eb209b0 100644 (file)
--- a/.project
+++ b/.project
@@ -11,7 +11,7 @@
                        <arguments>
                                <dictionary>
                                        <key>?children?</key>
-                                       <value>?name?=outputEntries\|?children?=?name?=entry\\\\\\\\\\\\\\\|\\\\\\\|\||</value>
+                                       <value>?name?=outputEntries\|?children?=?name?=entry\\\\\\\|\\\|\||</value>
                                </dictionary>
                                <dictionary>
                                        <key>?name?</key>
                                </dictionary>
                                <dictionary>
                                        <key>org.eclipse.cdt.make.core.buildArguments</key>
-                                       <value>-configuration Debug</value>
+                                       <value></value>
                                </dictionary>
                                <dictionary>
                                        <key>org.eclipse.cdt.make.core.buildCommand</key>
-                                       <value>xcodebuild</value>
+                                       <value>make</value>
                                </dictionary>
                                <dictionary>
                                        <key>org.eclipse.cdt.make.core.buildLocation</key>
index b36a01c..e3a564b 100644 (file)
@@ -28,6 +28,7 @@ BIN_DIRS=     \
        xbmc/FileSystem/MusicDatabaseDirectory \
        xbmc/FileSystem/VideoDatabaseDirectory \
        xbmc/karaoke \
+       xbmc/addons \
        xbmc/lib/cmyth/libcmyth \
        xbmc/lib/cmyth/librefmem \
        xbmc/lib/libhts \
@@ -45,6 +46,8 @@ BIN_DIRS=     \
        xbmc/lib/libsquish \
        xbmc/visualizations \
        xbmc/screensavers \
+       xbmc/pvr \
+       xbmc/pvrclients \
        xbmc/utils \
        xbmc/settings \
        xbmc/linux \
@@ -118,6 +121,18 @@ ifneq (@BUILD_GOOM@,1)
 VIS_DIRS+=xbmc/visualizations/Goom
 endif
 
+PVR_DIRS=\
+       xbmc/pvrclients/MediaPortal \
+       xbmc/pvrclients/mythtv \
+       xbmc/pvrclients/vdr-streamdev \
+       xbmc/pvrclients/vdr-vnsi \
+       xbmc/pvrclients/tvheadend
+
+LIBADDON_DIRS=\
+       lib/addons/library.xbmc.addon \
+       lib/addons/library.xbmc.pvr \
+       lib/addons/library.xbmc.gui \
+
 CONFLUENCE_MEDIA=addons/skin.confluence/media
 
 SKIN_DIRS=\
@@ -127,7 +142,8 @@ LIVE_DIRS=\
        tools/XBMCLive
 
 DIRS= $(BIN_DIRS) $(EC_DIRS) $(XBMCTEX_DIRS) $(DVDPCODECS_DIRS) $(PAPCODECS_DIRS) \
-       $(LIB_DIRS) $(SS_DIRS) $(VIS_DIRS) $(SKIN_DIRS) $(LIVE_DIRS)
+       $(LIB_DIRS) $(SS_DIRS) $(VIS_DIRS) $(PVR_DIRS) $(LIBADDON_DIRS) $(SKIN_DIRS) \
+       $(LIVE_DIRS)
 
 LIBS=@LIBS@
 CFLAGS=@CFLAGS@
@@ -148,8 +164,8 @@ all : Makefile externals xbmc.bin xbmc-xrandr skins
 
 include Makefile.include
 
-.PHONY : dllloader exports visualizations screensavers eventclients papcodecs \
-       dvdpcodecs imagelib codecs externals force skins
+.PHONY : dllloader exports pvrclients visualizations screensavers eventclients \
+       papcodecs dvdpcodecs imagelib codecs externals force libaddon skins
 
 # hack targets to keep build system up to date
 Makefile : config.status $(addsuffix .in, $(AUTOGENERATED_MAKEFILES))
@@ -268,6 +284,10 @@ xbmc/settings/settings.a: force
        $(MAKE) -C xbmc/settings
 xbmc/utils/utils.a: force
        $(MAKE) -C xbmc/utils
+xbmc/pvr/pvr.a: force
+       $(MAKE) -C xbmc/pvr
+xbmc/pvrclients/pvrclient.a: force
+       $(MAKE) -C xbmc/pvrclients
 xbmc/osx/osx.a: force
        $(MAKE) -C xbmc/osx
 xbmc/lib/libapetag/.libs/libapetag.a: force
@@ -317,10 +337,20 @@ ifeq ($(or $(findstring powerpc-linux,$(ARCH)),$(findstring powerpc64-linux,$(AR
 endif
 endif
 endif
+pvrclients: exports
+       $(MAKE) -C xbmc/pvrclients/MediaPortal
+       $(MAKE) -C xbmc/pvrclients/mythtv
+       $(MAKE) -C xbmc/pvrclients/vdr-streamdev
+       $(MAKE) -C xbmc/pvrclients/vdr-vnsi
+       $(MAKE) -C xbmc/pvrclients/tvheadend
 screensavers: exports
 ifneq (arm, $(ARCH))
        $(MAKE) -C xbmc/screensavers/rsxs-0.9/xbmc
 endif
+libaddon: exports
+       $(MAKE) -C lib/addons/library.xbmc.addon
+       $(MAKE) -C lib/addons/library.xbmc.gui
+       $(MAKE) -C lib/addons/library.xbmc.pvr
 libpython: dllloader
        $(MAKE) -C xbmc/lib/libPython
        $(MAKE) -C xbmc/lib/libPython/xbmcmodule
@@ -374,10 +404,10 @@ libs: libhdhomerun libid3tag imagelib libexif python system/libcpluff-$(ARCH).so
 else
 libs: libhdhomerun libid3tag imagelib libexif python system/libcpluff-$(ARCH).so
 endif
-externals: codecs libs python visualizations screensavers
+externals: libaddon codecs libs python pvrclients visualizations screensavers
 
 xcode_depends: \
-       codecs libs python visualizations screensavers eventclients skins \
+       codecs libs python pvrclients visualizations screensavers eventclients skins \
        xbmc/lib/libsquish/libsquish-@ARCH@.a \
        xbmc/lib/libapetag/.libs/libapetag.a \
        xbmc/lib/cmyth/libcmyth/libcmyth.a \
@@ -462,6 +492,7 @@ DYNOBJSXBMC= \
        xbmc/lib/cmyth/librefmem/librefmem.a \
        xbmc/linux/linux.a \
        xbmc/utils/utils.a \
+       xbmc/pvr/pvr.a \
        xbmc/cores/DllLoader/exports/util/exports_utils.a \
        xbmc/cores/DllLoader/exports/exports.a
 
@@ -517,7 +548,7 @@ endif
 
 install-arch:
        @# Arch dependent files
-       @find system addons -regextype posix-extended -type f -not -iregex ".*svn.*|.*script\.module\..*" -iregex ".*$(ARCH).*|.*\.vis|.*\.xbs|.*python.*\.zip" -exec install -D "{}" $(DESTDIR)$(libdir)/xbmc/"{}" \; -printf " -- %-75.75f\r"
+       @find system addons -regextype posix-extended -type f -not -iregex ".*svn.*|.*script\.module\..*" -iregex ".*$(ARCH).*|.*\.pvr|.*\.vis|.*\.xbs|.*\.so|.*python.*\.zip" -exec install -D "{}" $(DESTDIR)$(libdir)/xbmc/"{}" \; -printf " -- %-75.75f\r"
        @cp -r addons/script.module.pil $(DESTDIR)$(libdir)/xbmc/addons/
        @cp -r addons/script.module.pysqlite $(DESTDIR)$(libdir)/xbmc/addons/
 
@@ -534,7 +565,7 @@ install-datas: install-scripts
        @echo "Done!"
        @echo "Copying system files to $(DESTDIR)$(datarootdir)/xbmc"
        @# Arch independent files
-       @find addons language media sounds userdata system -regextype posix-extended -type f -not -iregex ".*script\.module\..*|.*$(ARCH).*|.*\.vis|.*\.xbs|.*svn.*|.*\.so|.*\.dll|.*\.pyd|.*python.*\.zip" -exec install -D -m 0644 "{}" $(DESTDIR)$(datarootdir)/xbmc/"{}" \; -printf " -- %-75.75f\r"
+       @find addons language media sounds userdata system -regextype posix-extended -type f -not -iregex ".*script\.module\..*|.*$(ARCH).*|.*\.pvr|.*\.vis|.*\.xbs|.*svn.*|.*\.so|.*\.dll|.*\.pyd|.*python.*\.zip" -exec install -D -m 0644 "{}" $(DESTDIR)$(datarootdir)/xbmc/"{}" \; -printf " -- %-75.75f\r"
        @# Icons and links
        @mkdir -p $(DESTDIR)$(datarootdir)/applications
        @cp -a tools/Linux/xbmc.desktop $(DESTDIR)$(datarootdir)/applications/
@@ -556,6 +587,8 @@ uninstall:
        @rm -rf $(DESTDIR)$(datarootdir)/xbmc $(DESTDIR)$(bindir)/xbmc
        @rm -rf $(DESTDIR)$(bindir)/xbmc-standalone
        @rm -rf $(DESTDIR)$(datarootdir)/xsessions/XBMC.desktop
+       @rm -rf $(libdir)/libXBMC_*
+       @rm -rf $(prefix)/include/xbmc
        @echo "Done!"
 
 reallyclean:
@@ -602,10 +635,12 @@ clean-screensavers:
        for d in $(SS_DIRS); do if test -f $$d/Makefile; then $(MAKE) -C $$d clean; fi; done
 clean-visualisations:
        for d in $(VIS_DIRS); do if test -f $$d/Makefile; then $(MAKE) -C $$d clean; fi; done
+clean-pvrclients:
+       for d in $(PVR_DIRS); do if test -f $$d/Makefile; then $(MAKE) -C $$d clean; fi; done
+clean-libaddons:
+       for d in $(LIBADDON_DIRS); do if test -f $$d/Makefile; then $(MAKE) -C $$d clean; fi; done
 
 clean-codecs: clean-dvdpcodecs clean-papcodecs
 
 clean-externals: clean-codecs clean-eventclients clean-xbmctex clean-libs \
-       clean-screensavers clean-visualisations
-
-
+       clean-pvrclients clean-screensavers clean-visualisations clean-libaddons
index 2b99555..05e30d6 100644 (file)
                810C9FA90D67D1FB0095F5DD /* MythDirectory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 810C9FA50D67D1FB0095F5DD /* MythDirectory.cpp */; };
                810C9FAA0D67D1FB0095F5DD /* MythFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 810C9FA70D67D1FB0095F5DD /* MythFile.cpp */; };
                815EE6350E17F1DC009FBE3C /* DVDInputStreamRTMP.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 815EE6330E17F1DC009FBE3C /* DVDInputStreamRTMP.cpp */; };
+               832F2C46120D8B5E00026B38 /* PVRManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 832F2C44120D8B5E00026B38 /* PVRManager.cpp */; };
+               832F2C4F120D8B7F00026B38 /* PVRChannels.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 832F2C47120D8B7F00026B38 /* PVRChannels.cpp */; };
+               832F2C50120D8B7F00026B38 /* PVREpg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 832F2C49120D8B7F00026B38 /* PVREpg.cpp */; };
+               832F2C51120D8B7F00026B38 /* PVRRecordings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 832F2C4B120D8B7F00026B38 /* PVRRecordings.cpp */; };
+               832F2C52120D8B7F00026B38 /* PVRTimers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 832F2C4D120D8B7F00026B38 /* PVRTimers.cpp */; };
+               832F2C69120D8BA900026B38 /* GUIDialogPVRChannelManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 832F2C53120D8BA800026B38 /* GUIDialogPVRChannelManager.cpp */; };
+               832F2C6A120D8BA900026B38 /* GUIDialogPVRChannelsOSD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 832F2C55120D8BA800026B38 /* GUIDialogPVRChannelsOSD.cpp */; };
+               832F2C6B120D8BA900026B38 /* GUIDialogPVRCutterOSD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 832F2C57120D8BA800026B38 /* GUIDialogPVRCutterOSD.cpp */; };
+               832F2C6C120D8BA900026B38 /* GUIDialogPVRDirectorOSD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 832F2C59120D8BA800026B38 /* GUIDialogPVRDirectorOSD.cpp */; };
+               832F2C6D120D8BA900026B38 /* GUIDialogPVRGroupManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 832F2C5B120D8BA800026B38 /* GUIDialogPVRGroupManager.cpp */; };
+               832F2C6E120D8BA900026B38 /* GUIDialogPVRGuideInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 832F2C5D120D8BA900026B38 /* GUIDialogPVRGuideInfo.cpp */; };
+               832F2C6F120D8BA900026B38 /* GUIDialogPVRGuideOSD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 832F2C5F120D8BA900026B38 /* GUIDialogPVRGuideOSD.cpp */; };
+               832F2C70120D8BA900026B38 /* GUIDialogPVRGuideSearch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 832F2C61120D8BA900026B38 /* GUIDialogPVRGuideSearch.cpp */; };
+               832F2C71120D8BA900026B38 /* GUIDialogPVRRecordingInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 832F2C63120D8BA900026B38 /* GUIDialogPVRRecordingInfo.cpp */; };
+               832F2C72120D8BA900026B38 /* GUIDialogPVRTimerSettings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 832F2C65120D8BA900026B38 /* GUIDialogPVRTimerSettings.cpp */; };
+               832F2C73120D8BA900026B38 /* GUIDialogPVRUpdateProgressBar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 832F2C67120D8BA900026B38 /* GUIDialogPVRUpdateProgressBar.cpp */; };
+               832F2C7E120D8C0300026B38 /* AddonHelpers_Addon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 832F2C76120D8C0300026B38 /* AddonHelpers_Addon.cpp */; };
+               832F2C7F120D8C0300026B38 /* AddonHelpers_GUI.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 832F2C78120D8C0300026B38 /* AddonHelpers_GUI.cpp */; };
+               832F2C80120D8C0300026B38 /* AddonHelpers_local.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 832F2C7A120D8C0300026B38 /* AddonHelpers_local.cpp */; };
+               832F2C81120D8C0300026B38 /* AddonHelpers_PVR.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 832F2C7C120D8C0300026B38 /* AddonHelpers_PVR.cpp */; };
+               832F2C84120D8C1000026B38 /* PVRClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 832F2C82120D8C0F00026B38 /* PVRClient.cpp */; };
+               832F2C87120D8C5300026B38 /* TVDatabase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 832F2C85120D8C5300026B38 /* TVDatabase.cpp */; };
+               832F2C8A120D8C9300026B38 /* GUIEPGGridContainer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 832F2C88120D8C9300026B38 /* GUIEPGGridContainer.cpp */; };
+               832F2C8F120D8CB500026B38 /* PVRDirectory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 832F2C8B120D8CB400026B38 /* PVRDirectory.cpp */; };
+               832F2C90120D8CB500026B38 /* PVRFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 832F2C8D120D8CB500026B38 /* PVRFile.cpp */; };
+               832F2C95120D8CF600026B38 /* TextSearch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 832F2C93120D8CF600026B38 /* TextSearch.cpp */; };
+               832F2C98120D8D0B00026B38 /* DVDDemuxPVRClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 832F2C96120D8D0B00026B38 /* DVDDemuxPVRClient.cpp */; };
+               832F2C9B120D8D3000026B38 /* GUIWindowTV.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 832F2C99120D8D3000026B38 /* GUIWindowTV.cpp */; };
+               832F2CA0120D8D5C00026B38 /* DVDInputStreamPVRManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 832F2C9E120D8D5C00026B38 /* DVDInputStreamPVRManager.cpp */; };
+               832F2CA3120D8D7600026B38 /* GUIViewStateTV.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 832F2CA1120D8D7600026B38 /* GUIViewStateTV.cpp */; };
                83A72B910FBC8DB000171871 /* CoreAudioRenderer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83A72B8E0FBC8DB000171871 /* CoreAudioRenderer.cpp */; };
                83A72B940FBC8DFF00171871 /* CoreAudio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83A72B920FBC8DFF00171871 /* CoreAudio.cpp */; };
                83A72B970FBC8E3B00171871 /* LockFree.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83A72B950FBC8E3B00171871 /* LockFree.cpp */; settings = {COMPILER_FLAGS = "-O0"; }; };
                184C472D1296BC6E0006DB3E /* Service.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Service.cpp; path = addons/Service.cpp; sourceTree = "<group>"; };
                184C472E1296BC6E0006DB3E /* Service.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Service.h; path = addons/Service.h; sourceTree = "<group>"; };
                18B49FF11152BFA5001AF8A6 /* Addon.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Addon.cpp; path = addons/Addon.cpp; sourceTree = "<group>"; };
-               18B49FF21152BFA5001AF8A6 /* Addon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Addon.h; path = addons/Addon.h; sourceTree = "<group>"; };
-               18B49FF31152BFA5001AF8A6 /* AddonDll.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AddonDll.h; path = addons/AddonDll.h; sourceTree = "<group>"; };
                18B49FF41152BFA5001AF8A6 /* AddonManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AddonManager.cpp; path = addons/AddonManager.cpp; sourceTree = "<group>"; };
                18B49FF51152BFA5001AF8A6 /* AddonManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AddonManager.h; path = addons/AddonManager.h; sourceTree = "<group>"; };
-               18B49FF61152BFA5001AF8A6 /* DllAddon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DllAddon.h; path = addons/DllAddon.h; sourceTree = "<group>"; };
                18B49FF71152BFA5001AF8A6 /* DllScreenSaver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DllScreenSaver.h; path = addons/DllScreenSaver.h; sourceTree = "<group>"; };
                18B49FF81152BFA5001AF8A6 /* DllVisualisation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DllVisualisation.h; path = addons/DllVisualisation.h; sourceTree = "<group>"; };
                18B49FF91152BFA5001AF8A6 /* fft.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = fft.cpp; path = addons/fft.cpp; sourceTree = "<group>"; };
                810CA0080D683DEF0095F5DD /* libSDL_mixer-x86-osx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libSDL_mixer-x86-osx.a"; path = "lib/libSDL-OSX/libSDL_mixer-x86-osx.a"; sourceTree = "<group>"; };
                815EE6330E17F1DC009FBE3C /* DVDInputStreamRTMP.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DVDInputStreamRTMP.cpp; sourceTree = "<group>"; };
                815EE6340E17F1DC009FBE3C /* DVDInputStreamRTMP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DVDInputStreamRTMP.h; sourceTree = "<group>"; };
+               832F2C44120D8B5E00026B38 /* PVRManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PVRManager.cpp; path = xbmc/PVRManager.cpp; sourceTree = SOURCE_ROOT; };
+               832F2C45120D8B5E00026B38 /* PVRManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PVRManager.h; path = xbmc/PVRManager.h; sourceTree = SOURCE_ROOT; };
+               832F2C47120D8B7F00026B38 /* PVRChannels.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PVRChannels.cpp; path = xbmc/utils/PVRChannels.cpp; sourceTree = "<group>"; };
+               832F2C48120D8B7F00026B38 /* PVRChannels.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PVRChannels.h; path = xbmc/utils/PVRChannels.h; sourceTree = "<group>"; };
+               832F2C49120D8B7F00026B38 /* PVREpg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PVREpg.cpp; path = xbmc/utils/PVREpg.cpp; sourceTree = "<group>"; };
+               832F2C4A120D8B7F00026B38 /* PVREpg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PVREpg.h; path = xbmc/utils/PVREpg.h; sourceTree = "<group>"; };
+               832F2C4B120D8B7F00026B38 /* PVRRecordings.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PVRRecordings.cpp; path = xbmc/utils/PVRRecordings.cpp; sourceTree = "<group>"; };
+               832F2C4C120D8B7F00026B38 /* PVRRecordings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PVRRecordings.h; path = xbmc/utils/PVRRecordings.h; sourceTree = "<group>"; };
+               832F2C4D120D8B7F00026B38 /* PVRTimers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PVRTimers.cpp; path = xbmc/utils/PVRTimers.cpp; sourceTree = "<group>"; };
+               832F2C4E120D8B7F00026B38 /* PVRTimers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PVRTimers.h; path = xbmc/utils/PVRTimers.h; sourceTree = "<group>"; };
+               832F2C53120D8BA800026B38 /* GUIDialogPVRChannelManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogPVRChannelManager.cpp; sourceTree = "<group>"; };
+               832F2C54120D8BA800026B38 /* GUIDialogPVRChannelManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogPVRChannelManager.h; sourceTree = "<group>"; };
+               832F2C55120D8BA800026B38 /* GUIDialogPVRChannelsOSD.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogPVRChannelsOSD.cpp; sourceTree = "<group>"; };
+               832F2C56120D8BA800026B38 /* GUIDialogPVRChannelsOSD.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogPVRChannelsOSD.h; sourceTree = "<group>"; };
+               832F2C57120D8BA800026B38 /* GUIDialogPVRCutterOSD.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogPVRCutterOSD.cpp; sourceTree = "<group>"; };
+               832F2C58120D8BA800026B38 /* GUIDialogPVRCutterOSD.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogPVRCutterOSD.h; sourceTree = "<group>"; };
+               832F2C59120D8BA800026B38 /* GUIDialogPVRDirectorOSD.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogPVRDirectorOSD.cpp; sourceTree = "<group>"; };
+               832F2C5A120D8BA800026B38 /* GUIDialogPVRDirectorOSD.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogPVRDirectorOSD.h; sourceTree = "<group>"; };
+               832F2C5B120D8BA800026B38 /* GUIDialogPVRGroupManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogPVRGroupManager.cpp; sourceTree = "<group>"; };
+               832F2C5C120D8BA900026B38 /* GUIDialogPVRGroupManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogPVRGroupManager.h; sourceTree = "<group>"; };
+               832F2C5D120D8BA900026B38 /* GUIDialogPVRGuideInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogPVRGuideInfo.cpp; sourceTree = "<group>"; };
+               832F2C5E120D8BA900026B38 /* GUIDialogPVRGuideInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogPVRGuideInfo.h; sourceTree = "<group>"; };
+               832F2C5F120D8BA900026B38 /* GUIDialogPVRGuideOSD.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogPVRGuideOSD.cpp; sourceTree = "<group>"; };
+               832F2C60120D8BA900026B38 /* GUIDialogPVRGuideOSD.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogPVRGuideOSD.h; sourceTree = "<group>"; };
+               832F2C61120D8BA900026B38 /* GUIDialogPVRGuideSearch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogPVRGuideSearch.cpp; sourceTree = "<group>"; };
+               832F2C62120D8BA900026B38 /* GUIDialogPVRGuideSearch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogPVRGuideSearch.h; sourceTree = "<group>"; };
+               832F2C63120D8BA900026B38 /* GUIDialogPVRRecordingInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogPVRRecordingInfo.cpp; sourceTree = "<group>"; };
+               832F2C64120D8BA900026B38 /* GUIDialogPVRRecordingInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogPVRRecordingInfo.h; sourceTree = "<group>"; };
+               832F2C65120D8BA900026B38 /* GUIDialogPVRTimerSettings.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogPVRTimerSettings.cpp; sourceTree = "<group>"; };
+               832F2C66120D8BA900026B38 /* GUIDialogPVRTimerSettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogPVRTimerSettings.h; sourceTree = "<group>"; };
+               832F2C67120D8BA900026B38 /* GUIDialogPVRUpdateProgressBar.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIDialogPVRUpdateProgressBar.cpp; sourceTree = "<group>"; };
+               832F2C68120D8BA900026B38 /* GUIDialogPVRUpdateProgressBar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIDialogPVRUpdateProgressBar.h; sourceTree = "<group>"; };
+               832F2C76120D8C0300026B38 /* AddonHelpers_Addon.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AddonHelpers_Addon.cpp; path = addons/AddonHelpers_Addon.cpp; sourceTree = "<group>"; };
+               832F2C77120D8C0300026B38 /* AddonHelpers_Addon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AddonHelpers_Addon.h; path = addons/AddonHelpers_Addon.h; sourceTree = "<group>"; };
+               832F2C78120D8C0300026B38 /* AddonHelpers_GUI.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AddonHelpers_GUI.cpp; path = addons/AddonHelpers_GUI.cpp; sourceTree = "<group>"; };
+               832F2C79120D8C0300026B38 /* AddonHelpers_GUI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AddonHelpers_GUI.h; path = addons/AddonHelpers_GUI.h; sourceTree = "<group>"; };
+               832F2C7A120D8C0300026B38 /* AddonHelpers_local.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AddonHelpers_local.cpp; path = addons/AddonHelpers_local.cpp; sourceTree = "<group>"; };
+               832F2C7B120D8C0300026B38 /* AddonHelpers_local.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AddonHelpers_local.h; path = addons/AddonHelpers_local.h; sourceTree = "<group>"; };
+               832F2C7C120D8C0300026B38 /* AddonHelpers_PVR.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AddonHelpers_PVR.cpp; path = addons/AddonHelpers_PVR.cpp; sourceTree = "<group>"; };
+               832F2C7D120D8C0300026B38 /* AddonHelpers_PVR.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AddonHelpers_PVR.h; path = addons/AddonHelpers_PVR.h; sourceTree = "<group>"; };
+               832F2C82120D8C0F00026B38 /* PVRClient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PVRClient.cpp; path = addons/PVRClient.cpp; sourceTree = "<group>"; };
+               832F2C83120D8C1000026B38 /* PVRClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PVRClient.h; path = addons/PVRClient.h; sourceTree = "<group>"; };
+               832F2C85120D8C5300026B38 /* TVDatabase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = TVDatabase.cpp; path = xbmc/TVDatabase.cpp; sourceTree = SOURCE_ROOT; };
+               832F2C86120D8C5300026B38 /* TVDatabase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TVDatabase.h; path = xbmc/TVDatabase.h; sourceTree = SOURCE_ROOT; };
+               832F2C88120D8C9300026B38 /* GUIEPGGridContainer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIEPGGridContainer.cpp; sourceTree = "<group>"; };
+               832F2C89120D8C9300026B38 /* GUIEPGGridContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIEPGGridContainer.h; sourceTree = "<group>"; };
+               832F2C8B120D8CB400026B38 /* PVRDirectory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PVRDirectory.cpp; path = xbmc/FileSystem/PVRDirectory.cpp; sourceTree = "<group>"; };
+               832F2C8C120D8CB500026B38 /* PVRDirectory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PVRDirectory.h; path = xbmc/FileSystem/PVRDirectory.h; sourceTree = "<group>"; };
+               832F2C8D120D8CB500026B38 /* PVRFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PVRFile.cpp; path = xbmc/FileSystem/PVRFile.cpp; sourceTree = "<group>"; };
+               832F2C8E120D8CB500026B38 /* PVRFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PVRFile.h; path = xbmc/FileSystem/PVRFile.h; sourceTree = "<group>"; };
+               832F2C93120D8CF600026B38 /* TextSearch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = TextSearch.cpp; path = xbmc/utils/TextSearch.cpp; sourceTree = SOURCE_ROOT; };
+               832F2C94120D8CF600026B38 /* TextSearch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TextSearch.h; path = xbmc/utils/TextSearch.h; sourceTree = SOURCE_ROOT; };
+               832F2C96120D8D0B00026B38 /* DVDDemuxPVRClient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DVDDemuxPVRClient.cpp; sourceTree = "<group>"; };
+               832F2C97120D8D0B00026B38 /* DVDDemuxPVRClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DVDDemuxPVRClient.h; sourceTree = "<group>"; };
+               832F2C99120D8D3000026B38 /* GUIWindowTV.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIWindowTV.cpp; sourceTree = "<group>"; };
+               832F2C9A120D8D3000026B38 /* GUIWindowTV.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIWindowTV.h; sourceTree = "<group>"; };
+               832F2C9E120D8D5C00026B38 /* DVDInputStreamPVRManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DVDInputStreamPVRManager.cpp; sourceTree = "<group>"; };
+               832F2C9F120D8D5C00026B38 /* DVDInputStreamPVRManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DVDInputStreamPVRManager.h; sourceTree = "<group>"; };
+               832F2CA1120D8D7600026B38 /* GUIViewStateTV.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GUIViewStateTV.cpp; sourceTree = "<group>"; };
+               832F2CA2120D8D7600026B38 /* GUIViewStateTV.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GUIViewStateTV.h; sourceTree = "<group>"; };
+               83840880116E243C009B5115 /* Addon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Addon.h; path = xbmc/addons/Addon.h; sourceTree = SOURCE_ROOT; };
+               83840881116E243C009B5115 /* AddonDll.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AddonDll.h; path = xbmc/addons/AddonDll.h; sourceTree = SOURCE_ROOT; };
+               8398EE72116F1297001227BB /* DllAddon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DllAddon.h; path = xbmc/addons/DllAddon.h; sourceTree = SOURCE_ROOT; };
+               8398EE73116F1297001227BB /* DllPVRClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DllPVRClient.h; path = xbmc/addons/DllPVRClient.h; sourceTree = SOURCE_ROOT; };
                83A72B8E0FBC8DB000171871 /* CoreAudioRenderer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CoreAudioRenderer.cpp; path = AudioRenderers/CoreAudioRenderer.cpp; sourceTree = "<group>"; };
                83A72B8F0FBC8DB000171871 /* CoreAudioRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CoreAudioRenderer.h; path = AudioRenderers/CoreAudioRenderer.h; sourceTree = "<group>"; };
                83A72B900FBC8DB000171871 /* IAudioRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = IAudioRenderer.h; path = AudioRenderers/IAudioRenderer.h; sourceTree = "<group>"; };
                                E38E238E0D2626E600618676 /* CoreAudio.framework */,
                                E38E238F0D2626E600618676 /* CoreServices.framework */,
                                F59879070FBAA0C3008EF4FB /* QuartzCore.framework */,
+                               832F2C47120D8B7F00026B38 /* PVRChannels.cpp */,
+                               832F2C48120D8B7F00026B38 /* PVRChannels.h */,
+                               832F2C8B120D8CB400026B38 /* PVRDirectory.cpp */,
+                               832F2C8C120D8CB500026B38 /* PVRDirectory.h */,
+                               832F2C8D120D8CB500026B38 /* PVRFile.cpp */,
+                               832F2C8E120D8CB500026B38 /* PVRFile.h */,
+                               832F2C49120D8B7F00026B38 /* PVREpg.cpp */,
+                               832F2C4A120D8B7F00026B38 /* PVREpg.h */,
+                               832F2C4B120D8B7F00026B38 /* PVRRecordings.cpp */,
+                               832F2C4C120D8B7F00026B38 /* PVRRecordings.h */,
+                               832F2C4D120D8B7F00026B38 /* PVRTimers.cpp */,
+                               832F2C4E120D8B7F00026B38 /* PVRTimers.h */,
                                E38E23900D2626E600618676 /* Foundation.framework */,
                                E38E23910D2626E600618676 /* OpenGL.framework */,
                                09AB6884FE841BABC02AAC07 /* CoreFoundation.framework */,
                                183FDF8811AF0B0500B81E9C /* PluginSource.cpp */,
                                183FDF8911AF0B0500B81E9C /* PluginSource.h */,
                                18B49FF11152BFA5001AF8A6 /* Addon.cpp */,
-                               18B49FF21152BFA5001AF8A6 /* Addon.h */,
-                               18B49FF31152BFA5001AF8A6 /* AddonDll.h */,
                                18B49FF41152BFA5001AF8A6 /* AddonManager.cpp */,
                                18B49FF51152BFA5001AF8A6 /* AddonManager.h */,
+                               832F2C76120D8C0300026B38 /* AddonHelpers_Addon.cpp */,
+                               832F2C77120D8C0300026B38 /* AddonHelpers_Addon.h */,
+                               832F2C78120D8C0300026B38 /* AddonHelpers_GUI.cpp */,
+                               832F2C82120D8C0F00026B38 /* PVRClient.cpp */,
+                               832F2C83120D8C1000026B38 /* PVRClient.h */,
+                               832F2C79120D8C0300026B38 /* AddonHelpers_GUI.h */,
+                               832F2C7A120D8C0300026B38 /* AddonHelpers_local.cpp */,
+                               832F2C7B120D8C0300026B38 /* AddonHelpers_local.h */,
+                               832F2C7C120D8C0300026B38 /* AddonHelpers_PVR.cpp */,
+                               832F2C7D120D8C0300026B38 /* AddonHelpers_PVR.h */,
                                F52BFFDA115D5574004B1D66 /* AddonStatusHandler.cpp */,
                                F52BFFD9115D5574004B1D66 /* AddonStatusHandler.h */,
-                               18B49FF61152BFA5001AF8A6 /* DllAddon.h */,
                                18B49FF71152BFA5001AF8A6 /* DllScreenSaver.h */,
                                18B49FF81152BFA5001AF8A6 /* DllVisualisation.h */,
                                18B49FF91152BFA5001AF8A6 /* fft.cpp */,
                                7CD9742711BC6EAA00649E31 /* GUITextureGLES.cpp */,
                                7CD9742811BC6EAA00649E31 /* GUITextureGLES.h */,
                                7C779DA0104A4B2E00F444C4 /* Texture.cpp */,
+                               832F2C93120D8CF600026B38 /* TextSearch.cpp */,
+                               832F2C94120D8CF600026B38 /* TextSearch.h */,
                                7C779DA1104A4B2E00F444C4 /* Texture.h */,
                                7C779DA2104A4B2E00F444C4 /* TextureDX.cpp */,
                                7C779DA3104A4B2E00F444C4 /* TextureDX.h */,
                                E38E13E10D25F9F900618676 /* GUIEditControl.h */,
                                E38E13E20D25F9F900618676 /* GUIFadeLabelControl.cpp */,
                                E38E13E30D25F9F900618676 /* GUIFadeLabelControl.h */,
+                               832F2C88120D8C9300026B38 /* GUIEPGGridContainer.cpp */,
+                               832F2C89120D8C9300026B38 /* GUIEPGGridContainer.h */,
                                7C779D9B104A4B2E00F444C4 /* GUIImage.cpp */,
                                7C779D9C104A4B2E00F444C4 /* GUIImage.h */,
                                E38E13F20D25F9F900618676 /* GUILabelControl.cpp */,
                                F5E55B65107412DE006E788A /* GUIDialogTeletext.cpp */,
                                F5E55B64107412DE006E788A /* GUIDialogTeletext.h */,
                                E38E17E00D25F9FA00618676 /* GUIDialogVideoBookmarks.cpp */,
+                               832F2C53120D8BA800026B38 /* GUIDialogPVRChannelManager.cpp */,
+                               832F2C54120D8BA800026B38 /* GUIDialogPVRChannelManager.h */,
+                               832F2C55120D8BA800026B38 /* GUIDialogPVRChannelsOSD.cpp */,
+                               832F2C56120D8BA800026B38 /* GUIDialogPVRChannelsOSD.h */,
+                               832F2C57120D8BA800026B38 /* GUIDialogPVRCutterOSD.cpp */,
+                               832F2C58120D8BA800026B38 /* GUIDialogPVRCutterOSD.h */,
+                               832F2C59120D8BA800026B38 /* GUIDialogPVRDirectorOSD.cpp */,
+                               832F2C5A120D8BA800026B38 /* GUIDialogPVRDirectorOSD.h */,
+                               832F2C5B120D8BA800026B38 /* GUIDialogPVRGroupManager.cpp */,
+                               832F2C5C120D8BA900026B38 /* GUIDialogPVRGroupManager.h */,
+                               832F2C5D120D8BA900026B38 /* GUIDialogPVRGuideInfo.cpp */,
+                               832F2C5E120D8BA900026B38 /* GUIDialogPVRGuideInfo.h */,
+                               832F2C5F120D8BA900026B38 /* GUIDialogPVRGuideOSD.cpp */,
+                               832F2C60120D8BA900026B38 /* GUIDialogPVRGuideOSD.h */,
+                               832F2C61120D8BA900026B38 /* GUIDialogPVRGuideSearch.cpp */,
+                               832F2C62120D8BA900026B38 /* GUIDialogPVRGuideSearch.h */,
+                               832F2C63120D8BA900026B38 /* GUIDialogPVRRecordingInfo.cpp */,
+                               832F2C64120D8BA900026B38 /* GUIDialogPVRRecordingInfo.h */,
+                               832F2C65120D8BA900026B38 /* GUIDialogPVRTimerSettings.cpp */,
+                               832F2C66120D8BA900026B38 /* GUIDialogPVRTimerSettings.h */,
+                               832F2C67120D8BA900026B38 /* GUIDialogPVRUpdateProgressBar.cpp */,
+                               832F2C68120D8BA900026B38 /* GUIDialogPVRUpdateProgressBar.h */,
                                E38E17E10D25F9FA00618676 /* GUIDialogVideoBookmarks.h */,
                                E38E17E20D25F9FA00618676 /* GUIDialogVideoScan.cpp */,
                                E38E17E30D25F9FA00618676 /* GUIDialogVideoScan.h */,
                                E38E17FB0D25F9FA00618676 /* GUIViewStateMusic.cpp */,
                                E38E17FC0D25F9FA00618676 /* GUIViewStateMusic.h */,
                                E38E17FF0D25F9FA00618676 /* GUIViewStateVideo.cpp */,
+                               832F2CA1120D8D7600026B38 /* GUIViewStateTV.cpp */,
+                               832F2CA2120D8D7600026B38 /* GUIViewStateTV.h */,
                                E38E18000D25F9FA00618676 /* GUIViewStateVideo.h */,
                                F5A7B433113CBC6A0059D6AA /* GUIWindowAddonBrowser.cpp */,
                                F5A7B432113CBC6A0059D6AA /* GUIWindowAddonBrowser.h */,
                                E38E183A0D25F9FA00618676 /* GUIWindowSystemInfo.h */,
                                F5F95D9F0E4E203700C3FA5C /* GUIWindowTestPattern.cpp */,
                                F5F95D9E0E4E203700C3FA5C /* GUIWindowTestPattern.h */,
+                               832F2C99120D8D3000026B38 /* GUIWindowTV.cpp */,
+                               832F2C9A120D8D3000026B38 /* GUIWindowTV.h */,
                                7C779E50104A58F900F444C4 /* GUIWindowTestPatternGL.cpp */,
                                7C779E51104A58F900F444C4 /* GUIWindowTestPatternGL.h */,
                                F50FDC59119B4B2C00C8B8CD /* GUIDialogTextViewer.cpp */,
                                E38E15490D25F9F900618676 /* DVDDemux.cpp */,
                                E38E154A0D25F9F900618676 /* DVDDemux.h */,
                                E38E154D0D25F9F900618676 /* DVDDemuxShoutcast.cpp */,
+                               832F2C96120D8D0B00026B38 /* DVDDemuxPVRClient.cpp */,
+                               832F2C97120D8D0B00026B38 /* DVDDemuxPVRClient.h */,
                                E38E154E0D25F9F900618676 /* DVDDemuxShoutcast.h */,
                                E38E154F0D25F9F900618676 /* DVDDemuxUtils.cpp */,
                                E38E15500D25F9F900618676 /* DVDDemuxUtils.h */,
                                815EE6330E17F1DC009FBE3C /* DVDInputStreamRTMP.cpp */,
                                815EE6340E17F1DC009FBE3C /* DVDInputStreamRTMP.h */,
                                E33979940D62FD47004ECDDA /* DVDInputStreamTV.cpp */,
+                               832F2C9E120D8D5C00026B38 /* DVDInputStreamPVRManager.cpp */,
+                               832F2C9F120D8D5C00026B38 /* DVDInputStreamPVRManager.h */,
                                E33979950D62FD47004ECDDA /* DVDInputStreamTV.h */,
                                E38E15580D25F9FA00618676 /* DllDvdNav.h */,
                                E38E15590D25F9FA00618676 /* DVDFactoryInputStream.cpp */,
                                E38E15E90D25F9FA00618676 /* CodecFactory.h */,
                                E38E15EE0D25F9FA00618676 /* DllAc3codec.h */,
                                E38E15EF0D25F9FA00618676 /* DllAdpcm.h */,
+                               8398EE72116F1297001227BB /* DllAddon.h */,
+                               8398EE73116F1297001227BB /* DllPVRClient.h */,
                                E38E15F20D25F9FA00618676 /* DllDCACodec.h */,
                                E38E15F50D25F9FA00618676 /* DllLibFlac.h */,
                                E38E15FA0D25F9FA00618676 /* DllNosefart.h */,
                                889B4D8D0E0EF86C00FAD25E /* RSSDirectory.h */,
                                F5A7B42A113CBB950059D6AA /* AddonsDirectory.h */,
                                F5A7B42B113CBB950059D6AA /* AddonsDirectory.cpp */,
+                               83840880116E243C009B5115 /* Addon.h */,
+                               83840881116E243C009B5115 /* AddonDll.h */,
                                88ACB0190DCF40800083CFDF /* ASAPFileDirectory.cpp */,
                                88ACB01A0DCF40800083CFDF /* ASAPFileDirectory.h */,
                                880DBE530DC224A100E26B71 /* MusicFileDirectory.cpp */,
                                E38E17840D25F9FA00618676 /* DirectoryNodeYear.cpp */,
                                E38E17850D25F9FA00618676 /* DirectoryNodeYear.h */,
                                E38E17880D25F9FA00618676 /* QueryParams.cpp */,
+                               832F2C44120D8B5E00026B38 /* PVRManager.cpp */,
+                               832F2C45120D8B5E00026B38 /* PVRManager.h */,
                                E38E17890D25F9FA00618676 /* QueryParams.h */,
                        );
                        path = VideoDatabaseDirectory;
                                7CCF7FC8106A0DF500992676 /* TimeUtils.h */,
                                E38E1E890D25F9FD00618676 /* TuxBoxUtil.cpp */,
                                E38E1E8A0D25F9FD00618676 /* TuxBoxUtil.h */,
+                               832F2C85120D8C5300026B38 /* TVDatabase.cpp */,
+                               832F2C86120D8C5300026B38 /* TVDatabase.h */,
                                E38E1E8B0D25F9FD00618676 /* UdpClient.cpp */,
                                E38E1E8C0D25F9FD00618676 /* UdpClient.h */,
                                7CF1FB09123B1AF000B2CBCB /* Variant.cpp */,
                                F5BDB80C120202F400F0B710 /* DVDSubtitleTagSami.cpp in Sources */,
                                F5BDB81A1202032400F0B710 /* DVDSubtitleTagMicroDVD.cpp in Sources */,
                                F5BDB820120203C200F0B710 /* AutoPtrHandle.cpp in Sources */,
+                               832F2C46120D8B5E00026B38 /* PVRManager.cpp in Sources */,
+                               832F2C4F120D8B7F00026B38 /* PVRChannels.cpp in Sources */,
+                               832F2C50120D8B7F00026B38 /* PVREpg.cpp in Sources */,
+                               832F2C51120D8B7F00026B38 /* PVRRecordings.cpp in Sources */,
+                               832F2C52120D8B7F00026B38 /* PVRTimers.cpp in Sources */,
+                               832F2C69120D8BA900026B38 /* GUIDialogPVRChannelManager.cpp in Sources */,
+                               832F2C6A120D8BA900026B38 /* GUIDialogPVRChannelsOSD.cpp in Sources */,
+                               832F2C6B120D8BA900026B38 /* GUIDialogPVRCutterOSD.cpp in Sources */,
+                               832F2C6C120D8BA900026B38 /* GUIDialogPVRDirectorOSD.cpp in Sources */,
+                               832F2C6D120D8BA900026B38 /* GUIDialogPVRGroupManager.cpp in Sources */,
+                               832F2C6E120D8BA900026B38 /* GUIDialogPVRGuideInfo.cpp in Sources */,
+                               832F2C6F120D8BA900026B38 /* GUIDialogPVRGuideOSD.cpp in Sources */,
+                               832F2C70120D8BA900026B38 /* GUIDialogPVRGuideSearch.cpp in Sources */,
+                               832F2C71120D8BA900026B38 /* GUIDialogPVRRecordingInfo.cpp in Sources */,
+                               832F2C72120D8BA900026B38 /* GUIDialogPVRTimerSettings.cpp in Sources */,
+                               832F2C73120D8BA900026B38 /* GUIDialogPVRUpdateProgressBar.cpp in Sources */,
+                               832F2C7E120D8C0300026B38 /* AddonHelpers_Addon.cpp in Sources */,
+                               832F2C7F120D8C0300026B38 /* AddonHelpers_GUI.cpp in Sources */,
+                               832F2C80120D8C0300026B38 /* AddonHelpers_local.cpp in Sources */,
+                               832F2C81120D8C0300026B38 /* AddonHelpers_PVR.cpp in Sources */,
+                               832F2C84120D8C1000026B38 /* PVRClient.cpp in Sources */,
+                               832F2C87120D8C5300026B38 /* TVDatabase.cpp in Sources */,
+                               832F2C8A120D8C9300026B38 /* GUIEPGGridContainer.cpp in Sources */,
+                               832F2C8F120D8CB500026B38 /* PVRDirectory.cpp in Sources */,
+                               832F2C90120D8CB500026B38 /* PVRFile.cpp in Sources */,
+                               832F2C95120D8CF600026B38 /* TextSearch.cpp in Sources */,
+                               832F2C98120D8D0B00026B38 /* DVDDemuxPVRClient.cpp in Sources */,
+                               832F2C9B120D8D3000026B38 /* GUIWindowTV.cpp in Sources */,
+                               832F2CA0120D8D5C00026B38 /* DVDInputStreamPVRManager.cpp in Sources */,
+                               832F2CA3120D8D7600026B38 /* GUIViewStateTV.cpp in Sources */,
                                F5E728A51227112500B152C1 /* URIUtils.cpp in Sources */,
                                7CF1FB0C123B1AF000B2CBCB /* Variant.cpp in Sources */,
                                C8D0B2AF1265A9A800F0C0AC /* SystemGlobals.cpp in Sources */,
+                               832F2C46120D8B5E00026B38 /* PVRManager.cpp in Sources */,
+                               832F2C4F120D8B7F00026B38 /* PVRChannels.cpp in Sources */,
+                               832F2C50120D8B7F00026B38 /* PVREpg.cpp in Sources */,
+                               832F2C51120D8B7F00026B38 /* PVRRecordings.cpp in Sources */,
+                               832F2C52120D8B7F00026B38 /* PVRTimers.cpp in Sources */,
+                               832F2C69120D8BA900026B38 /* GUIDialogPVRChannelManager.cpp in Sources */,
+                               832F2C6A120D8BA900026B38 /* GUIDialogPVRChannelsOSD.cpp in Sources */,
+                               832F2C6B120D8BA900026B38 /* GUIDialogPVRCutterOSD.cpp in Sources */,
+                               832F2C6C120D8BA900026B38 /* GUIDialogPVRDirectorOSD.cpp in Sources */,
+                               832F2C6D120D8BA900026B38 /* GUIDialogPVRGroupManager.cpp in Sources */,
+                               832F2C6E120D8BA900026B38 /* GUIDialogPVRGuideInfo.cpp in Sources */,
+                               832F2C6F120D8BA900026B38 /* GUIDialogPVRGuideOSD.cpp in Sources */,
+                               832F2C70120D8BA900026B38 /* GUIDialogPVRGuideSearch.cpp in Sources */,
+                               832F2C71120D8BA900026B38 /* GUIDialogPVRRecordingInfo.cpp in Sources */,
+                               832F2C72120D8BA900026B38 /* GUIDialogPVRTimerSettings.cpp in Sources */,
+                               832F2C73120D8BA900026B38 /* GUIDialogPVRUpdateProgressBar.cpp in Sources */,
+                               832F2C7E120D8C0300026B38 /* AddonHelpers_Addon.cpp in Sources */,
+                               832F2C7F120D8C0300026B38 /* AddonHelpers_GUI.cpp in Sources */,
+                               832F2C80120D8C0300026B38 /* AddonHelpers_local.cpp in Sources */,
+                               832F2C81120D8C0300026B38 /* AddonHelpers_PVR.cpp in Sources */,
+                               832F2C84120D8C1000026B38 /* PVRClient.cpp in Sources */,
+                               832F2C87120D8C5300026B38 /* TVDatabase.cpp in Sources */,
+                               832F2C8A120D8C9300026B38 /* GUIEPGGridContainer.cpp in Sources */,
+                               832F2C8F120D8CB500026B38 /* PVRDirectory.cpp in Sources */,
+                               832F2C90120D8CB500026B38 /* PVRFile.cpp in Sources */,
+                               832F2C95120D8CF600026B38 /* TextSearch.cpp in Sources */,
+                               832F2C98120D8D0B00026B38 /* DVDDemuxPVRClient.cpp in Sources */,
+                               832F2C9B120D8D3000026B38 /* GUIWindowTV.cpp in Sources */,
+                               832F2CA0120D8D5C00026B38 /* DVDInputStreamPVRManager.cpp in Sources */,
+                               832F2CA3120D8D7600026B38 /* GUIViewStateTV.cpp in Sources */,
                                7CBEBB8412912BA400431822 /* fstrcmp.c in Sources */,
                                184C472F1296BC6E0006DB3E /* Service.cpp in Sources */,
                                F57A83E912D3C713000525C5 /* FileUDF.cpp in Sources */,
                1DEB924808733DCA0010E9CD /* Debug */ = {
                        isa = XCBuildConfiguration;
                        buildSettings = {
-                               COPY_PHASE_STRIP = NO;
+                               COPY_PHASE_STRIP = YES;
                                DEAD_CODE_STRIPPING = NO;
                                FRAMEWORK_SEARCH_PATHS = "$(inherited)";
                                GCC_AUTO_VECTORIZATION = YES;
                1DEB924908733DCA0010E9CD /* Release */ = {
                        isa = XCBuildConfiguration;
                        buildSettings = {
-                               COPY_PHASE_STRIP = NO;
+                               COPY_PHASE_STRIP = YES;
                                DEAD_CODE_STRIPPING = NO;
                                DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
                                FRAMEWORK_SEARCH_PATHS = "$(inherited)";
diff --git a/addons/library.xbmc.addon/dlfcn-win32.cpp b/addons/library.xbmc.addon/dlfcn-win32.cpp
new file mode 100644 (file)
index 0000000..a6cf2c2
--- /dev/null
@@ -0,0 +1,263 @@
+/*\r
+ * dlfcn-win32\r
+ * Copyright (c) 2007 Ramiro Polla\r
+ *\r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ *\r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+ * Lesser General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA\r
+ */\r
+\r
+#include <windows.h>\r
+#include <stdio.h>\r
+\r
+#include "dlfcn-win32.h"\r
+\r
+/* Note:\r
+ * MSDN says these functions are not thread-safe. We make no efforts to have\r
+ * any kind of thread safety.\r
+ */\r
+\r
+/* I have no special reason to have set MAX_GLOBAL_OBJECTS to this value. Any\r
+ * comments are welcome.\r
+ */\r
+#define MAX_OBJECTS 255\r
+\r
+static HMODULE global_objects[MAX_OBJECTS];\r
+\r
+/* This function adds an object to the list of global objects.\r
+ * The implementation is very simple and slow.\r
+ * TODO: should failing this function be enough to fail the call to dlopen( )?\r
+ */\r
+static void global_object_add( HMODULE hModule )\r
+{\r
+    int i;\r
+\r
+    for( i = 0 ; i < MAX_OBJECTS ; i++ )\r
+    {\r
+        if( !global_objects[i] )\r
+        {\r
+            global_objects[i] = hModule;\r
+            break;\r
+        }\r
+    }\r
+}\r
+\r
+static void global_object_rem( HMODULE hModule )\r
+{\r
+    int i;\r
+\r
+    for( i = 0 ; i < MAX_OBJECTS ; i++ )\r
+    {\r
+        if( global_objects[i] == hModule )\r
+        {\r
+            global_objects[i] = 0;\r
+            break;\r
+        }\r
+    }\r
+}\r
+\r
+/* Argument to last function. Used in dlerror( ) */\r
+static char last_name[MAX_PATH];\r
+\r
+static int copy_string( char *dest, int dest_size, const char *src )\r
+{\r
+    int i = 0;\r
+\r
+    if( src && dest )\r
+    {\r
+        for( i = 0 ; i < dest_size-1 ; i++ )\r
+        {\r
+            if( !src[i] )\r
+                break;\r
+            else\r
+                dest[i] = src[i];\r
+        }\r
+    }\r
+    dest[i] = '\0';\r
+\r
+    return i;\r
+}\r
+\r
+void *dlopen( const char *file, int mode )\r
+{\r
+    HMODULE hModule;\r
+    UINT uMode;\r
+\r
+    /* Do not let Windows display the critical-error-handler message box */\r
+    uMode = SetErrorMode( SEM_FAILCRITICALERRORS );\r
+\r
+    if( file == 0 )\r
+    {\r
+        /* Save NULL pointer for error message */\r
+        _snprintf_s( last_name, MAX_PATH, MAX_PATH, "0x%p", file );\r
+\r
+        /* POSIX says that if the value of file is 0, a handle on a global\r
+         * symbol object must be provided. That object must be able to access\r
+         * all symbols from the original program file, and any objects loaded\r
+         * with the RTLD_GLOBAL flag.\r
+         * The return value from GetModuleHandle( ) allows us to retrieve\r
+         * symbols only from the original program file. For objects loaded with\r
+         * the RTLD_GLOBAL flag, we create our own list later on.\r
+         */\r
+        hModule = GetModuleHandle( NULL );\r
+    }\r
+    else\r
+    {\r
+        char lpFileName[MAX_PATH];\r
+        int i;\r
+\r
+        /* MSDN says backslashes *must* be used instead of forward slashes. */\r
+        for( i = 0 ; i < sizeof(lpFileName)-1 ; i++ )\r
+        {\r
+            if( !file[i] )\r
+                break;\r
+            else if( file[i] == '/' )\r
+                lpFileName[i] = '\\';\r
+            else\r
+                lpFileName[i] = file[i];\r
+        }\r
+        lpFileName[i] = '\0';\r
+\r
+        /* Save file name for error message */\r
+        copy_string( last_name, sizeof(last_name), lpFileName );\r
+\r
+        /* POSIX says the search path is implementation-defined.\r
+         * LOAD_WITH_ALTERED_SEARCH_PATH is used to make it behave more closely\r
+         * to UNIX's search paths (start with system folders instead of current\r
+         * folder).\r
+         */\r
+        hModule = LoadLibraryEx( (LPSTR) lpFileName, NULL,\r
+                                 LOAD_WITH_ALTERED_SEARCH_PATH );\r
+        /* If the object was loaded with RTLD_GLOBAL, add it to list of global\r
+         * objects, so that its symbols may be retrieved even if the handle for\r
+         * the original program file is passed. POSIX says that if the same\r
+         * file is specified in multiple invocations, and any of them are\r
+         * RTLD_GLOBAL, even if any further invocations use RTLD_LOCAL, the\r
+         * symbols will remain global.\r
+         */\r
+\r
+        if( hModule && (mode & RTLD_GLOBAL) )\r
+            global_object_add( hModule );\r
+    }\r
+\r
+    /* Return to previous state of the error-mode bit flags. */\r
+    SetErrorMode( uMode );\r
+\r
+    return (void *) hModule;\r
+}\r
+\r
+int dlclose( void *handle )\r
+{\r
+    HMODULE hModule = (HMODULE) handle;\r
+    BOOL ret;\r
+\r
+    /* Save handle for error message */\r
+    _snprintf_s( last_name, MAX_PATH, MAX_PATH, "0x%p", handle );\r
+\r
+    ret = FreeLibrary( hModule );\r
+\r
+    /* If the object was loaded with RTLD_GLOBAL, remove it from list of global\r
+     * objects.\r
+     */\r
+    if( ret )\r
+        global_object_rem( hModule );\r
+\r
+    /* dlclose's return value in inverted in relation to FreeLibrary's. */\r
+    ret = !ret;\r
+\r
+    return (int) ret;\r
+}\r
+\r
+void *dlsym( void *handle, const char *name )\r
+{\r
+    FARPROC symbol;\r
+    HMODULE myhandle = (HMODULE) handle;\r
+\r
+    /* Save symbol name for error message */\r
+    copy_string( last_name, sizeof(last_name), name );\r
+\r
+    symbol = GetProcAddress( myhandle, name );\r
+#if 0\r
+    if( symbol == NULL )\r
+    {\r
+        HMODULE hModule;\r
+\r
+        /* If the handle for the original program file is passed, also search\r
+         * in all globally loaded objects.\r
+         */\r
+\r
+        hModule = GetModuleHandle( NULL );\r
+\r
+        if( hModule == handle )\r
+        {\r
+            int i;\r
+           \r
+            for( i = 0 ; i < MAX_OBJECTS ; i++ )\r
+            {\r
+                if( global_objects[i] != 0 )\r
+                {\r
+                    symbol = GetProcAddress( global_objects[i], name );\r
+                    if( symbol != NULL )\r
+                        break;\r
+                }\r
+            }\r
+        }\r
+\r
+\r
+        CloseHandle( hModule );\r
+    }\r
+#endif\r
+    return (void*) symbol;\r
+}\r
+\r
+char *dlerror( void )\r
+{\r
+    DWORD dwMessageId;\r
+    /* POSIX says this function doesn't have to be thread-safe, so we use one\r
+     * static buffer.\r
+     * MSDN says the buffer cannot be larger than 64K bytes, so we set it to\r
+     * the limit.\r
+     */\r
+    static char lpBuffer[65535];\r
+    DWORD ret;\r
+\r
+    dwMessageId = GetLastError( );\r
+   \r
+    if( dwMessageId == 0 )\r
+        return NULL;\r
+\r
+    /* Format error message to:\r
+     * "<argument to function that failed>": <Windows localized error message>\r
+     */\r
+    ret  = copy_string( lpBuffer, sizeof(lpBuffer), "\"" );\r
+    ret += copy_string( lpBuffer+ret, sizeof(lpBuffer)-ret, last_name );\r
+    ret += copy_string( lpBuffer+ret, sizeof(lpBuffer)-ret, "\": " );\r
+    ret += FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwMessageId,\r
+                          MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),\r
+                          lpBuffer+ret, sizeof(lpBuffer)-ret, NULL );\r
+\r
+    if( ret > 1 )\r
+    {\r
+        /* POSIX says the string must not have trailing <newline> */\r
+        if( lpBuffer[ret-2] == '\r' && lpBuffer[ret-1] == '\n' )\r
+            lpBuffer[ret-2] = '\0';\r
+    }\r
+\r
+    /* POSIX says that invoking dlerror( ) a second time, immediately following\r
+     * a prior invocation, shall result in NULL being returned.\r
+     */\r
+    SetLastError(0);\r
+\r
+    return lpBuffer;\r
+}\r
+\r
diff --git a/addons/library.xbmc.addon/dlfcn-win32.h b/addons/library.xbmc.addon/dlfcn-win32.h
new file mode 100644 (file)
index 0000000..f906343
--- /dev/null
@@ -0,0 +1,46 @@
+#pragma once\r
+/*\r
+ * dlfcn-win32\r
+ * Copyright (c) 2007 Ramiro Polla\r
+ *\r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ *\r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+ * Lesser General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA\r
+ */\r
+\r
+#ifndef DLFCN_H\r
+#define DLFCN_H\r
+\r
+/* POSIX says these are implementation-defined.\r
+ * To simplify use with Windows API, we treat them the same way.\r
+ */\r
+\r
+#define RTLD_LAZY   0\r
+#define RTLD_NOW    0\r
+\r
+#define RTLD_GLOBAL (1 << 1)\r
+#define RTLD_LOCAL  (1 << 2)\r
+\r
+/* These two were added in The Open Group Base Specifications Issue 6.\r
+ * Note: All other RTLD_* flags in any dlfcn.h are not standard compliant.\r
+ */\r
+\r
+#define RTLD_DEFAULT    0\r
+#define RTLD_NEXT       0\r
+\r
+void *dlopen ( const char *file, int mode );\r
+int   dlclose( void *handle );\r
+void *dlsym  ( void *handle, const char *name );\r
+char *dlerror( void );\r
+\r
+#endif /* DLFCN-WIN32_H */\r
diff --git a/addons/library.xbmc.addon/libXBMC_addon.h b/addons/library.xbmc.addon/libXBMC_addon.h
new file mode 100644 (file)
index 0000000..0be6d46
--- /dev/null
@@ -0,0 +1,150 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <string>
+#include <vector>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#ifndef _LINUX
+#include "dlfcn-win32.h"
+#define ADDON_DLL "\\library.xbmc.addon\\libXBMC_addon.dll"
+#else
+#include <dlfcn.h>
+#if defined(__APPLE__)
+#if defined(__POWERPC__)
+#define ADDON_DLL "/library.xbmc.addon/libXBMC_addon-powerpc-osx.so"
+#else
+#define ADDON_DLL "/library.xbmc.addon/libXBMC_addon-x86-osx.so"
+#endif
+#elif defined(__x86_64__)
+#define ADDON_DLL "/library.xbmc.addon/libXBMC_addon-x86_64-linux.so"
+#elif defined(_POWERPC)
+#define ADDON_DLL "/library.xbmc.addon/libXBMC_addon-powerpc-linux.so"
+#elif defined(_POWERPC64)
+#define ADDON_DLL "/library.xbmc.addon/libXBMC_addon-powerpc64-linux.so"
+#else /* !__x86_64__ && !__powerpc__ */
+#define ADDON_DLL "/library.xbmc.addon/libXBMC_addon-i486-linux.so"
+#endif /* __x86_64__ */
+#endif /* _LINUX */
+
+typedef enum addon_log {
+  LOG_DEBUG,
+  LOG_INFO,
+  LOG_NOTICE,
+  LOG_ERROR
+} addon_log_t;
+
+typedef enum queue_msg {
+  QUEUE_INFO,
+  QUEUE_WARNING,
+  QUEUE_ERROR
+} queue_msg_t;
+
+class cHelper_libXBMC_addon
+{
+public:
+  cHelper_libXBMC_addon()
+  {
+    m_libXBMC_addon = NULL;
+    m_Handle        = NULL;
+  }
+
+  ~cHelper_libXBMC_addon()
+  {
+    if (m_libXBMC_addon)
+    {
+      XBMC_unregister_me();
+      dlclose(m_libXBMC_addon);
+    }
+  }
+
+  bool RegisterMe(void *Handle)
+  {
+    m_Handle = Handle;
+
+    std::string libBasePath;
+    libBasePath  = ((cb_array*)m_Handle)->libPath;
+    libBasePath += ADDON_DLL;
+
+    m_libXBMC_addon = dlopen(libBasePath.c_str(), RTLD_LAZY);
+    if (m_libXBMC_addon == NULL)
+    {
+      fprintf(stderr, "Unable to load %s\n", dlerror());
+      return false;
+    }
+
+    XBMC_register_me   = (int (*)(void *HANDLE))
+      dlsym(m_libXBMC_addon, "XBMC_register_me");
+    if (XBMC_register_me == NULL)   { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
+
+    XBMC_unregister_me = (void (*)())
+      dlsym(m_libXBMC_addon, "XBMC_unregister_me");
+    if (XBMC_unregister_me == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
+
+    Log                = (void (*)(const addon_log_t loglevel, const char *format, ... ))
+      dlsym(m_libXBMC_addon, "XBMC_log");
+    if (Log == NULL)                { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
+
+    GetSetting         = (bool (*)(const char* settingName, void *settingValue))
+      dlsym(m_libXBMC_addon, "XBMC_get_setting");
+    if (GetSetting == NULL)         { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
+
+    QueueNotification  = (void (*)(const queue_msg_t loglevel, const char *format, ... ))
+      dlsym(m_libXBMC_addon, "XBMC_queue_notification");
+    if (QueueNotification == NULL)  { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
+
+    UnknownToUTF8      = (void (*)(std::string &str))
+      dlsym(m_libXBMC_addon, "XBMC_unknown_to_utf8");
+    if (UnknownToUTF8 == NULL)      { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
+
+    GetLocalizedString = (const char* (*)(int dwCode))
+      dlsym(m_libXBMC_addon, "XBMC_get_localized_string");
+    if (GetLocalizedString == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
+
+    GetDVDMenuLanguage = (const char* (*)())
+      dlsym(m_libXBMC_addon, "XBMC_get_dvd_menu_language");
+    if (GetDVDMenuLanguage == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
+
+    return XBMC_register_me(m_Handle) > 0;
+  }
+
+  void (*Log)(const addon_log_t loglevel, const char *format, ... );
+  bool (*GetSetting)(const char* settingName, void *settingValue);
+  void (*QueueNotification)(const queue_msg_t type, const char *format, ... );
+  void (*UnknownToUTF8)(std::string &str);
+  const char* (*GetLocalizedString)(int dwCode);
+  const char* (*GetDVDMenuLanguage)();
+
+protected:
+  int (*XBMC_register_me)(void *HANDLE);
+  void (*XBMC_unregister_me)();
+
+private:
+  void *m_libXBMC_addon;
+  void *m_Handle;
+  struct cb_array
+  {
+    const char* libPath;
+  };
+};
diff --git a/addons/library.xbmc.gui/libXBMC_gui.h b/addons/library.xbmc.gui/libXBMC_gui.h
new file mode 100644 (file)
index 0000000..8d9c33d
--- /dev/null
@@ -0,0 +1,325 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <string>
+#include <vector>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+typedef void* GUIHANDLE;
+
+#ifndef _LINUX
+#include "../library.xbmc.addon/dlfcn-win32.h"
+#define GUI_HELPER_DLL "\\library.xbmc.gui\\libXBMC_gui.dll"
+#else
+#include <dlfcn.h>
+#if defined(__APPLE__)
+#if defined(__POWERPC__)
+#define GUI_HELPER_DLL "/library.xbmc.gui/libXBMC_gui-powerpc-osx.so"
+#else
+#define GUI_HELPER_DLL "/library.xbmc.gui/libXBMC_gui-x86-osx.so"
+#endif
+#elif defined(__x86_64__)
+#define GUI_HELPER_DLL "/library.xbmc.gui/libXBMC_gui-x86_64-linux.so"
+#elif defined(_POWERPC)
+#define GUI_HELPER_DLL "/library.xbmc.gui/libXBMC_gui-powerpc-linux.so"
+#elif defined(_POWERPC64)
+#define GUI_HELPER_DLL "/library.xbmc.gui/libXBMC_gui-powerpc64-linux.so"
+#else /* !__x86_64__ && !__powerpc__ */
+#define GUI_HELPER_DLL "/library.xbmc.gui/libXBMC_gui-i486-linux.so"
+#endif /* __x86_64__ */
+#endif /* _LINUX */
+
+#define ADDON_ACTION_PREVIOUS_MENU          10
+#define ADDON_ACTION_CLOSE_DIALOG           51
+
+class cGUIWindow;
+class cGUISpinControl;
+class cGUIRadioButton;
+class cGUIProgressControl;
+class cListItem;
+
+class cHelper_libXBMC_gui
+{
+public:
+  cHelper_libXBMC_gui()
+  {
+    m_libXBMC_gui = NULL;
+    m_Handle      = NULL;
+  }
+
+  ~cHelper_libXBMC_gui()
+  {
+    if (m_libXBMC_gui)
+    {
+      GUI_unregister_me();
+      dlclose(m_libXBMC_gui);
+    }
+  }
+
+  bool RegisterMe(void *Handle)
+  {
+    m_Handle = Handle;
+
+    std::string libBasePath;
+    libBasePath  = ((cb_array*)m_Handle)->libPath;
+    libBasePath += GUI_HELPER_DLL;
+
+    m_libXBMC_gui = dlopen(libBasePath.c_str(), RTLD_LAZY);
+    if (m_libXBMC_gui == NULL)
+    {
+      fprintf(stderr, "Unable to load %s\n", dlerror());
+      return false;
+    }
+
+    GUI_register_me         = (int (*)(void *HANDLE))
+      dlsym(m_libXBMC_gui, "GUI_register_me");
+    if (GUI_register_me == NULL)      { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
+
+    GUI_unregister_me       = (void (*)())
+      dlsym(m_libXBMC_gui, "GUI_unregister_me");
+    if (GUI_unregister_me == NULL)    { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
+
+    Lock                    = (void (*)())
+      dlsym(m_libXBMC_gui, "GUI_lock");
+    if (Lock == NULL)                 { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
+
+    Unlock                  = (void (*)())
+      dlsym(m_libXBMC_gui, "GUI_unlock");
+    if (Unlock == NULL)               { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
+
+    GetScreenHeight         = (int (*)())
+      dlsym(m_libXBMC_gui, "GUI_get_screen_height");
+    if (GetScreenHeight == NULL)      { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
+
+    GetScreenWidth          = (int (*)())
+      dlsym(m_libXBMC_gui, "GUI_get_screen_width");
+    if (GetScreenWidth == NULL)       { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
+
+    GetVideoResolution      = (int (*)())
+      dlsym(m_libXBMC_gui, "GUI_get_video_resolution");
+    if (GetVideoResolution == NULL)   { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
+
+    Window_create           = (cGUIWindow* (*)(const char *xmlFilename, const char *defaultSkin, bool forceFallback, bool asDialog))
+      dlsym(m_libXBMC_gui, "GUI_Window_create");
+    if (Window_create == NULL)        { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
+
+    Window_destroy          = (void (*)(cGUIWindow* p))
+      dlsym(m_libXBMC_gui, "GUI_Window_destroy");
+    if (Window_destroy == NULL)       { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
+
+    Control_getSpin         = (cGUISpinControl* (*)(cGUIWindow *window, int controlId))
+      dlsym(m_libXBMC_gui, "GUI_control_get_spin");
+    if (Control_getSpin == NULL)      { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
+
+    Control_releaseSpin     = (void (*)(cGUISpinControl* p))
+      dlsym(m_libXBMC_gui, "GUI_control_release_spin");
+    if (Control_releaseSpin == NULL)  { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
+
+    Control_getRadioButton  = (cGUIRadioButton* (*)(cGUIWindow *window, int controlId))
+      dlsym(m_libXBMC_gui, "GUI_control_get_radiobutton");
+    if (Control_getRadioButton == NULL)      { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
+
+    Control_releaseRadioButton = (void (*)(cGUIRadioButton* p))
+      dlsym(m_libXBMC_gui, "GUI_control_release_radiobutton");
+    if (Control_releaseRadioButton == NULL)  { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
+
+    Control_getProgress     = (cGUIProgressControl* (*)(cGUIWindow *window, int controlId))
+      dlsym(m_libXBMC_gui, "GUI_control_get_progress");
+    if (Control_getProgress == NULL)  { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
+
+    Control_releaseProgress = (void (*)(cGUIProgressControl* p))
+      dlsym(m_libXBMC_gui, "GUI_control_release_progress");
+    if (Control_releaseProgress == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
+
+    ListItem_create         = (cListItem* (*)(const char *label, const char *label2, const char *iconImage, const char *thumbnailImage, const char *path))
+      dlsym(m_libXBMC_gui, "GUI_ListItem_create");
+    if (ListItem_create == NULL)      { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
+
+    ListItem_destroy        = (void (*)(cListItem* p))
+      dlsym(m_libXBMC_gui, "GUI_ListItem_destroy");
+    if (ListItem_destroy == NULL)     { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
+
+
+    return GUI_register_me(m_Handle) > 0;
+  }
+
+  void (*Lock)();
+  void (*Unlock)();
+  int (*GetScreenHeight)();
+  int (*GetScreenWidth)();
+  int (*GetVideoResolution)();
+  cGUIWindow* (*Window_create)(const char *xmlFilename, const char *defaultSkin, bool forceFallback, bool asDialog);
+  void (*Window_destroy)(cGUIWindow* p);
+  cGUISpinControl* (*Control_getSpin)(cGUIWindow *window, int controlId);
+  void (*Control_releaseSpin)(cGUISpinControl* p);
+  cGUIRadioButton* (*Control_getRadioButton)(cGUIWindow *window, int controlId);
+  void (*Control_releaseRadioButton)(cGUIRadioButton* p);
+  cGUIProgressControl* (*Control_getProgress)(cGUIWindow *window, int controlId);
+  void (*Control_releaseProgress)(cGUIProgressControl* p);
+  cListItem* (*ListItem_create)(const char *label, const char *label2, const char *iconImage, const char *thumbnailImage, const char *path);
+  void (*ListItem_destroy)(cListItem* p);
+
+protected:
+  int (*GUI_register_me)(void *HANDLE);
+  void (*GUI_unregister_me)();
+
+private:
+  void *m_libXBMC_gui;
+  void *m_Handle;
+  struct cb_array
+  {
+    const char* libPath;
+  };
+};
+
+class cGUISpinControl
+{
+public:
+  cGUISpinControl(cGUIWindow *window, int controlId);
+  virtual ~cGUISpinControl(void) {}
+
+  virtual void SetVisible(bool yesNo);
+  virtual void SetText(const char *label);
+  virtual void Clear();
+  virtual void AddLabel(const char *label, int iValue);
+  virtual int GetValue();
+  virtual void SetValue(int iValue);
+
+private:
+  cGUIWindow *m_Window;
+  int         m_ControlId;
+  GUIHANDLE   m_SpinHandle;
+};
+
+class cGUIRadioButton
+{
+public:
+  cGUIRadioButton(cGUIWindow *window, int controlId);
+  ~cGUIRadioButton() {}
+
+  virtual void SetVisible(bool yesNo);
+  virtual void SetText(const char *label);
+  virtual void SetSelected(bool yesNo);
+  virtual bool IsSelected();
+
+private:
+  cGUIWindow *m_Window;
+  int         m_ControlId;
+  GUIHANDLE   m_ButtonHandle;
+};
+
+class cGUIProgressControl
+{
+public:
+  cGUIProgressControl(cGUIWindow *window, int controlId);
+  virtual ~cGUIProgressControl(void) {}
+
+  virtual void SetPercentage(float fPercent);
+  virtual float GetPercentage() const;
+  virtual void SetInfo(int iInfo);
+  virtual int GetInfo() const;
+  virtual std::string GetDescription() const;
+
+private:
+  cGUIWindow *m_Window;
+  int         m_ControlId;
+  GUIHANDLE   m_ProgressHandle;
+};
+
+class cListItem
+{
+friend class cGUIWindow;
+
+public:
+  cListItem(const char *label, const char *label2, const char *iconImage, const char *thumbnailImage, const char *path);
+  virtual ~cListItem(void) {}
+
+  virtual const char  *GetLabel();
+  virtual void         SetLabel(const char *label);
+  virtual const char  *GetLabel2();
+  virtual void         SetLabel2(const char *label);
+  virtual void         SetIconImage(const char *image);
+  virtual void         SetThumbnailImage(const char *image);
+  virtual void         SetInfo(const char *Info);
+  virtual void         SetProperty(const char *key, const char *value);
+  virtual const char  *GetProperty(const char *key) const;
+  virtual void         SetPath(const char *Path);
+
+//    {(char*)"select();
+//    {(char*)"isSelected();
+protected:
+  GUIHANDLE   m_ListItemHandle;
+};
+
+class cGUIWindow
+{
+friend class cGUISpinControl;
+friend class cGUIRadioButton;
+friend class cGUIProgressControl;
+
+public:
+  cGUIWindow(const char *xmlFilename, const char *defaultSkin, bool forceFallback, bool asDialog);
+  ~cGUIWindow();
+
+  virtual bool         Show();
+  virtual void         Close();
+  virtual void         DoModal();
+  virtual bool         SetFocusId(int iControlId);
+  virtual int          GetFocusId();
+  virtual bool         SetCoordinateResolution(int res);
+  virtual void         SetProperty(const char *key, const char *value);
+  virtual void         SetPropertyInt(const char *key, int value);
+  virtual void         SetPropertyBool(const char *key, bool value);
+  virtual void         SetPropertyDouble(const char *key, double value);
+  virtual const char  *GetProperty(const char *key) const;
+  virtual int          GetPropertyInt(const char *key) const;
+  virtual bool         GetPropertyBool(const char *key) const;
+  virtual double       GetPropertyDouble(const char *key) const;
+  virtual void         ClearProperties();
+  virtual int          GetListSize();
+  virtual void         ClearList();
+  virtual GUIHANDLE    AddStringItem(const char *name, int itemPosition = -1);
+  virtual void         AddItem(GUIHANDLE item, int itemPosition = -1);
+  virtual void         AddItem(cListItem *item, int itemPosition = -1);
+  virtual void         RemoveItem(int itemPosition);
+  virtual GUIHANDLE    GetListItem(int listPos);
+  virtual void         SetCurrentListPosition(int listPos);
+  virtual int          GetCurrentListPosition();
+  virtual void         SetControlLabel(int controlId, const char *label);
+
+  virtual bool         OnClick(int controlId);
+  virtual bool         OnFocus(int controlId);
+  virtual bool         OnInit();
+  virtual bool         OnAction(int actionId);
+
+  GUIHANDLE m_cbhdl;
+  bool (*CBOnInit)(GUIHANDLE cbhdl);
+  bool (*CBOnFocus)(GUIHANDLE cbhdl, int controlId);
+  bool (*CBOnClick)(GUIHANDLE cbhdl, int controlId);
+  bool (*CBOnAction)(GUIHANDLE cbhdl, int actionId);
+
+protected:
+  GUIHANDLE m_WindowHandle;
+};
+
diff --git a/addons/library.xbmc.pvr/libXBMC_pvr.h b/addons/library.xbmc.pvr/libXBMC_pvr.h
new file mode 100644 (file)
index 0000000..69c531b
--- /dev/null
@@ -0,0 +1,165 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <string>
+#include <vector>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "xbmc_pvr_types.h"
+
+#ifndef _LINUX
+#include "../library.xbmc.addon/dlfcn-win32.h"
+#define PVR_HELPER_DLL "\\library.xbmc.pvr\\libXBMC_pvr.dll"
+#else
+#include <dlfcn.h>
+#if defined(__APPLE__)
+#if defined(__POWERPC__)
+#define PVR_HELPER_DLL "/library.xbmc.pvr/libXBMC_pvr-powerpc-osx.so"
+#else
+#define PVR_HELPER_DLL "/library.xbmc.pvr/libXBMC_pvr-x86-osx.so"
+#endif
+#elif defined(__x86_64__)
+#define PVR_HELPER_DLL "/library.xbmc.pvr/libXBMC_pvr-x86_64-linux.so"
+#elif defined(_POWERPC)
+#define PVR_HELPER_DLL "/library.xbmc.pvr/libXBMC_pvr-powerpc-linux.so"
+#elif defined(_POWERPC64)
+#define PVR_HELPER_DLL "/library.xbmc.pvr/libXBMC_pvr-powerpc64-linux.so"
+#else /* !__x86_64__ && !__powerpc__ */
+#define PVR_HELPER_DLL "/library.xbmc.pvr/libXBMC_pvr-i486-linux.so"
+#endif /* __x86_64__ */
+#endif /* _LINUX */
+
+#define DVD_TIME_BASE 1000000
+#define DVD_NOPTS_VALUE    (-1LL<<52) // should be possible to represent in both double and __int64
+
+class cHelper_libXBMC_pvr
+{
+public:
+  cHelper_libXBMC_pvr()
+  {
+    m_libXBMC_pvr = NULL;
+    m_Handle      = NULL;
+  }
+
+  ~cHelper_libXBMC_pvr()
+  {
+    if (m_libXBMC_pvr)
+    {
+      PVR_unregister_me();
+      dlclose(m_libXBMC_pvr);
+    }
+  }
+
+  bool RegisterMe(void *Handle)
+  {
+    m_Handle = Handle;
+
+    std::string libBasePath;
+    libBasePath  = ((cb_array*)m_Handle)->libPath;
+    libBasePath += PVR_HELPER_DLL;
+
+    m_libXBMC_pvr = dlopen(libBasePath.c_str(), RTLD_LAZY);
+    if (m_libXBMC_pvr == NULL)
+    {
+      fprintf(stderr, "Unable to load %s\n", dlerror());
+      return false;
+    }
+
+    PVR_register_me         = (int (*)(void *HANDLE))
+      dlsym(m_libXBMC_pvr, "PVR_register_me");
+    if (PVR_register_me == NULL)      { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
+
+    PVR_unregister_me       = (void (*)())
+      dlsym(m_libXBMC_pvr, "PVR_unregister_me");
+    if (PVR_unregister_me == NULL)    { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
+
+    TransferEpgEntry        = (void (*)(const PVRHANDLE handle, const PVR_PROGINFO *epgentry))
+      dlsym(m_libXBMC_pvr, "PVR_transfer_epg_entry");
+    if (TransferEpgEntry == NULL)       { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
+
+    TransferChannelEntry    = (void (*)(const PVRHANDLE handle, const PVR_CHANNEL *chan))
+      dlsym(m_libXBMC_pvr, "PVR_transfer_channel_entry");
+    if (TransferChannelEntry == NULL)   { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
+
+    TransferTimerEntry      = (void (*)(const PVRHANDLE handle, const PVR_TIMERINFO *timer))
+      dlsym(m_libXBMC_pvr, "PVR_transfer_timer_entry");
+    if (TransferTimerEntry == NULL)     { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
+
+    TransferRecordingEntry  = (void (*)(const PVRHANDLE handle, const PVR_RECORDINGINFO *recording))
+      dlsym(m_libXBMC_pvr, "PVR_transfer_recording_entry");
+    if (TransferRecordingEntry == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
+
+    AddMenuHook             = (void (*)(PVR_MENUHOOK *hook))
+      dlsym(m_libXBMC_pvr, "PVR_add_menu_hook");
+    if (AddMenuHook == NULL)            { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
+
+    Recording               = (void (*)(const char *Name, const char *FileName, bool On))
+      dlsym(m_libXBMC_pvr, "PVR_recording");
+    if (Recording == NULL)              { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
+
+    TriggerTimerUpdate      = (void (*)())
+      dlsym(m_libXBMC_pvr, "PVR_trigger_timer_update");
+    if (TriggerTimerUpdate == NULL)     { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
+
+    TriggerRecordingUpdate  = (void (*)())
+      dlsym(m_libXBMC_pvr, "PVR_trigger_recording_update");
+    if (TriggerRecordingUpdate == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
+
+#ifdef USE_DEMUX
+    FreeDemuxPacket         = (void (*)(DemuxPacket* pPacket))
+      dlsym(m_libXBMC_pvr, "PVR_free_demux_packet");
+    if (FreeDemuxPacket == NULL)        { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
+
+    AllocateDemuxPacket     = (DemuxPacket* (*)(int iDataSize))
+      dlsym(m_libXBMC_pvr, "PVR_allocate_demux_packet");
+    if (AllocateDemuxPacket == NULL)    { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; }
+#endif
+
+    return PVR_register_me(m_Handle) > 0;
+  }
+
+  void (*TransferEpgEntry)(const PVRHANDLE handle, const PVR_PROGINFO *epgentry);
+  void (*TransferChannelEntry)(const PVRHANDLE handle, const PVR_CHANNEL *chan);
+  void (*TransferTimerEntry)(const PVRHANDLE handle, const PVR_TIMERINFO *timer);
+  void (*TransferRecordingEntry)(const PVRHANDLE handle, const PVR_RECORDINGINFO *recording);
+  void (*AddMenuHook)(PVR_MENUHOOK *hook);
+  void (*Recording)(const char *Name, const char *FileName, bool On);
+  void (*TriggerTimerUpdate)();
+  void (*TriggerRecordingUpdate)();
+#ifdef USE_DEMUX
+  void (*FreeDemuxPacket)(DemuxPacket* pPacket);
+  DemuxPacket* (*AllocateDemuxPacket)(int iDataSize);
+#endif
+
+protected:
+  int (*PVR_register_me)(void *HANDLE);
+  void (*PVR_unregister_me)();
+
+private:
+  void *m_libXBMC_pvr;
+  void *m_Handle;
+  struct cb_array
+  {
+    const char* libPath;
+  };
+};
diff --git a/addons/pvr.hts/addon.xml b/addons/pvr.hts/addon.xml
new file mode 100644 (file)
index 0000000..66cf72b
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<addon
+  id="pvr.hts"
+  version="1.0.0"
+  name="Tvheadend HTSP Client"
+  provider-name="Alwin Esch, Team XBMC">
+  <requires>
+    <c-pluff version="0.1"/>
+  </requires>
+  <extension
+    point="xbmc.pvrclient"
+    library_linux="XBMC_Tvheadend.pvr"
+    library_osx="XBMC_Tvheadend.pvr"
+    library_wingl="XBMC_Tvheadend_win32.pvr"
+    library_windx="XBMC_Tvheadend_win32.pvr"/>
+  <extension point="xbmc.addon.metadata">
+    <summary>XBMC's frontend for Tvheadend</summary>
+    <description>Tvheadend frontend; supporting streaming of Live TV &amp; Recordings, EPG, Timers</description>
+    <disclaimer>This is unstable software! The authors are in no way responsible for failed recordings, incorrect timers, wasted hours, or any other undesirable effects..</disclaimer>
+    <platform>all</platform>
+  </extension>
+</addon>
diff --git a/addons/pvr.hts/icon.png b/addons/pvr.hts/icon.png
new file mode 100644 (file)
index 0000000..874edcc
Binary files /dev/null and b/addons/pvr.hts/icon.png differ
diff --git a/addons/pvr.hts/pthreadVC2.dll b/addons/pvr.hts/pthreadVC2.dll
new file mode 100644 (file)
index 0000000..fdea676
Binary files /dev/null and b/addons/pvr.hts/pthreadVC2.dll differ
diff --git a/addons/pvr.hts/pthreadVC2d.dll b/addons/pvr.hts/pthreadVC2d.dll
new file mode 100644 (file)
index 0000000..6fffdc4
Binary files /dev/null and b/addons/pvr.hts/pthreadVC2d.dll differ
diff --git a/addons/pvr.hts/resources/language/Dutch/strings.xml b/addons/pvr.hts/resources/language/Dutch/strings.xml
new file mode 100644 (file)
index 0000000..840e485
--- /dev/null
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<strings>
+    <!-- settings labels -->
+    <string id="30000">Tvheadend server naam of IP</string>
+    <string id="30001">HTTP Poort</string>
+    <string id="30002">HTSP Poort</string>
+    <string id="30003">Gebruikersnaam</string>
+    <string id="30004">Wachtwoord</string>
+    <string id="30005">Sla eerste I-frame over</string>
+    <string id="30006">Gids tijdscorrectie</string>
+</strings>
diff --git a/addons/pvr.hts/resources/language/English/strings.xml b/addons/pvr.hts/resources/language/English/strings.xml
new file mode 100644 (file)
index 0000000..17c523d
--- /dev/null
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<strings>
+    <!-- settings labels -->
+    <string id="30000">Tvheadend Hostname or IP</string>
+    <string id="30001">HTTP Port</string>
+    <string id="30002">HTSP Port</string>
+    <string id="30003">Username</string>
+    <string id="30004">Password</string>
+    <string id="30005">Skip First I-frame</string>
+    <string id="30006">EPG offset correction</string>
+</strings>
diff --git a/addons/pvr.hts/resources/language/German/strings.xml b/addons/pvr.hts/resources/language/German/strings.xml
new file mode 100644 (file)
index 0000000..31d4071
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<strings>
+    <!-- settings labels -->
+    <string id="30000">Tvheadend Hostname oder IP</string>
+    <string id="30001">HTTP Port</string>
+    <string id="30002">HTSP Port</string>
+    <string id="30003">Benutzername</string>
+    <string id="30004">Passwort</string>
+</strings>
diff --git a/addons/pvr.hts/resources/settings.xml b/addons/pvr.hts/resources/settings.xml
new file mode 100644 (file)
index 0000000..539b988
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<settings>
+    <setting id="host" type="text" label="30000" default="127.0.0.1" />
+    <setting id="http_port" type="integer" label="30001" default="9981" />
+    <setting id="htsp_port" type="integer" label="30002" default="9982" />
+    <setting id="user" type="text" label="30003" default="" />
+    <setting id="pass" type="text" label="30004" option="hidden" default="" />
+    <setting id="skip_I_frame" type="bool" label="30005"  default="true" />
+    <setting id="epg_offset_correction" type="enum" label="30006" values="-12|-11|-10|-9|-8|-7|-6|-5|-4|-3|-2|-1|0|1|2|3|4|5|6|7|8|9|10|11|12" default="12" />
+</settings>
diff --git a/addons/pvr.mythtv/addon.xml b/addons/pvr.mythtv/addon.xml
new file mode 100644 (file)
index 0000000..dac8f16
--- /dev/null
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<addon
+  id="pvr.mythtv"
+  version="1.0.0"
+  name="MythTV PVR Client"
+  provider-name="Miscelleaneous People">
+  <requires>
+    <c-pluff version="0.1"/>
+  </requires>
+  <extension
+    point="xbmc.pvrclient"
+    library_linux="XBMC_MythTV.pvr"
+    library_osx="XBMC_MythTV.pvr"
+    library_wingl="XBMC_MythTV_win32.pvr"
+    library_windx="XBMC_MythTV_win32.pvr"/>
+  <extension point="xbmc.addon.metadata">
+    <summary>XBMC's frontend for MythTV</summary>
+    <description>MythTV frontend; supporting streaming of Live TV &amp; Recordings, EPG, Timers</description>
+    <disclaimer>This is unstable software! The authors are in no way responsible for failed recordings, incorrect timers, wasted hours, or any other undesirable effects..</disclaimer>
+    <platform>all</platform>
+  </extension>
+</addon>
+
diff --git a/addons/pvr.mythtv/resources/language/English/strings.xml b/addons/pvr.mythtv/resources/language/English/strings.xml
new file mode 100644 (file)
index 0000000..bb3167c
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<strings>
+    <!-- settings labels -->
+    <string id="30000">MythTV Backend Hostname or IP</string>
+    <string id="30001">MythXML Port</string>
+</strings>
+
diff --git a/addons/pvr.mythtv/resources/settings.xml b/addons/pvr.mythtv/resources/settings.xml
new file mode 100644 (file)
index 0000000..f50e749
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<settings>
+    <setting id="host" type="text" label="30000" option="urlencoded" default="127.0.0.1" />
+    <setting id="mythXMLPort" type="integer" label="30001" default="6544" />
+</settings>
+
diff --git a/addons/pvr.team-mediaportal.tvserver/addon.xml b/addons/pvr.team-mediaportal.tvserver/addon.xml
new file mode 100644 (file)
index 0000000..cd5593c
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<addon
+  id="pvr.team-mediaportal.tvserver"
+  version="1.0.0"
+  name="MediaPortal PVR Client"
+  provider-name="Marcel Groothuis">
+  <requires>
+    <c-pluff version="0.1"/>
+  </requires>
+  <extension
+    point="xbmc.pvrclient"
+    library_linux="XBMC_MPTV.pvr"
+    library_osx="XBMC_MPTV.pvr"
+    library_wingl="XBMC_MPTV_WIN32.pvr"
+    library_windx="XBMC_MPTV_WIN32.pvr"/>
+  <extension point="xbmc.addon.metadata">
+    <summary>XBMC frontend for the MediaPortal TV Server</summary>
+    <description>MediaPortal TV Server frontend; supporting streaming of Live TV &amp; Recordings, EPG, Timers.</description>
+    <disclaimer>This is unstable software! The authors are in no way responsible for failed recordings, incorrect timers, wasted hours, or any other undesirable effects..</disclaimer>
+    <platform>all</platform>
+  </extension>
+</addon>
diff --git a/addons/pvr.team-mediaportal.tvserver/icon.jpg b/addons/pvr.team-mediaportal.tvserver/icon.jpg
new file mode 100644 (file)
index 0000000..dbc561e
Binary files /dev/null and b/addons/pvr.team-mediaportal.tvserver/icon.jpg differ
diff --git a/addons/pvr.team-mediaportal.tvserver/resources/language/Dutch/strings.xml b/addons/pvr.team-mediaportal.tvserver/resources/language/Dutch/strings.xml
new file mode 100644 (file)
index 0000000..7bbf986
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>\r
+<strings>\r
+    <!-- settings labels -->\r
+    <string id="30000">Mediaportal hostnaam</string>\r
+    <string id="30001">Mediaportal XBMC plugin poort</string>\r
+    <string id="30002">Alleen vrij te ontvangen kanalen</string>\r
+    <string id="30003">Toon radio</string>\r
+    <string id="30004">Tekenset conversie (UTF-8)</string>\r
+    <string id="30005">Verbindingstimeout (s)</string>\r
+    <string id="30006">Importeer alleen TV kanalen uit groep</string>\r
+    <string id="30007">Importeer alleen radio kanalen uit groep</string>\r
+    <string id="30008">Converteer hostnaam naar IP adres</string>\r
+    <string id="30009">EPG: Genre tekst inlezen (traag)</string>\r
+</strings>\r
diff --git a/addons/pvr.team-mediaportal.tvserver/resources/language/English/strings.xml b/addons/pvr.team-mediaportal.tvserver/resources/language/English/strings.xml
new file mode 100644 (file)
index 0000000..4c681c1
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>\r
+<strings>\r
+    <!-- settings labels -->\r
+    <string id="30000">Mediaportal Hostname</string>\r
+    <string id="30001">Mediaportal XBMC plugin Port</string>\r
+    <string id="30002">Free-to-air only</string>\r
+    <string id="30003">Include Radio</string>\r
+    <string id="30004">Character Set Conversion</string>\r
+    <string id="30005">Connect timeout (s)</string>\r
+    <string id="30006">Import only TV Channels from group</string>\r
+    <string id="30007">Import only Radio Channels from group</string>\r
+    <string id="30008">Convert hostname to IP-adress</string>\r
+    <string id="30009">EPG: Read genre strings (slow)</string>\r
+</strings>\r
diff --git a/addons/pvr.team-mediaportal.tvserver/resources/language/German/strings.xml b/addons/pvr.team-mediaportal.tvserver/resources/language/German/strings.xml
new file mode 100644 (file)
index 0000000..d995e18
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>\r
+<strings>\r
+    <!-- settings labels -->\r
+    <string id="30000">Mediaportal Hostname oder IP</string>\r
+    <string id="30001">Mediaportal Port</string>\r
+    <string id="30002">Nur frei empfangbare Kanäle</string>\r
+    <string id="30003">Zeige Radiokanäle</string>\r
+    <string id="30004">Textkonvertierung (UTF-8)</string>\r
+    <string id="30005">Verbindungszeitüberlauf (s)</string>\r
+    <string id="30006">Importiere nur TV Kanäle aus Gruppe</string>\r
+    <string id="30007">Importiere nur Radiokanäle aus Gruppe</string>\r
+    <string id="30008">Konvertiere Hostname nach IP-Adresse</string>\r
+    <string id="30009">EPG: Genre Texte hochladen (langsam)</string>\r
+</strings>\r
diff --git a/addons/pvr.team-mediaportal.tvserver/resources/settings.xml b/addons/pvr.team-mediaportal.tvserver/resources/settings.xml
new file mode 100644 (file)
index 0000000..523da8f
--- /dev/null
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>\r
+<settings>\r
+    <setting id="host" type="ipaddress" label="30000" default="127.0.0.1" />\r
+    <setting id="port" type="integer" label="30001" default="9596" />\r
+    <setting id="ftaonly" type="bool" label="30002" default="false" />\r
+    <setting id="useradio" type="bool" label="30003" default="true" />\r
+    <setting id="convertchar" type="bool" label="30004" default="false" />\r
+    <setting id="timeout" type="enum" label="30005" values="0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15" default="3"/>\r
+    <setting id="tvgroup" type="text" label="30006" default="" />\r
+    <setting id="radiogroup" type="text" label="30007" default="" />\r
+    <setting id="resolvertsphostname" type="bool" label="30008" default="false" />\r
+    <setting id="readgenre" type="bool" label="30009" default="false" />\r
+</settings>\r
diff --git a/addons/pvr.vdr.streamdev/addon.xml b/addons/pvr.vdr.streamdev/addon.xml
new file mode 100644 (file)
index 0000000..3367c23
--- /dev/null
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<addon
+  id="pvr.vdr.streamdev"
+  version="1.0.0"
+  name="VDR Streamdev Client"
+  provider-name="Alwin Esch, Team XBMC">
+  <requires>
+    <c-pluff version="0.1"/>
+  </requires>
+  <extension
+    point="xbmc.pvrclient"
+    library_linux="XBMC_VDR.pvr"
+    library_osx="XBMC_VDR.pvr"
+    library_wingl="XBMC_VDR_WIN32.pvr"
+    library_windx="XBMC_VDR_WIN32.pvr"/>
+  <extension point="xbmc.addon.metadata">
+    <summary>PVR client to connect VDR to XBMC over the Streamdev interface</summary>
+    <description>VDR frontend; supporting streaming of Live TV &amp; Recordings, EPG, Timers over the VNSI plugin</description>
+    <description lang="de">Erlaubt das wiedergeben von Live TV und Aufnahmen mittels VDR auf XBMC. Des weiteren werden EPG, Kanalsuche und Timer unterstützt.</description>
+    <disclaimer>This is unstable software! The authors are in no way responsible for failed recordings, incorrect timers, wasted hours, or any other undesirable effects..</disclaimer>
+    <platform>all</platform>
+  </extension>
+</addon>
diff --git a/addons/pvr.vdr.streamdev/icon.jpg b/addons/pvr.vdr.streamdev/icon.jpg
new file mode 100644 (file)
index 0000000..37be1c9
Binary files /dev/null and b/addons/pvr.vdr.streamdev/icon.jpg differ
diff --git a/addons/pvr.vdr.streamdev/pthreadVC2.dll b/addons/pvr.vdr.streamdev/pthreadVC2.dll
new file mode 100644 (file)
index 0000000..fdea676
Binary files /dev/null and b/addons/pvr.vdr.streamdev/pthreadVC2.dll differ
diff --git a/addons/pvr.vdr.streamdev/pthreadVC2d.dll b/addons/pvr.vdr.streamdev/pthreadVC2d.dll
new file mode 100644 (file)
index 0000000..6fffdc4
Binary files /dev/null and b/addons/pvr.vdr.streamdev/pthreadVC2d.dll differ
diff --git a/addons/pvr.vdr.streamdev/resources/data/noSignal.mpg b/addons/pvr.vdr.streamdev/resources/data/noSignal.mpg
new file mode 100644 (file)
index 0000000..ebf1ff1
Binary files /dev/null and b/addons/pvr.vdr.streamdev/resources/data/noSignal.mpg differ
diff --git a/addons/pvr.vdr.streamdev/resources/language/Dutch/strings.xml b/addons/pvr.vdr.streamdev/resources/language/Dutch/strings.xml
new file mode 100644 (file)
index 0000000..cb648d0
--- /dev/null
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>\r
+<strings>\r
+    <!-- settings labels -->\r
+    <string id="30000">VDR Hostnaam</string>\r
+    <string id="30001">Streamdev Poort</string>\r
+    <string id="30002">Alleen Free-to-air</string>\r
+    <string id="30003">Radio zenders laten zien</string>\r
+    <string id="30004">Karakter set conversie</string>\r
+    <string id="30005">Connectie timeout (s)</string>\r
+    <string id="30006">Negeer kanalen zonder VPID and APID</string>\r
+    <string id="30007">Berichten vanuit VDR toestaan</string>\r
+    <string id="30008">Prioriteit</string>\r
+    <string id="30009">Lees opnames van directory</string>\r
+    <string id="30010">VDR opname directory</string>\r
+</strings>\r
diff --git a/addons/pvr.vdr.streamdev/resources/language/English/strings.xml b/addons/pvr.vdr.streamdev/resources/language/English/strings.xml
new file mode 100644 (file)
index 0000000..11b6614
--- /dev/null
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>\r
+<strings>\r
+    <!-- settings labels -->\r
+    <string id="30000">VDR Hostname</string>\r
+    <string id="30001">Streamdev Port</string>\r
+    <string id="30002">Free-to-air only</string>\r
+    <string id="30003">Include Radio</string>\r
+    <string id="30004">Character Set Conversion</string>\r
+    <string id="30005">Connect timeout (s)</string>\r
+    <string id="30006">Ignore Channels without VPID and APID</string>\r
+    <string id="30007">Allow VDR Messages</string>\r
+    <string id="30008">Priority</string>\r
+    <string id="30009">Read recordings from directory</string>\r
+    <string id="30010">VDR recordings directory</string>\r
+</strings>\r
diff --git a/addons/pvr.vdr.streamdev/resources/language/German/strings.xml b/addons/pvr.vdr.streamdev/resources/language/German/strings.xml
new file mode 100644 (file)
index 0000000..cbea98d
--- /dev/null
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>\r
+<strings>\r
+    <!-- settings labels -->\r
+    <string id="30000">VDR Hostname oder IP</string>\r
+    <string id="30001">Streamdev Port</string>\r
+    <string id="30002">Nur frei empfangbare Kanäle</string>\r
+    <string id="30003">Zeige Radiokanäle</string>\r
+    <string id="30004">Textkonvertierung (UTF-8)</string>\r
+    <string id="30005">Verbindungszeitüberlauf (s)</string>\r
+    <string id="30006">Ignoriere Känale ohne VPID und APID</string>\r
+    <string id="30007">VDR Nachrichten erlauben</string>\r
+    <string id="30008">Priorität</string>\r
+    <string id="30009">Aufnahmen aus Ordner lesen</string>\r
+    <string id="30010">VDR Aufnahmeordner</string>\r
+</strings>\r
diff --git a/addons/pvr.vdr.streamdev/resources/settings.xml b/addons/pvr.vdr.streamdev/resources/settings.xml
new file mode 100644 (file)
index 0000000..1516292
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>\r
+<settings>\r
+    <setting id="host" type="text" label="30000" default="127.0.0.1" />\r
+    <setting id="port" type="integer" label="30001" default="2004" />\r
+    <setting id="priority" type="enum" label="30008" values="-1|0|5|10|15|20|25|30|35|40|45|50|55|60|65|70|75|80|85|90|95|99|100" default="99"/>\r
+    <setting id="ftaonly" type="bool" label="30002" default="false" />\r
+    <setting id="useradio" type="bool" label="30003" default="true" />\r
+    <setting id="convertchar" type="bool" label="30004" default="true" />\r
+    <setting id="timeout" type="enum" label="30005" values="0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15" default="3"/>\r
+    <setting id="ignorechannels" type="bool" label="30006" default="true" />\r
+    <setting id="handlemessages" type="bool" label="30007" default="true" />\r
+    <setting id="usedirectory" type="bool" label="30009" default="false" />\r
+    <setting id="recordingdir" type="folder" label="30010" default="/" />\r
+</settings>\r
diff --git a/addons/pvr.vdr.vnsi/addon.xml b/addons/pvr.vdr.vnsi/addon.xml
new file mode 100644 (file)
index 0000000..a0b1e03
--- /dev/null
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<addon
+  id="pvr.vdr.vnsi"
+  version="1.0.0"
+  name="VDR VNSI Client"
+  provider-name="Alwin Esch, Team XBMC">
+  <requires>
+    <c-pluff version="0.1"/>
+  </requires>
+  <extension
+    point="xbmc.pvrclient"
+    library_linux="XBMC_VDR_vnsi.pvr"
+    library_osx="XBMC_VDR_vnsi.pvr"
+    library_wingl="XBMC_VDR_vnsi_WIN32.pvr"
+    library_windx="XBMC_VDR_vnsi_WIN32.pvr"/>
+  <extension point="xbmc.addon.metadata">
+    <summary>PVR client to connect VDR to XBMC over the VNSI interface</summary>
+    <description>VDR frontend; supporting streaming of Live TV &amp; Recordings, EPG, Timers over the VNSI plugin</description>
+    <description lang="de">Erlaubt das wiedergeben von Live TV und Aufnahmen mittels VDR auf XBMC. Des weiteren werden EPG, Kanalsuche und Timer unterstützt.</description>
+    <disclaimer>This is unstable software! The authors are in no way responsible for failed recordings, incorrect timers, wasted hours, or any other undesirable effects..</disclaimer>
+    <platform>all</platform>
+  </extension>
+</addon>
diff --git a/addons/pvr.vdr.vnsi/icon.jpg b/addons/pvr.vdr.vnsi/icon.jpg
new file mode 100644 (file)
index 0000000..d021160
Binary files /dev/null and b/addons/pvr.vdr.vnsi/icon.jpg differ
diff --git a/addons/pvr.vdr.vnsi/pthreadVC2.dll b/addons/pvr.vdr.vnsi/pthreadVC2.dll
new file mode 100644 (file)
index 0000000..fdea676
Binary files /dev/null and b/addons/pvr.vdr.vnsi/pthreadVC2.dll differ
diff --git a/addons/pvr.vdr.vnsi/pthreadVC2d.dll b/addons/pvr.vdr.vnsi/pthreadVC2d.dll
new file mode 100644 (file)
index 0000000..6fffdc4
Binary files /dev/null and b/addons/pvr.vdr.vnsi/pthreadVC2d.dll differ
diff --git a/addons/pvr.vdr.vnsi/resources/language/Dutch/strings.xml b/addons/pvr.vdr.vnsi/resources/language/Dutch/strings.xml
new file mode 100644 (file)
index 0000000..7a88831
--- /dev/null
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<strings>
+    <!-- settings labels -->
+    <string id="30000">VDR hostnaam of IP adres</string>
+    <string id="30001">VNSI Poort</string>
+    <string id="30002">Prioriteit</string>
+    <string id="30003">Character Set Conversie</string>
+    <string id="30004">Connectie timeout (s)</string>
+    <string id="30005">Berichten vanuit VDR toestaan</string>
+    <string id="30006">Lees opnames van directory</string>^M
+    <string id="30007">VDR opname directory</string>^M
+    <string id="30008">Kanalen scannen</string>
+    <string id="30009">Kanalen scan - opties</string>
+    <string id="30010">kanalen scan starten</string>
+    <string id="30011">Bron Type</string>
+    <string id="30012">TV kanalen</string>
+    <string id="30013">Radio kanalen</string>
+    <string id="30014">FTA kanalen</string>
+    <string id="30015">Gecodeerde kanalen</string>
+    <string id="30016">HD kanalen</string>
+    <string id="30017">Land</string>
+    <string id="30018">Kabel Inversion</string>
+    <string id="30019">Kabel Symbolrate</string>
+    <string id="30020">Kabel modulation</string>
+    <string id="30021">Antenne Inversion</string>
+    <string id="30022">Sateliet</string>
+    <string id="30023">ATSC type</string>
+    <string id="30024">Terug</string>
+    <string id="30025">Kanalen zoeken - bezig... %i %%</string>
+    <string id="30026">Type:</string>
+    <string id="30027">Device:</string>
+    <string id="30028">Scan: %i</string>
+    <string id="30029">Signaal: %i %%</string>
+    <string id="30030">Nieuwe kanalen: %i</string>
+    <string id="30031">Alle kanalen: %i</string>
+    <string id="30032">Analoge TV</string>
+    <string id="30033">Analoge Radio</string>
+    <string id="30034">Transponder:</string>
+    <string id="30035">Nieuwe kanalen</string>
+    <string id="30036">Kanalen zoeken - Klaar</string>
+    <string id="30037">Geen device beschikbaar - exiting</string>
+    <string id="30038">Geen DVB-S2 apparaat beschikbaar - We vallen terug op DVB-S</string>
+    <string id="30039">Bezig</string>
+    <string id="30040">Gestopt</string>
+    <string id="30041">Klaar</string>
+    <string id="30042">Kanalen scan - Geanuleerd</string>
+    <string id="30043">Kanalen scan - Fout</string>
+</strings>
diff --git a/addons/pvr.vdr.vnsi/resources/language/English/strings.xml b/addons/pvr.vdr.vnsi/resources/language/English/strings.xml
new file mode 100644 (file)
index 0000000..19006dc
--- /dev/null
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<strings>
+    <!-- settings labels -->
+    <string id="30000">VDR Hostname or IP</string>
+    <string id="30001">VNSI Port</string>
+    <string id="30002">Priority</string>
+    <string id="30003">Character Set Conversion</string>
+    <string id="30004">Connect timeout (s)</string>
+    <string id="30005">Allow VDR Messages</string>
+    <string id="30006">Read recordings from directory</string>
+    <string id="30007">VDR recordings directory</string>
+    <string id="30008">Channel search</string>
+    <string id="30009">Channel search - Settings</string>
+    <string id="30010">Start Channel search</string>
+    <string id="30011">Source Type</string>
+    <string id="30012">TV channels</string>
+    <string id="30013">Radio channels</string>
+    <string id="30014">FTA channels</string>
+    <string id="30015">Scrambled channels</string>
+    <string id="30016">HD channels</string>
+    <string id="30017">Country</string>
+    <string id="30018">Cable Inversion</string>
+    <string id="30019">Cable Symbolrate</string>
+    <string id="30020">Cable modulation</string>
+    <string id="30021">Terr Inversion</string>
+    <string id="30022">Satellite</string>
+    <string id="30023">ATSC Type</string>
+    <string id="30024">Back</string>
+    <string id="30025">Channel search - running... %i %%</string>
+    <string id="30026">Type:</string>
+    <string id="30027">Device:</string>
+    <string id="30028">Scan: %i</string>
+    <string id="30029">Signal: %i %%</string>
+    <string id="30030">New channels: %i</string>
+    <string id="30031">All channels: %i</string>
+    <string id="30032">Analog TV</string>
+    <string id="30033">Analog Radio</string>
+    <string id="30034">Transponder:</string>
+    <string id="30035">New channels</string>
+    <string id="30036">Channel search - Finished</string>
+    <string id="30037">No device available - exiting</string>
+    <string id="30038">No DVB-S2 device available - trying fallback to DVB-S</string>
+    <string id="30039">Running</string>
+    <string id="30040">Stopped</string>
+    <string id="30041">Finished</string>
+    <string id="30042">Channel search - Canceled</string>
+    <string id="30043">Channel search - Error</string>
+</strings>
diff --git a/addons/pvr.vdr.vnsi/resources/language/German/strings.xml b/addons/pvr.vdr.vnsi/resources/language/German/strings.xml
new file mode 100644 (file)
index 0000000..99f1435
--- /dev/null
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<strings>
+    <!-- settings labels -->
+    <string id="30000">VDR Hostname oder IP</string>
+    <string id="30001">VNSI Port</string>
+    <string id="30002">Priorität</string>
+    <string id="30003">Textkonvertierung (UTF-8)</string>
+    <string id="30004">Verbindungszeitüberlauf (s)</string>
+    <string id="30005">VDR Nachrichten erlauben</string>
+    <string id="30006">Aufnahmen aus Ordner lesen</string>
+    <string id="30007">VDR Aufnahmeordner</string>
+    <string id="30008">Kanalsuche</string>
+    <string id="30009">Kanalsuche - Einstellungen</string>
+    <string id="30010">Kanalsuche starten</string>
+    <string id="30011">Empfangsart</string>
+    <string id="30012">TV Kanäle</string>
+    <string id="30013">Radio Kanäle</string>
+    <string id="30014">Frei empfangbare Kanäle</string>
+    <string id="30015">Verschlüsselte Kanäle</string>
+    <string id="30016">HD Kanäle</string>
+    <string id="30017">Land</string>
+    <string id="30018">Kabel Inversion</string>
+    <string id="30019">Kabel Symbolrate</string>
+    <string id="30020">Kabel Modulation</string>
+    <string id="30021">Terrestrisch Inversion</string>
+    <string id="30022">Satellit</string>
+    <string id="30023">ATSC Type</string>
+    <string id="30024">Zurück</string>
+    <string id="30025">Kanalsuche - läuft... %i %%</string>
+    <string id="30026">Empfangsart:</string>
+    <string id="30027">Gerät:</string>
+    <string id="30028">Fortschritt: %i</string>
+    <string id="30029">Signal: %i %%</string>
+    <string id="30030">Neue Kanäle: %i</string>
+    <string id="30031">Alle Kanäle: %i</string>
+    <string id="30032">Analog TV</string>
+    <string id="30033">Analog Radio</string>
+    <string id="30034">Transponder:</string>
+    <string id="30035">Neue Kanäle</string>
+    <string id="30036">Kanalsuche - Abgeschlossen</string>
+    <string id="30037">Kein Empfangsgerät verfügbar</string>
+    <string id="30038">Kein DVB-S2 Empfangsgerät verfügbar - versuche DVB-S</string>
+    <string id="30039">Läuft...</string>
+    <string id="30040">Angehalten</string>
+    <string id="30041">Fertig</string>
+    <string id="30042">Kanalsuche - Abgebrochen</string>
+    <string id="30043">Kanalsuche - Fehler</string>
+</strings>
diff --git a/addons/pvr.vdr.vnsi/resources/settings.xml b/addons/pvr.vdr.vnsi/resources/settings.xml
new file mode 100644 (file)
index 0000000..d557cab
--- /dev/null
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<settings>
+    <setting id="host" type="text" label="30000" default="127.0.0.1" />
+    <setting id="port" type="integer" label="30001" default="34890" />
+    <setting id="priority" type="enum" label="30002" values="-1|0|5|10|15|20|25|30|35|40|45|50|55|60|65|70|75|80|85|90|95|99|100" default="99"/>
+    <setting id="convertchar" type="bool" label="30003" default="true" />
+    <setting id="timeout" type="enum" label="30004" values="0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15" default="3"/>
+    <setting id="handlemessages" type="bool" label="30005" default="true" />
+    <setting id="usedirectory" type="bool" label="30006" default="false" />
+    <setting id="recordingdir" type="folder" label="30007" default="/" />
+</settings>
diff --git a/addons/pvr.vdr.vnsi/resources/skins/Confluence/720p/ChannelScan.xml b/addons/pvr.vdr.vnsi/resources/skins/Confluence/720p/ChannelScan.xml
new file mode 100644 (file)
index 0000000..e4f6338
--- /dev/null
@@ -0,0 +1,733 @@
+<window>
+  <defaultcontrol always="true">5</defaultcontrol>
+  <allowoverlay>no</allowoverlay>
+  <views>2</views>
+  <controls>
+    <include>CommonSettingsBackground</include>
+    <include>CommonMediaPlayingBackground</include>
+    <control type="group">
+      <posx>90</posx>
+      <posy>50</posy>
+      <animation type="WindowOpen" reversible="false">
+        <effect type="zoom" start="80" end="100" center="640,360" easing="out" tween="back" time="300" />
+        <effect type="fade" start="0" end="100" time="300" />
+      </animation>
+      <animation type="WindowClose" reversible="false">
+        <effect type="zoom" start="100" end="80" center="640,360" easing="in" tween="back" time="300" />
+        <effect type="fade" start="100" end="0" time="300" />
+      </animation>
+      <control type="image">
+        <posx>0</posx>
+        <posy>0</posy>
+        <width>1100</width>
+        <height>640</height>
+        <texture border="20">DialogBack.png</texture>
+      </control>
+      <control type="image">
+        <description>LOGO</description>
+        <posx>30</posx>
+        <posy>15</posy>
+        <width>220</width>
+        <height>80</height>
+        <aspectratio>keep</aspectratio>
+        <texture>Confluence_Logo.png</texture>
+      </control>
+      <control type="image">
+        <posx>268</posx>
+        <posy>10</posy>
+        <width>790</width>
+        <height>618</height>
+        <texture border="5">black-back2.png</texture>
+      </control>
+      <control type="image">
+        <posx>268</posx>
+        <posy>10</posy>
+        <width>804</width>
+        <height>70</height>
+        <aspectratio>stretch</aspectratio>
+        <texture>GlassTitleBar.png</texture>
+      </control>
+      <control type="label" id ="8">
+        <description>header label</description>
+        <posx>300</posx>
+        <posy>20</posy>
+        <width>740</width>
+        <height>30</height>
+        <font>font16caps</font>
+        <label>$ADDON[pvr.vdr.vnsi 30009]</label>
+        <align>left</align>
+        <aligny>center</aligny>
+        <textcolor>white</textcolor>
+        <shadowcolor>black</shadowcolor>
+      </control>
+      <control type="button" id ="5">
+        <description>Start/Stop Channel search</description>
+        <posx>10</posx>
+        <posy>90</posy>
+        <width>260</width>
+        <height>60</height>
+        <textoffsety>13</textoffsety>
+        <label>$ADDON[pvr.vdr.vnsi 30010]</label>
+        <font>font13_title</font>
+        <align>right</align>
+        <aligny>center</aligny>
+        <texturenofocus border="5">MenuItemNF.png</texturenofocus>
+        <texturefocus border="5">MenuItemFO.png</texturefocus>
+        <onleft>2</onleft>
+        <onright>10</onright>
+        <onup>6</onup>
+        <ondown>6</ondown>
+      </control>
+      <control type="button" id ="6">
+        <description>Cancel</description>
+        <posx>10</posx>
+        <posy>150</posy>
+        <width>260</width>
+        <height>60</height>
+        <textoffsety>13</textoffsety>
+        <label>$ADDON[pvr.vdr.vnsi 30024]</label>
+        <font>font13_title</font>
+        <align>right</align>
+        <aligny>center</aligny>
+        <texturenofocus border="5">MenuItemNF.png</texturenofocus>
+        <texturefocus border="5">MenuItemFO.png</texturefocus>
+        <onleft>10</onleft>
+        <onright>10</onright>
+        <onup>5</onup>
+        <ondown>5</ondown>
+        <visible>IsEmpty(Window.Property(Scanning))</visible>
+      </control>
+      <control type="group">
+        <visible>IsEmpty(Window.Property(Scanning))</visible>
+        <control type="spincontrolex" id="10">
+          <description>Source Type</description>
+          <posx>268</posx>
+          <posy>80</posy>
+          <width>790</width>
+          <height>40</height>
+          <font>font13</font>
+          <textcolor>grey2</textcolor>
+          <focusedcolor>white</focusedcolor>
+          <texturefocus border="0,2,0,2">MenuItemFO.png</texturefocus>
+          <texturenofocus border="0,2,0,2">MenuItemNF.png</texturenofocus>
+          <aligny>center</aligny>
+          <label>$ADDON[pvr.vdr.vnsi 30011]</label>
+          <onright>5</onright>
+          <onleft>5</onleft>
+          <onup>22</onup>
+          <ondown>11</ondown>
+        </control>
+        <control type="radiobutton" id="11">
+          <description>Default RadioButton</description>
+          <posx>268</posx>
+          <posy>120</posy>
+          <width>790</width>
+          <height>40</height>
+          <font>font13</font>
+          <textcolor>grey2</textcolor>
+          <focusedcolor>white</focusedcolor>
+          <texturefocus border="0,2,0,2">MenuItemFO.png</texturefocus>
+          <texturenofocus border="0,2,0,2">MenuItemNF.png</texturenofocus>
+          <label>$ADDON[pvr.vdr.vnsi 30012]</label>
+          <onright>5</onright>
+          <onleft>5</onleft>
+          <onup>10</onup>
+          <ondown>12</ondown>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(10)">Conditional</animation>
+        </control>
+        <control type="radiobutton" id="12">
+          <description>Default RadioButton</description>
+          <posx>268</posx>
+          <posy>160</posy>
+          <width>790</width>
+          <height>40</height>
+          <font>font13</font>
+          <textcolor>grey2</textcolor>
+          <focusedcolor>white</focusedcolor>
+          <texturefocus border="0,2,0,2">MenuItemFO.png</texturefocus>
+          <texturenofocus border="0,2,0,2">MenuItemNF.png</texturenofocus>
+          <label>$ADDON[pvr.vdr.vnsi 30013]</label>
+          <onright>5</onright>
+          <onleft>5</onleft>
+          <onup>11</onup>
+          <ondown>13</ondown>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(10)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(11)">Conditional</animation>
+        </control>
+        <control type="radiobutton" id="13">
+          <description>Default RadioButton</description>
+          <posx>268</posx>
+          <posy>200</posy>
+          <width>790</width>
+          <height>40</height>
+          <font>font13</font>
+          <textcolor>grey2</textcolor>
+          <focusedcolor>white</focusedcolor>
+          <texturefocus border="0,2,0,2">MenuItemFO.png</texturefocus>
+          <texturenofocus border="0,2,0,2">MenuItemNF.png</texturenofocus>
+          <label>$ADDON[pvr.vdr.vnsi 30014]</label>
+          <onright>5</onright>
+          <onleft>5</onleft>
+          <onup>12</onup>
+          <ondown>14</ondown>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(10)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(11)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(12)">Conditional</animation>
+        </control>
+        <control type="radiobutton" id="14">
+          <description>Default RadioButton</description>
+          <posx>268</posx>
+          <posy>240</posy>
+          <width>790</width>
+          <height>40</height>
+          <font>font13</font>
+          <textcolor>grey2</textcolor>
+          <focusedcolor>white</focusedcolor>
+          <texturefocus border="0,2,0,2">MenuItemFO.png</texturefocus>
+          <texturenofocus border="0,2,0,2">MenuItemNF.png</texturenofocus>
+          <label>$ADDON[pvr.vdr.vnsi 30015]</label>
+          <onright>5</onright>
+          <onleft>5</onleft>
+          <onup>13</onup>
+          <ondown>15</ondown>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(10)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(11)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(12)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(13)">Conditional</animation>
+        </control>
+        <control type="radiobutton" id="15">
+          <description>Default RadioButton</description>
+          <posx>268</posx>
+          <posy>280</posy>
+          <width>790</width>
+          <height>40</height>
+          <font>font13</font>
+          <textcolor>grey2</textcolor>
+          <focusedcolor>white</focusedcolor>
+          <texturefocus border="0,2,0,2">MenuItemFO.png</texturefocus>
+          <texturenofocus border="0,2,0,2">MenuItemNF.png</texturenofocus>
+          <label>$ADDON[pvr.vdr.vnsi 30016]</label>
+          <onright>5</onright>
+          <onleft>5</onleft>
+          <onup>14</onup>
+          <ondown>16</ondown>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(10)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(11)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(12)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(13)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(14)">Conditional</animation>
+        </control>
+        <control type="spincontrolex" id="16">
+          <description>Country selection</description>
+          <posx>268</posx>
+          <posy>320</posy>
+          <width>790</width>
+          <height>40</height>
+          <font>font13</font>
+          <textcolor>grey2</textcolor>
+          <focusedcolor>white</focusedcolor>
+          <texturefocus border="0,2,0,2">MenuItemFO.png</texturefocus>
+          <texturenofocus border="0,2,0,2">MenuItemNF.png</texturenofocus>
+          <aligny>center</aligny>
+          <label>$ADDON[pvr.vdr.vnsi 30017]</label>
+          <onright>5</onright>
+          <onleft>5</onleft>
+          <onup>15</onup>
+          <ondown>17</ondown>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(10)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(11)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(12)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(13)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(14)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(15)">Conditional</animation>
+        </control>
+        <control type="spincontrolex" id="17">
+          <description>Satellite selection</description>
+          <posx>268</posx>
+          <posy>360</posy>
+          <width>790</width>
+          <height>40</height>
+          <font>font13</font>
+          <textcolor>grey2</textcolor>
+          <focusedcolor>white</focusedcolor>
+          <texturefocus border="0,2,0,2">MenuItemFO.png</texturefocus>
+          <texturenofocus border="0,2,0,2">MenuItemNF.png</texturenofocus>
+          <aligny>center</aligny>
+          <label>$ADDON[pvr.vdr.vnsi 30022]</label>
+          <onright>5</onright>
+          <onleft>5</onleft>
+          <onup>16</onup>
+          <ondown>18</ondown>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(10)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(11)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(12)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(13)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(14)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(15)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(16)">Conditional</animation>
+        </control>
+        <control type="spincontrolex" id="18">
+          <description>DVB-C Inversion</description>
+          <posx>268</posx>
+          <posy>400</posy>
+          <width>790</width>
+          <height>40</height>
+          <font>font13</font>
+          <textcolor>grey2</textcolor>
+          <focusedcolor>white</focusedcolor>
+          <texturefocus border="0,2,0,2">MenuItemFO.png</texturefocus>
+          <texturenofocus border="0,2,0,2">MenuItemNF.png</texturenofocus>
+          <aligny>center</aligny>
+          <label>$ADDON[pvr.vdr.vnsi 30018]</label>
+          <onright>5</onright>
+          <onleft>5</onleft>
+          <onup>17</onup>
+          <ondown>29</ondown>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(10)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(11)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(12)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(13)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(14)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(15)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(16)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(17)">Conditional</animation>
+        </control>
+        <control type="spincontrolex" id="29">
+          <description>DVB-C Symbolrate</description>
+          <posx>268</posx>
+          <posy>440</posy>
+          <width>790</width>
+          <height>40</height>
+          <font>font13</font>
+          <textcolor>grey2</textcolor>
+          <focusedcolor>white</focusedcolor>
+          <texturefocus border="0,2,0,2">MenuItemFO.png</texturefocus>
+          <texturenofocus border="0,2,0,2">MenuItemNF.png</texturenofocus>
+          <aligny>center</aligny>
+          <label>$ADDON[pvr.vdr.vnsi 30019]</label>
+          <onright>5</onright>
+          <onleft>5</onleft>
+          <onup>18</onup>
+          <ondown>20</ondown>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(10)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(11)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(12)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(13)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(14)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(15)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(16)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(17)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(18)">Conditional</animation>
+        </control>
+        <control type="spincontrolex" id="20">
+          <description>DVB-C QAM</description>
+          <posx>268</posx>
+          <posy>480</posy>
+          <width>790</width>
+          <height>40</height>
+          <font>font13</font>
+          <textcolor>grey2</textcolor>
+          <focusedcolor>white</focusedcolor>
+          <texturefocus border="0,2,0,2">MenuItemFO.png</texturefocus>
+          <texturenofocus border="0,2,0,2">MenuItemNF.png</texturenofocus>
+          <aligny>center</aligny>
+          <label>$ADDON[pvr.vdr.vnsi 30020]</label>
+          <onright>5</onright>
+          <onleft>5</onleft>
+          <onup>29</onup>
+          <ondown>21</ondown>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(10)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(11)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(12)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(13)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(14)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(15)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(16)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(17)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(18)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(29)">Conditional</animation>
+        </control>
+        <control type="spincontrolex" id="21">
+          <description>DVB-T Inversion</description>
+          <posx>268</posx>
+          <posy>520</posy>
+          <width>790</width>
+          <height>40</height>
+          <font>font13</font>
+          <textcolor>grey2</textcolor>
+          <focusedcolor>white</focusedcolor>
+          <texturefocus border="0,2,0,2">MenuItemFO.png</texturefocus>
+          <texturenofocus border="0,2,0,2">MenuItemNF.png</texturenofocus>
+          <aligny>center</aligny>
+          <label>$ADDON[pvr.vdr.vnsi 30021]</label>
+          <onright>5</onright>
+          <onleft>5</onleft>
+          <onup>20</onup>
+          <ondown>22</ondown>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(10)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(11)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(12)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(13)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(14)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(15)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(16)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(17)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(18)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(29)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(20)">Conditional</animation>
+        </control>
+        <control type="spincontrolex" id="22">
+          <description>ATSC Type</description>
+          <posx>268</posx>
+          <posy>560</posy>
+          <width>790</width>
+          <height>40</height>
+          <font>font13</font>
+          <textcolor>grey2</textcolor>
+          <focusedcolor>white</focusedcolor>
+          <texturefocus border="0,2,0,2">MenuItemFO.png</texturefocus>
+          <texturenofocus border="0,2,0,2">MenuItemNF.png</texturenofocus>
+          <aligny>center</aligny>
+          <label>$ADDON[pvr.vdr.vnsi 30023]</label>
+          <onright>5</onright>
+          <onleft>5</onleft>
+          <onup>21</onup>
+          <ondown>10</ondown>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(10)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(11)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(12)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(13)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(14)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(15)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(16)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(17)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(18)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(29)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(20)">Conditional</animation>
+          <animation effect="slide" start="0,0" end="0,-40" time="100" condition="!Control.IsVisible(21)">Conditional</animation>
+        </control>
+      </control>
+      <control type="group">
+        <visible>!IsEmpty(Window.Property(Scanning))</visible>
+        <control type="progress" id ="32">
+          <description>Progressbar</description>
+          <posx>275</posx>
+          <posy>60</posy>
+          <width>780</width>
+          <height>14</height>
+        </control>
+        <control type="label">
+          <description>type label</description>
+          <posx>275</posx>
+          <posy>85</posy>
+          <width>250</width>
+          <height>30</height>
+          <font>font13</font>
+          <label>$ADDON[pvr.vdr.vnsi 30026]</label>
+          <align>left</align>
+          <aligny>center</aligny>
+          <textcolor>grey</textcolor>
+          <shadowcolor>black</shadowcolor>
+        </control>
+        <control type="label" id ="30">
+          <description>type value</description>
+          <posx>1040</posx>
+          <posy>85</posy>
+          <width>500</width>
+          <height>30</height>
+          <font>font13</font>
+          <label>-</label>
+          <align>right</align>
+          <aligny>center</aligny>
+          <textcolor>white</textcolor>
+          <shadowcolor>black</shadowcolor>
+        </control>
+        <control type="label">
+          <description>device label</description>
+          <posx>275</posx>
+          <posy>115</posy>
+          <width>250</width>
+          <height>30</height>
+          <font>font13</font>
+          <label>$ADDON[pvr.vdr.vnsi 30027]</label>
+          <align>left</align>
+          <aligny>center</aligny>
+          <textcolor>white</textcolor>
+          <shadowcolor>black</shadowcolor>
+        </control>
+        <control type="label" id ="31">
+          <description>device value</description>
+          <posx>1040</posx>
+          <posy>115</posy>
+          <width>500</width>
+          <height>30</height>
+          <font>font13</font>
+          <label>-</label>
+          <align>right</align>
+          <aligny>center</aligny>
+          <textcolor>white</textcolor>
+          <shadowcolor>black</shadowcolor>
+        </control>
+        <control type="label">
+          <description>transponder label</description>
+          <posx>275</posx>
+          <posy>145</posy>
+          <width>250</width>
+          <height>30</height>
+          <font>font13</font>
+          <label>$ADDON[pvr.vdr.vnsi 30034]</label>
+          <align>left</align>
+          <aligny>center</aligny>
+          <textcolor>white</textcolor>
+          <shadowcolor>black</shadowcolor>
+        </control>
+        <control type="label" id ="33">
+          <description>transponder value</description>
+          <posx>1040</posx>
+          <posy>145</posy>
+          <width>500</width>
+          <height>30</height>
+          <font>font13</font>
+          <label>-</label>
+          <align>right</align>
+          <aligny>center</aligny>
+          <textcolor>white</textcolor>
+          <shadowcolor>black</shadowcolor>
+        </control>
+        <control type="progress" id ="35">
+          <description>Progressbar</description>
+          <posx>30</posx>
+          <posy>160</posy>
+          <width>220</width>
+          <height>50</height>
+        </control>
+        <control type="label" id ="34">
+          <description>Signal label</description>
+          <posx>40</posx>
+          <posy>168</posy>
+          <width>250</width>
+          <height>30</height>
+          <font>font13</font>
+          <label>-</label>
+          <align>left</align>
+          <aligny>center</aligny>
+          <textcolor>white</textcolor>
+          <shadowcolor>black</shadowcolor>
+        </control>
+        <control type="image">
+          <posx>215</posx>
+          <posy>170</posy>
+          <width>30</width>
+          <height>30</height>
+          <aspectratio>stretch</aspectratio>
+          <texture>amt-overlay-watched.png</texture>
+          <visible>!IsEmpty(Window.Property(Locked))</visible>
+        </control>
+        <control type="list" id="2">
+          <posx>290</posx>
+          <posy>180</posy>
+          <width>750</width>
+          <height>400</height>
+          <onup>2</onup>
+          <ondown>2</ondown>
+          <onleft>10</onleft>
+          <onright>60</onright>
+          <pagecontrol>60</pagecontrol>
+          <scrolltime>200</scrolltime>
+          <itemlayout height="40" width="750">
+            <control type="image">
+              <posx>0</posx>
+              <posy>0</posy>
+              <width>750</width>
+              <height>40</height>
+              <aspectratio>stretch</aspectratio>
+              <texture border="5">MenuItemNF.png</texture>
+            </control>
+            <control type="image">
+              <posx>710</posx>
+              <posy>5</posy>
+              <width>40</width>
+              <height>30</height>
+              <aspectratio>stretch</aspectratio>
+              <texture>OverlayLocked.png</texture>
+              <visible>!IsEmpty(ListItem.Property(IsEncrypted))</visible>
+            </control>
+            <control type="image">
+              <posx>690</posx>
+              <posy>7</posy>
+              <width>60</width>
+              <height>25</height>
+              <aspectratio>stretch</aspectratio>
+              <texture>OverlayHD.png</texture>
+              <animation effect="slide" start="0,0" end="-45,0" time="100" condition="!IsEmpty(ListItem.Property(IsEncrypted))">Conditional</animation>
+              <visible>!IsEmpty(ListItem.Property(IsHD))</visible>
+            </control>
+            <control type="image">
+              <posx>0</posx>
+              <posy>2</posy>
+              <width>36</width>
+              <height>36</height>
+              <aspectratio>stretch</aspectratio>
+              <texture>DefaultVideoCover.png</texture>
+              <visible>IsEmpty(ListItem.Property(IsRadio))</visible>
+            </control>
+            <control type="image">
+              <posx>0</posx>
+              <posy>2</posy>
+              <width>36</width>
+              <height>36</height>
+              <aspectratio>stretch</aspectratio>
+              <texture>DefaultAlbumCover.png</texture>
+              <visible>!IsEmpty(ListItem.Property(IsRadio))</visible>
+            </control>
+            <control type="label">
+              <posx>45</posx>
+              <posy>0</posy>
+              <width>500</width>
+              <height>40</height>
+              <font>font14</font>
+              <align>left</align>
+              <aligny>center</aligny>
+              <textcolor>grey2</textcolor>
+              <selectedcolor>selected</selectedcolor>
+              <info>ListItem.Label</info>
+            </control>
+          </itemlayout>
+          <focusedlayout height="40" width="750">
+            <control type="image">
+              <posx>0</posx>
+              <posy>0</posy>
+              <width>750</width>
+              <height>40</height>
+              <aspectratio>stretch</aspectratio>
+              <texture border="5">MenuItemNF.png</texture>
+              <visible>!Control.HasFocus(2)</visible>
+              <include>VisibleFadeEffect</include>
+            </control>
+            <control type="image">
+              <posx>0</posx>
+              <posy>0</posy>
+              <width>750</width>
+              <height>40</height>
+              <aspectratio>stretch</aspectratio>
+              <texture border="5">MenuItemFO.png</texture>
+              <visible>Control.HasFocus(2)</visible>
+              <include>VisibleFadeEffect</include>
+            </control>
+            <control type="image">
+              <posx>710</posx>
+              <posy>5</posy>
+              <width>40</width>
+              <height>30</height>
+              <aspectratio>stretch</aspectratio>
+              <texture>OverlayLocked.png</texture>
+              <visible>!IsEmpty(ListItem.Property(IsEncrypted))</visible>
+            </control>
+            <control type="image">
+              <posx>690</posx>
+              <posy>7</posy>
+              <width>60</width>
+              <height>25</height>
+              <aspectratio>stretch</aspectratio>
+              <texture>OverlayHD.png</texture>
+              <animation effect="slide" start="0,0" end="-45,0" time="100" condition="!IsEmpty(ListItem.Property(IsEncrypted))">Conditional</animation>
+              <visible>!IsEmpty(ListItem.Property(IsHD))</visible>
+            </control>
+            <control type="image">
+              <posx>0</posx>
+              <posy>2</posy>
+              <width>36</width>
+              <height>36</height>
+              <aspectratio>stretch</aspectratio>
+              <texture>DefaultVideoCover.png</texture>
+              <visible>IsEmpty(ListItem.Property(IsRadio))</visible>
+            </control>
+            <control type="image">
+              <posx>0</posx>
+              <posy>2</posy>
+              <width>36</width>
+              <height>36</height>
+              <aspectratio>stretch</aspectratio>
+              <texture>DefaultAlbumCover.png</texture>
+              <visible>!IsEmpty(ListItem.Property(IsRadio))</visible>
+            </control>
+            <control type="label">
+              <posx>45</posx>
+              <posy>0</posy>
+              <width>500</width>
+              <height>40</height>
+              <font>font14</font>
+              <align>left</align>
+              <aligny>center</aligny>
+              <textcolor>grey2</textcolor>
+              <selectedcolor>selected</selectedcolor>
+              <info>ListItem.Label</info>
+            </control>
+          </focusedlayout>
+        </control>
+        <control type="scrollbar" id="60">
+          <posx>1060</posx>
+          <posy>180</posy>
+          <width>25</width>
+          <height>410</height>
+          <texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground>
+          <texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar>
+          <texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus>
+          <textureslidernib>ScrollBarNib.png</textureslidernib>
+          <textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus>
+          <onleft>2</onleft>
+          <onright>10</onright>
+          <showonepage>true</showonepage>
+          <orientation>vertical</orientation>
+        </control>
+        <control type="label">
+          <description>Page Count Label</description>
+          <posx>1040</posx>
+          <posy>600</posy>
+          <width>500</width>
+          <height>20</height>
+          <font>font12</font>
+          <textcolor>grey</textcolor>
+          <scroll>false</scroll>
+          <align>right</align>
+          <aligny>center</aligny>
+          <label>([COLOR=blue]$INFO[Container(2).NumItems][/COLOR]) $LOCALIZE[19019] - $LOCALIZE[31024] ([COLOR=blue]$INFO[Container(2).CurrentPage]/$INFO[Container(2).NumPages][/COLOR])</label>
+        </control>
+        <control type="label" id="36">
+          <description>Status Label</description>
+          <posx>275</posx>
+          <posy>590</posy>
+          <width>500</width>
+          <height>20</height>
+          <font>font14</font>
+          <textcolor>yellow</textcolor>
+          <scroll>false</scroll>
+          <align>left</align>
+          <aligny>center</aligny>
+          <label>-</label>
+        </control>
+      </control>
+    </control>
+    <include>BehindDialogFadeOut</include>
+    <control type="group">
+      <posx>60</posx>
+      <posy>0</posy>
+      <animation effect="slide" end="-310,0" time="400" tween="quadratic" easing="out">WindowClose</animation>
+      <animation effect="slide" start="-310,0" time="400" tween="quadratic" easing="out">WindowOpen</animation>
+      <control type="image">
+        <posx>0</posx>
+        <posy>0</posy>
+        <width>250</width>
+        <height>35</height>
+        <texture border="0,0,32,0">header.png</texture>
+      </control>
+      <control type="label">
+        <include>WindowTitleCommons</include>
+        <posx>220</posx>
+        <label>$ADDON[pvr.vdr.vnsi 30008]</label>
+      </control>
+    </control>
+    <include>WindowTitleHomeButton</include>
+    <include>Clock</include>
+  </controls>
+</window>
index 484ebf4..5b6be02 100644 (file)
@@ -33,7 +33,7 @@
                                <aspectratio aligny="bottom">keep</aspectratio>
                                <bordertexture border="8">ThumbShadow.png</bordertexture>
                                <bordersize>8</bordersize>
-                               <visible>!VideoPlayer.Content(Movies)</visible>
+                               <visible>!VideoPlayer.Content(Movies) + !VideoPlayer.Content(LiveTV)</visible>
                        </control>
                        <control type="image">
                                <description>Movie cover image</description>
                                <bordersize>8</bordersize>
                                <visible>VideoPlayer.Content(Movies)</visible>
                        </control>
+                       <control type="image">
+                               <description>Live TV Channel Logo image</description>
+                               <posx>30</posx>
+                               <posy>290r</posy>
+                               <width>280</width>
+                               <height>260</height>
+                               <fadetime>200</fadetime>
+                               <texture background="true">$INFO[VideoPlayer.Cover]</texture>
+                               <aspectratio aligny="bottom">keep</aspectratio>
+                               <visible>VideoPlayer.Content(LiveTV)</visible>
+                       </control>
                        <control type="progress">
                                <description>Progressbar</description>
                                <posx>10</posx>
                                        <posy>35</posy>
                                        <width>880</width>
                                        <height>30</height>
+                                       <label>$INFO[VideoPlayer.ChannelNumber,[COLOR=blue]([/COLOR],[COLOR=blue])[/COLOR]] $INFO[VideoPlayer.ChannelName]</label>
+                                       <align>left</align>
+                                       <aligny>center</aligny>
+                                       <font>font13</font>
+                                       <textcolor>grey</textcolor>
+                                       <shadowcolor>black</shadowcolor>
+                                       <visible>VideoPlayer.Content(LiveTV)</visible>
+                               </control>
+                               <control type="label">
+                                       <posx>10</posx>
+                                       <posy>35</posy>
+                                       <width>880</width>
+                                       <height>30</height>
                                        <label>$INFO[VideoPlayer.Artist]$INFO[VideoPlayer.Album, - ]</label>
                                        <align>left</align>
                                        <aligny>center</aligny>
                                        <textcolor>grey2</textcolor>
                                        <shadowcolor>black</shadowcolor>
                                        <visible>Player.ChapterCount</visible>
+                                       <visible>!VideoPlayer.Content(LiveTV)</visible>
                                </control>
                                <control type="label">
                                        <description>Chapter Name</description>
                                        <font>font12_title</font>
                                        <textcolor>grey2</textcolor>
                                        <shadowcolor>black</shadowcolor>
+                                       <visible>!VideoPlayer.Content(LiveTV)</visible>
+                               </control>
+                               <control type="label">
+                                       <description>Live TV Channel Group</description>
+                                       <posx>0</posx>
+                                       <posy>110</posy>
+                                       <width>300</width>
+                                       <height>20</height>
+                                       <label>$INFO[VideoPlayer.ChannelGroup,[COLOR=blue]$LOCALIZE[31509]:[/COLOR] ]</label>
+                                       <align>left</align>
+                                       <aligny>center</aligny>
+                                       <font>font12_title</font>
+                                       <textcolor>grey2</textcolor>
+                                       <shadowcolor>black</shadowcolor>
+                                       <visible>VideoPlayer.Content(LiveTV)</visible>
                                </control>
                                <control type="label">
                                        <description>Player Times</description>
                                        <font>font12_title</font>
                                        <textcolor>grey2</textcolor>
                                        <shadowcolor>black</shadowcolor>
+                                       <visible>!VideoPlayer.Content(LiveTV)</visible>
+                               </control>
+                               <control type="label">
+                                       <description>Next Live TV Name</description>
+                                       <posx>890</posx>
+                                       <posy>150</posy>
+                                       <width>890</width>
+                                       <height>20</height>
+                                       <label>[COLOR=blue]$LOCALIZE[19113] :[/COLOR] $INFO[VideoPlayer.NextTitle]</label>
+                                       <align>right</align>
+                                       <aligny>center</aligny>
+                                       <font>font12_title</font>
+                                       <textcolor>grey2</textcolor>
+                                       <shadowcolor>black</shadowcolor>
+                                       <visible>VideoPlayer.Content(LiveTV)</visible>
                                </control>
                        </control>
                </control>
                <include>Clock</include>
        </controls>
-</window>
\ No newline at end of file
+</window>
diff --git a/addons/skin.confluence/720p/DialogPVRChannelManager.xml b/addons/skin.confluence/720p/DialogPVRChannelManager.xml
new file mode 100644 (file)
index 0000000..5eae1f2
--- /dev/null
@@ -0,0 +1,604 @@
+<window id="605">
+       <defaultcontrol always="true">6</defaultcontrol>
+       <allowoverlay>no</allowoverlay>
+       <controls>
+               <include>CommonSettingsBackground</include>
+               <include>CommonMediaPlayingBackground</include>
+               <control type="group">
+                       <posx>90</posx>
+                       <posy>50</posy>
+                       <animation type="WindowOpen" reversible="false">
+                               <effect type="zoom" start="80" end="100" center="640,360" easing="out" tween="back" time="300" />
+                               <effect type="fade" start="0" end="100" time="300" />
+                       </animation>
+                       <animation type="WindowClose" reversible="false">
+                               <effect type="zoom" start="100" end="80" center="640,360" easing="in" tween="back" time="300" />
+                               <effect type="fade" start="100" end="0" time="300" />
+                       </animation>
+                       <control type="image">
+                               <posx>0</posx>
+                               <posy>0</posy>
+                               <width>1100</width>
+                               <height>640</height>
+                               <texture border="20">DialogBack.png</texture>
+                       </control>
+                       <control type="image">
+                               <description>LOGO</description>
+                               <posx>30</posx>
+                               <posy>15</posy>
+                               <width>220</width>
+                               <height>80</height>
+                               <aspectratio>keep</aspectratio>
+                               <texture>Confluence_Logo.png</texture>
+                       </control>
+                       <control type="button" id ="4">
+                               <description>OK Button</description>
+        <posx>10</posx>
+        <posy>90</posy>
+        <width>260</width>
+        <height>60</height>
+                               <textoffsety>13</textoffsety>
+                               <label>186</label>
+                               <font>font13_title</font>
+                               <align>right</align>
+                               <aligny>top</aligny>
+                               <texturenofocus border="5">MenuItemNF.png</texturenofocus>
+                               <texturefocus border="5">MenuItemFO.png</texturefocus>
+                               <onleft>60</onleft>
+                               <onright>7</onright>
+                               <onup>6</onup>
+                               <ondown>5</ondown>
+                       </control>
+                       <control type="button" id ="5">
+                               <description>Apply changes Button</description>
+        <posx>10</posx>
+        <posy>150</posy>
+        <width>260</width>
+        <height>60</height>
+                               <textoffsety>13</textoffsety>
+                               <label>14070</label>
+                               <font>font13_title</font>
+                               <align>right</align>
+                               <aligny>top</aligny>
+                               <texturenofocus border="5">MenuItemNF.png</texturenofocus>
+                               <texturefocus border="5">MenuItemFO.png</texturefocus>
+                               <onleft>60</onleft>
+                               <onright>7</onright>
+                               <onup>4</onup>
+                               <ondown>6</ondown>
+                       </control>
+                       <control type="button" id ="6">
+                               <description>Cancel Button</description>
+        <posx>10</posx>
+        <posy>210</posy>
+        <width>260</width>
+        <height>60</height>
+                               <textoffsety>13</textoffsety>
+                               <label>222</label>
+                               <font>font13_title</font>
+                               <align>right</align>
+                               <aligny>top</aligny>
+                               <texturenofocus border="5">MenuItemNF.png</texturenofocus>
+                               <texturefocus border="5">MenuItemFO.png</texturefocus>
+                               <onleft>60</onleft>
+                               <onright>7</onright>
+                               <onup>5</onup>
+                               <ondown>4</ondown>
+                       </control>
+                       <control type="radiobutton" id ="7">
+                               <description>Channel activated</description>
+        <posx>268</posx>
+        <posy>80</posy>
+                               <width>380</width>
+                               <height>35</height>
+                               <font>font12</font>
+                               <textcolor>white</textcolor>
+                               <focusedcolor>white</focusedcolor>
+                               <shadowcolor>black</shadowcolor>
+                               <align>left</align>
+                               <aligny>center</aligny>
+                               <texturefocus border="5">button-focus.png</texturefocus>
+                               <texturenofocus border="5">button-nofocus.png</texturenofocus>
+                               <pulseonselect>no</pulseonselect>
+                               <label>19074</label>
+                               <onleft>4</onleft>
+                               <onright>20</onright>
+                               <onup>33</onup>
+                               <ondown>8</ondown>
+                       </control>
+                       <control type="edit" id ="8">
+                               <description>Channel name</description>
+        <posx>268</posx>
+        <posy>115</posy>
+                               <width>380</width>
+                               <height>35</height>
+                               <font>font12</font>
+                               <textcolor>white</textcolor>
+                               <focusedcolor>white</focusedcolor>
+                               <shadowcolor>black</shadowcolor>
+                               <texturefocus border="5">button-focus.png</texturefocus>
+                               <texturenofocus border="5">button-nofocus.png</texturenofocus>
+                               <label>19201</label>
+                               <onright>20</onright>
+                               <onleft>4</onleft>
+                               <onup>7</onup>
+                               <ondown>9</ondown>
+                       </control>
+                       <control type="button" id ="9">
+                               <description>Channellogo Button</description>
+        <posx>268</posx>
+        <posy>150</posy>
+                               <width>380</width>
+                               <height>35</height>
+                               <font>font12</font>
+                               <texturefocus border="5">button-focus.png</texturefocus>
+                               <texturenofocus border="5">button-nofocus.png</texturenofocus>
+                               <label>19202</label>
+                               <onleft>4</onleft>
+                               <onright>20</onright>
+                               <onup>8</onup>
+                               <ondown>11</ondown>
+                       </control>
+                       <control type="image" id ="10">
+                               <description>LOGO</description>
+                               <posx>613</posx>
+                               <posy>150</posy>
+                               <width>35</width>
+                               <height>35</height>
+                               <aspectratio>keep</aspectratio>
+                               <info>ListItem.Property(Icon)</info>
+                       </control>
+                       <control type="spincontrolex" id ="11">
+                               <description>Group selection</description>
+        <posx>268</posx>
+        <posy>185</posy>
+                               <width>380</width>
+                               <height>35</height>
+                               <font>font12</font>
+                               <texturefocus border="5">button-focus.png</texturefocus>
+                               <texturenofocus border="5">button-nofocus.png</texturenofocus>
+                               <label>19207</label>
+                               <onright>20</onright>
+                               <onleft>4</onleft>
+                               <onup>9</onup>
+                               <ondown>12</ondown>
+                       </control>
+                       <control type="radiobutton" id ="12">
+                               <description>EPG activated</description>
+        <posx>268</posx>
+        <posy>220</posy>
+                               <width>380</width>
+                               <height>35</height>
+                               <font>font12</font>
+                               <textcolor>white</textcolor>
+                               <focusedcolor>white</focusedcolor>
+                               <shadowcolor>black</shadowcolor>
+                               <align>left</align>
+                               <aligny>center</aligny>
+                               <texturefocus border="5">button-focus.png</texturefocus>
+                               <texturenofocus border="5">button-nofocus.png</texturenofocus>
+                               <pulseonselect>no</pulseonselect>
+                               <label>19206</label>
+                               <onleft>4</onleft>
+                               <onright>20</onright>
+                               <onup>11</onup>
+                               <ondown>13</ondown>
+                       </control>
+                       <control type="spincontrolex" id ="13">
+                               <description>EPG source</description>
+        <posx>268</posx>
+        <posy>255</posy>
+                               <width>380</width>
+                               <height>35</height>
+                               <font>font12</font>
+                               <texturefocus border="5">button-focus.png</texturefocus>
+                               <texturenofocus border="5">button-nofocus.png</texturenofocus>
+                               <label>19200</label>
+                               <onright>20</onright>
+                               <onleft>4</onleft>
+                               <onup>12</onup>
+                               <ondown>30</ondown>
+                       </control>
+                       <control type="button" id ="30">
+                               <description>Group Manager Button</description>
+        <posx>268</posx>
+        <posy>425</posy>
+                               <width>190</width>
+                               <height>35</height>
+                               <font>font12</font>
+                               <texturefocus border="5">button-focus.png</texturefocus>
+                               <texturenofocus border="5">button-nofocus.png</texturenofocus>
+                               <align>center</align>
+                               <label>19205</label>
+                               <onleft>4</onleft>
+                               <onright>34</onright>
+                               <onup>13</onup>
+                               <ondown>31</ondown>
+                       </control>
+                       <control type="button" id ="34">
+                               <description>TV/Radio Button</description>
+        <posx>458</posx>
+        <posy>425</posy>
+                               <width>190</width>
+                               <height>35</height>
+                               <font>font12</font>
+                               <visible>IsEmpty(Window.Property(IsRadio))</visible>
+                               <texturefocus border="5">button-focus.png</texturefocus>
+                               <texturenofocus border="5">button-nofocus.png</texturenofocus>
+                               <align>center</align>
+                               <label>19024</label>
+                               <onleft>30</onleft>
+                               <onright>20</onright>
+                               <onup>13</onup>
+                               <ondown>31</ondown>
+                       </control>
+                       <control type="button" id ="34">
+                               <description>TV/Radio Button</description>
+        <posx>458</posx>
+        <posy>425</posy>
+                               <width>190</width>
+                               <height>35</height>
+                               <font>font12</font>
+                               <visible>!IsEmpty(Window.Property(IsRadio))</visible>
+                               <texturefocus border="5">button-focus.png</texturefocus>
+                               <texturenofocus border="5">button-nofocus.png</texturenofocus>
+                               <align>center</align>
+                               <label>19023</label>
+                               <onleft>30</onleft>
+                               <onright>20</onright>
+                               <onup>13</onup>
+                               <ondown>31</ondown>
+                       </control>
+                       <control type="button" id ="31">
+                               <description>Edit channel Button</description>
+        <posx>268</posx>
+        <posy>460</posy>
+                               <width>380</width>
+                               <height>35</height>
+                               <font>font12</font>
+                               <texturefocus border="5">button-focus.png</texturefocus>
+                               <texturenofocus border="5">button-nofocus.png</texturenofocus>
+                               <align>center</align>
+                               <label>19203</label>
+                               <onleft>4</onleft>
+                               <onright>20</onright>
+                               <onup>30</onup>
+                               <ondown>32</ondown>
+                       </control>
+                       <control type="button" id ="32">
+                               <description>Delete channel Button</description>
+        <posx>268</posx>
+        <posy>495</posy>
+                               <width>380</width>
+                               <height>35</height>
+                               <font>font12</font>
+                               <texturefocus border="5">button-focus.png</texturefocus>
+                               <texturenofocus border="5">button-nofocus.png</texturenofocus>
+                               <align>center</align>
+                               <label>19211</label>
+                               <onleft>4</onleft>
+                               <onright>20</onright>
+                               <onup>31</onup>
+                               <ondown>33</ondown>
+                       </control>
+                       <control type="button" id ="33">
+                               <description>New channel Button</description>
+        <posx>268</posx>
+        <posy>530</posy>
+                               <width>380</width>
+                               <height>35</height>
+                               <font>font12</font>
+                               <texturefocus border="5">button-focus.png</texturefocus>
+                               <texturenofocus border="5">button-nofocus.png</texturenofocus>
+                               <align>center</align>
+                               <label>19204</label>
+                               <onleft>4</onleft>
+                               <onright>20</onright>
+                               <onup>32</onup>
+                               <ondown>7</ondown>
+                       </control>
+      <control type="image">
+        <posx>670</posx>
+        <posy>60</posy>
+        <width>380</width>
+        <height>530</height>
+        <texture>ContentPanel.png</texture>
+      </control>
+      <control type="list" id="20">
+        <posx>680</posx>
+        <posy>70</posy>
+        <width>350</width>
+        <height>500</height>
+        <onup>20</onup>
+        <ondown>20</ondown>
+        <onleft>7</onleft>
+        <onright>60</onright>
+        <pagecontrol>60</pagecontrol>
+        <scrolltime>200</scrolltime>
+        <itemlayout height="40">
+          <control type="image">
+            <posx>0</posx>
+            <posy>0</posy>
+            <width>350</width>
+            <height>40</height>
+            <texture border="5">MenuItemNF.png</texture>
+          </control>
+          <control type="image">
+            <posx>0</posx>
+            <posy>5</posy>
+            <width>30</width>
+            <height>30</height>
+            <info>ListItem.Property(Icon)</info>
+          </control>
+          <control type="image">
+            <posx>0</posx>
+            <posy>5</posy>
+            <width>30</width>
+            <height>30</height>
+            <texture>black-back2.png</texture>
+            <visible>!ListItem.Property(ActiveChannel)</visible>
+          </control>
+          <control type="image">
+            <posx>320</posx>
+            <posy>5</posy>
+            <width>30</width>
+            <height>30</height>
+            <texture border="5">amt-overlay-saved.png</texture>
+            <visible>ListItem.Property(Changed)</visible>
+          </control>
+          <control type="label">
+            <posx>35</posx>
+            <posy>0</posy>
+            <width>50</width>
+            <height>35</height>
+            <font>font12</font>
+            <align>left</align>
+            <aligny>center</aligny>
+            <textcolor>gray</textcolor>
+            <selectedcolor>selected</selectedcolor>
+            <info>ListItem.Property(Number)</info>
+            <visible>!ListItem.Property(ActiveChannel)</visible>
+          </control>
+          <control type="label">
+            <posx>35</posx>
+            <posy>0</posy>
+            <width>50</width>
+            <height>35</height>
+            <font>font12</font>
+            <align>left</align>
+            <aligny>center</aligny>
+            <textcolor>white</textcolor>
+            <selectedcolor>selected</selectedcolor>
+            <info>ListItem.Property(Number)</info>
+            <visible>ListItem.Property(ActiveChannel)</visible>
+          </control>
+          <control type="label">
+            <posx>85</posx>
+            <posy>0</posy>
+            <width>190</width>
+            <height>35</height>
+            <font>font12</font>
+            <align>left</align>
+            <aligny>center</aligny>
+            <textcolor>gray</textcolor>
+            <selectedcolor>selected</selectedcolor>
+            <info>ListItem.Property(Name)</info>
+            <visible>!ListItem.Property(ActiveChannel)</visible>
+          </control>
+          <control type="label">
+            <posx>85</posx>
+            <posy>0</posy>
+            <width>190</width>
+            <height>35</height>
+            <font>font12</font>
+            <align>left</align>
+            <aligny>center</aligny>
+            <textcolor>white</textcolor>
+            <selectedcolor>selected</selectedcolor>
+            <info>ListItem.Property(Name)</info>
+            <visible>ListItem.Property(ActiveChannel)</visible>
+          </control>
+        </itemlayout>
+        <focusedlayout height="60">
+          <control type="image">
+            <posx>0</posx>
+            <posy>0</posy>
+            <width>350</width>
+            <height>60</height>
+            <texture border="5">MenuItemNF.png</texture>
+            <visible>!Control.HasFocus(20)</visible>
+            <include>VisibleFadeEffect</include>
+          </control>
+          <control type="image">
+            <posx>0</posx>
+            <posy>0</posy>
+            <width>350</width>
+            <height>60</height>
+            <texture border="5">MenuItemFO.png</texture>
+            <visible>Control.HasFocus(20)</visible>
+            <include>VisibleFadeEffect</include>
+          </control>
+          <control type="image">
+            <posx>0</posx>
+            <posy>5</posy>
+            <width>30</width>
+            <height>30</height>
+            <info>ListItem.Property(Icon)</info>
+          </control>
+          <control type="image">
+            <posx>320</posx>
+            <posy>5</posy>
+            <width>30</width>
+            <height>30</height>
+            <texture border="5">amt-overlay-saved.png</texture>
+            <visible>ListItem.Property(Changed)</visible>
+          </control>
+          <control type="label">
+            <posx>35</posx>
+            <posy>0</posy>
+            <width>50</width>
+            <height>35</height>
+            <font>font12</font>
+            <align>left</align>
+            <aligny>center</aligny>
+            <textcolor>gray</textcolor>
+            <selectedcolor>selected</selectedcolor>
+            <info>ListItem.Property(Number)</info>
+            <visible>!ListItem.Property(ActiveChannel)</visible>
+          </control>
+          <control type="label">
+            <posx>35</posx>
+            <posy>0</posy>
+            <width>50</width>
+            <height>35</height>
+            <font>font12</font>
+            <align>left</align>
+            <aligny>center</aligny>
+            <textcolor>white</textcolor>
+            <selectedcolor>selected</selectedcolor>
+            <info>ListItem.Property(Number)</info>
+            <visible>ListItem.Property(ActiveChannel)</visible>
+          </control>
+          <control type="label">
+            <posx>85</posx>
+            <posy>0</posy>
+            <width>190</width>
+            <height>35</height>
+            <font>font12</font>
+            <align>left</align>
+            <aligny>center</aligny>
+            <textcolor>gray</textcolor>
+            <selectedcolor>selected</selectedcolor>
+            <info>ListItem.Property(Name)</info>
+            <visible>!ListItem.Property(ActiveChannel)</visible>
+          </control>
+          <control type="label">
+            <posx>85</posx>
+            <posy>0</posy>
+            <width>190</width>
+            <height>35</height>
+            <font>font12</font>
+            <align>left</align>
+            <aligny>center</aligny>
+            <textcolor>white</textcolor>
+            <selectedcolor>selected</selectedcolor>
+            <info>ListItem.Property(Name)</info>
+            <visible>ListItem.Property(ActiveChannel)</visible>
+          </control>
+          <control type="label">
+            <posx>0</posx>
+            <posy>30</posy>
+            <width>350</width>
+            <height>35</height>
+            <font>font10</font>
+            <align>left</align>
+            <aligny>center</aligny>
+            <textcolor>grey2</textcolor>
+            <selectedcolor>selected</selectedcolor>
+            <label>$LOCALIZE[19210]: $INFO[ListItem.Property(ClientName)]</label>
+          </control>
+          <control type="image">
+            <posx>0</posx>
+            <posy>0</posy>
+            <width>350</width>
+            <height>60</height>
+            <texture border="5">black-back2.png</texture>
+            <visible>!ListItem.Property(ActiveChannel)</visible>
+          </control>
+        </focusedlayout>
+      </control>
+                       <control type="scrollbar" id="60">
+                               <posx>1060</posx>
+                               <posy>60</posy>
+                               <width>25</width>
+                               <height>530</height>
+                               <texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground>
+                               <texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar>
+                               <texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus>
+                               <textureslidernib>ScrollBarNib.png</textureslidernib>
+                               <textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus>
+                               <onleft>20</onleft>
+                               <onright>4</onright>
+                               <showonepage>false</showonepage>
+                               <orientation>vertical</orientation>
+                       </control>
+      <control type="label">
+        <description>Page Count Label</description>
+        <posx>1040</posx>
+        <posy>600</posy>
+        <width>500</width>
+        <height>20</height>
+        <font>font12</font>
+        <textcolor>grey</textcolor>
+        <scroll>false</scroll>
+        <align>right</align>
+        <aligny>center</aligny>
+        <label>([COLOR=blue]$INFO[Container(20).NumItems][/COLOR]) $LOCALIZE[19019] - $LOCALIZE[31024] ([COLOR=blue]$INFO[Container(20).CurrentPage]/$INFO[Container(20).NumPages][/COLOR])</label>
+      </control>
+                       <control type="image">
+                               <posx>268</posx>
+                               <posy>10</posy>
+                               <width>790</width>
+                               <height>618</height>
+                               <texture border="5">black-back2.png</texture>
+                       </control>
+                       <control type="image">
+                               <posx>268</posx>
+                               <posy>10</posy>
+                               <width>804</width>
+                               <height>70</height>
+                               <aspectratio>stretch</aspectratio>
+                               <texture>GlassTitleBar.png</texture>
+                       </control>
+                       <control type="label">
+                               <description>header label</description>
+                               <posx>300</posx>
+                               <posy>20</posy>
+                               <width>740</width>
+                               <height>30</height>
+                               <font>font16caps</font>
+                               <label>$LOCALIZE[19199] - $LOCALIZE[19023]</label>
+                               <align>left</align>
+                               <aligny>center</aligny>
+                               <textcolor>white</textcolor>
+                               <shadowcolor>black</shadowcolor>
+                               <visible>IsEmpty(Window.Property(IsRadio))</visible>
+                       </control>
+                       <control type="label">
+                               <description>header label</description>
+                               <posx>300</posx>
+                               <posy>20</posy>
+                               <width>740</width>
+                               <height>30</height>
+                               <font>font16caps</font>
+                               <label>$LOCALIZE[19199] - $LOCALIZE[19024]</label>
+                               <align>left</align>
+                               <aligny>center</aligny>
+                               <textcolor>white</textcolor>
+                               <shadowcolor>black</shadowcolor>
+                               <visible>!IsEmpty(Window.Property(IsRadio))</visible>
+                       </control>
+               </control>
+               <include>BehindDialogFadeOut</include>
+               <control type="group">
+                       <posx>60</posx>
+                       <posy>0</posy>
+                       <animation effect="slide" end="-310,0" time="400" tween="quadratic" easing="out">WindowClose</animation>
+                       <animation effect="slide" start="-310,0" time="400" tween="quadratic" easing="out">WindowOpen</animation>
+                       <control type="image">
+                               <posx>0</posx>
+                               <posy>0</posy>
+                               <width>250</width>
+                               <height>35</height>
+                               <texture border="0,0,32,0">header.png</texture>
+                       </control>
+                       <control type="label">
+                               <include>WindowTitleCommons</include>
+                               <posx>220</posx>
+                               <label>$LOCALIZE[19199]</label>
+                       </control>
+               </control>
+               <include>WindowTitleHomeButton</include>
+               <include>Clock</include>
+       </controls>
+</window>
diff --git a/addons/skin.confluence/720p/DialogPVRChannelsOSD.xml b/addons/skin.confluence/720p/DialogPVRChannelsOSD.xml
new file mode 100644 (file)
index 0000000..c2b8d7c
--- /dev/null
@@ -0,0 +1,263 @@
+<window id="609">
+       <defaultcontrol always="true">11</defaultcontrol>
+       <coordinates>
+               <system>1</system>
+               <posx>780</posx>
+               <posy>30</posy>
+       </coordinates>
+       <include>dialogeffect</include>
+       <controls>
+               <control type="group">
+                       <control type="image">
+                               <description>background image</description>
+                               <posx>0</posx>
+                               <posy>0</posy>
+                               <width>480</width>
+                               <height>660</height>
+                               <colordiffuse>EEFFFFFF</colordiffuse>
+                               <texture border="40">DialogBack.png</texture>
+                       </control>
+                       <control type="button">
+                               <description>Close Window button</description>
+                               <posx>400</posx>
+                               <posy>9</posy>
+                               <width>64</width>
+                               <height>32</height>
+                               <label>-</label>
+                               <font>-</font>
+                               <onclick>PreviousMenu</onclick>
+                               <texturefocus>DialogCloseButton-focus.png</texturefocus>
+                               <texturenofocus>DialogCloseButton.png</texturenofocus>
+                               <onleft>2</onleft>
+                               <onright>2</onright>
+                               <onup>2</onup>
+                               <ondown>2</ondown>
+                               <visible>system.getbool(input.enablemouse)</visible>
+                       </control>
+                       <control type="image">
+                               <posx>40</posx>
+                               <posy>10</posy>
+                               <width>430</width>
+                               <height>90</height>
+                               <aspectratio>stretch</aspectratio>
+                               <texture>GlassTitleBar.png</texture>
+                       </control>
+                       <control type="label">
+                               <description>header label</description>
+                               <posx>40</posx>
+                               <posy>18</posy>
+                               <width>430</width>
+                               <height>30</height>
+                               <font>font12_title</font>
+                               <label>$LOCALIZE[19023] - $INFO[VideoPlayer.ChannelGroup]</label>
+                               <align>center</align>
+                               <aligny>center</aligny>
+                               <textcolor>white</textcolor>
+                               <shadowcolor>black</shadowcolor>
+                               <visible>pvr.IsPlayingTV</visible>
+                       </control>
+                       <control type="label">
+                               <description>header label</description>
+                               <posx>40</posx>
+                               <posy>18</posy>
+                               <width>430</width>
+                               <height>30</height>
+                               <font>font12_title</font>
+                               <label>$LOCALIZE[19024] - $INFO[MusicPlayer.ChannelGroup]</label>
+                               <align>center</align>
+                               <aligny>center</aligny>
+                               <textcolor>white</textcolor>
+                               <shadowcolor>black</shadowcolor>
+                               <visible>pvr.IsPlayingRadio</visible>
+                       </control>
+                       <control type="list" id="11">
+                               <posx>40</posx>
+                               <posy>64</posy>
+                               <width>390</width>
+                               <height>541</height>
+                               <onleft>60</onleft>
+                               <onright>60</onright>
+                               <onup>11</onup>
+                               <ondown>11</ondown>
+                               <viewtype label="535">list</viewtype>
+                               <pagecontrol>60</pagecontrol>
+                               <scrolltime>200</scrolltime>
+                               <itemlayout height="60" width="400">
+                                       <control type="image">
+                                               <posx>0</posx>
+                                               <posy>0</posy>
+                                               <width>390</width>
+                                               <height>61</height>
+                                               <texture border="2">MenuItemNF.png</texture>
+                                               <include>VisibleFadeEffect</include>
+                                       </control>
+                                       <control type="label">
+                                               <posx>5</posx>
+                                               <posy>-4</posy>
+                                               <width>40</width>
+                                               <height>35</height>
+                                               <font>font12</font>
+                                               <align>left</align>
+                                               <aligny>center</aligny>
+                                               <textcolor>grey</textcolor>
+                                               <selectedcolor>grey</selectedcolor>
+                                               <info>ListItem.ChannelNumber</info>
+                                       </control>
+                                       <control type="label">
+                                               <posx>50</posx>
+                                               <posy>0</posy>
+                                               <width>270</width>
+                                               <height>30</height>
+                                               <font>font13</font>
+                                               <textcolor>white</textcolor>
+                                               <selectedcolor>selected</selectedcolor>
+                                               <align>left</align>
+                                               <aligny>center</aligny>
+                                               <label>$INFO[ListItem.Label]</label>
+                                       </control>
+                                       <control type="label">
+                                               <posx>50</posx>
+                                               <posy>30</posy>
+                                               <width>330</width>
+                                               <height>30</height>
+                                               <font>font12</font>
+                                               <textcolor>grey</textcolor>
+                                               <selectedcolor>grey</selectedcolor>
+                                               <align>left</align>
+                                               <aligny>center</aligny>
+                                               <visible>IsEmpty(Listitem.Icon)</visible>
+                                               <label>$INFO[ListItem.Title]</label>
+                                       </control>
+                                       <control type="label">
+                                               <posx>50</posx>
+                                               <posy>30</posy>
+                                               <width>270</width>
+                                               <height>30</height>
+                                               <font>font12</font>
+                                               <textcolor>grey</textcolor>
+                                               <selectedcolor>grey</selectedcolor>
+                                               <align>left</align>
+                                               <aligny>center</aligny>
+                                               <visible>!IsEmpty(Listitem.Icon)</visible>
+                                               <label>$INFO[ListItem.Title]</label>
+                                       </control>
+                                       <control type="image">
+                                               <posx>330</posx>
+                                               <posy>4</posy>
+                                               <width>50</width>
+                                               <height>50</height>
+                                               <texture>$INFO[ListItem.Icon]</texture>
+                                       </control>
+                               </itemlayout>
+                               <focusedlayout height="60" width="500">
+                                       <control type="image">
+                                               <posx>0</posx>
+                                               <posy>0</posy>
+                                               <width>390</width>
+                                               <height>61</height>
+                                               <texture border="2">MenuItemNF.png</texture>
+                                               <visible>!Control.HasFocus(11)</visible>
+                                               <include>VisibleFadeEffect</include>
+                                       </control>
+                                       <control type="image">
+                                               <posx>0</posx>
+                                               <posy>0</posy>
+                                               <width>390</width>
+                                               <height>61</height>
+                                               <texture border="2">MenuItemFO.png</texture>
+                                               <visible>Control.HasFocus(11)</visible>
+                                               <include>VisibleFadeEffect</include>
+                                       </control>
+                                       <control type="label">
+                                               <posx>5</posx>
+                                               <posy>-4</posy>
+                                               <width>40</width>
+                                               <height>35</height>
+                                               <font>font12</font>
+                                               <align>left</align>
+                                               <aligny>center</aligny>
+                                               <textcolor>grey</textcolor>
+                                               <selectedcolor>grey</selectedcolor>
+                                               <info>ListItem.ChannelNumber</info>
+                                       </control>
+                                       <control type="label">
+                                               <posx>50</posx>
+                                               <posy>0</posy>
+                                               <width>270</width>
+                                               <height>30</height>
+                                               <font>font13</font>
+                                               <textcolor>white</textcolor>
+                                               <selectedcolor>selected</selectedcolor>
+                                               <align>left</align>
+                                               <aligny>center</aligny>
+                                               <label>$INFO[ListItem.Label]</label>
+                                       </control>
+                                       <control type="label">
+                                               <posx>50</posx>
+                                               <posy>30</posy>
+                                               <width>330</width>
+                                               <height>30</height>
+                                               <font>font12</font>
+                                               <textcolor>grey</textcolor>
+                                               <selectedcolor>grey</selectedcolor>
+                                               <align>left</align>
+                                               <aligny>center</aligny>
+                                               <visible>IsEmpty(Listitem.Icon)</visible>
+                                               <label>$INFO[ListItem.Title]</label>
+                                       </control>
+                                       <control type="label">
+                                               <posx>50</posx>
+                                               <posy>30</posy>
+                                               <width>270</width>
+                                               <height>30</height>
+                                               <font>font12</font>
+                                               <textcolor>grey</textcolor>
+                                               <selectedcolor>grey</selectedcolor>
+                                               <align>left</align>
+                                               <aligny>center</aligny>
+                                               <visible>!IsEmpty(Listitem.Icon)</visible>
+                                               <label>$INFO[ListItem.Title]</label>
+                                       </control>
+                                       <control type="image">
+                                               <posx>330</posx>
+                                               <posy>4</posy>
+                                               <width>50</width>
+                                               <height>50</height>
+                                               <texture>$INFO[ListItem.Icon]</texture>
+                                       </control>
+                               </focusedlayout>
+                       </control>
+                       <control type="scrollbar" id="60">
+                               <posx>435</posx>
+                               <posy>65</posy>
+                               <width>25</width>
+                               <height>541</height>
+                               <texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground>
+                               <texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar>
+                               <texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus>
+                               <textureslidernib>ScrollBarNib.png</textureslidernib>
+                               <textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus>
+                               <onleft>11</onleft>
+                               <onright>11</onright>
+                               <ondown>61</ondown>
+                               <onup>61</onup>
+                               <showonepage>false</showonepage>
+                               <orientation>vertical</orientation>
+                       </control>
+                       <control type="label">
+                               <description>Page Count Label</description>
+                               <posx>450</posx>
+                               <posy>615</posy>
+                               <width>400</width>
+                               <height>20</height>
+                               <font>font12</font>
+                               <textcolor>grey</textcolor>
+                               <scroll>false</scroll>
+                               <align>right</align>
+                               <aligny>center</aligny>
+                               <label>([COLOR=blue]$INFO[Container(11).NumItems][/COLOR]) $LOCALIZE[19019] - $LOCALIZE[31024] ([COLOR=blue]$INFO[Container(11).CurrentPage]/$INFO[Container(11).NumPages][/COLOR])</label>
+                               <include>Window_OpenClose_Animation</include>
+                       </control>
+               </control>
+       </controls>
+</window>
\ No newline at end of file
diff --git a/addons/skin.confluence/720p/DialogPVRGroupManager.xml b/addons/skin.confluence/720p/DialogPVRGroupManager.xml
new file mode 100644 (file)
index 0000000..f64deec
--- /dev/null
@@ -0,0 +1,435 @@
+<window id="604">
+       <defaultcontrol always="true">29</defaultcontrol>
+       <controls>
+               <control type="group">
+                       <visible>!Window.IsVisible(FileBrowser)</visible>
+                       <animation effect="slide" start="1150,0" end="0,0" time="400" tween="quadratic" easing="out">WindowOpen</animation>
+                       <animation effect="slide" start="0,0" end="1150,0" time="400" tween="quadratic" easing="out">WindowClose</animation>
+                       <control type="image">
+                               <posx>130</posx>
+                               <posy>0</posy>
+                               <width>1150</width>
+                               <height>720</height>
+                               <texture border="15,0,0,0" flipx="true">MediaBladeSub.png</texture>
+                       </control>
+                       <control type="button">
+                               <description>Close Window button</description>
+                               <posx>180</posx>
+                               <posy>0</posy>
+                               <width>64</width>
+                               <height>32</height>
+                               <label>-</label>
+                               <font>-</font>
+                               <onclick>PreviousMenu</onclick>
+                               <texturefocus>DialogCloseButton-focus.png</texturefocus>
+                               <texturenofocus>DialogCloseButton.png</texturenofocus>
+                               <onleft>9000</onleft>
+                               <onright>9000</onright>
+                               <onup>9000</onup>
+                               <ondown>9000</ondown>
+                               <visible>system.getbool(input.enablemouse)</visible>
+                       </control>
+                       <control type="group">
+                               <animation effect="fade" delay="400" start="0" end="100" time="200">WindowOpen</animation>
+                               <animation effect="fade" start="100" end="0" time="200">WindowClose</animation>
+                               <control type="label">
+                                       <description>header label</description>
+                                       <posx>160</posx>
+                                       <posy>50</posy>
+                                       <width>1080</width>
+                                       <height>30</height>
+                                       <font>font24_title</font>
+                                       <label>19143</label>
+                                       <align>center</align>
+                                       <aligny>center</aligny>
+                                       <textcolor>white</textcolor>
+                                       <shadowcolor>black</shadowcolor>
+                               </control>
+                               <control type="group">
+                                       <description>Group list</description>
+                                       <posx>160</posx>
+                                       <posy>90</posy>
+                                       <control type="label">
+                                               <description>name label</description>
+                                               <posx>0</posx>
+                                               <posy>0</posy>
+                                               <width>340</width>
+                                               <height>70</height>
+                                               <font>font13_title</font>
+                                               <label>31506</label>
+                                               <align>center</align>
+                                               <aligny>center</aligny>
+                                               <textcolor>blue</textcolor>
+                                       </control>
+                                       <control type="image">
+                                               <posx>0</posx>
+                                               <posy>75</posy>
+                                               <width>340</width>
+                                               <height>460</height>
+                                               <texture border="5">button-nofocus.png</texture>
+                                       </control>
+                                       <control type="list" id="13">
+                                               <posx>5</posx>
+                                               <posy>85</posy>
+                                               <width>330</width>
+                                               <height>440</height>
+                                               <onup>13</onup>
+                                               <ondown>13</ondown>
+                                               <onleft>29</onleft>
+                                               <onright>73</onright>
+                                               <pagecontrol>73</pagecontrol>
+                                               <scrolltime>200</scrolltime>
+                                               <itemlayout height="40">
+                                                       <control type="image">
+                                                               <posx>0</posx>
+                                                               <posy>0</posy>
+                                                               <width>330</width>
+                                                               <height>41</height>
+                                                               <texture border="3">MenuItemNF.png</texture>
+                                                       </control>
+                                                       <control type="label">
+                                                               <posx>10</posx>
+                                                               <posy>0</posy>
+                                                               <width>310</width>
+                                                               <height>40</height>
+                                                               <font>font12</font>
+                                                               <align>left</align>
+                                                               <aligny>center</aligny>
+                                                               <textcolor>grey2</textcolor>
+                                                               <selectedcolor>selected</selectedcolor>
+                                                               <info>ListItem.Label</info>
+                                                       </control>
+                                               </itemlayout>
+                                               <focusedlayout height="40">
+                                                       <control type="image">
+                                                               <posx>0</posx>
+                                                               <posy>0</posy>
+                                                               <width>330</width>
+                                                               <height>41</height>
+                                                               <visible>!Control.HasFocus(13)</visible>
+                                                               <texture border="3">MenuItemNF.png</texture>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>0</posx>
+                                                               <posy>0</posy>
+                                                               <width>330</width>
+                                                               <height>41</height>
+                                                               <visible>Control.HasFocus(13)</visible>
+                                                               <texture border="3">MenuItemFO.png</texture>
+                                                       </control>
+                                                       <control type="label">
+                                                               <posx>10</posx>
+                                                               <posy>0</posy>
+                                                               <width>310</width>
+                                                               <height>40</height>
+                                                               <font>font12</font>
+                                                               <align>left</align>
+                                                               <aligny>center</aligny>
+                                                               <textcolor>white</textcolor>
+                                                               <selectedcolor>selected</selectedcolor>
+                                                               <info>ListItem.Label</info>
+                                                       </control>
+                                               </focusedlayout>
+                                       </control>
+                                       <control type="scrollbar" id="73">
+                                               <posx>340</posx>
+                                               <posy>75</posy>
+                                               <width>25</width>
+                                               <height>460</height>
+                                               <texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground>
+                                               <texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar>
+                                               <texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus>
+                                               <textureslidernib>ScrollBarNib.png</textureslidernib>
+                                               <textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus>
+                                               <onleft>13</onleft>
+                                               <onright>11</onright>
+                                               <ondown>73</ondown>
+                                               <onup>73</onup>
+                                               <showonepage>false</showonepage>
+                                               <orientation>vertical</orientation>
+                                       </control>
+                               </control>
+                               <control type="group">
+                                       <description>Channels list</description>
+                                       <posx>525</posx>
+                                       <posy>90</posy>
+                                       <control type="label">
+                                               <description>name label</description>
+                                               <posx>0</posx>
+                                               <posy>0</posy>
+                                               <width>340</width>
+                                               <height>70</height>
+                                               <font>font13_title</font>
+                                               <label>31507</label>
+                                               <align>center</align>
+                                               <aligny>center</aligny>
+                                               <textcolor>blue</textcolor>
+                                       </control>
+                                       <control type="image">
+                                               <posx>0</posx>
+                                               <posy>75</posy>
+                                               <width>340</width>
+                                               <height>460</height>
+                                               <texture border="5">button-nofocus.png</texture>
+                                       </control>
+                                       <control type="list" id="11">
+                                               <posx>5</posx>
+                                               <posy>85</posy>
+                                               <width>330</width>
+                                               <height>440</height>
+                                               <onup>11</onup>
+                                               <ondown>11</ondown>
+                                               <onleft>73</onleft>
+                                               <onright>71</onright>
+                                               <pagecontrol>71</pagecontrol>
+                                               <scrolltime>200</scrolltime>
+                                               <itemlayout height="40">
+                                                       <control type="image">
+                                                               <posx>0</posx>
+                                                               <posy>0</posy>
+                                                               <width>330</width>
+                                                               <height>41</height>
+                                                               <texture border="3">MenuItemNF.png</texture>
+                                                       </control>
+                                                       <control type="image">
+                                                               <width>32</width>
+                                                               <height>32</height>
+                                                               <posx>5</posx>
+                                                               <posy>4</posy>
+                                                               <texture>$INFO[ListItem.Icon]</texture>
+                                                       </control>
+                                                       <control type="label">
+                                                               <posx>40</posx>
+                                                               <posy>0</posy>
+                                                               <width>280</width>
+                                                               <height>40</height>
+                                                               <font>font12</font>
+                                                               <align>left</align>
+                                                               <aligny>center</aligny>
+                                                               <textcolor>grey2</textcolor>
+                                                               <selectedcolor>selected</selectedcolor>
+                                                               <label>$INFO[ListItem.ChannelNumber,(,) - ]$INFO[ListItem.ChannelName]</label>
+                                                       </control>
+                                               </itemlayout>
+                                               <focusedlayout height="40">
+                                                       <control type="image">
+                                                               <posx>0</posx>
+                                                               <posy>0</posy>
+                                                               <width>330</width>
+                                                               <height>41</height>
+                                                               <visible>!Control.HasFocus(11)</visible>
+                                                               <texture border="3">MenuItemNF.png</texture>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>0</posx>
+                                                               <posy>0</posy>
+                                                               <width>330</width>
+                                                               <height>41</height>
+                                                               <visible>Control.HasFocus(11)</visible>
+                                                               <texture border="3">MenuItemFO.png</texture>
+                                                       </control>
+                                                       <control type="image">
+                                                               <width>32</width>
+                                                               <height>32</height>
+                                                               <posx>5</posx>
+                                                               <posy>4</posy>
+                                                               <texture>$INFO[ListItem.Icon]</texture>
+                                                       </control>
+                                                       <control type="label">
+                                                               <posx>40</posx>
+                                                               <posy>0</posy>
+                                                               <width>280</width>
+                                                               <height>40</height>
+                                                               <font>font12</font>
+                                                               <align>left</align>
+                                                               <aligny>center</aligny>
+                                                               <textcolor>white</textcolor>
+                                                               <selectedcolor>selected</selectedcolor>
+                                                               <label>$INFO[ListItem.ChannelNumber,(,) - ]$INFO[ListItem.ChannelName]</label>
+                                                       </control>
+                                               </focusedlayout>
+                                       </control>
+                                       <control type="scrollbar" id="71">
+                                               <posx>340</posx>
+                                               <posy>75</posy>
+                                               <width>25</width>
+                                               <height>460</height>
+                                               <texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground>
+                                               <texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar>
+                                               <texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus>
+                                               <textureslidernib>ScrollBarNib.png</textureslidernib>
+                                               <textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus>
+                                               <onleft>11</onleft>
+                                               <onright>12</onright>
+                                               <ondown>71</ondown>
+                                               <onup>71</onup>
+                                               <showonepage>false</showonepage>
+                                               <orientation>vertical</orientation>
+                                       </control>
+                               </control>
+                               <control type="group">
+                                       <description>Grouped Channels list</description>
+                                       <posx>890</posx>
+                                       <posy>90</posy>
+                                       <control type="label">
+                                               <description>name label</description>
+                                               <posx>0</posx>
+                                               <posy>0</posy>
+                                               <width>340</width>
+                                               <height>70</height>
+                                               <font>font13_title</font>
+                                               <label>$LOCALIZE[31508][CR]$INFO[Control.GetLabel(20)]</label>
+                                               <align>center</align>
+                                               <aligny>center</aligny>
+                                               <textcolor>blue</textcolor>
+                                       </control>
+                                       <control type="image">
+                                               <posx>0</posx>
+                                               <posy>75</posy>
+                                               <width>340</width>
+                                               <height>460</height>
+                                               <texture border="5">button-nofocus.png</texture>
+                                       </control>
+                                       <control type="list" id="12">
+                                               <posx>5</posx>
+                                               <posy>85</posy>
+                                               <width>330</width>
+                                               <height>440</height>
+                                               <onup>12</onup>
+                                               <ondown>12</ondown>
+                                               <onleft>71</onleft>
+                                               <onright>72</onright>
+                                               <pagecontrol>72</pagecontrol>
+                                               <scrolltime>200</scrolltime>
+                                               <itemlayout height="40">
+                                                       <control type="image">
+                                                               <posx>0</posx>
+                                                               <posy>0</posy>
+                                                               <width>330</width>
+                                                               <height>41</height>
+                                                               <texture border="3">MenuItemNF.png</texture>
+                                                       </control>
+                                                       <control type="image">
+                                                               <width>32</width>
+                                                               <height>32</height>
+                                                               <posx>5</posx>
+                                                               <posy>4</posy>
+                                                               <texture>$INFO[ListItem.Icon]</texture>
+                                                       </control>
+                                                       <control type="label">
+                                                               <posx>40</posx>
+                                                               <posy>0</posy>
+                                                               <width>280</width>
+                                                               <height>40</height>
+                                                               <font>font12</font>
+                                                               <align>left</align>
+                                                               <aligny>center</aligny>
+                                                               <textcolor>grey2</textcolor>
+                                                               <selectedcolor>selected</selectedcolor>
+                                                               <label>$INFO[ListItem.ChannelNumber,(,) - ]$INFO[ListItem.ChannelName]</label>
+                                                       </control>
+                                               </itemlayout>
+                                               <focusedlayout height="40">
+                                                       <control type="image">
+                                                               <posx>0</posx>
+                                                               <posy>0</posy>
+                                                               <width>330</width>
+                                                               <height>41</height>
+                                                               <visible>!Control.HasFocus(12)</visible>
+                                                               <texture border="3">MenuItemNF.png</texture>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>0</posx>
+                                                               <posy>0</posy>
+                                                               <width>330</width>
+                                                               <height>41</height>
+                                                               <visible>Control.HasFocus(12)</visible>
+                                                               <texture border="3">MenuItemFO.png</texture>
+                                                       </control>
+                                                       <control type="image">
+                                                               <width>32</width>
+                                                               <height>32</height>
+                                                               <posx>5</posx>
+                                                               <posy>4</posy>
+                                                               <texture>$INFO[ListItem.Icon]</texture>
+                                                       </control>
+                                                       <control type="label">
+                                                               <posx>40</posx>
+                                                               <posy>0</posy>
+                                                               <width>280</width>
+                                                               <height>40</height>
+                                                               <font>font12</font>
+                                                               <align>left</align>
+                                                               <aligny>center</aligny>
+                                                               <textcolor>white</textcolor>
+                                                               <selectedcolor>selected</selectedcolor>
+                                                               <label>$INFO[ListItem.ChannelNumber,(,) - ]$INFO[ListItem.ChannelName]</label>
+                                                       </control>
+                                               </focusedlayout>
+                                       </control>
+                                       <control type="scrollbar" id="72">
+                                               <posx>340</posx>
+                                               <posy>75</posy>
+                                               <width>25</width>
+                                               <height>460</height>
+                                               <texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground>
+                                               <texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar>
+                                               <texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus>
+                                               <textureslidernib>ScrollBarNib.png</textureslidernib>
+                                               <textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus>
+                                               <onleft>12</onleft>
+                                               <onright>26</onright>
+                                               <ondown>72</ondown>
+                                               <onup>72</onup>
+                                               <showonepage>false</showonepage>
+                                               <orientation>vertical</orientation>
+                                       </control>
+                               </control>
+                               <control type="grouplist" id="9000">
+                                       <posx>160</posx>
+                                       <posy>660</posy>
+                                       <width>1080</width>
+                                       <height>40</height>
+                                       <itemgap>2</itemgap>
+                                       <align>center</align>
+                                       <orientation>horizontal</orientation>
+                                       <onleft>72</onleft>
+                                       <onright>13</onright>
+                                       <onup>9000</onup>
+                                       <ondown>9000</ondown>
+                                       <control type="button" id="26">
+                                               <description>Add Group</description>
+                                               <width>210</width>
+                                               <include>ButtonInfoDialogsCommonValues</include>
+                                               <label>31503</label>
+                                       </control>
+                                       <control type="button" id="27">
+                                               <description>Rename Group</description>
+                                               <width>210</width>
+                                               <include>ButtonInfoDialogsCommonValues</include>
+                                               <label>31504</label>
+                                       </control>
+                                       <control type="button" id="28">
+                                               <description>Delete Group</description>
+                                               <width>210</width>
+                                               <include>ButtonInfoDialogsCommonValues</include>
+                                               <label>31505</label>
+                                       </control>
+                                       <control type="button" id="29">
+                                               <description>OK</description>
+                                               <width>210</width>
+                                               <include>ButtonInfoDialogsCommonValues</include>
+                                               <label>186</label>
+                                       </control>
+                               </control>
+                       </control>
+               </control>
+               <include>SideBladeRight</include>
+               <include>Clock</include>
+
+               <control type="label" id="20">
+                       <description>Fake Label used to pass on name label</description>
+                       <visible>false</visible>
+               </control>
+       </controls>
+</window>
\ No newline at end of file
diff --git a/addons/skin.confluence/720p/DialogPVRGuideInfo.xml b/addons/skin.confluence/720p/DialogPVRGuideInfo.xml
new file mode 100644 (file)
index 0000000..938becb
--- /dev/null
@@ -0,0 +1,274 @@
+<window id="601">
+       <defaultcontrol always="true">7</defaultcontrol>
+       <coordinates>
+               <system>1</system>
+               <posx>20</posx>
+               <posy>30</posy>
+               <origin x="275" y="30">![Window.IsVisible(FullscreenVideo) | Window.IsVisible(Visualisation)]</origin>
+       </coordinates>
+       <include>dialogeffect</include>
+       <controls>
+               <control type="group">
+                       <control type="image">
+                               <description>background image</description>
+                               <posx>0</posx>
+                               <posy>0</posy>
+                               <width>730</width>
+                               <height>660</height>
+                               <colordiffuse>EEFFFFFF</colordiffuse>
+                               <texture border="40">DialogBack.png</texture>
+                       </control>
+                       <control type="button">
+                               <description>Close Window button</description>
+                               <posx>650</posx>
+                               <posy>9</posy>
+                               <width>64</width>
+                               <height>32</height>
+                               <label>-</label>
+                               <font>-</font>
+                               <onclick>PreviousMenu</onclick>
+                               <texturefocus>DialogCloseButton-focus.png</texturefocus>
+                               <texturenofocus>DialogCloseButton.png</texturenofocus>
+                               <onleft>2</onleft>
+                               <onright>2</onright>
+                               <onup>2</onup>
+                               <ondown>2</ondown>
+                               <visible>system.getbool(input.enablemouse)</visible>
+                       </control>
+                       <control type="image">
+                               <posx>80</posx>
+                               <posy>10</posy>
+                               <width>570</width>
+                               <height>90</height>
+                               <aspectratio>stretch</aspectratio>
+                               <texture>GlassTitleBar.png</texture>
+                       </control>
+                       <control type="label">
+                               <description>header label</description>
+                               <posx>40</posx>
+                               <posy>18</posy>
+                               <width>650</width>
+                               <height>30</height>
+                               <font>font13_title</font>
+                               <label>$LOCALIZE[19047]</label>
+                               <align>center</align>
+                               <aligny>center</aligny>
+                               <textcolor>white</textcolor>
+                               <shadowcolor>black</shadowcolor>
+                       </control>
+                       <control type="label">
+                               <description>Title label</description>
+                               <posx>40</posx>
+                               <posy>70</posy>
+                               <width>650</width>
+                               <height>30</height>
+                               <font>font13caps</font>
+                               <label>$INFO[ListItem.Title]</label>
+                               <align>center</align>
+                               <aligny>center</aligny>
+                               <textcolor>white</textcolor>
+                               <shadowcolor>black</shadowcolor>
+                       </control>
+                       <control type="group">
+                               <control type="group">
+                                       <posx>40</posx>
+                                       <posy>140</posy>
+                                       <control type="label">
+                                               <description>Start Date</description>
+                                               <posx>170</posx>
+                                               <posy>0</posy>
+                                               <width>170</width>
+                                               <height>25</height>
+                                               <align>right</align>
+                                               <aligny>center</aligny>
+                                               <font>font13_title</font>
+                                               <textcolor>blue</textcolor>
+                                               <label>$LOCALIZE[552]:</label>
+                                       </control>
+                                       <control type="label">
+                                               <description>Start date value</description>
+                                               <posx>180</posx>
+                                               <posy>0</posy>
+                                               <width>470</width>
+                                               <height>25</height>
+                                               <align>left</align>
+                                               <aligny>center</aligny>
+                                               <font>font13</font>
+                                               <textcolor>white</textcolor>
+                                               <label>$INFO[ListItem.StartDate]</label>
+                                       </control>
+                                       <control type="label">
+                                               <description>Start time</description>
+                                               <posx>170</posx>
+                                               <posy>35</posy>
+                                               <width>170</width>
+                                               <height>25</height>
+                                               <align>right</align>
+                                               <aligny>center</aligny>
+                                               <font>font13_title</font>
+                                               <textcolor>blue</textcolor>
+                                               <label>$LOCALIZE[142]</label>
+                                       </control>
+                                       <control type="label">
+                                               <description>Start Time value</description>
+                                               <posx>180</posx>
+                                               <posy>35</posy>
+                                               <width>470</width>
+                                               <height>25</height>
+                                               <align>left</align>
+                                               <aligny>center</aligny>
+                                               <font>font13</font>
+                                               <textcolor>white</textcolor>
+                                               <label>$INFO[ListItem.StartTime]</label>
+                                       </control>
+                                       <control type="label">
+                                               <description>Channel Name</description>
+                                               <posx>170</posx>
+                                               <posy>70</posy>
+                                               <width>170</width>
+                                               <height>25</height>
+                                               <align>right</align>
+                                               <aligny>center</aligny>
+                                               <font>font13_title</font>
+                                               <textcolor>blue</textcolor>
+                                               <label>$LOCALIZE[19148]:</label>
+                                       </control>
+                                       <control type="fadelabel">
+                                               <description>Channel Value</description>
+                                               <posx>180</posx>
+                                               <posy>70</posy>
+                                               <width>470</width>
+                                               <height>25</height>
+                                               <align>left</align>
+                                               <aligny>center</aligny>
+                                               <font>font13</font>
+                                               <textcolor>white</textcolor>
+                                               <label>$INFO[ListItem.ChannelName]</label>
+                                       </control>
+                                       <control type="label">
+                                               <description>Duration</description>
+                                               <posx>170</posx>
+                                               <posy>105</posy>
+                                               <width>170</width>
+                                               <height>25</height>
+                                               <align>right</align>
+                                               <aligny>center</aligny>
+                                               <font>font13_title</font>
+                                               <textcolor>blue</textcolor>
+                                               <label>$LOCALIZE[180]:</label>
+                                       </control>
+                                       <control type="label">
+                                               <description>Duration value</description>
+                                               <posx>180</posx>
+                                               <posy>105</posy>
+                                               <width>470</width>
+                                               <label>$INFO[ListItem.Duration]</label>
+                                               <align>left</align>
+                                               <font>font13</font>
+                                               <scroll>true</scroll>
+                                       </control>
+                                       <control type="label">
+                                               <description>Genre</description>
+                                               <posx>170</posx>
+                                               <posy>140</posy>
+                                               <width>170</width>
+                                               <height>25</height>
+                                               <align>right</align>
+                                               <aligny>center</aligny>
+                                               <font>font13_title</font>
+                                               <textcolor>blue</textcolor>
+                                               <label>$LOCALIZE[135]:</label>
+                                       </control>
+                                       <control type="label">
+                                               <description>Genre value</description>
+                                               <posx>180</posx>
+                                               <posy>140</posy>
+                                               <width>470</width>
+                                               <label fallback="161">$INFO[ListItem.Genre]</label>
+                                               <align>left</align>
+                                               <font>font13</font>
+                                               <scroll>true</scroll>
+                                       </control>
+                                       <control type="label">
+                                               <description>Subtitle value</description>
+                                               <posx>0</posx>
+                                               <posy>185</posy>
+                                               <width>650</width>
+                                               <label>$INFO[ListItem.PlotOutline]</label>
+                                               <align>center</align>
+                                               <font>font13caps</font>
+                                               <textcolor>blue</textcolor>
+                                               <scroll>true</scroll>
+                                               <visible>!IsEmpty(ListItem.PlotOutline)</visible>
+                                       </control>
+                               </control>
+                               <control type="label">
+                                       <posx>610</posx>
+                                       <posy>370</posy>
+                                       <width>400</width>
+                                       <height>30</height>
+                                       <font>font13_title</font>
+                                       <textcolor>grey2</textcolor>
+                                       <shadowcolor>black</shadowcolor>
+                                       <scroll>true</scroll>
+                                       <align>right</align>
+                                       <aligny>center</aligny>
+                                       <label>[COLOR=blue]$LOCALIZE[207][/COLOR]$INFO[Container(400).CurrentPage, ( $LOCALIZE[31024] ]$INFO[Container(400).NumPages,/, )]</label>
+                               </control>
+                               <control type="spincontrol" id="60">
+                                       <description>Next page button</description>
+                                       <posx>620</posx>
+                                       <posy>375</posy>
+                                       <subtype>page</subtype>
+                                       <font>-</font>
+                                       <onleft>60</onleft>
+                                       <onright>60</onright>
+                                       <ondown>9000</ondown>
+                                       <onup>9000</onup>
+                                       <textcolor>-</textcolor>
+                                       <showonepage>true</showonepage>
+                               </control>
+                               <control type="textbox" id="400">
+                                       <description>PLOT</description>
+                                       <posx>40</posx>
+                                       <posy>400</posy>
+                                       <width>650</width>
+                                       <height>180</height>
+                                       <font>font12</font>
+                                       <align>justify</align>
+                                       <pagecontrol>60</pagecontrol>
+                                       <label fallback="161">$INFO[ListItem.Plot]</label>
+                               </control>
+                               <control type="grouplist" id="9000">
+                                       <posx>40</posx>
+                                       <posy>590</posy>
+                                       <width>650</width>
+                                       <height>40</height>
+                                       <itemgap>5</itemgap>
+                                       <align>center</align>
+                                       <orientation>horizontal</orientation>
+                                       <onleft>9000</onleft>
+                                       <onright>9000</onright>
+                                       <onup>60</onup>
+                                       <ondown>60</ondown>
+                                       <control type="button" id="5">
+                                               <description>Switch to Channel</description>
+                                               <include>ButtonInfoDialogsCommonValues</include>
+                                               <label>19165</label>
+                                       </control>
+                                       <control type="button" id="6">
+                                               <description>Record</description>
+                                               <include>ButtonInfoDialogsCommonValues</include>
+                                               <label>-</label>
+                                       </control>
+                                       <control type="button" id="7">
+                                               <description>OK</description>
+                                               <include>ButtonInfoDialogsCommonValues</include>
+                                               <label>186</label>
+                                       </control>
+                               </control>
+                       </control>
+               </control>
+               <include>SideBladeRight</include>
+       </controls>
+</window>
\ No newline at end of file
diff --git a/addons/skin.confluence/720p/DialogPVRGuideOSD.xml b/addons/skin.confluence/720p/DialogPVRGuideOSD.xml
new file mode 100644 (file)
index 0000000..6f6c3f4
--- /dev/null
@@ -0,0 +1,266 @@
+<window id="610">
+       <defaultcontrol always="true">11</defaultcontrol>
+       <coordinates>
+               <system>1</system>
+               <posx>780</posx>
+               <posy>30</posy>
+       </coordinates>
+       <include>dialogeffect</include>
+       <controls>
+               <control type="group">
+                       <control type="image">
+                               <description>background image</description>
+                               <posx>0</posx>
+                               <posy>0</posy>
+                               <width>480</width>
+                               <height>660</height>
+                               <colordiffuse>EEFFFFFF</colordiffuse>
+                               <texture border="40">DialogBack.png</texture>
+                       </control>
+                       <control type="button">
+                               <description>Close Window button</description>
+                               <posx>400</posx>
+                               <posy>9</posy>
+                               <width>64</width>
+                               <height>32</height>
+                               <label>-</label>
+                               <font>-</font>
+                               <onclick>PreviousMenu</onclick>
+                               <texturefocus>DialogCloseButton-focus.png</texturefocus>
+                               <texturenofocus>DialogCloseButton.png</texturenofocus>
+                               <onleft>2</onleft>
+                               <onright>2</onright>
+                               <onup>2</onup>
+                               <ondown>2</ondown>
+                               <visible>system.getbool(input.enablemouse)</visible>
+                       </control>
+                       <control type="image">
+                               <posx>40</posx>
+                               <posy>10</posy>
+                               <width>430</width>
+                               <height>90</height>
+                               <aspectratio>stretch</aspectratio>
+                               <texture>GlassTitleBar.png</texture>
+                       </control>
+                       <control type="label">
+                               <description>header label</description>
+                               <posx>40</posx>
+                               <posy>18</posy>
+                               <width>430</width>
+                               <height>30</height>
+                               <font>font12_title</font>
+                               <label>$LOCALIZE[19029] - $INFO[VideoPlayer.ChannelName]</label>
+                               <align>center</align>
+                               <aligny>center</aligny>
+                               <textcolor>white</textcolor>
+                               <shadowcolor>black</shadowcolor>
+                       </control>
+                       <control type="list" id="11">
+                               <posx>40</posx>
+                               <posy>64</posy>
+                               <width>390</width>
+                               <height>541</height>
+                               <onleft>60</onleft>
+                               <onright>60</onright>
+                               <onup>11</onup>
+                               <ondown>11</ondown>
+                               <viewtype label="535">list</viewtype>
+                               <pagecontrol>60</pagecontrol>
+                               <scrolltime>200</scrolltime>
+                               <itemlayout height="60" width="400">
+                                       <control type="image">
+                                               <posx>0</posx>
+                                               <posy>0</posy>
+                                               <width>390</width>
+                                               <height>61</height>
+                                               <texture border="2">MenuItemNF.png</texture>
+                                               <include>VisibleFadeEffect</include>
+                                       </control>
+                                       <control type="label">
+                                               <posx>5</posx>
+                                               <posy>0</posy>
+                                               <width>200</width>
+                                               <height>30</height>
+                                               <font>font13</font>
+                                               <textcolor>blue</textcolor>
+                                               <selectedcolor>selected</selectedcolor>
+                                               <align>left</align>
+                                               <aligny>center</aligny>
+                                               <label>$INFO[ListItem.StartTime]</label>
+                                       </control>
+                                       <control type="label">
+                                               <posx>390</posx>
+                                               <posy>0</posy>
+                                               <width>370</width>
+                                               <height>30</height>
+                                               <font>font12</font>
+                                               <textcolor>grey2</textcolor>
+                                               <selectedcolor>selected</selectedcolor>
+                                               <align>right</align>
+                                               <aligny>center</aligny>
+                                               <label>$INFO[ListItem.StartDate]</label>
+                                       </control>
+                                       <control type="label">
+                                               <posx>5</posx>
+                                               <posy>30</posy>
+                                               <width>390</width>
+                                               <height>30</height>
+                                               <font>font12</font>
+                                               <textcolor>grey</textcolor>
+                                               <selectedcolor>selected</selectedcolor>
+                                               <align>left</align>
+                                               <aligny>center</aligny>
+                                               <label>$INFO[ListItem.Label]</label>
+                                               <visible>![ListItem.IsRecording | ListItem.HasTimer]</visible>
+                                       </control>
+                                       <control type="label">
+                                               <posx>5</posx>
+                                               <posy>30</posy>
+                                               <width>350</width>
+                                               <height>30</height>
+                                               <font>font12</font>
+                                               <textcolor>grey</textcolor>
+                                               <selectedcolor>selected</selectedcolor>
+                                               <align>left</align>
+                                               <aligny>center</aligny>
+                                               <label>$INFO[ListItem.Label]</label>
+                                               <visible>ListItem.IsRecording | ListItem.HasTimer</visible>
+                                       </control>
+                                       <control type="image">
+                                               <posx>360</posx>
+                                               <posy>30</posy>
+                                               <width>30</width>
+                                               <height>20</height>
+                                               <texture>PVR-IsRecording.png</texture>
+                                               <visible>ListItem.IsRecording</visible>
+                                       </control>
+                                       <control type="image">
+                                               <posx>370</posx>
+                                               <posy>30</posy>
+                                               <width>20</width>
+                                               <height>20</height>
+                                               <texture>PVR-HasTimer.png</texture>
+                                               <visible>ListItem.HasTimer</visible>
+                                       </control>
+                               </itemlayout>
+                               <focusedlayout height="60" width="500">
+                                       <control type="image">
+                                               <posx>0</posx>
+                                               <posy>0</posy>
+                                               <width>390</width>
+                                               <height>61</height>
+                                               <texture border="2">MenuItemNF.png</texture>
+                                               <visible>!Control.HasFocus(11)</visible>
+                                               <include>VisibleFadeEffect</include>
+                                       </control>
+                                       <control type="image">
+                                               <posx>0</posx>
+                                               <posy>0</posy>
+                                               <width>390</width>
+                                               <height>61</height>
+                                               <texture border="2">MenuItemFO.png</texture>
+                                               <visible>Control.HasFocus(11)</visible>
+                                               <include>VisibleFadeEffect</include>
+                                       </control>
+                                       <control type="label">
+                                               <posx>5</posx>
+                                               <posy>0</posy>
+                                               <width>200</width>
+                                               <height>30</height>
+                                               <font>font13</font>
+                                               <textcolor>selected</textcolor>
+                                               <selectedcolor>selected</selectedcolor>
+                                               <align>left</align>
+                                               <aligny>center</aligny>
+                                               <label>$INFO[ListItem.StartTime]</label>
+                                       </control>
+                                       <control type="label">
+                                               <posx>390</posx>
+                                               <posy>0</posy>
+                                               <width>370</width>
+                                               <height>30</height>
+                                               <font>font12</font>
+                                               <textcolor>selected</textcolor>
+                                               <selectedcolor>selected</selectedcolor>
+                                               <align>right</align>
+                                               <aligny>center</aligny>
+                                               <label>$INFO[ListItem.StartDate]</label>
+                                       </control>
+                                       <control type="label">
+                                               <posx>5</posx>
+                                               <posy>30</posy>
+                                               <width>390</width>
+                                               <height>30</height>
+                                               <font>font12</font>
+                                               <textcolor>white</textcolor>
+                                               <selectedcolor>selected</selectedcolor>
+                                               <align>left</align>
+                                               <aligny>center</aligny>
+                                               <label>[B]$INFO[ListItem.Label][/B]</label>
+                                               <visible>![ListItem.IsRecording | ListItem.HasTimer]</visible>
+                                       </control>
+                                       <control type="label">
+                                               <posx>5</posx>
+                                               <posy>30</posy>
+                                               <width>350</width>
+                                               <height>30</height>
+                                               <font>font12</font>
+                                               <textcolor>white</textcolor>
+                                               <selectedcolor>selected</selectedcolor>
+                                               <align>left</align>
+                                               <aligny>center</aligny>
+                                               <label>[B]$INFO[ListItem.Label][/B]</label>
+                                               <visible>ListItem.IsRecording | ListItem.HasTimer</visible>
+                                       </control>
+                                       <control type="image">
+                                               <posx>360</posx>
+                                               <posy>30</posy>
+                                               <width>30</width>
+                                               <height>20</height>
+                                               <texture>PVR-IsRecording.png</texture>
+                                               <visible>ListItem.IsRecording</visible>
+                                       </control>
+                                       <control type="image">
+                                               <posx>370</posx>
+                                               <posy>30</posy>
+                                               <width>20</width>
+                                               <height>20</height>
+                                               <texture>PVR-HasTimer.png</texture>
+                                               <visible>ListItem.HasTimer</visible>
+                                       </control>
+                               </focusedlayout>
+                       </control>
+                       <control type="scrollbar" id="60">
+                               <posx>435</posx>
+                               <posy>65</posy>
+                               <width>25</width>
+                               <height>541</height>
+                               <texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground>
+                               <texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar>
+                               <texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus>
+                               <textureslidernib>ScrollBarNib.png</textureslidernib>
+                               <textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus>
+                               <onleft>11</onleft>
+                               <onright>11</onright>
+                               <ondown>61</ondown>
+                               <onup>61</onup>
+                               <showonepage>false</showonepage>
+                               <orientation>vertical</orientation>
+                       </control>
+                       <control type="label">
+                               <description>Page Count Label</description>
+                               <posx>450</posx>
+                               <posy>615</posy>
+                               <width>400</width>
+                               <height>20</height>
+                               <font>font12</font>
+                               <textcolor>grey</textcolor>
+                               <scroll>false</scroll>
+                               <align>right</align>
+                               <aligny>center</aligny>
+                               <label>([COLOR=blue]$INFO[Container(11).NumItems][/COLOR]) $LOCALIZE[31025] - $LOCALIZE[31024] ([COLOR=blue]$INFO[Container(11).CurrentPage]/$INFO[Container(11).NumPages][/COLOR])</label>
+                               <include>Window_OpenClose_Animation</include>           
+                       </control>
+               </control>
+       </controls>
+</window>
diff --git a/addons/skin.confluence/720p/DialogPVRGuideSearch.xml b/addons/skin.confluence/720p/DialogPVRGuideSearch.xml
new file mode 100644 (file)
index 0000000..3953799
--- /dev/null
@@ -0,0 +1,461 @@
+<window id="606">
+       <defaultcontrol always="true">9</defaultcontrol>
+       <coordinates>
+               <system>1</system>
+               <posx>210</posx>
+               <posy>65</posy>
+       </coordinates>
+       <include>dialogeffect</include>
+       <controls>
+               <control type="image">
+                       <description>background image</description>
+                       <posx>0</posx>
+                       <posy>0</posy>
+                       <width>865</width>
+                       <height>605</height>
+                       <texture border="40">DialogBack.png</texture>
+               </control>
+               <control type="image">
+                       <posx>80</posx>
+                       <posy>10</posy>
+                       <width>700</width>
+                       <height>90</height>
+                       <aspectratio>stretch</aspectratio>
+                       <texture>GlassTitleBar.png</texture>
+               </control>
+               <control type="label">
+                       <description>header label</description>
+                       <posx>80</posx>
+                       <posy>18</posy>
+                       <width>700</width>
+                       <height>30</height>
+                       <font>font13_title</font>
+                       <label>$LOCALIZE[19142]</label>
+                       <align>center</align>
+                       <aligny>center</aligny>
+                       <textcolor>white</textcolor>
+                       <shadowcolor>black</shadowcolor>
+               </control>
+               <control type="button">
+                       <description>Close Window button</description>
+                       <posx>770</posx>
+                       <posy>9</posy>
+                       <width>64</width>
+                       <height>32</height>
+                       <label>-</label>
+                       <font>-</font>
+                       <onclick>PreviousMenu</onclick>
+                       <texturefocus>DialogCloseButton-focus.png</texturefocus>
+                       <texturenofocus>DialogCloseButton.png</texturenofocus>
+                       <onleft>3</onleft>
+                       <onright>3</onright>
+                       <onup>3</onup>
+                       <ondown>3</ondown>
+                       <visible>system.getbool(input.enablemouse)</visible>
+               </control>
+               <control type="label">
+                       <description>Search string</description>
+                       <posx>30</posx>
+                       <posy>60</posy>
+                       <width>320</width>
+                       <height>40</height>
+                       <font>font12caps</font>
+                       <label>$LOCALIZE[19133]</label>
+                       <align>left</align>
+                       <aligny>center</aligny>
+                       <textcolor>blue</textcolor>
+                       <shadowcolor>black</shadowcolor>
+               </control>
+               <control type="image">
+                       <posx>30</posx>
+                       <posy>100</posy>
+                       <width>800</width>
+                       <height>50</height>
+                       <aspectratio>stretch</aspectratio>
+                       <texture border="20">KeyboardEditArea.png</texture>
+               </control>
+               <control type="edit" id="9">
+                       <description>Search string</description>
+                       <posx>35</posx>
+                       <posy>105</posy>
+                       <width>790</width>
+                       <height>40</height>
+                       <font>font13</font>
+                       <textcolor>white</textcolor>
+                       <shadowcolor>black</shadowcolor>
+                       <texturefocus>-</texturefocus>
+                       <texturenofocus>-</texturenofocus>
+                       <align>left</align>
+                       <onleft>9</onleft>
+                       <onright>9</onright>
+                       <onup>26</onup>
+                       <ondown>10</ondown>
+               </control>
+               <control type="textbox">
+                       <description>Search help</description>
+                       <posx>30</posx>
+                       <posy>155</posy>
+                       <width>800</width>
+                       <height>70</height>
+                       <align>left</align>
+                       <font>font12</font>
+                       <textcolor>grey2</textcolor>
+                       <label>$LOCALIZE[19001] $LOCALIZE[19002]</label>
+               </control>
+               <control type="group">
+                       <posx>30</posx>
+                       <posy>230</posy>
+                       <control type="radiobutton" id="10">
+                               <description>Include Description</description>
+                               <posx>0</posx>
+                               <posy>0</posy>
+                               <height>35</height>
+                               <width>400</width>
+                               <font>font12</font>
+                               <textcolor>white</textcolor>
+                               <focusedcolor>white</focusedcolor>
+                               <shadowcolor>black</shadowcolor>
+                               <texturefocus border="5">button-focus.png</texturefocus>
+                               <texturenofocus border="5">button-nofocus.png</texturenofocus>
+                               <align>left</align>
+                               <aligny>center</aligny>
+                               <label>19134</label>
+                               <onleft>12</onleft>
+                               <onright>12</onright>
+                               <onup>9</onup>
+                               <ondown>11</ondown>
+                       </control>
+                       <control type="radiobutton" id="11">
+                               <description>Case Sensitive</description>
+                               <posx>0</posx>
+                               <posy>35</posy>
+                               <height>35</height>
+                               <width>400</width>
+                               <font>font12</font>
+                               <textcolor>white</textcolor>
+                               <focusedcolor>white</focusedcolor>
+                               <shadowcolor>black</shadowcolor>
+                               <texturefocus border="5">button-focus.png</texturefocus>
+                               <texturenofocus border="5">button-nofocus.png</texturenofocus>
+                               <align>left</align>
+                               <aligny>center</aligny>
+                               <label>19135</label>
+                               <onleft>13</onleft>
+                               <onright>13</onright>
+                               <onup>10</onup>
+                               <ondown>14</ondown>
+                       </control>
+                       <control type="edit" id="14">
+                               <description>Start Date</description>
+                               <posx>0</posx>
+                               <posy>70</posy>
+                               <width>400</width>
+                               <height>35</height>
+                               <font>font12</font>
+                               <textcolor>white</textcolor>
+                               <focusedcolor>white</focusedcolor>
+                               <shadowcolor>black</shadowcolor>
+                               <texturefocus border="5">button-focus.png</texturefocus>
+                               <texturenofocus border="5">button-nofocus.png</texturenofocus>
+                               <label>19128</label>
+                               <onright>16</onright>
+                               <onleft>16</onleft>
+                               <onup>11</onup>
+                               <ondown>15</ondown>
+                       </control>
+                       <control type="edit" id="15">
+                               <description>Stop Date</description>
+                               <posx>0</posx>
+                               <posy>105</posy>
+                               <width>400</width>
+                               <height>35</height>
+                               <font>font12</font>
+                               <textcolor>white</textcolor>
+                               <focusedcolor>white</focusedcolor>
+                               <shadowcolor>black</shadowcolor>
+                               <texturefocus border="5">button-focus.png</texturefocus>
+                               <texturenofocus border="5">button-nofocus.png</texturenofocus>
+                               <label>19129</label>
+                               <onright>17</onright>
+                               <onleft>17</onleft>
+                               <onup>14</onup>
+                               <ondown>18</ondown>
+                       </control>
+                       <control type="spincontrolex" id="18">
+                               <description>Genre</description>
+                               <posx>0</posx>
+                               <posy>140</posy>
+                               <width>400</width>
+                               <height>35</height>
+                               <font>font12</font>
+                               <texturefocus border="5">button-focus.png</texturefocus>
+                               <texturenofocus border="5">button-nofocus.png</texturenofocus>
+                               <label>515</label>
+                               <onright>19</onright>
+                               <onleft>19</onleft>
+                               <onup>15</onup>
+                               <ondown>20</ondown>
+                       </control>
+                       <control type="radiobutton" id="20">
+                               <description>Include unknown Genres</description>
+                               <posx>0</posx>
+                               <posy>175</posy>
+                               <height>35</height>
+                               <width>400</width>
+                               <font>font12</font>
+                               <textcolor>white</textcolor>
+                               <focusedcolor>white</focusedcolor>
+                               <shadowcolor>black</shadowcolor>
+                               <align>left</align>
+                               <aligny>center</aligny>
+                               <texturefocus border="5">button-focus.png</texturefocus>
+                               <texturenofocus border="5">button-nofocus.png</texturenofocus>
+                               <pulseonselect>no</pulseonselect>
+                               <label>19132</label>
+                               <onleft>21</onleft>
+                               <onright>21</onright>
+                               <onup>18</onup>
+                               <ondown>22</ondown>
+                       </control>
+                       <control type="radiobutton" id="22">
+                               <description>FTA only</description>
+                               <posx>0</posx>
+                               <posy>210</posy>
+                               <height>35</height>
+                               <width>400</width>
+                               <font>font12</font>
+                               <textcolor>white</textcolor>
+                               <focusedcolor>white</focusedcolor>
+                               <shadowcolor>black</shadowcolor>
+                               <align>left</align>
+                               <aligny>center</aligny>
+                               <texturefocus border="5">button-focus.png</texturefocus>
+                               <texturenofocus border="5">button-nofocus.png</texturenofocus>
+                               <pulseonselect>no</pulseonselect>
+                               <label>19123</label>
+                               <onleft>23</onleft>
+                               <onright>23</onright>
+                               <onup>20</onup>
+                               <ondown>24</ondown>
+                       </control>
+                       <control type="radiobutton" id="24">
+                               <description>Ignore Timers</description>
+                               <posx>0</posx>
+                               <posy>245</posy>
+                               <height>35</height>
+                               <width>400</width>
+                               <font>font12</font>
+                               <textcolor>white</textcolor>
+                               <focusedcolor>white</focusedcolor>
+                               <shadowcolor>black</shadowcolor>
+                               <align>left</align>
+                               <aligny>center</aligny>
+                               <texturefocus border="5">button-focus.png</texturefocus>
+                               <texturenofocus border="5">button-nofocus.png</texturenofocus>
+                               <pulseonselect>no</pulseonselect>
+                               <label>19124</label>
+                               <onleft>27</onleft>
+                               <onright>27</onright>
+                               <onup>22</onup>
+                               <ondown>26</ondown>
+                       </control>
+               </control>
+               <control type="group">
+                       <posx>440</posx>
+                       <posy>230</posy>
+                       <control type="spincontrolex" id="12">
+                               <description>Min Duration</description>
+                               <posx>0</posx>
+                               <posy>0</posy>
+                               <width>400</width>
+                               <height>35</height>
+                               <font>font12</font>
+                               <texturefocus border="5">button-focus.png</texturefocus>
+                               <texturenofocus border="5">button-nofocus.png</texturenofocus>
+                               <label>19130</label>
+                               <onright>10</onright>
+                               <onleft>10</onleft>
+                               <onup>9</onup>
+                               <ondown>13</ondown>
+                       </control>
+                       <control type="spincontrolex" id="13">
+                               <description>Max Duration</description>
+                               <posx>0</posx>
+                               <posy>35</posy>
+                               <width>400</width>
+                               <height>35</height>
+                               <font>font12</font>
+                               <texturefocus border="5">button-focus.png</texturefocus>
+                               <texturenofocus border="5">button-nofocus.png</texturenofocus>
+                               <label>19131</label>
+                               <onright>11</onright>
+                               <onleft>11</onleft>
+                               <onup>12</onup>
+                               <ondown>16</ondown>
+                       </control>
+                       <control type="edit" id="16">
+                               <description>Start time</description>
+                               <posx>0</posx>
+                               <posy>70</posy>
+                               <width>400</width>
+                               <height>35</height>
+                               <font>font12</font>
+                               <textcolor>white</textcolor>
+                               <focusedcolor>white</focusedcolor>
+                               <shadowcolor>black</shadowcolor>
+                               <texturefocus border="5">button-focus.png</texturefocus>
+                               <texturenofocus border="5">button-nofocus.png</texturenofocus>
+                               <label>19126</label>
+                               <onright>14</onright>
+                               <onleft>14</onleft>
+                               <onup>13</onup>
+                               <ondown>17</ondown>
+                       </control>
+                       <control type="edit" id="17">
+                               <description>Stop time</description>
+                               <posx>0</posx>
+                               <posy>105</posy>
+                               <width>400</width>
+                               <height>35</height>
+                               <font>font12</font>
+                               <textcolor>white</textcolor>
+                               <focusedcolor>white</focusedcolor>
+                               <shadowcolor>black</shadowcolor>
+                               <texturefocus border="5">button-focus.png</texturefocus>
+                               <texturenofocus border="5">button-nofocus.png</texturenofocus>
+                               <label>19127</label>
+                               <onright>15</onright>
+                               <onleft>15</onleft>
+                               <onup>16</onup>
+                               <ondown>19</ondown>
+                       </control>
+                       <control type="radiobutton" id="19">
+                               <description>avoid repeats</description>
+                               <posx>0</posx>
+                               <posy>140</posy>
+                               <width>400</width>
+                               <height>35</height>
+                               <font>font12</font>
+                               <textcolor>white</textcolor>
+                               <focusedcolor>white</focusedcolor>
+                               <shadowcolor>black</shadowcolor>
+                               <align>left</align>
+                               <aligny>center</aligny>
+                               <texturefocus border="5">button-focus.png</texturefocus>
+                               <texturenofocus border="5">button-nofocus.png</texturenofocus>
+                               <pulseonselect>no</pulseonselect>
+                               <label>19121</label>
+                               <onright>18</onright>
+                               <onleft>18</onleft>
+                               <onup>17</onup>
+                               <ondown>21</ondown>
+                       </control>
+                       <control type="spincontrolex" id="21">
+                               <description>Groups</description>
+                               <posx>0</posx>
+                               <posy>175</posy>
+                               <width>400</width>
+                               <height>35</height>
+                               <font>font12</font>
+                               <texturefocus border="5">button-focus.png</texturefocus>
+                               <texturenofocus border="5">button-nofocus.png</texturenofocus>
+                               <label>19141</label>
+                               <onright>20</onright>
+                               <onleft>20</onleft>
+                               <onup>19</onup>
+                               <ondown>23</ondown>
+                       </control>
+                       <control type="spincontrolex" id="23">
+                               <description>Channels</description>
+                               <posx>0</posx>
+                               <posy>210</posy>
+                               <width>400</width>
+                               <height>35</height>
+                               <font>font12</font>
+                               <texturefocus border="5">button-focus.png</texturefocus>
+                               <texturenofocus border="5">button-nofocus.png</texturenofocus>
+                               <label>19148</label>
+                               <onleft>22</onleft>
+                               <onright>22</onright>
+                               <onup>21</onup>
+                               <ondown>27</ondown>
+                       </control>
+                       <control type="radiobutton" id="27">
+                               <description>Ignore Recordings</description>
+                               <posx>0</posx>
+                               <posy>245</posy>
+                               <height>35</height>
+                               <width>400</width>
+                               <font>font12</font>
+                               <textcolor>white</textcolor>
+                               <focusedcolor>white</focusedcolor>
+                               <shadowcolor>black</shadowcolor>
+                               <align>left</align>
+                               <aligny>center</aligny>
+                               <texturefocus border="5">button-focus.png</texturefocus>
+                               <texturenofocus border="5">button-nofocus.png</texturenofocus>
+                               <pulseonselect>no</pulseonselect>
+                               <label>19125</label>
+                               <onleft>24</onleft>
+                               <onright>24</onright>
+                               <onup>23</onup>
+                               <ondown>26</ondown>
+                       </control>
+               </control>
+               <control type="group" id="9000">
+                       <posy>540</posy>
+                       <posx>125</posx>
+                       <control type="button" id="28">
+                               <description>Defaults Button</description>
+                               <posx>0</posx>
+                               <posy>0</posy>
+                               <width>200</width>
+                               <height>40</height>
+                               <align>center</align>
+                               <aligny>center</aligny>
+                               <texturenofocus border="5">MenuItemNF.png</texturenofocus>
+                               <texturefocus border="5">button-focus.png</texturefocus>
+                               <font>font12_title</font>
+                               <label>409</label>
+                               <onleft>26</onleft>
+                               <onright>25</onright>
+                               <onup>24</onup>
+                               <ondown>9</ondown>
+                       </control>
+                       <control type="button" id="25">
+                               <description>Cancel Button</description>
+                               <posx>210</posx>
+                               <posy>0</posy>
+                               <width>200</width>
+                               <height>40</height>
+                               <align>center</align>
+                               <aligny>center</aligny>
+                               <texturenofocus border="5">MenuItemNF.png</texturenofocus>
+                               <texturefocus border="5">button-focus.png</texturefocus>
+                               <font>font12_title</font>
+                               <label>222</label>
+                               <onleft>28</onleft>
+                               <onright>26</onright>
+                               <onup>27</onup>
+                               <ondown>9</ondown>
+                       </control>
+                       <control type="button" id="26">
+                               <description>Search Button</description>
+                               <posx>420</posx>
+                               <posy>0</posy>
+                               <width>200</width>
+                               <height>40</height>
+                               <align>center</align>
+                               <aligny>center</aligny>
+                               <texturenofocus border="5">MenuItemNF.png</texturenofocus>
+                               <texturefocus border="5">button-focus.png</texturefocus>
+                               <font>font12_title</font>
+                               <label>137</label>
+                               <onleft>25</onleft>
+                               <onright>28</onright>
+                               <onup>27</onup>
+                               <ondown>9</ondown>
+                       </control>
+               </control>
+       </controls>
+</window>
\ No newline at end of file
diff --git a/addons/skin.confluence/720p/DialogPVRRecordingInfo.xml b/addons/skin.confluence/720p/DialogPVRRecordingInfo.xml
new file mode 100644 (file)
index 0000000..e824e8a
--- /dev/null
@@ -0,0 +1,261 @@
+<window id="602">
+       <defaultcontrol always="true">10</defaultcontrol>
+       <controls>
+               <control type="group">
+                       <posx>580</posx>
+                       <animation effect="slide" start="700,0" end="0,0" time="400" tween="quadratic" easing="out">WindowOpen</animation>
+                       <animation effect="slide" start="0,0" end="700,0" time="400" tween="quadratic" easing="out">WindowClose</animation>
+                       <control type="image">
+                               <posx>0</posx>
+                               <posy>0</posy>
+                               <width>1100</width>
+                               <height>720</height>
+                               <texture border="15,0,0,0" flipx="true">MediaBladeSub.png</texture>
+                       </control>
+                       <control type="button">
+                               <description>Close Window button</description>
+                               <posx>20</posx>
+                               <posy>0</posy>
+                               <width>64</width>
+                               <height>32</height>
+                               <label>-</label>
+                               <font>-</font>
+                               <onclick>PreviousMenu</onclick>
+                               <texturefocus>DialogCloseButton-focus.png</texturefocus>
+                               <texturenofocus>DialogCloseButton.png</texturenofocus>
+                               <onleft>450</onleft>
+                               <onright>450</onright>
+                               <onup>450</onup>
+                               <ondown>450</ondown>
+                               <visible>system.getbool(input.enablemouse)</visible>
+                       </control>
+                       <control type="image">
+                               <description>media info background image</description>
+                               <posx>0</posx>
+                               <posy>0</posy>
+                               <width>800</width>
+                               <height>720</height>
+                               <texture border="20,0,0,0" flipx="true">VisOsdPanel.png</texture>
+                               <visible>Window.IsVisible(124)</visible>
+                       </control>
+                       <control type="group">
+                               <animation effect="fade" delay="400" start="0" end="100" time="200">WindowOpen</animation>
+                               <animation effect="fade" start="100" end="0" time="200">WindowClose</animation>
+                               <control type="label" id="411">
+                                       <description>header label</description>
+                                       <posx>660</posx>
+                                       <posy>40</posy>
+                                       <width>630</width>
+                                       <height>30</height>
+                                       <font>font30_title</font>
+                                       <label>19053</label>
+                                       <align>right</align>
+                                       <aligny>center</aligny>
+                                       <textcolor>white</textcolor>
+                                       <shadowcolor>black</shadowcolor>
+                               </control>
+                               <control type="label">
+                                       <description>Title label</description>
+                                       <posx>660</posx>
+                                       <posy>70</posy>
+                                       <width>630</width>
+                                       <height>30</height>
+                                       <font>font13caps</font>
+                                       <label>$INFO[ListItem.Title]</label>
+                                       <align>right</align>
+                                       <aligny>center</aligny>
+                                       <textcolor>white</textcolor>
+                                       <shadowcolor>black</shadowcolor>
+                               </control>
+                               <control type="group">
+                                       <posx>0</posx>
+                                       <posy>140</posy>
+                                       <control type="label">
+                                               <description>Start Date</description>
+                                               <posx>190</posx>
+                                               <posy>0</posy>
+                                               <width>160</width>
+                                               <height>25</height>
+                                               <align>right</align>
+                                               <aligny>center</aligny>
+                                               <font>font13_title</font>
+                                               <textcolor>blue</textcolor>
+                                               <label>$LOCALIZE[552]:</label>
+                                       </control>
+                                       <control type="label">
+                                               <description>Start date value</description>
+                                               <posx>200</posx>
+                                               <posy>0</posy>
+                                               <width>450</width>
+                                               <height>25</height>
+                                               <align>left</align>
+                                               <aligny>center</aligny>
+                                               <font>font13</font>
+                                               <textcolor>white</textcolor>
+                                               <label>$INFO[ListItem.StartDate]</label>
+                                       </control>
+                                       <control type="label">
+                                               <description>Start time</description>
+                                               <posx>190</posx>
+                                               <posy>35</posy>
+                                               <width>160</width>
+                                               <height>25</height>
+                                               <align>right</align>
+                                               <aligny>center</aligny>
+                                               <font>font13_title</font>
+                                               <textcolor>blue</textcolor>
+                                               <label>$LOCALIZE[142]</label>
+                                       </control>
+                                       <control type="label">
+                                               <description>Start Time value</description>
+                                               <posx>200</posx>
+                                               <posy>35</posy>
+                                               <width>450</width>
+                                               <height>25</height>
+                                               <align>left</align>
+                                               <aligny>center</aligny>
+                                               <font>font13</font>
+                                               <textcolor>white</textcolor>
+                                               <label>$INFO[ListItem.StartTime]</label>
+                                       </control>
+                                       <control type="label">
+                                               <description>Channel Name</description>
+                                               <posx>190</posx>
+                                               <posy>70</posy>
+                                               <width>160</width>
+                                               <height>25</height>
+                                               <align>right</align>
+                                               <aligny>center</aligny>
+                                               <font>font13_title</font>
+                                               <textcolor>blue</textcolor>
+                                               <label>$LOCALIZE[19148]:</label>
+                                       </control>
+                                       <control type="fadelabel">
+                                               <description>Channel Value</description>
+                                               <posx>200</posx>
+                                               <posy>70</posy>
+                                               <width>450</width>
+                                               <height>25</height>
+                                               <align>left</align>
+                                               <aligny>center</aligny>
+                                               <font>font13</font>
+                                               <textcolor>white</textcolor>
+                                               <label>$INFO[ListItem.ChannelName]</label>
+                                       </control>
+                                       <control type="label">
+                                               <description>Duration</description>
+                                               <posx>190</posx>
+                                               <posy>105</posy>
+                                               <width>160</width>
+                                               <height>25</height>
+                                               <align>right</align>
+                                               <aligny>center</aligny>
+                                               <font>font13_title</font>
+                                               <textcolor>blue</textcolor>
+                                               <label>$LOCALIZE[180]:</label>
+                                       </control>
+                                       <control type="label">
+                                               <description>Duration value</description>
+                                               <posx>200</posx>
+                                               <posy>105</posy>
+                                               <width>450</width>
+                                               <label>$INFO[ListItem.Duration]</label>
+                                               <align>left</align>
+                                               <font>font13</font>
+                                               <scroll>true</scroll>
+                                       </control>
+                                       <control type="label">
+                                               <description>Genre</description>
+                                               <posx>190</posx>
+                                               <posy>140</posy>
+                                               <width>160</width>
+                                               <height>25</height>
+                                               <align>right</align>
+                                               <aligny>center</aligny>
+                                               <font>font13_title</font>
+                                               <textcolor>blue</textcolor>
+                                               <label>$LOCALIZE[135]:</label>
+                                       </control>
+                                       <control type="label">
+                                               <description>Genre value</description>
+                                               <posx>200</posx>
+                                               <posy>140</posy>
+                                               <width>450</width>
+                                               <label fallback="161">$INFO[ListItem.Genre]</label>
+                                               <align>left</align>
+                                               <font>font13</font>
+                                               <scroll>true</scroll>
+                                       </control>
+                                       <control type="label">
+                                               <description>Subtitle value</description>
+                                               <posx>40</posx>
+                                               <posy>185</posy>
+                                               <width>610</width>
+                                               <label>$INFO[ListItem.PlotOutline]</label>
+                                               <align>center</align>
+                                               <font>font13caps</font>
+                                               <textcolor>blue</textcolor>
+                                               <scroll>true</scroll>
+                                               <visible>!IsEmpty(ListItem.PlotOutline)</visible>
+                                       </control>
+                               </control>
+                               <control type="label">
+                                       <posx>570</posx>
+                                       <posy>370</posy>
+                                       <width>400</width>
+                                       <height>30</height>
+                                       <font>font13_title</font>
+                                       <textcolor>grey2</textcolor>
+                                       <shadowcolor>black</shadowcolor>
+                                       <scroll>true</scroll>
+                                       <align>right</align>
+                                       <aligny>center</aligny>
+                                       <label>[COLOR=blue]$LOCALIZE[207][/COLOR]$INFO[Container(400).CurrentPage, ( $LOCALIZE[31024] ]$INFO[Container(400).NumPages,/, )]</label>
+                               </control>
+                               <control type="spincontrol" id="60">
+                                       <description>Next page button</description>
+                                       <posx>580</posx>
+                                       <posy>375</posy>
+                                       <subtype>page</subtype>
+                                       <font>-</font>
+                                       <onleft>7</onleft>
+                                       <onright>5</onright>
+                                       <ondown>9000</ondown>
+                                       <onup>9000</onup>
+                                       <textcolor>-</textcolor>
+                                       <showonepage>true</showonepage>
+                               </control>
+                               <control type="textbox" id="400">
+                                       <description>PLOT</description>
+                                       <posx>40</posx>
+                                       <posy>400</posy>
+                                       <width>610</width>
+                                       <height>220</height>
+                                       <font>font12</font>
+                                       <align>justify</align>
+                                       <pagecontrol>60</pagecontrol>
+                                       <label fallback="161">$INFO[ListItem.Plot]</label>
+                               </control>
+                               <control type="grouplist" id="9000">
+                                       <posx>40</posx>
+                                       <posy>660</posy>
+                                       <width>620</width>
+                                       <height>40</height>
+                                       <itemgap>5</itemgap>
+                                       <align>center</align>
+                                       <orientation>horizontal</orientation>
+                                       <onleft>60</onleft>
+                                       <onright>60</onright>
+                                       <onup>9000</onup>
+                                       <ondown>9000</ondown>
+                                       <control type="button" id="10">
+                                               <description>OK</description>
+                                               <include>ButtonInfoDialogsCommonValues</include>
+                                               <label>186</label>
+                                       </control>
+                               </control>
+                       </control>
+               </control>
+               <include>SideBladeRight</include>
+       </controls>
+</window>
\ No newline at end of file
diff --git a/addons/skin.confluence/720p/DialogPVRTimerSettings.xml b/addons/skin.confluence/720p/DialogPVRTimerSettings.xml
new file mode 100644 (file)
index 0000000..3675f72
--- /dev/null
@@ -0,0 +1,159 @@
+<window id="603">
+       <defaultcontrol>29</defaultcontrol>
+       <coordinates>
+               <system>1</system>
+               <posx>240</posx>
+               <posy>55</posy>
+       </coordinates>
+       <include>dialogeffect</include>
+       <controls>
+               <control type="image">
+                       <description>background image</description>
+                       <posx>0</posx>
+                       <posy>0</posy>
+                       <width>800</width>
+                       <height>620</height>
+                       <texture border="40">DialogBack.png</texture>
+               </control>
+               <control type="image">
+                       <posx>80</posx>
+                       <posy>10</posy>
+                       <width>640</width>
+                       <height>90</height>
+                       <aspectratio>stretch</aspectratio>
+                       <texture>GlassTitleBar.png</texture>
+               </control>
+               <control type="button">
+                       <description>Close Window button</description>
+                       <posx>710</posx>
+                       <posy>9</posy>
+                       <width>64</width>
+                       <height>32</height>
+                       <label>-</label>
+                       <font>-</font>
+                       <onclick>PreviousMenu</onclick>
+                       <texturefocus>DialogCloseButton-focus.png</texturefocus>
+                       <texturenofocus>DialogCloseButton.png</texturenofocus>
+                       <onleft>2</onleft>
+                       <onright>2</onright>
+                       <onup>2</onup>
+                       <ondown>2</ondown>
+                       <visible>system.getbool(input.enablemouse)</visible>
+               </control>
+               <control type="label" id="2">
+                       <description>header label</description>
+                       <posx>20</posx>
+                       <posy>18</posy>
+                       <width>760</width>
+                       <height>30</height>
+                       <font>font13_title</font>
+                       <label>-</label>
+                       <align>center</align>
+                       <aligny>center</aligny>
+                       <textcolor>white</textcolor>
+                       <shadowcolor>black</shadowcolor>
+               </control>
+               <control type="grouplist" id="5">
+                       <description>control area</description>
+                       <posx>30</posx>
+                       <posy>80</posy>
+                       <width>740</width>
+                       <height>440</height>
+                       <itemgap>-1</itemgap>
+                       <onup>9001</onup>
+                       <ondown>9001</ondown>
+                       <onleft>9001</onleft>
+                       <onright>9001</onright>
+               </control>
+               <control type="button" id="7">
+                       <description>Default Button</description>
+                       <height>40</height>
+                       <font>font13</font>
+                       <textcolor>grey2</textcolor>
+                       <focusedcolor>white</focusedcolor>
+                       <texturefocus border="0,2,0,2">MenuItemFO.png</texturefocus>
+                       <texturenofocus border="0,2,0,2">MenuItemNF.png</texturenofocus>
+               </control>
+               <control type="radiobutton" id="8">
+                       <description>Default RadioButton</description>
+                       <height>40</height>
+                       <font>font13</font>
+                       <textcolor>grey2</textcolor>
+                       <focusedcolor>white</focusedcolor>
+                       <texturefocus border="0,2,0,2">MenuItemFO.png</texturefocus>
+                       <texturenofocus border="0,2,0,2">MenuItemNF.png</texturenofocus>
+               </control>
+               <control type="spincontrolex" id="9">
+                       <description>Default spincontrolex</description>
+                       <height>40</height>
+                       <textcolor>grey2</textcolor>
+                       <focusedcolor>white</focusedcolor>
+                       <texturefocus border="0,2,0,2">MenuItemFO.png</texturefocus>
+                       <texturenofocus border="0,2,0,2">MenuItemNF.png</texturenofocus>
+                       <font>font13</font>
+                       <aligny>center</aligny>
+                       <reverse>yes</reverse>
+               </control>
+               <control type="sliderex" id="10">
+                       <description>Default Slider</description>
+                       <height>40</height>
+                       <texturefocus border="0,2,0,2">MenuItemFO.png</texturefocus>
+                       <texturenofocus border="0,2,0,2">MenuItemNF.png</texturenofocus>
+                       <font>font13</font>
+                       <textcolor>grey2</textcolor>
+                       <focusedcolor>white</focusedcolor>
+               </control>
+               <control type="image" id="11">
+                       <description>Default Seperator</description>
+                       <height>2</height>
+                       <texture>separator2.png</texture>
+               </control>
+               <control type="edit" id="12">
+                       <description>Default Edit</description>
+                       <height>40</height>
+                       <font>font13</font>
+                       <textcolor>grey2</textcolor>
+                       <focusedcolor>white</focusedcolor>
+                       <texturefocus border="0,2,0,2">MenuItemFO.png</texturefocus>
+                       <texturenofocus border="0,2,0,2">MenuItemNF.png</texturenofocus>
+               </control>
+               <control type="group" id="9001">
+                       <posx>200</posx>
+                       <posy>550</posy>
+                       <control type="button" id="28">
+                               <description>OK Button</description>
+                               <posx>0</posx>
+                               <posy>0</posy>
+                               <width>200</width>
+                               <height>40</height>
+                               <align>center</align>
+                               <aligny>center</aligny>
+                               <texturenofocus border="5">MenuItemNF.png</texturenofocus>
+                               <texturefocus border="5">button-focus.png</texturefocus>
+                               <font>font12_title</font>
+                               <label>186</label>
+                               <onleft>29</onleft>
+                               <onright>29</onright>
+                               <onup>5</onup>
+                               <ondown>5</ondown>
+                       </control>
+                       <control type="button" id="29">
+                               <description>Cancel Button</description>
+                               <posx>200</posx>
+                               <posy>0</posy>
+                               <width>200</width>
+                               <height>40</height>
+                               <align>center</align>
+                               <aligny>center</aligny>
+                               <texturenofocus border="5">MenuItemNF.png</texturenofocus>
+                               <texturefocus border="5">button-focus.png</texturefocus>
+                               <font>font12_title</font>
+                               <label>222</label>
+                               <onleft>28</onleft>
+                               <onright>28</onright>
+                               <onup>5</onup>
+                               <ondown>5</ondown>
+                       </control>
+               </control>
+       </controls>
+</window>
\ No newline at end of file
diff --git a/addons/skin.confluence/720p/DialogPVRUpdateProgressBar.xml b/addons/skin.confluence/720p/DialogPVRUpdateProgressBar.xml
new file mode 100644 (file)
index 0000000..412d92c
--- /dev/null
@@ -0,0 +1,48 @@
+<window id="614">
+       <defaultcontrol></defaultcontrol>
+       <animation effect="slide" start="0,-70" end="0,0" time="100">WindowOpen</animation>
+       <animation effect="slide" start="0,0" end="0,-70" delay="400" time="100">WindowClose</animation>
+       <controls>
+               <control type="group">
+                       <posx>720</posx>
+                       <posy>0</posy>
+                       <animation effect="slide" end="-400,0" time="200" condition="Window.IsVisible(133)">conditional</animation>
+                       <animation effect="slide" end="0,-80" time="200" condition="Window.IsVisible(FullscreenVideo) | Window.IsVisible(Visualisation)">conditional</animation>
+                       <control type="image">
+                               <posx>0</posx>
+                               <posy>-10</posy>
+                               <width>400</width>
+                               <height>70</height>
+                               <texture flipy="true" border="20,20,20,2">InfoMessagePanel.png</texture>
+                       </control>
+                       <control type="label" id="30">
+                               <description>Header Label</description>
+                               <posx>15</posx>
+                               <posy>4</posy>
+                               <width>370</width>
+                               <height>18</height>
+                               <font>font10_title</font>
+                               <textcolor>selected</textcolor>
+                               <align>left</align>
+                               <aligny>center</aligny>
+                       </control>
+                       <control type="label" id="31">
+                               <description>Title Label</description>
+                               <posx>15</posx>
+                               <posy>20</posy>
+                               <width>370</width>
+                               <height>20</height>
+                               <font>font10</font>
+                               <align>left</align>
+                               <aligny>center</aligny>
+                       </control>
+                       <control type="progress" id="32">
+                               <description>progress control</description>
+                               <posx>15</posx>
+                               <posy>42</posy>
+                               <width>370</width>
+                               <height>8</height>
+                       </control>
+               </control>
+       </controls>
+</window>
\ No newline at end of file
index f1ac013..bdc477a 100644 (file)
                                <shadowcolor>black</shadowcolor>
                        </control>
                </control>
+               <!-- LiveTV Info -->
+               <control type="group">
+                       <posx>490r</posx>
+                       <posy>40</posy>
+                       <visible>Container(9000).HasFocus(12) + [PVR.IsRecording | PVR.HasTimer]</visible>
+                       <animation type="Visible" reversible="false">
+                               <effect type="zoom" start="80" end="100" center="640,360" easing="out" tween="back" time="300" />
+                               <effect type="fade" start="0" end="100" time="300" />
+                       </animation>
+                       <animation type="Hidden" reversible="false">
+                               <effect type="zoom" start="100" end="80" center="640,360" easing="in" tween="back" time="300" />
+                               <effect type="fade" start="100" end="0" time="300" />
+                       </animation>                
+                       <animation type="WindowOpen" reversible="false">
+                               <effect type="zoom" start="80" end="100" center="640,360" easing="out" tween="back" time="300" />
+                               <effect type="fade" start="0" end="100" time="300" />
+                       </animation>
+                       <animation type="WindowClose" reversible="false">
+                               <effect type="zoom" start="100" end="80" center="640,360" easing="in" tween="back" time="300" />
+                               <effect type="fade" start="100" end="0" time="300" />
+                       </animation>                
+                       <animation effect="fade" start="100" end="0" time="200" condition="Window.IsActive(Favourites)">conditional</animation>
+                       <control type="image">
+                               <description>background</description>
+                               <posx>0</posx>
+                               <posy>0</posy>
+                               <width>480</width>
+                               <height>135</height>
+                               <colordiffuse>DDFFFFFF</colordiffuse>
+                               <texture border="20">OverlayDialogBackground.png</texture>
+                       </control>
+                       <control type="group">
+                               <visible>!PVR.IsRecording + PVR.HasTimer</visible>
+                               <control type="image">
+                                       <posx>435</posx>
+                                       <posy>15</posy>
+                                       <width>25</width>
+                                       <height>25</height>
+                                       <texture>PVR-HasTimer.png</texture>
+                               </control>
+                               <control type="label">
+                                       <description>Next Timer Header label</description>
+                                       <posx>420</posx>
+                                       <posy>15</posy>
+                                       <height>25</height>
+                                       <width>400</width>
+                                       <label>$LOCALIZE[19157]</label>
+                                       <align>right</align>
+                                       <aligny>center</aligny>
+                                       <font>font12_title</font>
+                                       <textcolor>blue</textcolor>
+                                       <shadowcolor>black</shadowcolor>
+                               </control>
+                               <control type="label">
+                                       <description>NextRecordingTitle</description>
+                                       <posx>460</posx>
+                                       <posy>40</posy>
+                                       <height>30</height>
+                                       <width>440</width>
+                                       <label>$INFO[PVR.NextRecordingTitle]</label>
+                                       <align>right</align>
+                                       <aligny>center</aligny>
+                                       <font>font13</font>
+                                       <shadowcolor>black</shadowcolor>
+                                       <scroll>true</scroll>
+                               </control>
+                               <control type="label">
+                                       <description>NextRecordingChannel</description>
+                                       <posx>460</posx>
+                                       <posy>70</posy>
+                                       <height>25</height>
+                                       <width>440</width>
+                                       <label>$INFO[PVR.NextRecordingChannel]</label>
+                                       <align>right</align>
+                                       <aligny>center</aligny>
+                                       <font>font12</font>
+                                       <shadowcolor>black</shadowcolor>
+                               </control>
+                               <control type="label">
+                                       <description>NextRecordingDateTime</description>
+                                       <posx>460</posx>
+                                       <posy>95</posy>
+                                       <height>25</height>
+                                       <width>440</width>
+                                       <label>$INFO[PVR.NextRecordingDateTime,$LOCALIZE[19126] - ]</label>
+                                       <align>right</align>
+                                       <aligny>center</aligny>
+                                       <font>font12</font>
+                                       <shadowcolor>black</shadowcolor>
+                               </control>
+                       </control>
+                       <control type="group">
+                               <visible>PVR.IsRecording</visible>
+                               <control type="image">
+                                       <posx>423</posx>
+                                       <posy>15</posy>
+                                       <width>37</width>
+                                       <height>25</height>
+                                       <texture>PVR-IsRecording.png</texture>
+                               </control>
+                               <control type="label">
+                                       <description>Is Recording Header label</description>
+                                       <posx>410</posx>
+                                       <posy>15</posy>
+                                       <height>25</height>
+                                       <width>390</width>
+                                       <label>$LOCALIZE[19158]</label>
+                                       <align>right</align>
+                                       <aligny>center</aligny>
+                                       <font>font12_title</font>
+                                       <textcolor>blue</textcolor>
+                                       <shadowcolor>black</shadowcolor>
+                               </control>
+                               <control type="label">
+                                       <description>NextRecordingTitle</description>
+                                       <posx>460</posx>
+                                       <posy>40</posy>
+                                       <height>30</height>
+                                       <width>440</width>
+                                       <label>$INFO[PVR.NowRecordingTitle]</label>
+                                       <align>right</align>
+                                       <aligny>center</aligny>
+                                       <font>font13</font>
+                                       <shadowcolor>black</shadowcolor>
+                                       <scroll>true</scroll>
+                               </control>
+                               <control type="label">
+                                       <description>NextRecordingChannel</description>
+                                       <posx>460</posx>
+                                       <posy>70</posy>
+                                       <height>25</height>
+                                       <width>440</width>
+                                       <label>$INFO[PVR.NowRecordingChannel]</label>
+                                       <align>right</align>
+                                       <aligny>center</aligny>
+                                       <font>font12</font>
+                                       <shadowcolor>black</shadowcolor>
+                               </control>
+                               <control type="label">
+                                       <description>NextRecordingDateTime</description>
+                                       <posx>460</posx>
+                                       <posy>95</posy>
+                                       <height>25</height>
+                                       <width>440</width>
+                                       <label>$INFO[PVR.NowRecordingDateTime,$LOCALIZE[19126] - ]</label>
+                                       <align>right</align>
+                                       <aligny>center</aligny>
+                                       <font>font12</font>
+                                       <shadowcolor>black</shadowcolor>
+                               </control>
+                       </control>
+               </control>
                <!-- Video Info -->
                <control type="group">
                        <posx>0</posx>
                                <texture>HomeNowPlayingBack.png</texture>
                        </control>
                        <control type="group">
-                               <visible>!VideoPlayer.Content(Movies) + !VideoPlayer.Content(Episodes)</visible>
+                               <visible>!VideoPlayer.Content(Movies) + !VideoPlayer.Content(Episodes) + !VideoPlayer.Content(LiveTV)</visible>
                                <control type="image">
                                        <description>Cover image</description>
                                        <posx>200r</posx>
                                </control>
                        </control>
                        <control type="group">
+                               <visible>VideoPlayer.Content(LiveTV)</visible>
+                               <control type="image">
+                                       <description>Cover image</description>
+                                       <posx>200r</posx>
+                                       <posy>0</posy>
+                                       <width>180</width>
+                                       <height>340</height>
+                                       <aspectratio aligny="bottom">keep</aspectratio>
+                                       <texture>$INFO[VideoPlayer.Cover]</texture>
+                                       <bordertexture border="8">ThumbBorder.png</bordertexture>
+                                       <bordersize>5</bordersize>
+                               </control>
+                               <control type="label">
+                                       <description>NowPlaying label</description>
+                                       <posx>210r</posx>
+                                       <posy>210</posy>
+                                       <height>30</height>
+                                       <width>660</width>
+                                       <label>$LOCALIZE[31040] ($LOCALIZE[31502])</label>
+                                       <align>right</align>
+                                       <aligny>center</aligny>
+                                       <font>font12_title</font>
+                                       <textcolor>blue</textcolor>
+                                       <shadowcolor>black</shadowcolor>
+                               </control>
+                               <control type="label">
+                                       <description>Channel label</description>
+                                       <posx>210r</posx>
+                                       <posy>235</posy>
+                                       <height>30</height>
+                                       <width>660</width>
+                                       <label>$INFO[VideoPlayer.ChannelName]</label>
+                                       <align>right</align>
+                                       <aligny>center</aligny>
+                                       <font>font12_title</font>
+                                       <textcolor>white</textcolor>
+                                       <shadowcolor>black</shadowcolor>
+                               </control>
+                               <control type="label">
+                                       <description>Title label</description>
+                                       <posx>210r</posx>
+                                       <posy>260</posy>
+                                       <height>30</height>
+                                       <width>660</width>
+                                       <label>$INFO[VideoPlayer.Title]</label>
+                                       <align>right</align>
+                                       <aligny>center</aligny>
+                                       <font>font13_title</font>
+                                       <textcolor>white</textcolor>
+                                       <shadowcolor>black</shadowcolor>
+                               </control>
+                               <control type="label">
+                                       <description>Next Label</description>
+                                       <posx>210r</posx>
+                                       <posy>285</posy>
+                                       <height>30</height>
+                                       <width>660</width>
+                                       <label>$LOCALIZE[209] : $INFO[VideoPlayer.NextTitle]</label>
+                                       <align>right</align>
+                                       <aligny>center</aligny>
+                                       <font>font12</font>
+                                       <textcolor>grey2</textcolor>
+                                       <shadowcolor>black</shadowcolor>
+                               </control>
+                               <control type="label">
+                                       <description>Time Label</description>
+                                       <posx>210r</posx>
+                                       <posy>310</posy>
+                                       <height>30</height>
+                                       <width>325</width>
+                                       <label>$INFO[Player.Time]$INFO[Player.Duration, / ]</label>
+                                       <align>right</align>
+                                       <aligny>center</aligny>
+                                       <font>font12</font>
+                                       <textcolor>white</textcolor>
+                                       <shadowcolor>black</shadowcolor>
+                               </control>
+                       </control>
+                       <control type="group">
                                <visible>VideoPlayer.Content(Movies)</visible>
                                <control type="image">
                                        <description>Cover image</description>
                                                <thumb>$INFO[Skin.String(Home_Custom_Back_Pictures_Folder)]</thumb>
                                                <visible>!Skin.HasSetting(HomeMenuNoPicturesButton)</visible>
                                        </item>
+                                       <item id="12">
+                                               <label>31502</label>
+                                               <onclick>ActivateWindow(TV)</onclick>
+                                               <icon>special://skin/backgrounds/tv.jpg</icon>
+                                               <thumb>$INFO[Skin.String(Home_Custom_Back_TV_Folder)]</thumb>
+                                               <visible>System.GetBool(pvrmanager.enabled)</visible>
+                                       </item>
                                        <item id="2">
                                                <label>3</label>
                                                <onclick>ActivateWindow(Videos)</onclick>
                        </control>
                </control>
        </controls>     
-</window>
\ No newline at end of file
+</window>
diff --git a/addons/skin.confluence/720p/MyTV.xml b/addons/skin.confluence/720p/MyTV.xml
new file mode 100644 (file)
index 0000000..81079c3
--- /dev/null
@@ -0,0 +1,2476 @@
+<window id="600">
+       <defaultcontrol>32</defaultcontrol>
+       <allowoverlay>no</allowoverlay>
+       <controls>
+               <include>CommonTVBackground</include>
+               <control type="group">
+                       <visible>!Control.IsVisible(11) + !Control.IsVisible(12)</visible>
+                       <control type="image">
+                               <posx>0</posx>
+                               <posy>0</posy>
+                               <width>1280</width>
+                               <height>720</height>
+                               <texture>special://skin/backgrounds/media-overlay.png</texture>
+                               <visible>Player.HasVideo + !Skin.HasSetting(ShowBackgroundVideo)</visible>
+                       </control>
+                       <control type="visualisation">
+                               <posx>0</posx>
+                               <posy>0</posy>
+                               <width>1280</width>
+                               <height>720</height>
+                               <visible>Player.HasAudio + !Skin.HasSetting(ShowBackgroundVis)</visible>
+                       </control>
+                       <control type="videowindow">
+                               <posx>0</posx>
+                               <posy>0</posy>
+                               <width>1280</width>
+                               <height>720</height>
+                               <visible>Player.HasVideo + !Skin.HasSetting(ShowBackgroundVideo)</visible>
+                       </control>
+               </control>
+               <control type="group">
+                       <include>Window_OpenClose_Animation</include>           
+                       <control type="image">
+                               <posx>0</posx>
+                               <posy>0</posy>
+                               <width>1280</width>
+                               <height>720</height>
+                               <texture>black-back.png</texture>
+                               <include>VisibleFadeEffect</include>
+                       </control>
+                       <control type="image">
+                               <posx>0</posx>
+                               <posy>128r</posy>
+                               <width>1280</width>
+                               <height>128</height>
+                               <texture>floor.png</texture>
+                       </control>
+                       <control type="image">
+                               <posx>55</posx>
+                               <posy>60</posy>
+                               <width>1170</width>
+                               <height>600</height>
+                               <texture border="20">ContentPanel.png</texture>
+                       </control>
+                       <control type="image">
+                               <posx>55</posx>
+                               <posy>645</posy>
+                               <width>1170</width>
+                               <height>600</height>
+                               <aspectratio aligny="top">keep</aspectratio>
+                               <texture diffuse="diffuse_mirror3.png" flipy="true" border="20">ContentPanel.png</texture>
+                       </control>
+               </control>
+               <control type="group">
+                       <description>Small Media Window</description>
+                       <posx>80</posx>
+                       <posy>80</posy>
+                       <visible>Control.IsVisible(11) | Control.IsVisible(12)</visible>
+                       <include>VisibleFadeEffect</include>
+                       <include>Window_OpenClose_Animation</include>           
+                       <control type="image">
+                               <posx>0</posx>
+                               <posy>0</posy>
+                               <width>710</width>
+                               <height>400</height>
+                               <texture border="5">button-nofocus.png</texture>
+                       </control>
+                       <control type="image">
+                               <posx>5</posx>
+                               <posy>5</posy>
+                               <width>700</width>
+                               <height>390</height>
+                               <texture fallback="special://skin/backgrounds/tv.jpg">$INFO[Skin.String(Home_Custom_Back_TV_Folder)]</texture>
+                               <include>VisibleFadeEffect</include>
+                               <visible>!Player.HasVideo</visible>
+                       </control>
+                       <control type="videowindow">
+                               <posx>5</posx>
+                               <posy>5</posy>
+                               <width>700</width>
+                               <height>390</height>
+                               <visible>Player.HasVideo</visible>
+                               <animation effect="slide" start="0,0" end="-1000,0" time="0">WindowClose</animation>
+                       </control>
+                       <control type="image">
+                               <posx>1</posx>
+                               <posy>1</posy>
+                               <width>708</width>
+                               <height>35</height>
+                               <texture>black-back.png</texture>
+                               <colordiffuse>DDFFFFFF</colordiffuse>
+                               <visible>Player.HasVideo</visible>
+                       </control>
+                       <control type="label">
+                               <description>Current Video label</description>
+                               <posx>30</posx>
+                               <posy>1</posy>
+                               <width>650</width>
+                               <height>35</height>
+                               <font>font12</font>
+                               <textcolor>grey2</textcolor>
+                               <align>center</align>
+                               <aligny>center</aligny>
+                               <label>$INFO[VideoPlayer.Title]</label>
+                               <visible>Player.HasVideo</visible>
+                       </control>
+                       <!-- control type="visualisation">
+                               <posx>85</posx>
+                               <posy>85</posy>
+                               <width>700</width>
+                               <height>390</height>
+                               <visible>Player.HasAudio</visible>
+                       </control -->
+               </control>
+               <control type="label">
+                       <posx>40</posx>
+                       <posy>30r</posy>
+                       <width>700</width>
+                       <height>20</height>
+                       <label>([COLOR=blue]$INFO[Player.Time] / $INFO[Player.Duration,][/COLOR]) - $INFO[MusicPlayer.Title]$INFO[VideoPlayer.Title]</label>
+                       <align>left</align>
+                       <aligny>center</aligny>
+                       <font>font12</font>
+                       <textcolor>grey2</textcolor>
+                       <shadowcolor>black</shadowcolor>
+                       <visible>Player.HasMedia</visible>
+                       <include>VisibleFadeEffect</include>
+                       <include>Window_OpenClose_Animation</include>           
+               </control>
+               <control type="group">
+                       <include>Window_OpenClose_Animation</include>           
+                       <control type="group">
+                               <description>TV Guide Channel</description>
+                               <visible>Control.IsVisible(15)</visible>
+                               <include>VisibleFadeEffect</include>
+                               <control type="group">
+                                       <posx>80</posx>
+                                       <posy>60</posy>
+                                       <control type="label">
+                                               <description>Date Time label</description>
+                                               <posx>0</posx>
+                                               <posy>20</posy>
+                                               <width>300</width>
+                                               <height>20</height>
+                                               <font>font13_title</font>
+                                               <textcolor>white</textcolor>
+                                               <shadowcolor>black</shadowcolor>
+                                               <align>center</align>
+                                               <aligny>center</aligny>
+                                               <label>21820</label>
+                                       </control>
+                                       <control type="label">
+                                               <description>Title</description>
+                                               <posx>300</posx>
+                                               <posy>20</posy>
+                                               <width>600</width>
+                                               <height>20</height>
+                                               <font>font13_title</font>
+                                               <textcolor>white</textcolor>
+                                               <shadowcolor>black</shadowcolor>
+                                               <align>center</align>
+                                               <aligny>center</aligny>
+                                               <label>369</label>
+                                       </control>
+                                       <control type="label">
+                                               <description>Status header label</description>
+                                               <posx>960</posx>
+                                               <posy>20</posy>
+                                               <width>140</width>
+                                               <height>20</height>
+                                               <font>font13_title</font>
+                                               <textcolor>white</textcolor>
+                                               <shadowcolor>black</shadowcolor>
+                                               <align>center</align>
+                                               <aligny>center</aligny>
+                                               <label>126</label>
+                                       </control>
+                                       <control type="image">
+                                               <description>separator image</description>
+                                               <posx>0</posx>
+                                               <posy>50</posy>
+                                               <width>1100</width>
+                                               <height>1</height>
+                                               <colordiffuse>88FFFFFF</colordiffuse>
+                                               <texture>separator2.png</texture>
+                                       </control>
+                                       <control type="list" id="15">
+                                               <posx>0</posx>
+                                               <posy>60</posy>
+                                               <width>1100</width>
+                                               <height>500</height>
+                                               <onup>15</onup>
+                                               <ondown>15</ondown>
+                                               <onleft>31</onleft>
+                                               <onright>75</onright>
+                                               <pagecontrol>75</pagecontrol>
+                                               <scrolltime>200</scrolltime>
+                                               <itemlayout height="40">
+                                                       <control type="image">
+                                                               <posx>0</posx>
+                                                               <posy>0</posy>
+                                                               <width>1100</width>
+                                                               <height>41</height>
+                                                               <texture border="5">MenuItemNF.png</texture>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>0</posx>
+                                                               <posy>0</posy>
+                                                               <width>300</width>
+                                                               <height>40</height>
+                                                               <colordiffuse>33FFFFFF</colordiffuse>
+                                                               <texture border="5">StackFO.png</texture>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>960</posx>
+                                                               <posy>0</posy>
+                                                               <width>140</width>
+                                                               <height>40</height>
+                                                               <colordiffuse>33FFFFFF</colordiffuse>
+                                                               <texture border="5">StackFO.png</texture>
+                                                       </control>
+                                                       <control type="label">
+                                                               <posx>150</posx>
+                                                               <posy>0</posy>
+                                                               <width>280</width>
+                                                               <height>40</height>
+                                                               <font>font12</font>
+                                                               <align>center</align>
+                                                               <aligny>center</aligny>
+                                                               <textcolor>grey2</textcolor>
+                                                               <selectedcolor>selected</selectedcolor>
+                                                               <info>ListItem.Label2</info>
+                                                       </control>
+                                                       <control type="label">
+                                                               <posx>310</posx>
+                                                               <posy>0</posy>
+                                                               <width>640</width>
+                                                               <height>40</height>
+                                                               <font>font13</font>
+                                                               <align>left</align>
+                                                               <aligny>center</aligny>
+                                                               <textcolor>grey2</textcolor>
+                                                               <selectedcolor>selected</selectedcolor>
+                                                               <info>ListItem.Label</info>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>970</posx>
+                                                               <posy>10</posy>
+                                                               <width>30</width>
+                                                               <height>20</height>
+                                                               <texture>PVR-IsRecording.png</texture>
+                                                               <visible>ListItem.IsRecording</visible>
+                                                       </control>
+                                                       <control type="label">
+                                                               <posx>1005</posx>
+                                                               <posy>0</posy>
+                                                               <width>80</width>
+                                                               <height>40</height>
+                                                               <font>font10</font>
+                                                               <align>left</align>
+                                                               <aligny>center</aligny>
+                                                               <textcolor>grey2</textcolor>
+                                                               <selectedcolor>selected</selectedcolor>
+                                                               <label>19043</label>
+                                                               <visible>ListItem.IsRecording</visible>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>970</posx>
+                                                               <posy>10</posy>
+                                                               <width>20</width>
+                                                               <height>20</height>
+                                                               <texture>PVR-HasTimer.png</texture>
+                                                               <visible>ListItem.HasTimer</visible>
+                                                       </control>
+                                                       <control type="label">
+                                                               <posx>1000</posx>
+                                                               <posy>0</posy>
+                                                               <width>80</width>
+                                                               <height>40</height>
+                                                               <font>font10</font>
+                                                               <align>left</align>
+                                                               <aligny>center</aligny>
+                                                               <textcolor>grey2</textcolor>
+                                                               <selectedcolor>selected</selectedcolor>
+                                                               <label>31510</label>
+                                                               <visible>ListItem.HasTimer</visible>
+                                                       </control>
+                                               </itemlayout>
+                                               <focusedlayout height="100">
+                                                       <control type="image">
+                                                               <posx>0</posx>
+                                                               <posy>1</posy>
+                                                               <width>1100</width>
+                                                               <height>98</height>
+                                                               <colordiffuse>AAFFFFFF</colordiffuse>
+                                                               <texture border="5">black-back2.png</texture>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>0</posx>
+                                                               <posy>0</posy>
+                                                               <width>1100</width>
+                                                               <height>101</height>
+                                                               <texture border="5">MenuItemNF.png</texture>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>0</posx>
+                                                               <posy>0</posy>
+                                                               <width>300</width>
+                                                               <height>40</height>
+                                                               <colordiffuse>AAFFFFFF</colordiffuse>
+                                                               <texture border="5">StackFO.png</texture>
+                                                               <visible>!Control.HasFocus(15)</visible>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>960</posx>
+                                                               <posy>0</posy>
+                                                               <width>140</width>
+                                                               <height>40</height>
+                                                               <colordiffuse>AAFFFFFF</colordiffuse>
+                                                               <texture border="5">StackFO.png</texture>
+                                                               <visible>!Control.HasFocus(15)</visible>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>0</posx>
+                                                               <posy>0</posy>
+                                                               <width>300</width>
+                                                               <height>40</height>
+                                                               <colordiffuse>88FFFFFF</colordiffuse>
+                                                               <texture border="5">StackFO.png</texture>
+                                                               <visible>Control.HasFocus(15)</visible>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>960</posx>
+                                                               <posy>0</posy>
+                                                               <width>140</width>
+                                                               <height>40</height>
+                                                               <colordiffuse>88FFFFFF</colordiffuse>
+                                                               <texture border="5">StackFO.png</texture>
+                                                               <visible>Control.HasFocus(15)</visible>
+                                                       </control>
+                                                       <control type="label">
+                                                               <posx>150</posx>
+                                                               <posy>0</posy>
+                                                               <width>280</width>
+                                                               <height>40</height>
+                                                               <font>font12</font>
+                                                               <align>center</align>
+                                                               <aligny>center</aligny>
+                                                               <textcolor>white</textcolor>
+                                                               <selectedcolor>selected</selectedcolor>
+                                                               <info>ListItem.Label2</info>
+                                                       </control>
+                                                       <control type="label">
+                                                               <posx>310</posx>
+                                                               <posy>0</posy>
+                                                               <width>640</width>
+                                                               <height>40</height>
+                                                               <font>font13</font>
+                                                               <align>left</align>
+                                                               <aligny>center</aligny>
+                                                               <textcolor>white</textcolor>
+                                                               <selectedcolor>selected</selectedcolor>
+                                                               <info>ListItem.Label</info>
+                                                       </control>
+                                                       <control type="textbox">
+                                                               <description>Plot Value for TVShow</description>
+                                                               <posx>50</posx>
+                                                               <posy>40</posy>
+                                                               <width>1000</width>
+                                                               <height>60</height>
+                                                               <font>font12</font>
+                                                               <align>justify</align>
+                                                               <textcolor>grey2</textcolor>
+                                                               <shadowcolor>black</shadowcolor>
+                                                               <pagecontrol>-</pagecontrol>
+                                                               <label>$INFO[ListItem.Plot]</label>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>970</posx>
+                                                               <posy>10</posy>
+                                                               <width>30</width>
+                                                               <height>20</height>
+                                                               <texture>PVR-IsRecording.png</texture>
+                                                               <visible>ListItem.IsRecording</visible>
+                                                       </control>
+                                                       <control type="label">
+                                                               <posx>1005</posx>
+                                                               <posy>0</posy>
+                                                               <width>80</width>
+                                                               <height>40</height>
+                                                               <font>font10</font>
+                                                               <align>left</align>
+                                                               <aligny>center</aligny>
+                                                               <textcolor>grey2</textcolor>
+                                                               <selectedcolor>selected</selectedcolor>
+                                                               <label>19043</label>
+                                                               <visible>ListItem.IsRecording</visible>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>970</posx>
+                                                               <posy>10</posy>
+                                                               <width>20</width>
+                                                               <height>20</height>
+                                                               <texture>PVR-HasTimer.png</texture>
+                                                               <visible>ListItem.HasTimer</visible>
+                                                       </control>
+                                                       <control type="label">
+                                                               <posx>1000</posx>
+                                                               <posy>0</posy>
+                                                               <width>80</width>
+                                                               <height>40</height>
+                                                               <font>font10</font>
+                                                               <align>left</align>
+                                                               <aligny>center</aligny>
+                                                               <textcolor>grey2</textcolor>
+                                                               <selectedcolor>selected</selectedcolor>
+                                                               <label>31510</label>
+                                                               <visible>ListItem.HasTimer</visible>
+                                                       </control>
+                                               </focusedlayout>
+                                       </control>
+                                       <control type="scrollbar" id="75">
+                                               <posx>1105</posx>
+                                               <posy>60</posy>
+                                               <width>25</width>
+                                               <height>500</height>
+                                               <texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground>
+                                               <texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar>
+                                               <texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus>
+                                               <textureslidernib>ScrollBarNib.png</textureslidernib>
+                                               <textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus>
+                                               <onleft>15</onleft>
+                                               <onright>31</onright>
+                                               <ondown>75</ondown>
+                                               <onup>75</onup>
+                                               <showonepage>false</showonepage>
+                                               <orientation>vertical</orientation>
+                                               <visible>Control.IsVisible(15)</visible>
+                                       </control>
+                               </control>
+                               <control type="label">
+                                       <description>Page Count Label</description>
+                                       <posx>40r</posx>
+                                       <posy>30r</posy>
+                                       <width>500</width>
+                                       <height>20</height>
+                                       <font>font12</font>
+                                       <textcolor>grey</textcolor>
+                                       <scroll>false</scroll>
+                                       <align>right</align>
+                                       <aligny>center</aligny>
+                                       <label>([COLOR=blue]$INFO[Container(15).NumItems][/COLOR]) $LOCALIZE[31025] - $LOCALIZE[31024] ([COLOR=blue]$INFO[Container(15).CurrentPage]/$INFO[Container(15).NumPages][/COLOR])</label>
+                               </control>
+                       </control>
+                       <control type="group">
+                               <description>TV Guide Now/Next</description>
+                               <visible>Control.IsVisible(16)</visible>
+                               <include>VisibleFadeEffect</include>
+                               <control type="group">
+                                       <posx>80</posx>
+                                       <posy>60</posy>
+                                       <control type="label">
+                                               <description>Time label</description>
+                                               <posx>0</posx>
+                                               <posy>20</posy>
+                                               <width>100</width>
+                                               <height>20</height>
+                                               <font>font13_title</font>
+                                               <textcolor>white</textcolor>
+                                               <shadowcolor>black</shadowcolor>
+                                               <align>center</align>
+                                               <aligny>center</aligny>
+                                               <label>555</label>
+                                       </control>
+                                       <control type="label">
+                                               <description>Channel label</description>
+                                               <posx>100</posx>
+                                               <posy>20</posy>
+                                               <width>250</width>
+                                               <height>20</height>
+                                               <font>font13_title</font>
+                                               <textcolor>white</textcolor>
+                                               <shadowcolor>black</shadowcolor>
+                                               <align>center</align>
+                                               <aligny>center</aligny>
+                                               <label>19148</label>
+                                       </control>
+                                       <control type="label">
+                                               <description>Title</description>
+                                               <posx>350</posx>
+                                               <posy>20</posy>
+                                               <width>550</width>
+                                               <height>20</height>
+                                               <font>font13_title</font>
+                                               <textcolor>white</textcolor>
+                                               <shadowcolor>black</shadowcolor>
+                                               <align>center</align>
+                                               <aligny>center</aligny>
+                                               <label>369</label>
+                                       </control>
+                                       <control type="label">
+                                               <description>Status header label</description>
+                                               <posx>960</posx>
+                                               <posy>20</posy>
+                                               <width>140</width>
+                                               <height>20</height>
+                                               <font>font13_title</font>
+                                               <textcolor>white</textcolor>
+                                               <shadowcolor>black</shadowcolor>
+                                               <align>center</align>
+                                               <aligny>center</aligny>
+                                               <label>126</label>
+                                       </control>
+                                       <control type="image">
+                                               <description>separator image</description>
+                                               <posx>0</posx>
+                                               <posy>50</posy>
+                                               <width>1100</width>
+                                               <height>1</height>
+                                               <colordiffuse>88FFFFFF</colordiffuse>
+                                               <texture>separator2.png</texture>
+                                       </control>
+                                       <control type="list" id="16">
+                                               <posx>0</posx>
+                                               <posy>60</posy>
+                                               <width>1100</width>
+                                               <height>500</height>
+                                               <onup>16</onup>
+                                               <ondown>16</ondown>
+                                               <onleft>31</onleft>
+                                               <onright>76</onright>
+                                               <pagecontrol>76</pagecontrol>
+                                               <scrolltime>200</scrolltime>
+                                               <itemlayout height="40">
+                                                       <control type="image">
+                                                               <posx>0</posx>
+                                                               <posy>0</posy>
+                                                               <width>1100</width>
+                                                               <height>41</height>
+                                                               <texture border="5">MenuItemNF.png</texture>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>100</posx>
+                                                               <posy>0</posy>
+                                                               <width>250</width>
+                                                               <height>40</height>
+                                                               <colordiffuse>33FFFFFF</colordiffuse>
+                                                               <texture border="5">StackFO.png</texture>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>960</posx>
+                                                               <posy>0</posy>
+                                                               <width>140</width>
+                                                               <height>40</height>
+                                                               <colordiffuse>33FFFFFF</colordiffuse>
+                                                               <texture border="5">StackFO.png</texture>
+                                                       </control>
+                                                       <control type="label">
+                                                               <posx>50</posx>
+                                                               <posy>0</posy>
+                                                               <width>100</width>
+                                                               <height>40</height>
+                                                               <font>font12</font>
+                                                               <align>center</align>
+                                                               <aligny>center</aligny>
+                                                               <textcolor>grey2</textcolor>
+                                                               <selectedcolor>selected</selectedcolor>
+                                                               <info>ListItem.StartTime</info>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>110</posx>
+                                                               <posy>5</posy>
+                                                               <width>30</width>
+                                                               <height>30</height>
+                                                               <info>ListItem.Icon</info>
+                                                       </control>
+                                                       <control type="label">
+                                                               <posx>150</posx>
+                                                               <posy>0</posy>
+                                                               <width>190</width>
+                                                               <height>35</height>
+                                                               <font>font12</font>
+                                                               <align>left</align>
+                                                               <aligny>center</aligny>
+                                                               <textcolor>grey2</textcolor>
+                                                               <selectedcolor>selected</selectedcolor>
+                                                               <info>ListItem.ChannelName</info>
+                                                       </control>
+                                                       <control type="label">
+                                                               <posx>360</posx>
+                                                               <posy>0</posy>
+                                                               <width>590</width>
+                                                               <height>35</height>
+                                                               <font>font13</font>
+                                                               <align>left</align>
+                                                               <aligny>center</aligny>
+                                                               <textcolor>grey2</textcolor>
+                                                               <selectedcolor>selected</selectedcolor>
+                                                               <info>ListItem.Label</info>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>970</posx>
+                                                               <posy>10</posy>
+                                                               <width>30</width>
+                                                               <height>20</height>
+                                                               <texture>PVR-IsRecording.png</texture>
+                                                               <visible>ListItem.IsRecording</visible>
+                                                       </control>
+                                                       <control type="label">
+                                                               <posx>1005</posx>
+                                                               <posy>0</posy>
+                                                               <width>80</width>
+                                                               <height>40</height>
+                                                               <font>font10</font>
+                                                               <align>left</align>
+                                                               <aligny>center</aligny>
+                                                               <textcolor>grey2</textcolor>
+                                                               <selectedcolor>selected</selectedcolor>
+                                                               <label>19043</label>
+                                                               <visible>ListItem.IsRecording</visible>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>970</posx>
+                                                               <posy>10</posy>
+                                                               <width>20</width>
+                                                               <height>20</height>
+                                                               <texture>PVR-HasTimer.png</texture>
+                                                               <visible>ListItem.HasTimer</visible>
+                                                       </control>
+                                                       <control type="label">
+                                                               <posx>1000</posx>
+                                                               <posy>0</posy>
+                                                               <width>80</width>
+                                                               <height>40</height>
+                                                               <font>font10</font>
+                                                               <align>left</align>
+                                                               <aligny>center</aligny>
+                                                               <textcolor>grey2</textcolor>
+                                                               <selectedcolor>selected</selectedcolor>
+                                                               <label>31510</label>
+                                                               <visible>ListItem.HasTimer</visible>
+                                                       </control>
+                                               </itemlayout>
+                                               <focusedlayout height="100">
+                                                       <control type="image">
+                                                               <posx>0</posx>
+                                                               <posy>1</posy>
+                                                               <width>1100</width>
+                                                               <height>98</height>
+                                                               <colordiffuse>AAFFFFFF</colordiffuse>
+                                                               <texture border="5">black-back2.png</texture>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>0</posx>
+                                                               <posy>0</posy>
+                                                               <width>1100</width>
+                                                               <height>100</height>
+                                                               <texture border="5">MenuItemNF.png</texture>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>100</posx>
+                                                               <posy>0</posy>
+                                                               <width>250</width>
+                                                               <height>40</height>
+                                                               <colordiffuse>AAFFFFFF</colordiffuse>
+                                                               <texture border="5">StackFO.png</texture>
+                                                               <visible>!Control.HasFocus(16)</visible>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>960</posx>
+                                                               <posy>0</posy>
+                                                               <width>140</width>
+                                                               <height>40</height>
+                                                               <colordiffuse>AAFFFFFF</colordiffuse>
+                                                               <texture border="5">StackFO.png</texture>
+                                                               <visible>!Control.HasFocus(16)</visible>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>100</posx>
+                                                               <posy>0</posy>
+                                                               <width>250</width>
+                                                               <height>40</height>
+                                                               <colordiffuse>88FFFFFF</colordiffuse>
+                                                               <texture border="5">StackFO.png</texture>
+                                                               <visible>Control.HasFocus(16)</visible>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>960</posx>
+                                                               <posy>0</posy>
+                                                               <width>140</width>
+                                                               <height>40</height>
+                                                               <colordiffuse>88FFFFFF</colordiffuse>
+                                                               <texture border="5">StackFO.png</texture>
+                                                               <visible>Control.HasFocus(16)</visible>
+                                                       </control>
+                                                       <control type="label">
+                                                               <posx>50</posx>
+                                                               <posy>0</posy>
+                                                               <width>100</width>
+                                                               <height>40</height>
+                                                               <font>font12</font>
+                                                               <align>center</align>
+                                                               <aligny>center</aligny>
+                                                               <textcolor>white</textcolor>
+                                                               <selectedcolor>selected</selectedcolor>
+                                                               <info>ListItem.StartTime</info>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>110</posx>
+                                                               <posy>5</posy>
+                                                               <width>30</width>
+                                                               <height>30</height>
+                                                               <info>ListItem.Icon</info>
+                                                       </control>
+                                                       <control type="label">
+                                                               <posx>150</posx>
+                                                               <posy>0</posy>
+                                                               <width>190</width>
+                                                               <height>35</height>
+                                                               <font>font12</font>
+                                                               <align>left</align>
+                                                               <aligny>center</aligny>
+                                                               <textcolor>white</textcolor>
+                                                               <selectedcolor>selected</selectedcolor>
+                                                               <info>ListItem.ChannelName</info>
+                                                       </control>
+                                                       <control type="label">
+                                                               <posx>360</posx>
+                                                               <posy>0</posy>
+                                                               <width>590</width>
+                                                               <height>35</height>
+                                                               <font>font13</font>
+                                                               <align>left</align>
+                                                               <aligny>center</aligny>
+                                                               <textcolor>white</textcolor>
+                                                               <selectedcolor>selected</selectedcolor>
+                                                               <info>ListItem.Label</info>
+                                                       </control>
+                                                       <control type="textbox">
+                                                               <description>Plot Value for TVShow</description>
+                                                               <posx>50</posx>
+                                                               <posy>40</posy>
+                                                               <width>1000</width>
+                                                               <height>60</height>
+                                                               <font>font12</font>
+                                                               <align>justify</align>
+                                                               <textcolor>grey2</textcolor>
+                                                               <shadowcolor>black</shadowcolor>
+                                                               <pagecontrol>-</pagecontrol>
+                                                               <label>$INFO[ListItem.Plot]</label>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>970</posx>
+                                                               <posy>10</posy>
+                                                               <width>30</width>
+                                                               <height>20</height>
+                                                               <texture>PVR-IsRecording.png</texture>
+                                                               <visible>ListItem.IsRecording</visible>
+                                                       </control>
+                                                       <control type="label">
+                                                               <posx>1005</posx>
+                                                               <posy>0</posy>
+                                                               <width>80</width>
+                                                               <height>40</height>
+                                                               <font>font10</font>
+                                                               <align>left</align>
+                                                               <aligny>center</aligny>
+                                                               <textcolor>grey2</textcolor>
+                                                               <selectedcolor>selected</selectedcolor>
+                                                               <label>19043</label>
+                                                               <visible>ListItem.IsRecording</visible>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>970</posx>
+                                                               <posy>10</posy>
+                                                               <width>20</width>
+                                                               <height>20</height>
+                                                               <texture>PVR-HasTimer.png</texture>
+                                                               <visible>ListItem.HasTimer</visible>
+                                                       </control>
+                                                       <control type="label">
+                                                               <posx>1000</posx>
+                                                               <posy>0</posy>
+                                                               <width>80</width>
+                                                               <height>40</height>
+                                                               <font>font10</font>
+                                                               <align>left</align>
+                                                               <aligny>center</aligny>
+                                                               <textcolor>grey2</textcolor>
+                                                               <selectedcolor>selected</selectedcolor>
+                                                               <label>31510</label>
+                                                               <visible>ListItem.HasTimer</visible>
+                                                       </control>
+                                               </focusedlayout>
+                                       </control>
+                                       <control type="scrollbar" id="76">
+                                               <posx>1105</posx>
+                                               <posy>60</posy>
+                                               <width>25</width>
+                                               <height>500</height>
+                                               <texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground>
+                                               <texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar>
+                                               <texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus>
+                                               <textureslidernib>ScrollBarNib.png</textureslidernib>
+                                               <textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus>
+                                               <onleft>16</onleft>
+                                               <onright>31</onright>
+                                               <ondown>76</ondown>
+                                               <onup>76</onup>
+                                               <showonepage>false</showonepage>
+                                               <orientation>vertical</orientation>
+                                               <visible>Control.IsVisible(16)</visible>
+                                       </control>
+                               </control>
+                               <control type="label">
+                                       <description>Page Count Label</description>
+                                       <posx>40r</posx>
+                                       <posy>30r</posy>
+                                       <width>500</width>
+                                       <height>20</height>
+                                       <font>font12</font>
+                                       <textcolor>grey</textcolor>
+                                       <scroll>false</scroll>
+                                       <align>right</align>
+                                       <aligny>center</aligny>
+                                       <label>([COLOR=blue]$INFO[Container(16).NumItems][/COLOR]) $LOCALIZE[31025] - $LOCALIZE[31024] ([COLOR=blue]$INFO[Container(16).CurrentPage]/$INFO[Container(16).NumPages][/COLOR])</label>
+                               </control>
+                       </control>
+                       <control type="group">
+                               <description>TV Guide Timeline</description>
+                               <visible>Control.IsVisible(10)</visible>
+                               <include>VisibleFadeEffect</include>
+                               <control type="epggrid" id="10">
+                                       <description>EPG Grid</description>
+                                       <posx>80</posx>
+                                       <posy>81</posy>
+                                       <width>1120</width>
+                                       <height>555</height>
+                                       <pagecontrol>10</pagecontrol>
+                                       <scrolltime>350</scrolltime>
+                                       <timeblocks>40</timeblocks>
+                                       <rulerunit>6</rulerunit>
+                                       <onleft>31</onleft>
+                                       <onright>31</onright>
+                                       <onup>10</onup>
+                                       <ondown>10</ondown>
+                                       <rulerlayout height="35" width="40">
+                                               <control type="image" id="1">
+                                                       <width>40</width>
+                                                       <height>29</height>
+                                                       <posx>0</posx>
+                                                       <posy>0</posy>
+                                                       <texture border="5">button-nofocus.png</texture>
+                                               </control>
+                                               <control type="label" id="2">
+                                                       <posx>10</posx>
+                                                       <posy>0</posy>
+                                                       <width>34</width>
+                                                       <height>29</height>
+                                                       <font>font12</font>
+                                                       <aligny>center</aligny>
+                                                       <selectedcolor>selected</selectedcolor>
+                                                       <align>left</align>
+                                                       <label>$INFO[ListItem.Label]</label>
+                                               </control>
+                                       </rulerlayout>
+                                       <channellayout height="52" width="230">
+                                               <animation effect="fade" start="110" time="200">UnFocus</animation>
+                                               <control type="image" id="1">
+                                                       <posx>0</posx>
+                                                       <posy>0</posy>
+                                                       <width>220</width>
+                                                       <height>52</height>
+                                                       <texture border="5">button-nofocus.png</texture>
+                                               </control>
+                                               <control type="image">
+                                                       <posx>5</posx>
+                                                       <posy>4</posy>
+                                                       <width>45</width>
+                                                       <height>44</height>
+                                                       <texture>$INFO[ListItem.Icon]</texture>
+                                               </control>
+                                               <control type="label" id="1">
+                                                       <posx>54</posx>
+                                                       <posy>0</posy>
+                                                       <width>160</width>
+                                                       <height>52</height>
+                                                       <font>special12</font>
+                                                       <aligny>center</aligny>
+                                                       <selectedcolor>selected</selectedcolor>
+                                                       <align>left</align>
+                                                       <label>$INFO[ListItem.ChannelName]</label>
+                                               </control>
+                                       </channellayout>
+                                       <focusedchannellayout height="52" width="230">
+                                               <animation effect="fade" start="110" time="200">OnFocus</animation>
+                                               <control type="image" id="1">
+                                                       <posx>0</posx>
+                                                       <posy>0</posy>
+                                                       <width>220</width>
+                                                       <height>52</height>
+                                                       <texture border="5">button-focus.png</texture>
+                                               </control>
+                                               <control type="image">
+                                                       <posx>5</posx>
+                                                       <posy>4</posy>
+                                                       <width>45</width>
+                                                       <height>44</height>
+                                                       <texture>$INFO[ListItem.Icon]</texture>
+                                               </control>
+                                               <control type="label" id="1">
+                                                       <posx>54</posx>
+                                                       <posy>0</posy>
+                                                       <width>160</width>
+                                                       <height>52</height>
+                                                       <font>special12</font>
+                                                       <aligny>center</aligny>
+                                                       <selectedcolor>selected</selectedcolor>
+                                                       <align>left</align>
+                                                       <label>$INFO[ListItem.ChannelName]</label>
+                                               </control>
+                                       </focusedchannellayout>
+                                       <itemlayout height="52" width="40">
+                                               <control type="image" id="2">
+                                                       <width>40</width>
+                                                       <height>52</height>
+                                                       <posx>0</posx>
+                                                       <posy>0</posy>
+                                                       <aspectratio>stretch</aspectratio>
+                                                       <texture border="3">epg-genres/$INFO[ListItem.Property(GenreType)].png</texture>
+                                               </control>
+                                               <control type="label" id="1">
+                                                       <posx>6</posx>
+                                                       <posy>3</posy>
+                                                       <width>30</width>
+                                                       <height>25</height>
+                                                       <font>font12</font>
+                                                       <aligny>center</aligny>
+                                                       <selectedcolor>selected</selectedcolor>
+                                                       <align>left</align>
+                                                       <info>ListItem.Label</info>
+                                               </control>
+                                               <control type="image">
+                                                       <posx>5</posx>
+                                                       <posy>28</posy>
+                                                       <width>30</width>
+                                                       <height>20</height>
+                                                       <texture>PVR-IsRecording.png</texture>
+                                                       <visible>ListItem.IsRecording</visible>
+                                               </control>
+                                               <control type="image">
+                                                       <posx>5</posx>
+                                                       <posy>28</posy>
+                                                       <width>20</width>
+                                                       <height>20</height>
+                                                       <texture>PVR-HasTimer.png</texture>
+                                                       <visible>ListItem.HasTimer</visible>
+                                               </control>
+                                       </itemlayout>
+                                       <focusedlayout height="52" width="40">
+                                               <control type="image" id="14">
+                                                       <width>40</width>
+                                                       <height>52</height>
+                                                       <posx>0</posx>
+                                                       <posy>0</posy>
+                                                       <texture border="5">folder-focus.png</texture>
+                                               </control>
+                                               <control type="image" id="2">
+                                                       <width>40</width>
+                                                       <height>52</height>
+                                                       <posx>0</posx>
+                                                       <posy>0</posy>
+                                                       <aspectratio>stretch</aspectratio>
+                                                       <texture border="3">epg-genres/$INFO[ListItem.Property(GenreType)].png</texture>
+                                               </control>
+                                               <control type="label" id="1">
+                                                       <posx>6</posx>
+                                                       <posy>3</posy>
+                                                       <width>30</width>
+                                                       <height>25</height>
+                                                       <font>font12</font>
+                                                       <aligny>center</aligny>
+                                                       <selectedcolor>selected</selectedcolor>
+                                                       <align>left</align>
+                                                       <info>ListItem.Label</info>
+                                               </control>
+                                               <control type="image">
+                                                       <posx>5</posx>
+                                                       <posy>28</posy>
+                                                       <width>30</width>
+                                                       <height>20</height>
+                                                       <texture>PVR-IsRecording.png</texture>
+                                                       <visible>ListItem.IsRecording</visible>
+                                               </control>
+                                               <control type="image">
+                                                       <posx>5</posx>
+                                                       <posy>28</posy>
+                                                       <width>20</width>
+                                                       <height>20</height>
+                                                       <texture>PVR-HasTimer.png</texture>
+                                                       <visible>ListItem.HasTimer</visible>
+                                               </control>
+                                       </focusedlayout>
+                               </control>
+                       </control>
+
+                       <!-- TV Channels group -->
+                       <control type="group">
+                               <description>TV Channels group</description>
+                               <visible>Control.IsVisible(11)</visible>
+                               <include>VisibleFadeEffect</include>
+                               <control type="group">
+                                       <posx>85</posx>
+                                       <posy>490</posy>
+                                       <control type="label">
+                                               <posx>0</posx>
+                                               <posy>0</posy>
+                                               <width>700</width>
+                                               <height>30</height>
+                                               <font>font13</font>
+                                               <textcolor>white</textcolor>
+                                               <shadowcolor>black</shadowcolor>
+                                               <scroll>true</scroll>
+                                               <align>center</align>
+                                               <aligny>center</aligny>
+                                               <label>[B]$INFO[Container(11).ListItem.Title][/B]</label>
+                                       </control>
+                                       <control type="textbox">
+                                               <description>Plot Value for TVShow</description>
+                                               <posx>0</posx>
+                                               <posy>30</posy>
+                                               <width>700</width>
+                                               <height>90</height>
+                                               <font>font12</font>
+                                               <align>justify</align>
+                                               <textcolor>white</textcolor>
+                                               <shadowcolor>black</shadowcolor>
+                                               <pagecontrol>-</pagecontrol>
+                                               <label>$INFO[Container(11).ListItem.Plot]</label>
+                                               <autoscroll time="2000" delay="3000" repeat="5000">true</autoscroll>
+                                       </control>
+                               </control>
+                               <control type="list" id="11">
+                                       <posx>800</posx>
+                                       <posy>85</posy>
+                                       <width>390</width>
+                                       <height>541</height>
+                                       <onleft>32</onleft>
+                                       <onright>70</onright>
+                                       <onup>11</onup>
+                                       <ondown>11</ondown>
+                                       <viewtype label="535">list</viewtype>
+                                       <pagecontrol>70</pagecontrol>
+                                       <scrolltime>200</scrolltime>
+                                       <itemlayout height="60" width="400">
+                                               <control type="image">
+                                                       <posx>0</posx>
+                                                       <posy>0</posy>
+                                                       <width>390</width>
+                                                       <height>61</height>
+                                                       <texture border="2">MenuItemNF.png</texture>
+                                                       <include>VisibleFadeEffect</include>
+                                               </control>
+                                               <control type="label">
+                                                       <posx>5</posx>
+                                                       <posy>-4</posy>
+                                                       <width>40</width>
+                                                       <height>35</height>
+                                                       <font>font12</font>
+                                                       <align>left</align>
+                                                       <aligny>center</aligny>
+                                                       <textcolor>grey</textcolor>
+                                                       <selectedcolor>grey</selectedcolor>
+                                                       <info>ListItem.ChannelNumber</info>
+                                               </control>
+                                               <control type="label">
+                                                       <posx>50</posx>
+                                                       <posy>0</posy>
+                                                       <width>270</width>
+                                                       <height>30</height>
+                                                       <font>font13</font>
+                                                       <textcolor>white</textcolor>
+                                                       <selectedcolor>selected</selectedcolor>
+                                                       <align>left</align>
+                                                       <aligny>center</aligny>
+                                                       <label>$INFO[ListItem.Label]</label>
+                                               </control>
+                                               <control type="label">
+                                                       <posx>50</posx>
+                                                       <posy>30</posy>
+                                                       <width>330</width>
+                                                       <height>30</height>
+                                                       <font>font12</font>
+                                                       <textcolor>grey</textcolor>
+                                                       <selectedcolor>grey</selectedcolor>
+                                                       <align>left</align>
+                                                       <aligny>center</aligny>
+                                                       <visible>IsEmpty(Listitem.Icon)</visible>
+                                                       <label>$INFO[ListItem.Title]</label>
+                                               </control>
+                                               <control type="label">
+                                                       <posx>50</posx>
+                                                       <posy>30</posy>
+                                                       <width>270</width>
+                                                       <height>30</height>
+                                                       <font>font12</font>
+                                                       <textcolor>grey</textcolor>
+                                                       <selectedcolor>grey</selectedcolor>
+                                                       <align>left</align>
+                                                       <aligny>center</aligny>
+                                                       <visible>!IsEmpty(Listitem.Icon)</visible>
+                                                       <label>$INFO[ListItem.Title]</label>
+                                               </control>
+                                               <control type="image">
+                                                       <posx>330</posx>
+                                                       <posy>4</posy>
+                                                       <width>50</width>
+                                                       <height>50</height>
+                                                       <texture>$INFO[ListItem.Icon]</texture>
+                                               </control>
+                                       </itemlayout>
+                                       <focusedlayout height="60" width="500">
+                                               <control type="image">
+                                                       <posx>0</posx>
+                                                       <posy>0</posy>
+                                                       <width>390</width>
+                                                       <height>61</height>
+                                                       <texture border="2">MenuItemNF.png</texture>
+                                                       <visible>!Control.HasFocus(11)</visible>
+                                                       <include>VisibleFadeEffect</include>
+                                               </control>
+                                               <control type="image">
+                                                       <posx>0</posx>
+                                                       <posy>0</posy>
+                                                       <width>390</width>
+                                                       <height>61</height>
+                                                       <texture border="2">MenuItemFO.png</texture>
+                                                       <visible>Control.HasFocus(11)</visible>
+                                                       <include>VisibleFadeEffect</include>
+                                               </control>
+                                               <control type="label">
+                                                       <posx>5</posx>
+                                                       <posy>-4</posy>
+                                                       <width>40</width>
+                                                       <height>35</height>
+                                                       <font>font12</font>
+                                                       <align>left</align>
+                                                       <aligny>center</aligny>
+                                                       <textcolor>grey</textcolor>
+                                                       <selectedcolor>grey</selectedcolor>
+                                                       <info>ListItem.ChannelNumber</info>
+                                               </control>
+                                               <control type="label">
+                                                       <posx>50</posx>
+                                                       <posy>0</posy>
+                                                       <width>270</width>
+                                                       <height>30</height>
+                                                       <font>font13</font>
+                                                       <textcolor>white</textcolor>
+                                                       <selectedcolor>selected</selectedcolor>
+                                                       <align>left</align>
+                                                       <aligny>center</aligny>
+                                                       <label>$INFO[ListItem.Label]</label>
+                                               </control>
+                                               <control type="label">
+                                                       <posx>50</posx>
+                                                       <posy>30</posy>
+                                                       <width>330</width>
+                                                       <height>30</height>
+                                                       <font>font12</font>
+                                                       <textcolor>grey</textcolor>
+                                                       <selectedcolor>grey</selectedcolor>
+                                                       <align>left</align>
+                                                       <aligny>center</aligny>
+                                                       <visible>IsEmpty(Listitem.Icon)</visible>
+                                                       <label>$INFO[ListItem.Title]</label>
+                                               </control>
+                                               <control type="label">
+                                                       <posx>50</posx>
+                                                       <posy>30</posy>
+                                                       <width>270</width>
+                                                       <height>30</height>
+                                                       <font>font12</font>
+                                                       <textcolor>grey</textcolor>
+                                                       <selectedcolor>grey</selectedcolor>
+                                                       <align>left</align>
+                                                       <aligny>center</aligny>
+                                                       <visible>!IsEmpty(Listitem.Icon)</visible>
+                                                       <label>$INFO[ListItem.Title]</label>
+                                               </control>
+                                               <control type="image">
+                                                       <posx>330</posx>
+                                                       <posy>4</posy>
+                                                       <width>50</width>
+                                                       <height>50</height>
+                                                       <texture>$INFO[ListItem.Icon]</texture>
+                                               </control>
+                                       </focusedlayout>
+                               </control>
+                               <control type="scrollbar" id="70">
+                                       <posx>1190</posx>
+                                       <posy>85</posy>
+                                       <width>25</width>
+                                       <height>540</height>
+                                       <texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground>
+                                       <texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar>
+                                       <texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus>
+                                       <textureslidernib>ScrollBarNib.png</textureslidernib>
+                                       <textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus>
+                                       <onleft>11</onleft>
+                                       <onright>32</onright>
+                                       <ondown>70</ondown>
+                                       <onup>70</onup>
+                                       <showonepage>false</showonepage>
+                                       <orientation>vertical</orientation>
+                                       <visible>Control.IsVisible(11)</visible>
+                               </control>
+                               <control type="label">
+                                       <description>Page Count Label</description>
+                                       <posx>40r</posx>
+                                       <posy>30r</posy>
+                                       <width>500</width>
+                                       <height>20</height>
+                                       <font>font12</font>
+                                       <textcolor>grey</textcolor>
+                                       <scroll>false</scroll>
+                                       <align>right</align>
+                                       <aligny>center</aligny>
+                                       <label>([COLOR=blue]$INFO[Container(11).NumItems][/COLOR]) $LOCALIZE[19019] - $LOCALIZE[31024] ([COLOR=blue]$INFO[Container(11).CurrentPage]/$INFO[Container(11).NumPages][/COLOR])</label>
+                                       <include>Window_OpenClose_Animation</include>           
+                               </control>
+                       </control>
+                       <!-- Radio Channels group -->
+                       <control type="group">
+                               <description>Radio Channels group</description>
+                               <visible>Control.IsVisible(12)</visible>
+                               <include>VisibleFadeEffect</include>
+                               <control type="group">
+                                       <posx>85</posx>
+                                       <posy>490</posy>
+                                       <control type="label">
+                                               <posx>0</posx>
+                                               <posy>0</posy>
+                                               <width>700</width>
+                                               <height>30</height>
+                                               <font>font13</font>
+                                               <textcolor>white</textcolor>
+                                               <shadowcolor>black</shadowcolor>
+                                               <scroll>true</scroll>
+                                               <align>center</align>
+                                               <aligny>center</aligny>
+                                               <label>[B]$INFO[Container(12).ListItem.Title][/B]</label>
+                                       </control>
+                                       <control type="textbox">
+                                               <description>Plot Value for TVShow</description>
+                                               <posx>0</posx>
+                                               <posy>30</posy>
+                                               <width>700</width>
+                                               <height>90</height>
+                                               <font>font12</font>
+                                               <align>justify</align>
+                                               <textcolor>white</textcolor>
+                                               <shadowcolor>black</shadowcolor>
+                                               <pagecontrol>-</pagecontrol>
+                                               <label>$INFO[Container(12).ListItem.Plot]</label>
+                                               <autoscroll time="2000" delay="3000" repeat="5000">true</autoscroll>
+                                       </control>
+                               </control>
+                               <control type="list" id="12">
+                                       <posx>800</posx>
+                                       <posy>85</posy>
+                                       <width>390</width>
+                                       <height>541</height>
+                                       <onleft>33</onleft>
+                                       <onright>71</onright>
+                                       <onup>12</onup>
+                                       <ondown>12</ondown>
+                                       <viewtype label="535">list</viewtype>
+                                       <pagecontrol>71</pagecontrol>
+                                       <scrolltime>200</scrolltime>
+                                       <itemlayout height="60" width="400">
+                                               <control type="image">
+                                                       <posx>0</posx>
+                                                       <posy>0</posy>
+                                                       <width>390</width>
+                                                       <height>61</height>
+                                                       <texture border="2">MenuItemNF.png</texture>
+                                                       <include>VisibleFadeEffect</include>
+                                               </control>
+                                               <control type="label">
+                                                       <posx>5</posx>
+                                                       <posy>-4</posy>
+                                                       <width>40</width>
+                                                       <height>35</height>
+                                                       <font>font12</font>
+                                                       <align>left</align>
+                                                       <aligny>center</aligny>
+                                                       <textcolor>grey</textcolor>
+                                                       <selectedcolor>grey</selectedcolor>
+                                                       <info>ListItem.ChannelNumber</info>
+                                               </control>
+                                               <control type="label">
+                                                       <posx>50</posx>
+                                                       <posy>0</posy>
+                                                       <width>270</width>
+                                                       <height>30</height>
+                                                       <font>font13</font>
+                                                       <textcolor>white</textcolor>
+                                                       <selectedcolor>selected</selectedcolor>
+                                                       <align>left</align>
+                                                       <aligny>center</aligny>
+                                                       <label>$INFO[ListItem.Label]</label>
+                                               </control>
+                                               <control type="label">
+                                                       <posx>50</posx>
+                                                       <posy>30</posy>
+                                                       <width>330</width>
+                                                       <height>30</height>
+                                                       <font>font12</font>
+                                                       <textcolor>grey</textcolor>
+                                                       <selectedcolor>grey</selectedcolor>
+                                                       <align>left</align>
+                                                       <aligny>center</aligny>
+                                                       <visible>IsEmpty(Listitem.Icon)</visible>
+                                                       <label>$INFO[ListItem.Title]</label>
+                                               </control>
+                                               <control type="label">
+                                                       <posx>50</posx>
+                                                       <posy>30</posy>
+                                                       <width>270</width>
+                                                       <height>30</height>
+                                                       <font>font12</font>
+                                                       <textcolor>grey</textcolor>
+                                                       <selectedcolor>grey</selectedcolor>
+                                                       <align>left</align>
+                                                       <aligny>center</aligny>
+                                                       <visible>!IsEmpty(Listitem.Icon)</visible>
+                                                       <label>$INFO[ListItem.Title]</label>
+                                               </control>
+                                               <control type="image">
+                                                       <posx>330</posx>
+                                                       <posy>4</posy>
+                                                       <width>50</width>
+                                                       <height>50</height>
+                                                       <texture>$INFO[ListItem.Icon]</texture>
+                                               </control>
+                                       </itemlayout>
+                                       <focusedlayout height="60" width="500">
+                                               <control type="image">
+                                                       <posx>0</posx>
+                                                       <posy>0</posy>
+                                                       <width>390</width>
+                                                       <height>61</height>
+                                                       <texture border="2">MenuItemNF.png</texture>
+                                                       <visible>!Control.HasFocus(12)</visible>
+                                                       <include>VisibleFadeEffect</include>
+                                               </control>
+                                               <control type="image">
+                                                       <posx>0</posx>
+                                                       <posy>0</posy>
+                                                       <width>390</width>
+                                                       <height>61</height>
+                                                       <texture border="2">MenuItemFO.png</texture>
+                                                       <visible>Control.HasFocus(12)</visible>
+                                                       <include>VisibleFadeEffect</include>
+                                               </control>
+                                               <control type="label">
+                                                       <posx>5</posx>
+                                                       <posy>-4</posy>
+                                                       <width>40</width>
+                                                       <height>35</height>
+                                                       <font>font12</font>
+                                                       <align>left</align>
+                                                       <aligny>center</aligny>
+                                                       <textcolor>grey</textcolor>
+                                                       <selectedcolor>grey</selectedcolor>
+                                                       <info>ListItem.ChannelNumber</info>
+                                               </control>
+                                               <control type="label">
+                                                       <posx>50</posx>
+                                                       <posy>0</posy>
+                                                       <width>270</width>
+                                                       <height>30</height>
+                                                       <font>font13</font>
+                                                       <textcolor>white</textcolor>
+                                                       <selectedcolor>selected</selectedcolor>
+                                                       <align>left</align>
+                                                       <aligny>center</aligny>
+                                                       <label>$INFO[ListItem.Label]</label>
+                                               </control>
+                                               <control type="label">
+                                                       <posx>50</posx>
+                                                       <posy>30</posy>
+                                                       <width>330</width>
+                                                       <height>30</height>
+                                                       <font>font12</font>
+                                                       <textcolor>grey</textcolor>
+                                                       <selectedcolor>grey</selectedcolor>
+                                                       <align>left</align>
+                                                       <aligny>center</aligny>
+                                                       <visible>IsEmpty(Listitem.Icon)</visible>
+                                                       <label>$INFO[ListItem.Title]</label>
+                                               </control>
+                                               <control type="label">
+                                                       <posx>50</posx>
+                                                       <posy>30</posy>
+                                                       <width>270</width>
+                                                       <height>30</height>
+                                                       <font>font12</font>
+                                                       <textcolor>grey</textcolor>
+                                                       <selectedcolor>grey</selectedcolor>
+                                                       <align>left</align>
+                                                       <aligny>center</aligny>
+                                                       <visible>!IsEmpty(Listitem.Icon)</visible>
+                                                       <label>$INFO[ListItem.Title]</label>
+                                               </control>
+                                               <control type="image">
+                                                       <posx>330</posx>
+                                                       <posy>4</posy>
+                                                       <width>50</width>
+                                                       <height>50</height>
+                                                       <texture>$INFO[ListItem.Icon]</texture>
+                                               </control>
+                                       </focusedlayout>
+                               </control>
+                               <control type="scrollbar" id="71">
+                                       <posx>1190</posx>
+                                       <posy>85</posy>
+                                       <width>25</width>
+                                       <height>540</height>
+                                       <texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground>
+                                       <texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar>
+                                       <texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus>
+                                       <textureslidernib>ScrollBarNib.png</textureslidernib>
+                                       <textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus>
+                                       <onleft>11</onleft>
+                                       <onright>32</onright>
+                                       <ondown>71</ondown>
+                                       <onup>71</onup>
+                                       <showonepage>false</showonepage>
+                                       <orientation>vertical</orientation>
+                                       <visible>Control.IsVisible(12)</visible>
+                               </control>
+                               <control type="label">
+                                       <description>Page Count Label</description>
+                                       <posx>40r</posx>
+                                       <posy>30r</posy>
+                                       <width>500</width>
+                                       <height>20</height>
+                                       <font>font12</font>
+                                       <textcolor>grey</textcolor>
+                                       <scroll>false</scroll>
+                                       <align>right</align>
+                                       <aligny>center</aligny>
+                                       <label>([COLOR=blue]$INFO[Container(12).NumItems][/COLOR]) $LOCALIZE[19019] - $LOCALIZE[31024] ([COLOR=blue]$INFO[Container(12).CurrentPage]/$INFO[Container(12).NumPages][/COLOR])</label>
+                                       <include>Window_OpenClose_Animation</include>           
+                               </control>
+                       </control>
+                       <!-- Recordings group -->
+                       <control type="group">
+                               <description>Recordings group</description>
+                               <visible>Control.IsVisible(13)</visible>
+                               <include>VisibleFadeEffect</include>
+                               <control type="list" id="13">
+                                       <posx>490</posx>
+                                       <posy>100</posy>
+                                       <width>700</width>
+                                       <height>521</height>
+                                       <onleft>34</onleft>
+                                       <onright>72</onright>
+                                       <onup>13</onup>
+                                       <ondown>13</ondown>
+                                       <viewtype label="535">list</viewtype>
+                                       <pagecontrol>72</pagecontrol>
+                                       <scrolltime>200</scrolltime>
+                                       <itemlayout height="40" width="700">
+                                               <control type="image">
+                                                       <posx>0</posx>
+                                                       <posy>0</posy>
+                                                       <width>700</width>
+                                                       <height>41</height>
+                                                       <texture border="2">MenuItemNF.png</texture>
+                                                       <include>VisibleFadeEffect</include>
+                                               </control>
+                                               <control type="image">
+                                                       <posx>10</posx>
+                                                       <posy>5</posy>
+                                                       <width>30</width>
+                                                       <height>30</height>
+                                                       <texture background="true" fallback="DefaultVideoCover.png">$INFO[ListItem.Icon]</texture>
+                                               </control>
+                                               <control type="label">
+                                                       <posx>50</posx>
+                                                       <posy>0</posy>
+                                                       <width>630</width>
+                                                       <height>40</height>
+                                                       <font>font13</font>
+                                                       <textcolor>grey2</textcolor>
+                                                       <selectedcolor>selected</selectedcolor>
+                                                       <align>left</align>
+                                                       <aligny>center</aligny>
+                                                       <label>$INFO[ListItem.Label]</label>
+                                               </control>
+                                               <control type="label">
+                                                       <posx>690</posx>
+                                                       <posy>0</posy>
+                                                       <width>500</width>
+                                                       <height>40</height>
+                                                       <font>font12</font>
+                                                       <textcolor>grey2</textcolor>
+                                                       <selectedcolor>selected</selectedcolor>
+                                                       <align>right</align>
+                                                       <aligny>center</aligny>
+                                                       <label>$INFO[ListItem.Date]</label>
+                                               </control>
+                                       </itemlayout>
+                                       <focusedlayout height="40" width="700">
+                                               <control type="image">
+                                                       <posx>0</posx>
+                                                       <posy>0</posy>
+                                                       <width>700</width>
+                                                       <height>41</height>
+                                                       <texture border="2">MenuItemFO.png</texture>
+                                                       <visible>Control.HasFocus(13)</visible>
+                                                       <include>VisibleFadeEffect</include>
+                                               </control>
+                                               <control type="image">
+                                                       <posx>0</posx>
+                                                       <posy>0</posy>
+                                                       <width>700</width>
+                                                       <height>41</height>
+                                                       <texture border="2">MenuItemNF.png</texture>
+                                                       <include>VisibleFadeEffect</include>
+                                                       <visible>!Control.HasFocus(13)</visible>
+                                               </control>
+                                               <control type="image">
+                                                       <posx>500</posx>
+                                                       <posy>5</posy>
+                                                       <width>200</width>
+                                                       <height>31</height>
+                                                       <texture border="0,0,14,0">MediaItemDetailBG.png</texture>
+                                                       <visible>Control.HasFocus(13) + !IsEmpty(ListItem.Date)</visible>
+                                               </control>
+                                               <control type="image">
+                                                       <posx>10</posx>
+                                                       <posy>5</posy>
+                                                       <width>30</width>
+                                                       <height>30</height>
+                                                       <texture background="true" fallback="DefaultVideoCover.png">$INFO[ListItem.Icon]</texture>
+                                               </control>
+                                               <control type="label">
+                                                       <posx>50</posx>
+                                                       <posy>0</posy>
+                                                       <width>630</width>
+                                                       <height>40</height>
+                                                       <font>font13</font>
+                                                       <textcolor>white</textcolor>
+                                                       <selectedcolor>selected</selectedcolor>
+                                                       <align>left</align>
+                                                       <aligny>center</aligny>
+                                                       <label>$INFO[ListItem.Label]</label>
+                                               </control>
+                                               <control type="label">
+                                                       <posx>690</posx>
+                                                       <posy>0</posy>
+                                                       <width>500</width>
+                                                       <height>40</height>
+                                                       <font>font12</font>
+                                                       <textcolor>grey2</textcolor>
+                                                       <selectedcolor>selected</selectedcolor>
+                                                       <align>right</align>
+                                                       <aligny>center</aligny>
+                                                       <label>$INFO[ListItem.Date]</label>
+                                               </control>
+                                       </focusedlayout>
+                               </control>
+                               <control type="scrollbar" id="72">
+                                       <posx>1190</posx>
+                                       <posy>100</posy>
+                                       <width>25</width>
+                                       <height>521</height>
+                                       <texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground>
+                                       <texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar>
+                                       <texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus>
+                                       <textureslidernib>ScrollBarNib.png</textureslidernib>
+                                       <textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus>
+                                       <onleft>12</onleft>
+                                       <onright>34</onright>
+                                       <ondown>72</ondown>
+                                       <onup>72</onup>
+                                       <showonepage>false</showonepage>
+                                       <orientation>vertical</orientation>
+                                       <visible>Control.IsVisible(13)</visible>
+                               </control>
+                               <control type="label">
+                                       <description>Page Count Label</description>
+                                       <posx>40r</posx>
+                                       <posy>30r</posy>
+                                       <width>500</width>
+                                       <height>20</height>
+                                       <font>font12</font>
+                                       <textcolor>grey</textcolor>
+                                       <scroll>false</scroll>
+                                       <align>right</align>
+                                       <aligny>center</aligny>
+                                       <label>([COLOR=blue]$INFO[Container(13).NumItems][/COLOR]) $LOCALIZE[19163] - $LOCALIZE[31024] ([COLOR=blue]$INFO[Container(13).CurrentPage]/$INFO[Container(13).NumPages][/COLOR])</label>
+                                       <include>Window_OpenClose_Animation</include>           
+                               </control>
+                               <control type="group">
+                                       <posx>80</posx>
+                                       <posy>100</posy>
+                                       <control type="image">
+                                               <posx>0</posx>
+                                               <posy>0</posy>
+                                               <width>380</width>
+                                               <height>270</height>
+                                               <aspectratio aligny="bottom">keep</aspectratio>
+                                               <fadetime>IconCrossfadeTime</fadetime>
+                                               <texture fallback="DefaultVideoCover.png">$INFO[Container(13).ListItem.Icon]</texture>
+                                               <bordertexture border="8">ThumbShadow.png</bordertexture>
+                                               <bordersize>8</bordersize>
+                                       </control>
+                                       <control type="image">
+                                               <posx>0</posx>
+                                               <posy>263</posy>
+                                               <width>380</width>
+                                               <height>80</height>
+                                               <aspectratio>stretch</aspectratio>
+                                               <texture>GlassTitleBar.png</texture>
+                                               <colordiffuse>AAFFFFFF</colordiffuse>
+                                       </control>
+                                       <control type="label">
+                                               <posx>0</posx>
+                                               <posy>290</posy>
+                                               <width>380</width>
+                                               <height>25</height>
+                                               <label>$INFO[Container(13).ListItem.Title]</label>
+                                               <scroll>true</scroll>
+                                               <align>center</align>
+                                               <aligny>center</aligny>
+                                               <font>font24_title</font>
+                                               <textcolor>white</textcolor>
+                                               <shadowcolor>black</shadowcolor>
+                                       </control>
+                                       <control type="textbox">
+                                               <description>Plot Value for TVShow</description>
+                                               <posx>0</posx>
+                                               <posy>330</posy>
+                                               <width>380</width>
+                                               <height>190</height>
+                                               <font>font12</font>
+                                               <align>justify</align>
+                                               <textcolor>white</textcolor>
+                                               <shadowcolor>black</shadowcolor>
+                                               <pagecontrol>9999999999</pagecontrol>
+                                               <label>$INFO[Container(13).ListItem.Plot]</label>
+                                               <autoscroll time="2000" delay="3000" repeat="5000">true</autoscroll>
+                                       </control>
+                               </control>
+                       </control>
+                       <!-- Timers group -->
+                       <control type="group">
+                               <description>Timers group</description>
+                               <visible>Control.IsVisible(14)</visible>
+                               <include>VisibleFadeEffect</include>
+                               <control type="group">
+                                       <posx>80</posx>
+                                       <posy>60</posy>
+                                       <control type="label">
+                                               <description>Title header label</description>
+                                               <posx>0</posx>
+                                               <posy>20</posy>
+                                               <width>300</width>
+                                               <height>20</height>
+                                               <font>font13_title</font>
+                                               <textcolor>white</textcolor>
+                                               <shadowcolor>black</shadowcolor>
+                                               <align>center</align>
+                                               <aligny>center</aligny>
+                                               <label>369</label>
+                                       </control>
+                                       <control type="label">
+                                               <description>Schedule Time header label</description>
+                                               <posx>300</posx>
+                                               <posy>20</posy>
+                                               <width>600</width>
+                                               <height>20</height>
+                                               <font>font13_title</font>
+                                               <textcolor>white</textcolor>
+                                               <shadowcolor>black</shadowcolor>
+                                               <align>center</align>
+                                               <aligny>center</aligny>
+                                               <label>31501</label>
+                                       </control>
+                                       <control type="label">
+                                               <description>Status header label</description>
+                                               <posx>900</posx>
+                                               <posy>20</posy>
+                                               <width>200</width>
+                                               <height>20</height>
+                                               <font>font13_title</font>
+                                               <textcolor>white</textcolor>
+                                               <shadowcolor>black</shadowcolor>
+                                               <align>center</align>
+                                               <aligny>center</aligny>
+                                               <label>126</label>
+                                       </control>
+                                       <control type="image">
+                                               <description>separator image</description>
+                                               <posx>0</posx>
+                                               <posy>50</posy>
+                                               <width>1100</width>
+                                               <height>1</height>
+                                               <colordiffuse>88FFFFFF</colordiffuse>
+                                               <texture>separator2.png</texture>
+                                       </control>
+                                       <control type="list" id="14">
+                                               <posx>0</posx>
+                                               <posy>55</posy>
+                                               <width>1100</width>
+                                               <height>480</height>
+                                               <onup>14</onup>
+                                               <ondown>14</ondown>
+                                               <onleft>35</onleft>
+                                               <onright>73</onright>
+                                               <pagecontrol>73</pagecontrol>
+                                               <scrolltime>200</scrolltime>
+                                               <itemlayout height="40">
+                                                       <control type="image">
+                                                               <posx>0</posx>
+                                                               <posy>0</posy>
+                                                               <width>1100</width>
+                                                               <height>41</height>
+                                                               <texture border="5">MenuItemNF.png</texture>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>0</posx>
+                                                               <posy>0</posy>
+                                                               <width>300</width>
+                                                               <height>40</height>
+                                                               <colordiffuse>33FFFFFF</colordiffuse>
+                                                               <texture border="5">StackFO.png</texture>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>900</posx>
+                                                               <posy>0</posy>
+                                                               <width>200</width>
+                                                               <height>40</height>
+                                                               <colordiffuse>33FFFFFF</colordiffuse>
+                                                               <texture border="5">StackFO.png</texture>
+                                                       </control>
+                                                       <control type="label">
+                                                               <posx>150</posx>
+                                                               <posy>0</posy>
+                                                               <width>290</width>
+                                                               <height>40</height>
+                                                               <font>font12</font>
+                                                               <align>center</align>
+                                                               <aligny>center</aligny>
+                                                               <selectedcolor>selected</selectedcolor>
+                                                               <info>ListItem.Label</info>
+                                                       </control>
+                                                       <control type="label">
+                                                               <posx>600</posx>
+                                                               <posy>0</posy>
+                                                               <width>590</width>
+                                                               <height>40</height>
+                                                               <font>font12</font>
+                                                               <align>center</align>
+                                                               <aligny>center</aligny>
+                                                               <selectedcolor>selected</selectedcolor>
+                                                               <info>ListItem.Date</info>
+                                                       </control>
+                                                       <control type="label">
+                                                               <posx>1000</posx>
+                                                               <posy>0</posy>
+                                                               <width>190</width>
+                                                               <height>40</height>
+                                                               <font>font12</font>
+                                                               <align>center</align>
+                                                               <aligny>center</aligny>
+                                                               <selectedcolor>selected</selectedcolor>
+                                                               <info>ListItem.Comment</info>
+                                                       </control>
+                                               </itemlayout>
+                                               <focusedlayout height="40">
+                                                       <control type="image">
+                                                               <posx>0</posx>
+                                                               <posy>0</posy>
+                                                               <width>1100</width>
+                                                               <height>41</height>
+                                                               <texture border="5">MenuItemNF.png</texture>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>0</posx>
+                                                               <posy>0</posy>
+                                                               <width>300</width>
+                                                               <height>40</height>
+                                                               <colordiffuse>33FFFFFF</colordiffuse>
+                                                               <texture border="5">StackFO.png</texture>
+                                                               <visible>!Control.HasFocus(14)</visible>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>900</posx>
+                                                               <posy>0</posy>
+                                                               <width>200</width>
+                                                               <height>40</height>
+                                                               <colordiffuse>33FFFFFF</colordiffuse>
+                                                               <texture border="5">StackFO.png</texture>
+                                                               <visible>!Control.HasFocus(14)</visible>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>0</posx>
+                                                               <posy>0</posy>
+                                                               <width>300</width>
+                                                               <height>40</height>
+                                                               <colordiffuse>88FFFFFF</colordiffuse>
+                                                               <texture border="5">StackFO.png</texture>
+                                                               <visible>Control.HasFocus(14)</visible>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>900</posx>
+                                                               <posy>0</posy>
+                                                               <width>200</width>
+                                                               <height>40</height>
+                                                               <colordiffuse>88FFFFFF</colordiffuse>
+                                                               <texture border="5">StackFO.png</texture>
+                                                               <visible>Control.HasFocus(14)</visible>
+                                                       </control>
+                                                       <control type="label">
+                                                               <posx>150</posx>
+                                                               <posy>0</posy>
+                                                               <width>290</width>
+                                                               <height>40</height>
+                                                               <font>font12</font>
+                                                               <align>center</align>
+                                                               <aligny>center</aligny>
+                                                               <selectedcolor>selected</selectedcolor>
+                                                               <info>ListItem.Label</info>
+                                                       </control>
+                                                       <control type="label">
+                                                               <posx>600</posx>
+                                                               <posy>0</posy>
+                                                               <width>590</width>
+                                                               <height>40</height>
+                                                               <font>font12</font>
+                                                               <align>center</align>
+                                                               <aligny>center</aligny>
+                                                               <selectedcolor>selected</selectedcolor>
+                                                               <info>ListItem.Date</info>
+                                                       </control>
+                                                       <control type="label">
+                                                               <posx>1000</posx>
+                                                               <posy>0</posy>
+                                                               <width>190</width>
+                                                               <height>40</height>
+                                                               <font>font12</font>
+                                                               <align>center</align>
+                                                               <aligny>center</aligny>
+                                                               <selectedcolor>selected</selectedcolor>
+                                                               <info>ListItem.Comment</info>
+                                                       </control>
+                                               </focusedlayout>
+                                       </control>
+                                       <control type="scrollbar" id="73">
+                                               <posx>1105</posx>
+                                               <posy>50</posy>
+                                               <width>25</width>
+                                               <height>480</height>
+                                               <texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground>
+                                               <texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar>
+                                               <texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus>
+                                               <textureslidernib>ScrollBarNib.png</textureslidernib>
+                                               <textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus>
+                                               <onleft>11</onleft>
+                                               <onright>35</onright>
+                                               <ondown>73</ondown>
+                                               <onup>73</onup>
+                                               <showonepage>false</showonepage>
+                                               <orientation>vertical</orientation>
+                                               <visible>Control.IsVisible(14)</visible>
+                                       </control>
+                                       <control type="image">
+                                               <description>separator image</description>
+                                               <posx>55</posx>
+                                               <posy>540</posy>
+                                               <width>1010</width>
+                                               <height>1</height>
+                                               <colordiffuse>88FFFFFF</colordiffuse>
+                                               <texture>separator2.png</texture>
+                                       </control>
+                                       <control type="label">
+                                               <description>Next timer date</description>
+                                               <posx>55</posx>
+                                               <posy>545</posy>
+                                               <width>1010</width>
+                                               <height>30</height>
+                                               <font>font13</font>
+                                               <align>center</align>
+                                               <aligny>center</aligny>
+                                               <scroll>true</scroll>
+                                               <textcolor>white</textcolor>
+                                               <label>$INFO[PVR.NextTimer]</label>
+                                               <visible>PVR.HasTimer</visible>
+                                       </control>
+                               </control>
+                               <control type="label">
+                                       <description>Page Count Label</description>
+                                       <posx>40r</posx>
+                                       <posy>30r</posy>
+                                       <width>500</width>
+                                       <height>20</height>
+                                       <font>font12</font>
+                                       <textcolor>grey</textcolor>
+                                       <scroll>false</scroll>
+                                       <align>right</align>
+                                       <aligny>center</aligny>
+                                       <label>([COLOR=blue]$INFO[Container(14).NumItems][/COLOR]) $LOCALIZE[19040] - $LOCALIZE[31024] ([COLOR=blue]$INFO[Container(14).CurrentPage]/$INFO[Container(14).NumPages][/COLOR])</label>
+                                       <include>Window_OpenClose_Animation</include>           
+                               </control>
+                       </control>
+                       <!-- EGP search group -->
+                       <control type="group">
+                               <description>TV Search group</description>
+                               <visible>Control.IsVisible(17)</visible>
+                               <include>VisibleFadeEffect</include>
+                               <control type="group">
+                                       <posx>80</posx>
+                                       <posy>60</posy>
+                                       <control type="label">
+                                               <description>Channel label</description>
+                                               <posx>0</posx>
+                                               <posy>20</posy>
+                                               <width>250</width>
+                                               <height>20</height>
+                                               <font>font13_title</font>
+                                               <textcolor>white</textcolor>
+                                               <shadowcolor>black</shadowcolor>
+                                               <align>center</align>
+                                               <aligny>center</aligny>
+                                               <label>19148</label>
+                                       </control>
+                                       <control type="label">
+                                               <description>Title</description>
+                                               <posx>290</posx>
+                                               <posy>20</posy>
+                                               <width>350</width>
+                                               <height>20</height>
+                                               <font>font13_title</font>
+                                               <textcolor>white</textcolor>
+                                               <shadowcolor>black</shadowcolor>
+                                               <align>left</align>
+                                               <aligny>center</aligny>
+                                               <label>369</label>
+                                       </control>
+                                       <control type="label">
+                                               <description>Time label</description>
+                                               <posx>920</posx>
+                                               <posy>20</posy>
+                                               <width>300</width>
+                                               <height>20</height>
+                                               <font>font13_title</font>
+                                               <textcolor>white</textcolor>
+                                               <shadowcolor>black</shadowcolor>
+                                               <align>right</align>
+                                               <aligny>center</aligny>
+                                               <label>21820</label>
+                                       </control>
+                                       <control type="label">
+                                               <description>Status header label</description>
+                                               <posx>960</posx>
+                                               <posy>20</posy>
+                                               <width>140</width>
+                                               <height>20</height>
+                                               <font>font13_title</font>
+                                               <textcolor>white</textcolor>
+                                               <shadowcolor>black</shadowcolor>
+                                               <align>center</align>
+                                               <aligny>center</aligny>
+                                               <label>126</label>
+                                       </control>
+                                       <control type="image">
+                                               <description>separator image</description>
+                                               <posx>0</posx>
+                                               <posy>50</posy>
+                                               <width>1100</width>
+                                               <height>1</height>
+                                               <colordiffuse>88FFFFFF</colordiffuse>
+                                               <texture>separator2.png</texture>
+                                       </control>
+                                       <control type="list" id="17">
+                                               <posx>0</posx>
+                                               <posy>55</posy>
+                                               <width>1100</width>
+                                               <height>520</height>
+                                               <onup>17</onup>
+                                               <ondown>17</ondown>
+                                               <onleft>36</onleft>
+                                               <onright>77</onright>
+                                               <pagecontrol>77</pagecontrol>
+                                               <scrolltime>200</scrolltime>
+                                               <itemlayout height="40">
+                                                       <control type="image">
+                                                               <posx>0</posx>
+                                                               <posy>0</posy>
+                                                               <width>1100</width>
+                                                               <height>41</height>
+                                                               <texture border="5">MenuItemNF.png</texture>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>0</posx>
+                                                               <posy>0</posy>
+                                                               <width>250</width>
+                                                               <height>40</height>
+                                                               <colordiffuse>33FFFFFF</colordiffuse>
+                                                               <texture border="5">StackFO.png</texture>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>960</posx>
+                                                               <posy>0</posy>
+                                                               <width>140</width>
+                                                               <height>40</height>
+                                                               <colordiffuse>33FFFFFF</colordiffuse>
+                                                               <texture border="5">StackFO.png</texture>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>10</posx>
+                                                               <posy>5</posy>
+                                                               <width>30</width>
+                                                               <height>30</height>
+                                                               <info>ListItem.Icon</info>
+                                                       </control>
+                                                       <control type="label">
+                                                               <posx>50</posx>
+                                                               <posy>0</posy>
+                                                               <width>190</width>
+                                                               <height>35</height>
+                                                               <font>font12</font>
+                                                               <align>left</align>
+                                                               <aligny>center</aligny>
+                                                               <textcolor>grey2</textcolor>
+                                                               <selectedcolor>selected</selectedcolor>
+                                                               <info>ListItem.ChannelName</info>
+                                                       </control>
+                                                       <control type="label">
+                                                               <posx>260</posx>
+                                                               <posy>0</posy>
+                                                               <width>650</width>
+                                                               <height>35</height>
+                                                               <font>font13</font>
+                                                               <align>left</align>
+                                                               <aligny>center</aligny>
+                                                               <textcolor>grey2</textcolor>
+                                                               <selectedcolor>selected</selectedcolor>
+                                                               <info>ListItem.Label</info>
+                                                       </control>
+                                                       <control type="label">
+                                                               <posx>950</posx>
+                                                               <posy>0</posy>
+                                                               <width>500</width>
+                                                               <height>40</height>
+                                                               <font>font12</font>
+                                                               <align>right</align>
+                                                               <aligny>center</aligny>
+                                                               <textcolor>grey2</textcolor>
+                                                               <selectedcolor>selected</selectedcolor>
+                                                               <info>ListItem.Date</info>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>970</posx>
+                                                               <posy>10</posy>
+                                                               <width>30</width>
+                                                               <height>20</height>
+                                                               <texture>PVR-IsRecording.png</texture>
+                                                               <visible>ListItem.IsRecording</visible>
+                                                       </control>
+                                                       <control type="label">
+                                                               <posx>1005</posx>
+                                                               <posy>0</posy>
+                                                               <width>80</width>
+                                                               <height>40</height>
+                                                               <font>font10</font>
+                                                               <align>left</align>
+                                                               <aligny>center</aligny>
+                                                               <textcolor>grey2</textcolor>
+                                                               <selectedcolor>selected</selectedcolor>
+                                                               <label>19043</label>
+                                                               <visible>ListItem.IsRecording</visible>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>970</posx>
+                                                               <posy>10</posy>
+                                                               <width>20</width>
+                                                               <height>20</height>
+                                                               <texture>PVR-HasTimer.png</texture>
+                                                               <visible>ListItem.HasTimer</visible>
+                                                       </control>
+                                                       <control type="label">
+                                                               <posx>1000</posx>
+                                                               <posy>0</posy>
+                                                               <width>80</width>
+                                                               <height>40</height>
+                                                               <font>font10</font>
+                                                               <align>left</align>
+                                                               <aligny>center</aligny>
+                                                               <textcolor>grey2</textcolor>
+                                                               <selectedcolor>selected</selectedcolor>
+                                                               <label>31510</label>
+                                                               <visible>ListItem.HasTimer</visible>
+                                                       </control>
+                                               </itemlayout>
+                                               <focusedlayout height="40">
+                                                       <control type="image">
+                                                               <posx>0</posx>
+                                                               <posy>0</posy>
+                                                               <width>1100</width>
+                                                               <height>41</height>
+                                                               <texture border="5">MenuItemNF.png</texture>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>0</posx>
+                                                               <posy>0</posy>
+                                                               <width>250</width>
+                                                               <height>40</height>
+                                                               <colordiffuse>33FFFFFF</colordiffuse>
+                                                               <texture border="5">StackFO.png</texture>
+                                                               <visible>!Control.HasFocus(17)</visible>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>960</posx>
+                                                               <posy>0</posy>
+                                                               <width>140</width>
+                                                               <height>40</height>
+                                                               <colordiffuse>33FFFFFF</colordiffuse>
+                                                               <texture border="5">StackFO.png</texture>
+                                                               <visible>!Control.HasFocus(17)</visible>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>0</posx>
+                                                               <posy>0</posy>
+                                                               <width>250</width>
+                                                               <height>40</height>
+                                                               <colordiffuse>88FFFFFF</colordiffuse>
+                                                               <texture border="5">StackFO.png</texture>
+                                                               <visible>Control.HasFocus(17)</visible>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>960</posx>
+                                                               <posy>0</posy>
+                                                               <width>140</width>
+                                                               <height>40</height>
+                                                               <colordiffuse>88FFFFFF</colordiffuse>
+                                                               <texture border="5">StackFO.png</texture>
+                                                               <visible>Control.HasFocus(17)</visible>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>10</posx>
+                                                               <posy>5</posy>
+                                                               <width>30</width>
+                                                               <height>30</height>
+                                                               <info>ListItem.Icon</info>
+                                                       </control>
+                                                       <control type="label">
+                                                               <posx>50</posx>
+                                                               <posy>0</posy>
+                                                               <width>190</width>
+                                                               <height>35</height>
+                                                               <font>font12</font>
+                                                               <align>left</align>
+                                                               <aligny>center</aligny>
+                                                               <textcolor>grey2</textcolor>
+                                                               <selectedcolor>selected</selectedcolor>
+                                                               <info>ListItem.ChannelName</info>
+                                                       </control>
+                                                       <control type="label">
+                                                               <posx>260</posx>
+                                                               <posy>0</posy>
+                                                               <width>650</width>
+                                                               <height>35</height>
+                                                               <font>font13</font>
+                                                               <align>left</align>
+                                                               <aligny>center</aligny>
+                                                               <textcolor>white</textcolor>
+                                                               <selectedcolor>selected</selectedcolor>
+                                                               <info>ListItem.Label</info>
+                                                       </control>
+                                                       <control type="label">
+                                                               <posx>950</posx>
+                                                               <posy>0</posy>
+                                                               <width>500</width>
+                                                               <height>40</height>
+                                                               <font>font12</font>
+                                                               <align>right</align>
+                                                               <aligny>center</aligny>
+                                                               <textcolor>white</textcolor>
+                                                               <selectedcolor>selected</selectedcolor>
+                                                               <info>ListItem.Date</info>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>970</posx>
+                                                               <posy>10</posy>
+                                                               <width>30</width>
+                                                               <height>20</height>
+                                                               <texture>PVR-IsRecording.png</texture>
+                                                               <visible>ListItem.IsRecording</visible>
+                                                       </control>
+                                                       <control type="label">
+                                                               <posx>1005</posx>
+                                                               <posy>0</posy>
+                                                               <width>80</width>
+                                                               <height>40</height>
+                                                               <font>font10</font>
+                                                               <align>left</align>
+                                                               <aligny>center</aligny>
+                                                               <textcolor>grey2</textcolor>
+                                                               <selectedcolor>selected</selectedcolor>
+                                                               <label>19043</label>
+                                                               <visible>ListItem.IsRecording</visible>
+                                                       </control>
+                                                       <control type="image">
+                                                               <posx>970</posx>
+                                                               <posy>10</posy>
+                                                               <width>20</width>
+                                                               <height>20</height>
+                                                               <texture>PVR-HasTimer.png</texture>
+                                                               <visible>ListItem.HasTimer</visible>
+                                                       </control>
+                                                       <control type="label">
+                                                               <posx>1000</posx>
+                                                               <posy>0</posy>
+                                                               <width>80</width>
+                                                               <height>40</height>
+                                                               <font>font10</font>
+                                                               <align>left</align>
+                                                               <aligny>center</aligny>
+                                                               <textcolor>grey2</textcolor>
+                                                               <selectedcolor>selected</selectedcolor>
+                                                               <label>31510</label>
+                                                               <visible>ListItem.HasTimer</visible>
+                                                       </control>
+                                               </focusedlayout>
+                                       </control>
+                                       <control type="scrollbar" id="77">
+                                               <posx>1105</posx>
+                                               <posy>50</posy>
+                                               <width>25</width>
+                                               <height>520</height>
+                                               <texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground>
+                                               <texturesliderbar border="0,14,0,14">ScrollBarV_bar.png</texturesliderbar>
+                                               <texturesliderbarfocus border="0,14,0,14">ScrollBarV_bar_focus.png</texturesliderbarfocus>
+                                               <textureslidernib>ScrollBarNib.png</textureslidernib>
+                                               <textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus>
+                                               <onleft>16</onleft>
+                                               <onright>31</onright>
+                                               <ondown>77</ondown>
+                                               <onup>77</onup>
+                                               <showonepage>false</showonepage>
+                                               <orientation>vertical</orientation>
+                                               <visible>Control.IsVisible(17)</visible>
+                                       </control>
+                               </control>
+                               <control type="label">
+                                       <description>Page Count Label</description>
+                                       <posx>40r</posx>
+                                       <posy>30r</posy>
+                                       <width>500</width>
+                                       <height>20</height>
+                                       <font>font12</font>
+                                       <textcolor>grey</textcolor>
+                                       <scroll>false</scroll>
+                                       <align>right</align>
+                                       <aligny>center</aligny>
+                                       <label>([COLOR=blue]$INFO[Container(17).NumItems][/COLOR]) $LOCALIZE[31025] - $LOCALIZE[31024] ([COLOR=blue]$INFO[Container(17).CurrentPage]/$INFO[Container(17).NumPages][/COLOR])</label>
+                               </control>
+                       </control>
+               </control>
+               <control type="image">
+                       <posx>0</posx>
+                       <posy>0</posy>
+                       <width>1280</width>
+                       <height>720</height>
+                       <texture>black-back.png</texture>
+                       <animation effect="fade" time="400">Visible</animation>
+                       <animation effect="fade" time="200">Hidden</animation>
+                       <visible>Window.IsActive(FileBrowser) | Window.IsActive(601) | Window.IsActive(602) | Window.IsActive(603) | Window.IsActive(604) | Window.IsActive(606)</visible>
+               </control>
+               <control type="group">
+                       <animation effect="slide" start="0,0" end="250,0" time="400" tween="quadratic" easing="out" condition="[ControlGroup(9000).HasFocus | Control.HasFocus(8999)] + !Window.IsActive(606)">Conditional</animation>
+                       <animation effect="slide" start="-250,0" end="0,0" time="400" tween="quadratic" easing="out" condition="ControlGroup(9000).HasFocus">WindowOpen</animation>
+                       <animation effect="slide" start="0,0" end="-250,0" time="400" tween="quadratic" easing="out" condition="ControlGroup(9000).HasFocus">WindowClose</animation>
+                       <control type="button" id="8999">
+                               <description>Fake button for mouse control</description>
+                               <posx>-250</posx>
+                               <posy>0</posy>
+                               <width>265</width>
+                               <height>720</height>
+                               <label>-</label>
+                               <font>-</font>
+                               <texturenofocus>-</texturenofocus>
+                               <texturefocus>-</texturefocus>
+                               <visible>true</visible>
+                       </control>
+                       <include>SideBladeLeft</include>
+                       <control type="image">
+                               <description>LOGO</description>
+                               <posx>-230</posx>
+                               <posy>50</posy>
+                               <width>220</width>
+                               <height>80</height>
+                               <aspectratio>keep</aspectratio>
+                               <texture>Confluence_Logo.png</texture>
+                       </control>
+                       <control type="group" id="9000">
+                               <posx>-250</posx>
+                               <posy>130</posy>
+                               <include>CommonNowPlaying</include>
+                               <control type="button" id="32">
+                                       <description>TV Channels</description>
+                                       <posx>0</posx>
+                                       <posy>0</posy>
+                                       <textwidth>235</textwidth>
+                                       <include>ButtonCommonValues</include>
+                                       <label>19023</label>
+                                       <onleft>11</onleft>
+                                       <onright>11</onright>
+                                       <onup>611</onup>
+                                       <ondown>33</ondown>
+                               </control>
+                               <control type="button" id="33">
+                                       <description>Radio Channels</description>
+                                       <posx>0</posx>
+                                       <posy>40</posy>
+                                       <textwidth>235</textwidth>
+                                       <include>ButtonCommonValues</include>
+                                       <label>19024</label>
+                                       <onleft>12</onleft>
+                                       <onright>12</onright>
+                                       <onup>32</onup>
+                                       <ondown>31</ondown>
+                               </control>
+                               <control type="button" id="31">
+                                       <description>TV Guide</description>
+                                       <posx>0</posx>
+                                       <posy>80</posy>
+                                       <textwidth>235</textwidth>
+                                       <include>ButtonCommonValues</include>
+                                       <label>19029</label>
+                                       <onleft>10</onleft>
+                                       <onright>10</onright>
+                                       <onup>33</onup>
+                                       <ondown>34</ondown>
+                               </control>
+                               <control type="button" id="34">
+                                       <description>Recordings</description>
+                                       <posx>0</posx>
+                                       <posy>120</posy>
+                                       <textwidth>235</textwidth>
+                                       <include>ButtonCommonValues</include>
+                                       <label>19163</label>
+                                       <onleft>13</onleft>
+                                       <onright>13</onright>
+                                       <onup>31</onup>
+                                       <ondown>35</ondown>
+                               </control>
+                               <control type="button" id="35">
+                                       <description>Timers</description>
+                                       <posx>0</posx>
+                                       <posy>160</posy>
+                                       <textwidth>235</textwidth>
+                                       <include>ButtonCommonValues</include>
+                                       <label>19040</label>
+                                       <onleft>14</onleft>
+                                       <onright>14</onright>
+                                       <onup>34</onup>
+                                       <ondown>36</ondown>
+                               </control>
+                               <control type="button" id="36">
+                                       <description>Search</description>
+                                       <posx>0</posx>
+                                       <posy>200</posy>
+                                       <textwidth>235</textwidth>
+                                       <include>ButtonCommonValues</include>
+                                       <label>137</label>
+                                       <onleft>17</onleft>
+                                       <onright>17</onright>
+                                       <onup>35</onup>
+                                       <ondown>610</ondown>
+                               </control>
+                               <control type="button" id="610">
+                                       <description>Fake Button to fix Player Controls Navigation</description>
+                                       <onup>36</onup>
+                                       <ondown>603</ondown>
+                                       <visible>false</visible>
+                               </control>
+                               <control type="group">
+                                       <posx>0</posx>
+                                       <posy>260</posy>
+                                       <include>CommonNowPlaying_Controls</include>
+                               </control>
+                               <control type="button" id="611">
+                                       <description>Fake Button to fix Player Controls Navigation</description>
+                                       <onup>608</onup>
+                                       <ondown>32</ondown>
+                                       <visible>false</visible>
+                               </control>
+                       </control>
+               </control>
+               <control type="group">
+                       <posx>440</posx>
+                       <posy>0</posy>
+                       <visible>!IsEmpty(Control.GetLabel(30)) + [Control.IsVisible(10) | Control.IsVisible(11) | Control.IsVisible(12) | Control.IsVisible(15) | Control.IsVisible(16)]</visible>
+                       <include>VisibleFadeEffect</include>
+                       <animation effect="slide" end="-710,0" time="400" tween="quadratic" easing="out">WindowClose</animation>
+                       <animation effect="slide" start="-710,0" time="400" tween="quadratic" easing="out">WindowOpen</animation>
+                       <control type="image">
+                               <posx>0</posx>
+                               <posy>0</posy>
+                               <width>250</width>
+                               <height>35</height>
+                               <texture border="0,0,32,0">header.png</texture>
+                       </control>
+                       <control type="label" id="30">
+                               <include>WindowTitleCommons</include>
+                               <posx>220</posx>
+                       </control>
+               </control>
+               <control type="group">
+                       <posx>240</posx>
+                       <posy>0</posy>
+                       <animation effect="slide" end="-510,0" time="400" tween="quadratic" easing="out">WindowClose</animation>
+                       <animation effect="slide" start="-510,0" time="400" tween="quadratic" easing="out">WindowOpen</animation>
+                       <animation effect="slide" end="70,0" time="200" tween="quadratic" easing="out" condition="Control.IsVisible(14)">Conditional</animation>
+                       <control type="image">
+                               <posx>0</posx>
+                               <posy>0</posy>
+                               <width>250</width>
+                               <height>35</height>
+                               <texture border="0,0,32,0">header.png</texture>
+                       </control>
+                       <control type="label" id="29">
+                               <include>WindowTitleCommons</include>
+                               <posx>220</posx>
+                       </control>
+               </control>
+               <control type="group">
+                       <posx>60</posx>
+                       <posy>0</posy>
+                       <animation effect="slide" end="-310,0" time="400" tween="quadratic" easing="out">WindowClose</animation>
+                       <animation effect="slide" start="-310,0" time="400" tween="quadratic" easing="out">WindowOpen</animation>
+                       <control type="image">
+                               <posx>0</posx>
+                               <posy>0</posy>
+                               <width>250</width>
+                               <height>35</height>
+                               <texture border="0,0,32,0">header.png</texture>
+                       </control>
+                       <control type="label">
+                               <include>WindowTitleCommons</include>
+                               <posx>220</posx>
+                               <label>$LOCALIZE[31502]</label>
+                       </control>
+               </control>
+               <include>WindowTitleHomeButton</include>
+               <include>Clock</include>
+       </controls>
+</window>
\ No newline at end of file
index 7f1899e..d03e55c 100644 (file)
@@ -1,5 +1,5 @@
 <window type="dialog" id="114">
-       <defaultcontrol always="true">603</defaultcontrol>
+       <defaultcontrol always="true">100</defaultcontrol>
        <include>dialogeffect</include>
        <visible>Player.HasMedia + Window.IsActive(PlayerControls) + !Window.IsActive(FullscreenVideo) + !Window.IsActive(Visualisation)</visible>
        <coordinates>
@@ -18,6 +18,8 @@
                <control type="group" id="100">
                        <posx>25</posx>
                        <posy>162</posy>
+                       <defaultcontrol always="true">603</defaultcontrol>
+                       <visible>!VideoPlayer.Content(LiveTV)</visible>
                        <control type="button" id="600">
                                <posx>0</posx>
                                <posy>0</posy>
@@ -31,6 +33,7 @@
                                <onup>300</onup>
                                <ondown>200</ondown>
                                <onclick>XBMC.PlayerControl(Previous)</onclick>
+                               <visible>!VideoPlayer.Content(LiveTV)</visible>
                        </control>
                        <control type="button" id="601">
                                <posx>40</posx>
@@ -45,6 +48,7 @@
                                <onup>300</onup>
                                <ondown>200</ondown>
                                <onclick>XBMC.PlayerControl(Rewind)</onclick>
+                               <visible>!VideoPlayer.Content(LiveTV)</visible>
                        </control>
                        <control type="togglebutton" id="603">
                                <posx>80</posx>
@@ -62,6 +66,7 @@
                                <onup>300</onup>
                                <ondown>200</ondown>
                                <onclick>XBMC.PlayerControl(Play)</onclick>
+                               <visible>!VideoPlayer.Content(LiveTV)</visible>
                        </control>
                        <control type="button" id="602">
                                <posx>120</posx>
@@ -77,6 +82,7 @@
                                <ondown>200</ondown>
                                <onclick>down</onclick>
                                <onclick>XBMC.PlayerControl(Stop)</onclick>
+                               <visible>!VideoPlayer.Content(LiveTV)</visible>
                        </control>
                        <control type="button" id="604">
                                <posx>160</posx>
@@ -91,6 +97,7 @@
                                <onup>300</onup>
                                <ondown>200</ondown>
                                <onclick>XBMC.PlayerControl(Forward)</onclick>
+                               <visible>!VideoPlayer.Content(LiveTV)</visible>
                        </control>
                        <control type="button" id="605">
                                <posx>200</posx>
                                <onup>300</onup>
                                <ondown>200</ondown>
                                <onclick>XBMC.PlayerControl(Next)</onclick>
+                               <visible>!VideoPlayer.Content(LiveTV)</visible>
                        </control>
                        <control type="button" id="606">
                                <posx>240</posx>
                                <onclick>XBMC.PlayerControl(record)</onclick>
                                <enable>Player.CanRecord</enable>
                                <animation effect="fade" start="100" end="30" time="100" condition="!Player.CanRecord">Conditional</animation>
+                               <visible>!VideoPlayer.Content(LiveTV)</visible>
                        </control>
                        <control type="button" id="607">
                                <posx>365</posx>
                                <onright>608</onright>
                                <onup>100</onup>
                                <ondown>100</ondown>
+                               <visible>!VideoPlayer.Content(LiveTV)</visible>
                        </control>
                        <control type="image">
                                <posx>365</posx>
                                <texture>OSDRepeatNF.png</texture>
                                <visible>!Playlist.IsRepeat + !Playlist.IsRepeatOne</visible>
                                <visible>!Control.HasFocus(607)</visible>
+                               <visible>!VideoPlayer.Content(LiveTV)</visible>
                        </control>
                        <control type="image">
                                <posx>365</posx>
                                <texture>OSDRepeatFO.png</texture>
                                <visible>!Playlist.IsRepeat + !Playlist.IsRepeatOne</visible>
                                <visible>Control.HasFocus(607)</visible>
+                               <visible>!VideoPlayer.Content(LiveTV)</visible>
                        </control>
                        <control type="image">
                                <posx>365</posx>
                                <texture>OSDRepeatOneNF.png</texture>
                                <visible>Playlist.IsRepeatOne</visible>
                                <visible>!Control.HasFocus(607)</visible>
+                               <visible>!VideoPlayer.Content(LiveTV)</visible>
                        </control>
                        <control type="image">
                                <posx>365</posx>
                                <texture>OSDRepeatOneFO.png</texture>
                                <visible>Playlist.IsRepeatOne</visible>
                                <visible>Control.HasFocus(607)</visible>
+                               <visible>!VideoPlayer.Content(LiveTV)</visible>
                        </control>
                        <control type="image">
                                <posx>365</posx>
                                <texture>OSDRepeatAllNF.png</texture>
                                <visible>Playlist.IsRepeat</visible>
                                <visible>!Control.HasFocus(607)</visible>
+                               <visible>!VideoPlayer.Content(LiveTV)</visible>
                        </control>
                        <control type="image">
                                <posx>365</posx>
                                <texture>OSDRepeatAllFO.png</texture>
                                <visible>Playlist.IsRepeat</visible>
                                <visible>Control.HasFocus(607)</visible>
+                               <visible>!VideoPlayer.Content(LiveTV)</visible>
                        </control>
                        <control type="togglebutton" id="608">
                                <posx>405</posx>
                                <onright>600</onright>
                                <onup>100</onup>
                                <ondown>100</ondown>
+                               <visible>!VideoPlayer.Content(LiveTV)</visible>
+                       </control>
+               </control>
+               <control type="group" id="100">
+                       <posx>25</posx>
+                       <posy>162</posy>
+                       <defaultcontrol always="true">700</defaultcontrol>
+                       <visible>VideoPlayer.Content(LiveTV)</visible>
+                       <control type="button" id="701">
+                               <posx>0</posx>
+                               <posy>0</posy>
+                               <width>40</width>
+                               <height>40</height>
+                               <label>-</label>
+                               <texturefocus>OSDRewindFO.png</texturefocus>
+                               <texturenofocus>OSDRewindNF.png</texturenofocus>
+                               <onleft>706</onleft>
+                               <onright>702</onright>
+                               <onup>300</onup>
+                               <ondown>200</ondown>
+                               <onclick>XBMC.PlayerControl(Rewind)</onclick>
+                               <visible>VideoPlayer.Content(LiveTV)</visible>
+                               <enable>false</enable>
+                               <animation effect="fade" start="100" end="50" time="100" condition="true">Conditional</animation>
+                       </control>
+                       <control type="button" id="702">
+                               <posx>40</posx>
+                               <posy>0</posy>
+                               <width>40</width>
+                               <height>40</height>
+                               <label>-</label>
+                               <texturefocus>OSDStopFO.png</texturefocus>
+                               <texturenofocus>OSDStopNF.png</texturenofocus>
+                               <onleft>701</onleft>
+                               <onright>703</onright>
+                               <onup>300</onup>
+                               <ondown>200</ondown>
+                               <onclick>down</onclick>
+                               <onclick>XBMC.PlayerControl(Stop)</onclick>
+                               <visible>VideoPlayer.Content(LiveTV)</visible>
+                       </control>
+                       <control type="togglebutton" id="703">
+                               <posx>80</posx>
+                               <posy>0</posy>
+                               <width>40</width>
+                               <height>40</height>
+                               <label>-</label>
+                               <texturefocus>OSDPauseFO.png</texturefocus>
+                               <texturenofocus>OSDPauseNF.png</texturenofocus>
+                               <usealttexture>Player.Paused | Player.Forwarding | Player.Rewinding</usealttexture>
+                               <alttexturefocus>OSDPlayFO.png</alttexturefocus>
+                               <alttexturenofocus>OSDPlayNF.png</alttexturenofocus>
+                               <onleft>702</onleft>
+                               <onright>704</onright>
+                               <onup>300</onup>
+                               <ondown>200</ondown>
+                               <onclick>XBMC.PlayerControl(Play)</onclick>
+                               <visible>VideoPlayer.Content(LiveTV)</visible>
+                               <enable>false</enable>
+                               <animation effect="fade" start="100" end="50" time="100" condition="true">Conditional</animation>
+                       </control>
+                       <control type="button" id="704">
+                               <posx>120</posx>
+                               <posy>0</posy>
+                               <width>40</width>
+                               <height>40</height>
+                               <label>-</label>
+                               <texturefocus>OSDForwardFO.png</texturefocus>
+                               <texturenofocus>OSDForwardNF.png</texturenofocus>
+                               <onleft>703</onleft>
+                               <onright>700</onright>
+                               <onup>300</onup>
+                               <ondown>200</ondown>
+                               <onclick>XBMC.PlayerControl(Forward)</onclick>
+                               <visible>VideoPlayer.Content(LiveTV)</visible>
+                               <enable>false</enable>
+                               <animation effect="fade" start="100" end="50" time="100" condition="true">Conditional</animation>
+                       </control>
+                       <control type="button" id="700">
+                               <posx>200</posx>
+                               <posy>0</posy>
+                               <width>40</width>
+                               <height>40</height>
+                               <label>-</label>
+                               <texturefocus>OSDChannelUPFO.png</texturefocus>
+                               <texturenofocus>OSDChannelUPNF.png</texturenofocus>
+                               <onleft>704</onleft>
+                               <onright>705</onright>
+                               <onup>300</onup>
+                               <ondown>200</ondown>
+                               <onclick>XBMC.PlayerControl(Previous)</onclick>
+                               <visible>VideoPlayer.Content(LiveTV)</visible>
+                       </control>
+                       <control type="button" id="705">
+                               <posx>240</posx>
+                               <posy>0</posy>
+                               <width>40</width>
+                               <height>40</height>
+                               <label>-</label>
+                               <texturefocus>OSDChannelDownFO.png</texturefocus>
+                               <texturenofocus>OSDChannelDownNF.png</texturenofocus>
+                               <onleft>700</onleft>
+                               <onright>706</onright>
+                               <onup>300</onup>
+                               <ondown>200</ondown>
+                               <onclick>XBMC.PlayerControl(Next)</onclick>
+                               <visible>VideoPlayer.Content(LiveTV)</visible>
+                       </control>
+                       <control type="button" id="706">
+                               <posx>280</posx>
+                               <posy>0</posy>
+                               <width>40</width>
+                               <height>40</height>
+                               <label>-</label>
+                               <texturefocus>OSDRecordFO.png</texturefocus>
+                               <texturenofocus>OSDRecordNF.png</texturenofocus>
+                               <onleft>705</onleft>
+                               <onright>701</onright>
+                               <onup>300</onup>
+                               <ondown>200</ondown>
+                               <onclick>XBMC.PlayerControl(record)</onclick>
+                               <enable>Player.CanRecord</enable>
+                               <animation effect="fade" start="100" end="30" time="100" condition="!Player.CanRecord">Conditional</animation>
+                               <visible>VideoPlayer.Content(LiveTV)</visible>
                        </control>
                </control>
                <!-- Music Info -->
                        <include>SmallVideoInfo</include>
                </control>
        </controls>
-</window>
\ No newline at end of file
+</window>
index a1982a1..e52e234 100644 (file)
                                                <icon>special://skin/backgrounds/videos.jpg</icon>
                                        </item>
                                        <item id="3">
+                                               <label>31502</label>
+                                               <label2>31409</label2>
+                                               <onclick>ActivateWindow(MyTVSettings)</onclick>
+                                               <icon>special://skin/backgrounds/tv.jpg</icon>
+                                       </item>
+                                       <item id="4">
                                                <label>2</label>
                                                <label2>31402</label2>
                                                <onclick>ActivateWindow(MusicSettings)</onclick>
                                                <icon>special://skin/backgrounds/music.jpg</icon>
                                        </item>
-                                       <item id="4">
+                                       <item id="5">
                                                <label>1</label>
                                                <label2>31403</label2>
                                                <onclick>ActivateWindow(PicturesSettings)</onclick>
                                                <icon>special://skin/backgrounds/pictures.jpg</icon>
                                        </item>
-                                       <item id="5">
+                                       <item id="6">
                                                <label>8</label>
                                                <label2>31404</label2>
                                                <onclick>ActivateWindow(WeatherSettings)</onclick>
                                                <icon>special://skin/backgrounds/weather.jpg</icon>
                                        </item>
-                                       <item id="6">
+                                       <item id="7">
                                                <label>24001</label>
                                                <label2>31408</label2>
                                                <onclick>ActivateWindow(AddonBrowser)</onclick>
                                                <icon>special://skin/backgrounds/addons.jpg</icon>
                                        </item>
-                                       <item id="7">
+                                       <item id="8">
                                                <label>705</label>
                                                <label2>31405</label2>
                                                <onclick>ActivateWindow(NetworkSettings)</onclick>
                                                <icon>special://skin/backgrounds/network.jpg</icon>
                                        </item>
-                                       <item id="8">
+                                       <item id="9">
                                                <label>13000</label>
                                                <label2>31406</label2>
                                                <onclick>ActivateWindow(SystemSettings)</onclick>
                                                <icon>special://skin/backgrounds/system.jpg</icon>
                                        </item>
-                                       <item id="9">
+                                       <item id="10">
                                                <label>166</label>
                                                <label2>31407</label2>
                                                <onclick>ActivateWindow(1111)</onclick>
index fb04f0f..2116dfc 100644 (file)
                                        <pulseonselect>false</pulseonselect>
                                        <label>13281</label>
                                </control>
+                               <control type="button" id="99">
+                                       <description>Button PVR</description>
+                                       <height>60</height>
+                                       <width>241</width>
+                                       <textoffsetx>0</textoffsetx>
+                                       <align>right</align>
+                                       <aligny>center</aligny>
+                                       <font>font13_title</font>
+                                       <textcolor>grey2</textcolor>
+                                       <focusedcolor>white</focusedcolor>
+                                       <texturefocus border="5">MenuItemFO.png</texturefocus>
+                                       <texturenofocus border="5">MenuItemNF.png</texturenofocus>
+                                       <pulseonselect>false</pulseonselect>
+                                       <label>19191</label>
+                               </control>
                        </control>
                        <control type="image">
                                <posx>268</posx>
index e3c0e35..a96df70 100644 (file)
                        <font>font12</font>
                        <include>VisibleFadeEffect</include>
                </control>
+               <control type="selectbutton" id="503">
+                       <posx>440</posx>
+                       <posy>100</posy>
+                       <width>400</width>
+                       <height>100</height>
+                       <font>font13caps</font>
+                       <description>TV Channel Group Select Button</description>
+                       <texturebg border="20">OverlayDialogBackground.png</texturebg>
+                       <onleft>503</onleft>
+                       <onright>503</onright>
+                       <onup>500</onup>
+                       <ondown>500</ondown>
+                       <include>VisibleFadeEffect</include>
+               </control>
+               <control type="group">
+                       <visible>Player.ShowCodec + VideoPlayer.Content(LiveTV) + system.getbool(pvrplayback.signalquality)</visible>
+                       <posy>160</posy>
+                       <control type="image">
+                               <description>media info background image</description>
+                               <posx>0</posx>
+                               <posy>0</posy>
+                               <width>1280</width>
+                               <height>220</height>
+                               <texture>black-back.png</texture>
+                       </control>
+                       <control type="label">
+                               <description>Header</description>
+                               <posx>50</posx>
+                               <posy>5</posy>
+                               <width>1200</width>
+                               <height>25</height>
+                               <label>$LOCALIZE[19005]</label>
+                               <align>left</align>
+                               <aligny>center</aligny>
+                               <font>font13_title</font>
+                               <textcolor>blue</textcolor>
+                       </control>
+                       <control type="label">
+                               <description>Backend</description>
+                               <posx>50</posx>
+                               <posy>40</posy>
+                               <width>165</width>
+                               <height>25</height>
+                               <label>$LOCALIZE[19012]:</label>
+                               <align>left</align>
+                               <aligny>center</aligny>
+                               <font>font12</font>
+                               <textcolor>grey2</textcolor>
+                       </control>
+                       <control type="label">
+                               <description>Backend value</description>
+                               <posx>220</posx>
+                               <posy>40</posy>
+                               <width>1000</width>
+                               <height>25</height>
+                               <label>$INFO[PVR.ActStreamClient]</label>
+                               <align>left</align>
+                               <aligny>center</aligny>
+                               <font>font12</font>
+                               <textcolor>white</textcolor>
+                       </control>
+                       <control type="label">
+                               <description>Device</description>
+                               <posx>50</posx>
+                               <posy>65</posy>
+                               <width>165</width>
+                               <height>25</height>
+                               <label>$LOCALIZE[19006]:</label>
+                               <align>left</align>
+                               <aligny>center</aligny>
+                               <font>font12</font>
+                               <textcolor>grey2</textcolor>
+                       </control>
+                       <control type="label">
+                               <description>Device value</description>
+                               <posx>220</posx>
+                               <posy>65</posy>
+                               <width>1000</width>
+                               <height>25</height>
+                               <label>$INFO[PVR.ActStreamDevice]</label>
+                               <align>left</align>
+                               <aligny>center</aligny>
+                               <font>font12</font>
+                               <textcolor>white</textcolor>
+                       </control>
+                       <control type="label">
+                               <description>Status</description>
+                               <posx>50</posx>
+                               <posy>90</posy>
+                               <width>165</width>
+                               <height>25</height>
+                               <label>$LOCALIZE[19007]:</label>
+                               <align>left</align>
+                               <aligny>center</aligny>
+                               <font>font12</font>
+                               <textcolor>grey2</textcolor>
+                       </control>
+                       <control type="label">
+                               <description>Status value</description>
+                               <posx>220</posx>
+                               <posy>90</posy>
+                               <width>1000</width>
+                               <height>25</height>
+                               <label>$INFO[PVR.ActStreamStatus]</label>
+                               <align>left</align>
+                               <aligny>center</aligny>
+                               <font>font12</font>
+                               <textcolor>white</textcolor>
+                       </control>
+                       <control type="label">
+                               <description>Signal</description>
+                               <posx>50</posx>
+                               <posy>115</posy>
+                               <width>165</width>
+                               <height>25</height>
+                               <label>$LOCALIZE[19008]:</label>
+                               <align>left</align>
+                               <aligny>center</aligny>
+                               <font>font12</font>
+                               <textcolor>grey2</textcolor>
+                       </control>
+                       <control type="progress">
+                               <description>Progressbar</description>
+                               <posx>220</posx>
+                               <posy>122</posy>
+                               <width>910</width>
+                               <height>14</height>
+                               <info>PVR.ActStreamProgrSignal</info>
+                       </control>
+                       <control type="label">
+                               <description>Signal value</description>
+                               <posx>1200</posx>
+                               <posy>115</posy>
+                               <width>180</width>
+                               <height>25</height>
+                               <label>$INFO[PVR.ActStreamSignal]</label>
+                               <align>left</align>
+                               <aligny>center</aligny>
+                               <font>font12</font>
+                               <textcolor>white</textcolor>
+                       </control>
+                       <control type="label">
+                               <description>SNR</description>
+                               <posx>50</posx>
+                               <posy>140</posy>
+                               <width>165</width>
+                               <height>25</height>
+                               <label>$LOCALIZE[19009]:</label>
+                               <align>left</align>
+                               <aligny>center</aligny>
+                               <font>font12</font>
+                               <textcolor>grey2</textcolor>
+                       </control>
+                       <control type="progress">
+                               <description>Progressbar</description>
+                               <posx>220</posx>
+                               <posy>147</posy>
+                               <width>910</width>
+                               <height>14</height>
+                               <overlaytexture>-</overlaytexture>
+                               <info>PVR.ActStreamProgrSNR</info>
+                       </control>
+                       <control type="label">
+                               <description>SNR value</description>
+                               <posx>1200</posx>
+                               <posy>140</posy>
+                               <width>180</width>
+                               <height>25</height>
+                               <label>$INFO[PVR.ActStreamSNR]</label>
+                               <align>left</align>
+                               <aligny>center</aligny>
+                               <font>font12</font>
+                               <textcolor>white</textcolor>
+                       </control>
+                       <control type="label">
+                               <description>BER</description>
+                               <posx>50</posx>
+                               <posy>165</posy>
+                               <width>165</width>
+                               <height>25</height>
+                               <label>$LOCALIZE[19010]:</label>
+                               <align>left</align>
+                               <aligny>center</aligny>
+                               <font>font12</font>
+                               <textcolor>grey2</textcolor>
+                       </control>
+                       <control type="label">
+                               <description>BER value</description>
+                               <posx>220</posx>
+                               <posy>165</posy>
+                               <width>1000</width>
+                               <height>25</height>
+                               <label>$INFO[PVR.ActStreamBER]</label>
+                               <align>left</align>
+                               <aligny>center</aligny>
+                               <font>font12</font>
+                               <textcolor>white</textcolor>
+                       </control>
+                       <control type="label">
+                               <description>UNC</description>
+                               <posx>430</posx>
+                               <posy>165</posy>
+                               <width>165</width>
+                               <height>25</height>
+                               <label>$LOCALIZE[19011]:</label>
+                               <align>left</align>
+                               <aligny>center</aligny>
+                               <font>font12</font>
+                               <textcolor>grey2</textcolor>
+                       </control>
+                       <control type="label">
+                               <description>UNC value</description>
+                               <posx>600</posx>
+                               <posy>165</posy>
+                               <width>1000</width>
+                               <height>25</height>
+                               <label>$INFO[PVR.ActStreamUNC]</label>
+                               <align>left</align>
+                               <aligny>center</aligny>
+                               <font>font12</font>
+                               <textcolor>white</textcolor>
+                       </control>
+                       <control type="label">
+                               <description>Encryption</description>
+                               <posx>50</posx>
+                               <posy>190</posy>
+                               <width>165</width>
+                               <height>25</height>
+                               <label>$LOCALIZE[19015]:</label>
+                               <align>left</align>
+                               <aligny>center</aligny>
+                               <font>font12</font>
+                               <textcolor>grey2</textcolor>
+                       </control>
+                       <control type="label">
+                               <description>Encryption value</description>
+                               <posx>220</posx>
+                               <posy>190</posy>
+                               <width>1000</width>
+                               <height>25</height>
+                               <label>$INFO[PVR.ActStreamEncryptionName]</label>
+                               <align>left</align>
+                               <aligny>center</aligny>
+                               <font>font12</font>
+                               <textcolor>white</textcolor>
+                       </control>
+               </control>
        </controls>
-</window>
\ No newline at end of file
+</window>
index 2d517f2..f725ddb 100644 (file)
@@ -4,7 +4,9 @@
                <control type="group">
                        <animation effect="slide" start="0,-145" end="0,0" time="300" tween="quadratic" easing="out">WindowOpen</animation>
                        <animation effect="slide" start="0,0" end="0,-145" time="300" delay="150" tween="quadratic" easing="out">WindowClose</animation>
-                       <animation effect="slide" start="0,0" end="0,-145" time="300" tween="quadratic" easing="out" condition="Window.IsVisible(SliderDialog) | Window.IsVisible(OSDVideoSettings) | Window.IsVisible(OSDAudioSettings) | Window.IsVisible(VideoBookmarks)">Conditional</animation>
+                       <animation effect="slide" start="0,0" end="0,-145" time="300" tween="quadratic" easing="out" condition="Window.IsVisible(SliderDialog) | Window.IsVisible(OSDVideoSettings) | Window.IsVisible(OSDAudioSettings) | Window.IsVisible(VideoBookmarks) | Window.IsVisible(PVROSDChannels) | Window.IsVisible(PVROSDGuide)">Conditional</animation>
+                       
+                       <!-- background image -->
                        <control type="image">
                                <description>media info background image</description>
                                <posx>0</posx>
@@ -13,6 +15,9 @@
                                <height>256</height>
                                <texture>MediaInfoBackUpper.png</texture>
                        </control>
+                       
+                       <!-- bookmarks, audio, video -->
+                       <!-- !LiveTV -->
                        <control type="group" id="200">
                                <posx>417</posx>
                                <posy>4</posy>
@@ -30,6 +35,7 @@
                                        <onup>600</onup>
                                        <ondown>600</ondown>
                                        <onclick>ActivateWindow(125)</onclick>
+                                       <visible>!VideoPlayer.Content(LiveTV)</visible>
                                </control>
                                <control type="button" id="701">
                                        <posx>50</posx>
@@ -45,6 +51,7 @@
                                        <onup>600</onup>
                                        <ondown>600</ondown>
                                        <onclick>ActivateWindow(124)</onclick>
+                                       <visible>!VideoPlayer.Content(LiveTV)</visible>
                                </control>
                                <control type="button" id="702">
                                        <posx>100</posx>
                                        <onup>601</onup>
                                        <ondown>601</ondown>
                                        <onclick>ActivateWindow(123)</onclick>
+                                       <visible>!VideoPlayer.Content(LiveTV)</visible>
                                </control>
+                               <visible>!VideoPlayer.Content(LiveTV)</visible>
                        </control>
+                       <!-- LiveTV -->
+                       <control type="group" id="200">
+                               <posx>417</posx>
+                               <posy>4</posy>
+                               <control type="button" id="700">
+                                       <posx>0</posx>
+                                       <posy>0</posy>
+                                       <width>45</width>
+                                       <height>45</height>
+                                       <label>13396</label>
+                                       <font>-</font>
+                                       <texturefocus>OSDAudioFO.png</texturefocus>
+                                       <texturenofocus>OSDAudioNF.png</texturenofocus>
+                                       <onleft>705</onleft>
+                                       <onright>701</onright>
+                                       <onup>100</onup>
+                                       <ondown>100</ondown>
+                                       <onclick>ActivateWindow(124)</onclick>
+                                       <visible>VideoPlayer.Content(LiveTV)</visible>
+                               </control>
+                               <control type="button" id="701">
+                                       <posx>50</posx>
+                                       <posy>0</posy>
+                                       <width>45</width>
+                                       <height>45</height>
+                                       <label>13395</label>
+                                       <font>-</font>
+                                       <texturefocus>OSDVideoFO.png</texturefocus>
+                                       <texturenofocus>OSDVideoNF.png</texturenofocus>
+                                       <onleft>700</onleft>
+                                       <onright>702</onright>
+                                       <onup>100</onup>
+                                       <ondown>100</ondown>
+                                       <onclick>ActivateWindow(123)</onclick>
+                                       <visible>VideoPlayer.Content(LiveTV)</visible>
+                               </control>
+                               <visible>VideoPlayer.Content(LiveTV)</visible>
+                       </control>
+
+                       <!-- separator -->
+                       <!-- !LiveTV -->
                        <control type="image" id="11">
                                <description>separator image</description>
                                <posx>567</posx>
                                <height>1</height>
                                <colordiffuse>66FFFFFF</colordiffuse>
                                <texture>separator2.png</texture>
+                               <visible>!VideoPlayer.Content(LiveTV)</visible>
                        </control>
+                       <!-- LiveTV -->
+                       <control type="image" id="11">
+                               <description>separator image</description>
+                               <posx>517</posx>
+                               <posy>25</posy>
+                               <width>200</width>
+                               <height>1</height>
+                               <colordiffuse>66FFFFFF</colordiffuse>
+                               <texture>separator2.png</texture>
+                               <visible>VideoPlayer.Content(LiveTV)</visible>
+                       </control>
+                       
+                       <!-- subtitles, dvd, record -->
+                       <!-- !LiveTV -->
                        <control type="group" id="201">
                                <posx>718</posx>
                                <posy>4</posy>
-                               <control type="togglebutton" id="703">
+                               <control type="button" id="703">
                                        <posx>0</posx>
                                        <posy>0</posy>
                                        <width>45</width>
                                        <font>-</font>
                                        <texturefocus>OSDSubtitlesFO.png</texturefocus>
                                        <texturenofocus>OSDSubtitlesNF.png</texturenofocus>
-                                       <alttexturefocus>OSDSubtitlesFO.png</alttexturefocus>
-                                       <alttexturenofocus>OSDSubtitlesNF.png</alttexturenofocus>
                                        <onleft>702</onleft>
                                        <onright>704</onright>
                                        <onup>604</onup>
                                        <ondown>604</ondown>
                                        <onclick>Close</onclick>
                                        <onclick>XBMC.RunScript($INFO[Skin.String(SubtitleScript_Path)])</onclick>
-                                       <altclick>Skin.SetAddon(SubtitleScript_Path,xbmc.python.subtitles)</altclick>
-                                       <altclick>Close</altclick>
-                                       <altclick>XBMC.RunScript($INFO[Skin.String(SubtitleScript_Path)])</altclick>
-                                       <usealttexture>IsEmpty(Skin.String(SubtitleScript_Path))</usealttexture>
+                                       <enable>Skin.HasSetting(SubtitleDownload_Enable) + !IsEmpty(Skin.String(SubtitleScript_Path))</enable>
+                                       <animation effect="fade" start="100" end="50" time="100" condition="![Skin.HasSetting(SubtitleDownload_Enable) + !IsEmpty(Skin.String(SubtitleScript_Path))]">Conditional</animation>
+                                       <visible>!VideoPlayer.Content(LiveTV)</visible>
                                </control>
                                <control type="button" id="704">
                                        <posx>50</posx>
                                        <onclick>PlayerControl(ShowVideoMenu)</onclick>
                                        <enable>VideoPlayer.HasMenu</enable>
                                        <animation effect="fade" start="100" end="50" time="100" condition="!VideoPlayer.HasMenu">Conditional</animation>
+                                       <visible>!VideoPlayer.Content(LiveTV)</visible>
                                </control>
                                <control type="button" id="705">
                                        <posx>100</posx>
                                        <onclick>XBMC.PlayerControl(record)</onclick>
                                        <enable>Player.CanRecord</enable>
                                        <animation effect="fade" start="100" end="50" time="100" condition="!Player.CanRecord">Conditional</animation>
+                                       <visible>!VideoPlayer.Content(LiveTV)</visible>
+                               </control>
+                               <visible>!VideoPlayer.Content(LiveTV)</visible>
+                       </control>
+                       <!-- LiveTV -->
+                       <control type="group" id="201">
+                               <posx>718</posx>
+                               <posy>4</posy>
+                               <control type="button" id="703">
+                                       <posx>0</posx>
+                                       <posy>0</posy>
+                                       <width>45</width>
+                                       <height>45</height>
+                                       <label>19019</label>
+                                       <font>-</font>
+                                       <texturefocus>OSDChannelListFO.png</texturefocus>
+                                       <texturenofocus>OSDChannelListNF.png</texturenofocus>
+                                       <onleft>702</onleft>
+                                       <onright>704</onright>
+                                       <onup>602</onup>
+                                       <ondown>602</ondown>
+                                       <onclick>ActivateWindow(PVROSDChannels)</onclick>
+                                       <onclick>Dialog.Close(VideoOSD)</onclick>
+                                       <visible>VideoPlayer.Content(LiveTV)</visible>
+                               </control>
+                               <control type="button" id="704">
+                                       <posx>50</posx>
+                                       <posy>0</posy>
+                                       <width>45</width>
+                                       <height>45</height>
+                                       <label>$LOCALIZE[19029]$INFO[VideoPlayer.ChannelName, - ]</label>
+                                       <font>-</font>
+                                       <texturefocus>OSDepgFO.png</texturefocus>
+                                       <texturenofocus>OSDepgNF.png</texturenofocus>
+                                       <onleft>703</onleft>
+                                       <onright>705</onright>
+                                       <onup>605</onup>
+                                       <ondown>605</ondown>
+                                       <onclick>ActivateWindow(PVROSDGuide)</onclick>
+                                       <visible>VideoPlayer.Content(LiveTV)</visible>
                                </control>
+                               <control type="button" id="705">
+                                       <posx>100</posx>
+                                       <posy>0</posy>
+                                       <width>45</width>
+                                       <height>45</height>
+                                       <label>23050</label>
+                                       <font>-</font>
+                                       <texturefocus>OSDTeleTextFO.png</texturefocus>
+                                       <texturenofocus>OSDTeleTextNF.png</texturenofocus>
+                                       <onleft>704</onleft>
+                                       <onright>700</onright>
+                                       <onup>606</onup>
+                                       <ondown>606</ondown>
+                                       <onclick>ActivateWindow(Teletext)</onclick>
+                                       <visible>VideoPlayer.Content(LiveTV)</visible>
+                               </control>
+                               <visible>VideoPlayer.Content(LiveTV)</visible>
                        </control>
+                                               
+                       <!-- player controls -->
+                       <!-- !LiveTV -->
                        <control type="group" id="100">
                                <posx>490</posx>
                                <posy>40</posy>
                                        <onup>701</onup>
                                        <ondown>701</ondown>
                                        <onclick>PlayerControl(Previous)</onclick>
+                                       <visible>!VideoPlayer.Content(LiveTV)</visible>
                                </control>
                                <control type="button" id="601">
                                        <posx>50</posx>
                                        <onup>702</onup>
                                        <ondown>702</ondown>
                                        <onclick>PlayerControl(Rewind)</onclick>
+                                       <visible>!VideoPlayer.Content(LiveTV)</visible>
                                </control>
                                <control type="togglebutton" id="602">
                                        <posx>100</posx>
                                        <onup>702</onup>
                                        <ondown>702</ondown>
                                        <onclick>PlayerControl(Play)</onclick>
+                                       <visible>!VideoPlayer.Content(LiveTV)</visible>
                                </control>
                                <control type="button" id="603">
                                        <posx>150</posx>
                                        <onup>703</onup>
                                        <ondown>703</ondown>
                                        <onclick>PlayerControl(Stop)</onclick>
+                                       <visible>!VideoPlayer.Content(LiveTV)</visible>
                                </control>
                                <control type="button" id="604">
                                        <posx>200</posx>
                                        <onup>703</onup>
                                        <ondown>703</ondown>
                                        <onclick>PlayerControl(Forward)</onclick>
+                                       <visible>!VideoPlayer.Content(LiveTV)</visible>
                                </control>
                                <control type="button" id="605">
                                        <posx>250</posx>
                                        <onup>704</onup>
                                        <ondown>704</ondown>
                                        <onclick>PlayerControl(Next)</onclick>
+                                       <visible>!VideoPlayer.Content(LiveTV)</visible>
+                               </control>
+                               <visible>!VideoPlayer.Content(LiveTV)</visible>
+                       </control>
+                       <!-- LiveTV -->
+                       <control type="group" id="100">
+                               <posx>440</posx>
+                               <posy>40</posy>
+                               <control type="button" id="600">
+                                       <posx>0</posx>
+                                       <posy>0</posy>
+                                       <width>50</width>
+                                       <height>50</height>
+                                       <label>31354</label>
+                                       <font>-</font>
+                                       <texturefocus>OSDRewindFO.png</texturefocus>
+                                       <texturenofocus>OSDRewindNF.png</texturenofocus>
+                                       <onleft>606</onleft>
+                                       <onright>601</onright>
+                                       <onup>700</onup>
+                                       <ondown>700</ondown>
+                                       <onclick>PlayerControl(Rewind)</onclick>
+                                       <enable>false</enable>
+                                       <animation effect="fade" start="100" end="50" time="100" condition="true">Conditional</animation>
+                                       <visible>VideoPlayer.Content(LiveTV)</visible>
+                               </control>
+                               <control type="togglebutton" id="601">
+                                       <posx>50</posx>
+                                       <posy>0</posy>
+                                       <width>50</width>
+                                       <height>50</height>
+                                       <label>31351</label>
+                                       <altlabel>208</altlabel>
+                                       <font>-</font>
+                                       <texturefocus>OSDPauseFO.png</texturefocus>
+                                       <texturenofocus>OSDPauseNF.png</texturenofocus>
+                                       <usealttexture>Player.Paused | Player.Forwarding | Player.Rewinding</usealttexture>
+                                       <alttexturefocus>OSDPlayFO.png</alttexturefocus>
+                                       <alttexturenofocus>OSDPlayNF.png</alttexturenofocus>
+                                       <onleft>600</onleft>
+                                       <onright>603</onright>
+                                       <onup>700</onup>
+                                       <ondown>700</ondown>
+                                       <onclick>PlayerControl(Play)</onclick>
+                                       <enable>false</enable>
+                                       <animation effect="fade" start="100" end="50" time="100" condition="true">Conditional</animation>
+                                       <visible>VideoPlayer.Content(LiveTV)</visible>
+                               </control>
+                               <control type="button" id="603">
+                                       <posx>100</posx>
+                                       <posy>0</posy>
+                                       <width>50</width>
+                                       <height>50</height>
+                                       <label>31352</label>
+                                       <font>-</font>
+                                       <texturefocus>OSDStopFO.png</texturefocus>
+                                       <texturenofocus>OSDStopNF.png</texturenofocus>
+                                       <onleft>601</onleft>
+                                       <onright>604</onright>
+                                       <onup>700</onup>
+                                       <ondown>700</ondown>
+                                       <onclick>PlayerControl(Stop)</onclick>
+                                       <visible>VideoPlayer.Content(LiveTV)</visible>
+                               </control>
+                               <control type="button" id="604">
+                                       <posx>150</posx>
+                                       <posy>0</posy>
+                                       <width>50</width>
+                                       <height>50</height>
+                                       <label>31353</label>
+                                       <font>-</font>
+                                       <texturefocus>OSDForwardFO.png</texturefocus>
+                                       <texturenofocus>OSDForwardNF.png</texturenofocus>
+                                       <onleft>603</onleft>
+                                       <onright>602</onright>
+                                       <onup>70</onup>
+                                       <ondown>700</ondown>
+                                       <onclick>PlayerControl(Forward)</onclick>
+                                       <enable>false</enable>
+                                       <animation effect="fade" start="100" end="50" time="100" condition="true">Conditional</animation>
+                                       <visible>VideoPlayer.Content(LiveTV)</visible>
+                               </control>
+                               <control type="button" id="602">
+                                       <posx>250</posx>
+                                       <posy>0</posy>
+                                       <width>50</width>
+                                       <height>50</height>
+                                       <label>210</label>
+                                       <font>-</font>
+                                       <texturefocus>OSDChannelUPFO.png</texturefocus>
+                                       <texturenofocus>OSDChannelUPNF.png</texturenofocus>
+                                       <onleft>604</onleft>
+                                       <onright>605</onright>
+                                       <onup>703</onup>
+                                       <ondown>703</ondown>
+                                       <onclick>PlayerControl(Previous)</onclick>
+                                       <visible>VideoPlayer.Content(LiveTV)</visible>
+                               </control>
+                               <control type="button" id="605">
+                                       <posx>300</posx>
+                                       <posy>0</posy>
+                                       <width>50</width>
+                                       <height>50</height>
+                                       <label>209</label>
+                                       <font>-</font>
+                                       <texturefocus>OSDChannelDownFO.png</texturefocus>
+                                       <texturenofocus>OSDChannelDownNF.png</texturenofocus>
+                                       <onleft>602</onleft>
+                                       <onright>606</onright>
+                                       <onup>704</onup>
+                                       <ondown>704</ondown>
+                                       <onclick>PlayerControl(Next)</onclick>
+                                       <visible>VideoPlayer.Content(LiveTV)</visible>
+                               </control>
+                               <control type="button" id="606">
+                                       <posx>350</posx>
+                                       <posy>0</posy>
+                                       <width>50</width>
+                                       <height>50</height>
+                                       <label>264</label>
+                                       <font>-</font>
+                                       <texturefocus>OSDRecordFO.png</texturefocus>
+                                       <texturenofocus>OSDRecordNF.png</texturenofocus>
+                                       <onleft>605</onleft>
+                                       <onright>600</onright>
+                                       <onup>705</onup>
+                                       <ondown>705</ondown>
+                                       <onclick>XBMC.PlayerControl(record)</onclick>
+                                       <enable>Player.CanRecord</enable>
+                                       <animation effect="fade" start="100" end="50" time="100" condition="!Player.CanRecord">Conditional</animation>
+                                       <visible>VideoPlayer.Content(LiveTV) + Player.CanRecord + !Player.Recording</visible>
+                               </control>
+                               <control type="button" id="606">
+                                       <posx>350</posx>
+                                       <posy>0</posy>
+                                       <width>50</width>
+                                       <height>50</height>
+                                       <label>264</label>
+                                       <font>-</font>
+                                       <texturefocus>OSDRecord2.png</texturefocus>
+                                       <texturenofocus>OSDRecordNF2.png</texturenofocus>
+                                       <onleft>605</onleft>
+                                       <onright>600</onright>
+                                       <onup>705</onup>
+                                       <ondown>705</ondown>
+                                       <onclick>XBMC.PlayerControl(record)</onclick>
+                                       <animation effect="fade" start="100" end="50" time="100" condition="!Player.CanRecord">Conditional</animation>
+                                       <visible>VideoPlayer.Content(LiveTV) + Player.CanRecord + Player.Recording</visible>
                                </control>
+                               <visible>VideoPlayer.Content(LiveTV)</visible>
                        </control>
+                       
+                       <!-- play status -->
                        <control type="label">
                                <posx>20</posx>
                                <posy>60</posy>
                                <shadowcolor>black</shadowcolor>
                                <visible>Window.IsTopmost(VideoOSD)</visible>
                        </control>
+                       
+                       <!-- timer -->
                        <control type="label">
                                <posx>1260</posx>
                                <posy>60</posy>
                                <textcolor>grey</textcolor>
                                <shadowcolor>black</shadowcolor>
                        </control>
+                       
+                       <!-- window title -->
                        <control type="group">
                                <posx>0</posx>
                                <posy>0</posy>
                                        <label>31141</label>
                                </control>
                        </control>
+                       
+                       <!-- clock -->
                        <include>Clock</include>
                </control>
        </controls>
-</window>
\ No newline at end of file
+</window>
+
index a58e74b..7744b0b 100644 (file)
                                                                <icon>special://skin/backgrounds/videos.jpg</icon>
                                                                <thumb>$INFO[Skin.String(Home_Custom_Back_Video_Folder)]</thumb>
                                                        </item>
+                                                       <item id="12">
+                                                               <label>31502</label>
+                                                               <onclick>-</onclick>
+                                                               <icon>special://skin/backgrounds/tv.jpg</icon>
+                                                               <thumb>$INFO[Skin.String(Home_Custom_Back_TV_Folder)]</thumb>
+                                                               <visible>System.GetBool(pvrmanager.enabled)</visible>
+                                                       </item>
                                                        <item id="10">
                                                                <label>20342</label>
                                                                <onclick>-</onclick>
                                                                <ondown>304</ondown>
                                                        </control>
                                                </control>
+                                               <control type="group">
+                                                       <visible>Container(9003).HasFocus(12)</visible>
+                                                       <control type="button" id="300">
+                                                               <description>Single Image button</description>
+                                                               <posx>5</posx>
+                                                               <posy>0</posy>
+                                                               <width>180</width>
+                                                               <height>40</height>
+                                                               <label>31113</label>
+                                                               <font>font12_title</font>
+                                                               <textcolor>grey2</textcolor>
+                                                               <focusedcolor>white</focusedcolor>
+                                                               <align>center</align>
+                                                               <texturenofocus border="5">button-nofocus.png</texturenofocus>
+                                                               <texturefocus border="5">button-focus.png</texturefocus>
+                                                               <onclick>Skin.SetImage(Home_Custom_Back_TV_Folder)</onclick>
+                                                               <onleft>302</onleft>
+                                                               <onright>301</onright>
+                                                               <onup>304</onup>
+                                                               <ondown>304</ondown>
+                                                       </control>
+                                                       <control type="button" id="301">
+                                                               <description>Multi Image button</description>
+                                                               <posx>190</posx>
+                                                               <posy>0</posy>
+                                                               <width>180</width>
+                                                               <height>40</height>
+                                                               <label>31114</label>
+                                                               <font>font12_title</font>
+                                                               <textcolor>grey2</textcolor>
+                                                               <focusedcolor>white</focusedcolor>
+                                                               <align>center</align>
+                                                               <texturenofocus border="5">button-nofocus.png</texturenofocus>
+                                                               <texturefocus border="5">button-focus.png</texturefocus>
+                                                               <onclick>Skin.SetPath(Home_Custom_Back_TV_Folder)</onclick>
+                                                               <onleft>300</onleft>
+                                                               <onright>302</onright>
+                                                               <onup>304</onup>
+                                                               <ondown>304</ondown>
+                                                       </control>
+                                                       <control type="button" id="302">
+                                                               <description>Default Image button</description>
+                                                               <posx>375</posx>
+                                                               <posy>0</posy>
+                                                               <width>180</width>
+                                                               <height>40</height>
+                                                               <label>571</label>
+                                                               <font>font12_title</font>
+                                                               <textcolor>grey2</textcolor>
+                                                               <focusedcolor>white</focusedcolor>
+                                                               <align>center</align>
+                                                               <texturenofocus border="5">button-nofocus.png</texturenofocus>
+                                                               <texturefocus border="5">button-focus.png</texturefocus>
+                                                               <onclick>Skin.Reset(Home_Custom_Back_TV_Folder)</onclick>
+                                                               <onleft>301</onleft>
+                                                               <onright>300</onright>
+                                                               <onup>304</onup>
+                                                               <ondown>304</ondown>
+                                                       </control>
+                                               </control>
                                        </control>
                                        <control type="multiimage">
                                                <posx>95</posx>
index 5c617c9..6b980db 100644 (file)
                <disabledcolor>grey3</disabledcolor>
                <textoffsetx>7</textoffsetx>
                <aligny>center</aligny>
+               <pulseonselect>no</pulseonselect>
        </default>
        <default type="selectbutton">
                <posx>490</posx>
index d6d17d0..05e000d 100644 (file)
                        <include>Window_OpenClose_Animation</include>
                </control>
        </include>
+       <include name="CommonTVBackground">
+               <control type="multiimage">
+                       <posx>0</posx>
+                       <posy>0</posy>
+                       <width>1280</width>
+                       <height>720</height>
+                       <imagepath fallback="special://skin/backgrounds/tv.jpg" background="true">$INFO[Skin.String(Home_Custom_Back_TV_Folder)]</imagepath>
+                       <timeperimage>10000</timeperimage>
+                       <randomize>true</randomize>
+                       <fadetime>1000</fadetime>
+               </control>
+       </include>
        <include name="CommonMusicBackground">
                <control type="multiimage">
                        <posx>0</posx>
                        <font>font12_title</font>
                        <textcolor>grey2</textcolor>
                        <shadowcolor>black</shadowcolor>
-                       <visible>!videoplayer.content(episodes) + !videoplayer.content(musicvideos)</visible>
+                       <visible>!videoplayer.content(episodes) + !videoplayer.content(musicvideos) + !videoplayer.content(LiveTV)</visible>
                </control>
                <control type="label">
                        <description>TV Show Title label</description>
                        <pauseatend>2000</pauseatend>
                </control>
                <control type="fadelabel">
+                       <description>Channel label</description>
+                       <posx>160</posx>
+                       <posy>20</posy>
+                       <height>30</height>
+                       <width>325</width>
+                       <label>$INFO[VideoPlayer.ChannelName]</label>
+                       <align>left</align>
+                       <aligny>center</aligny>
+                       <font>font12_title</font>
+                       <textcolor>white</textcolor>
+                       <shadowcolor>black</shadowcolor>
+                       <visible>videoplayer.content(LiveTV)</visible>
+                       <scrollout>false</scrollout>
+                       <pauseatend>2000</pauseatend>
+               </control>
+               <control type="fadelabel">
                        <description>Title label</description>
                        <posx>160</posx>
                        <posy>43</posy>
                        <aligny>center</aligny>
                        <label>31023</label>
                </control>
+               <control type="group">
+                       <posx>12</posx>
+                       <posy>40</posy>
+                       <visible>VideoPlayer.Content(LiveTV)</visible>
+                       <control type="button" id="600">
+                               <posx>20</posx>
+                               <posy>2</posy>
+                               <width>39</width>
+                               <height>39</height>
+                               <label>-</label>
+                               <texturefocus>OSDChannelUPFO.png</texturefocus>
+                               <texturenofocus>OSDChannelUPNF.png</texturenofocus>
+                               <onleft>50</onleft>
+                               <onright>606</onright>
+                               <onup>610</onup>
+                               <ondown>608</ondown>
+                               <onclick>XBMC.PlayerControl(Previous)</onclick>
+                       </control>
+                       <control type="button" id="601">
+                               <posx>60</posx>
+                               <posy>2</posy>
+                               <width>39</width>
+                               <height>39</height>
+                               <label>-</label>
+                               <texturefocus>OSDChannelDownFO.png</texturefocus>
+                               <texturenofocus>OSDChannelDownNF.png</texturenofocus>
+                               <onleft>600</onleft>
+                               <onright>603</onright>
+                               <onup>610</onup>
+                               <ondown>608</ondown>
+                               <onclick>XBMC.PlayerControl(Next)</onclick>
+                       </control>
+                       <control type="button" id="603">
+                               <posx>100</posx>
+                               <posy>2</posy>
+                               <width>39</width>
+                               <height>39</height>
+                               <label>-</label>
+                               <texturefocus>OSDStopFO.png</texturefocus>
+                               <texturenofocus>OSDStopNF.png</texturenofocus>
+                               <onleft>601</onleft>
+                               <onright>604</onright>
+                               <onup>610</onup>
+                               <ondown>608</ondown>
+                               <onclick>down</onclick>
+                               <onclick>XBMC.PlayerControl(Stop)</onclick>
+                       </control>
+                       <control type="button" id="604">
+                               <posx>180</posx>
+                               <posy>2</posy>
+                               <width>39</width>
+                               <height>39</height>
+                               <label>-</label>
+                               <texturefocus>OSDRecordFO.png</texturefocus>
+                               <texturenofocus>OSDRecordNF.png</texturenofocus>
+                               <onleft>603</onleft>
+                               <onright>50</onright>
+                               <onup>610</onup>
+                               <ondown>608</ondown>
+                               <onclick>XBMC.PlayerControl(record)</onclick>
+                               <enable>Player.CanRecord</enable>
+                               <animation effect="fade" start="100" end="30" time="100" condition="!Player.CanRecord">Conditional</animation>
+                       </control>
+               </control>
                <control type="group" id="9005">
                        <posx>12</posx>
                        <posy>40</posy>
+                       <visible>!VideoPlayer.Content(LiveTV)</visible>
                        <control type="button" id="600">
                                <posx>0</posx>
                                <posy>2</posy>
diff --git a/addons/skin.confluence/backgrounds/tv.jpg b/addons/skin.confluence/backgrounds/tv.jpg
new file mode 100644 (file)
index 0000000..9414690
Binary files /dev/null and b/addons/skin.confluence/backgrounds/tv.jpg differ
index 4894ba1..0bddbc6 100644 (file)
   <string id="31406">[B]CONFIGURE SYSTEM SETTINGS[/B][CR][CR]Setup and calibrate displays · Configure audio output · Setup remote controls[CR]Set power saving options · Enable debugging · Setup master lock</string>
   <string id="31407">[B]CONFIGURE SKIN SETTINGS[/B][CR][CR]Setup the Confluence skin · Add and remove home menu items[CR]Change skin backgrounds</string>
   <string id="31408">[B]CONFIGURE ADD-ONS[/B][CR][CR]Manage your installed Add-ons · Browse for and install Add-ons from xbmc.org[CR]Modify Add-on settings</string>
+  <string id="31409">[B]CONFIGURE TV SETTINGS[/B][CR][CR]Change fullscreen info · Manage EPG data settings</string>
 
   <string id="31421">Select your XBMC user Profile[CR]to login and continue</string>
+
+  <!-- Extra Unified PVR labels -->
+  <string id="31500">Recording Timers</string>
+  <string id="31501">Scheduled Time</string>
+  <string id="31502">Live TV</string>
+  <string id="31503">Add Group</string>
+  <string id="31504">Rename Group</string>
+  <string id="31505">Delete Group</string>
+  <string id="31506">Available[CR]Groups</string>
+  <string id="31507">Ungrouped[CR]Channels</string>
+  <string id="31508">Channels in Group</string>
+  <string id="31509">Channel Group</string>
+  <string id="31510">Timer Set</string>
 </strings>
index 33edf18..33ec2ee 100644 (file)
   <string id="31405">[B]KONFIGURIERE NETZWERK EINSTELLUNGEN[/B][CR][CR]Einrichten der Steuerung von XBMC via UPnP und HTTP · Konfiguriere Datei Zugriff[CR]Setze Internet Zugriffs Optionen</string>
   <string id="31406">[B]KONFIGURIERE SYSTEM EINSTELLUNGEN[/B][CR][CR]Setze und kalibriere Displays · Konfiguriere Audioausgabe · Setze Fernbedienungs Einstellungen · Setze Energiespar Optionen · Aktiviere Debugging · Einrichten Master Sperre</string>
   <string id="31407">[B]KONFIGURIERE SKIN EINSTELLUNGEN[/B][CR][CR]Einrichten des Confluence Skin · Hinzufügen und entfernen der Home Menü Einträge[CR]Wechseln der Skin Hintergründe</string>
-  <string id="31408">[B]KONFIGURIERE ADD-ONS[/B][CR][CR]Organisiere die installierten Add-ons · Installiere Add-ons von xbmc.org[CR]Add-on Einstellungen anpassen</string>
+  <string id="31408">[B]KONFIGURIERE ERWEITERUNGEN[/B][CR][CR]Organisiere die installierten Add-ons · Installiere Add-ons von xbmc.org[CR]Add-on Einstellungen anpassen</string>
+  <string id="31409">[B]KONFIGURIERE TV EINSTELLUNGEN[/B][CR][CR]Verändere Vollbild Modus · Verwalte EPG Daten Einstellungen</string>
+
   <string id="31421">Wähle Dein XBMC Benutzer Profil[CR]Zum Anmelden und Weitermachen</string>
+
+  <!-- Extra Unified PVR labels -->
+  <string id="31500">Recording Timers</string>
+  <string id="31501">Scheduled Time</string>
+  <string id="31502">Live TV</string>
+  <string id="31503">Gruppe hinzufügen</string>
+  <string id="31504">Gruppe umbenennen</string>
+  <string id="31505">Gruppe löschen</string>
+  <string id="31506">Verfügbare[CR]Gruppen</string>
+  <string id="31507">Ungruppierte[CR]Kanäle</string>
+  <string id="31508">Kanäle in Gruppe</string>
+  <string id="31509">Kanalgruppe</string>
+  <string id="31510">Timer Set</string>
 </strings>
index d8aaf26..35eb7de 100644 (file)
@@ -241,6 +241,7 @@ IMAGES= \
        OSDRandomOnFO.png \
        OSDRandomOnNF.png \
        OSDRecord2.png \
+       OSDRecordNF2.png \
        OSDRecordFO.png \
        OSDRecordNF.png \
        OSDRecordOff.png \
diff --git a/addons/skin.confluence/media/OSDChannelDownFO.png b/addons/skin.confluence/media/OSDChannelDownFO.png
new file mode 100644 (file)
index 0000000..5116c32
Binary files /dev/null and b/addons/skin.confluence/media/OSDChannelDownFO.png differ
diff --git a/addons/skin.confluence/media/OSDChannelDownNF.png b/addons/skin.confluence/media/OSDChannelDownNF.png
new file mode 100644 (file)
index 0000000..6795c43
Binary files /dev/null and b/addons/skin.confluence/media/OSDChannelDownNF.png differ
diff --git a/addons/skin.confluence/media/OSDChannelListFO.png b/addons/skin.confluence/media/OSDChannelListFO.png
new file mode 100644 (file)
index 0000000..a08bc13
Binary files /dev/null and b/addons/skin.confluence/media/OSDChannelListFO.png differ
diff --git a/addons/skin.confluence/media/OSDChannelListNF.png b/addons/skin.confluence/media/OSDChannelListNF.png
new file mode 100644 (file)
index 0000000..8339fdc
Binary files /dev/null and b/addons/skin.confluence/media/OSDChannelListNF.png differ
diff --git a/addons/skin.confluence/media/OSDChannelUPFO.png b/addons/skin.confluence/media/OSDChannelUPFO.png
new file mode 100644 (file)
index 0000000..a3e6dba
Binary files /dev/null and b/addons/skin.confluence/media/OSDChannelUPFO.png differ
diff --git a/addons/skin.confluence/media/OSDChannelUPNF.png b/addons/skin.confluence/media/OSDChannelUPNF.png
new file mode 100644 (file)
index 0000000..47e6e33
Binary files /dev/null and b/addons/skin.confluence/media/OSDChannelUPNF.png differ
diff --git a/addons/skin.confluence/media/OSDRecordNF2.png b/addons/skin.confluence/media/OSDRecordNF2.png
new file mode 100644 (file)
index 0000000..10060e7
Binary files /dev/null and b/addons/skin.confluence/media/OSDRecordNF2.png differ
diff --git a/addons/skin.confluence/media/OSDTeleTextFO.png b/addons/skin.confluence/media/OSDTeleTextFO.png
new file mode 100644 (file)
index 0000000..53eb576
Binary files /dev/null and b/addons/skin.confluence/media/OSDTeleTextFO.png differ
diff --git a/addons/skin.confluence/media/OSDTeleTextNF.png b/addons/skin.confluence/media/OSDTeleTextNF.png
new file mode 100644 (file)
index 0000000..111c068
Binary files /dev/null and b/addons/skin.confluence/media/OSDTeleTextNF.png differ
diff --git a/addons/skin.confluence/media/OSDepgFO.png b/addons/skin.confluence/media/OSDepgFO.png
new file mode 100644 (file)
index 0000000..141f7ad
Binary files /dev/null and b/addons/skin.confluence/media/OSDepgFO.png differ
diff --git a/addons/skin.confluence/media/OSDepgNF.png b/addons/skin.confluence/media/OSDepgNF.png
new file mode 100644 (file)
index 0000000..cf9a86b
Binary files /dev/null and b/addons/skin.confluence/media/OSDepgNF.png differ
diff --git a/addons/skin.confluence/media/PVR-HasTimer.png b/addons/skin.confluence/media/PVR-HasTimer.png
new file mode 100644 (file)
index 0000000..99c2ee9
Binary files /dev/null and b/addons/skin.confluence/media/PVR-HasTimer.png differ
diff --git a/addons/skin.confluence/media/PVR-IsRecording.png b/addons/skin.confluence/media/PVR-IsRecording.png
new file mode 100644 (file)
index 0000000..e64b346
Binary files /dev/null and b/addons/skin.confluence/media/PVR-IsRecording.png differ
index 17e5052..0cbcd83 100644 (file)
Binary files a/addons/skin.confluence/media/StackNF.png and b/addons/skin.confluence/media/StackNF.png differ
diff --git a/addons/skin.confluence/media/epg-genres/0.png b/addons/skin.confluence/media/epg-genres/0.png
new file mode 100644 (file)
index 0000000..80c6192
Binary files /dev/null and b/addons/skin.confluence/media/epg-genres/0.png differ
diff --git a/addons/skin.confluence/media/epg-genres/112.png b/addons/skin.confluence/media/epg-genres/112.png
new file mode 100644 (file)
index 0000000..7701fc6
Binary files /dev/null and b/addons/skin.confluence/media/epg-genres/112.png differ
diff --git a/addons/skin.confluence/media/epg-genres/128.png b/addons/skin.confluence/media/epg-genres/128.png
new file mode 100644 (file)
index 0000000..3071d4e
Binary files /dev/null and b/addons/skin.confluence/media/epg-genres/128.png differ
diff --git a/addons/skin.confluence/media/epg-genres/144.png b/addons/skin.confluence/media/epg-genres/144.png
new file mode 100644 (file)
index 0000000..a9325a6
Binary files /dev/null and b/addons/skin.confluence/media/epg-genres/144.png differ
diff --git a/addons/skin.confluence/media/epg-genres/16.png b/addons/skin.confluence/media/epg-genres/16.png
new file mode 100644 (file)
index 0000000..ffaae9e
Binary files /dev/null and b/addons/skin.confluence/media/epg-genres/16.png differ
diff --git a/addons/skin.confluence/media/epg-genres/160.png b/addons/skin.confluence/media/epg-genres/160.png
new file mode 100644 (file)
index 0000000..8d825ab
Binary files /dev/null and b/addons/skin.confluence/media/epg-genres/160.png differ
diff --git a/addons/skin.confluence/media/epg-genres/176.png b/addons/skin.confluence/media/epg-genres/176.png
new file mode 100644 (file)
index 0000000..7ae6320
Binary files /dev/null and b/addons/skin.confluence/media/epg-genres/176.png differ
diff --git a/addons/skin.confluence/media/epg-genres/192.png b/addons/skin.confluence/media/epg-genres/192.png
new file mode 100644 (file)
index 0000000..80c6192
Binary files /dev/null and b/addons/skin.confluence/media/epg-genres/192.png differ
diff --git a/addons/skin.confluence/media/epg-genres/208.png b/addons/skin.confluence/media/epg-genres/208.png
new file mode 100644 (file)
index 0000000..80c6192
Binary files /dev/null and b/addons/skin.confluence/media/epg-genres/208.png differ
diff --git a/addons/skin.confluence/media/epg-genres/224.png b/addons/skin.confluence/media/epg-genres/224.png
new file mode 100644 (file)
index 0000000..80c6192
Binary files /dev/null and b/addons/skin.confluence/media/epg-genres/224.png differ
diff --git a/addons/skin.confluence/media/epg-genres/240.png b/addons/skin.confluence/media/epg-genres/240.png
new file mode 100644 (file)
index 0000000..80c6192
Binary files /dev/null and b/addons/skin.confluence/media/epg-genres/240.png differ
diff --git a/addons/skin.confluence/media/epg-genres/32.png b/addons/skin.confluence/media/epg-genres/32.png
new file mode 100644 (file)
index 0000000..be2bc8e
Binary files /dev/null and b/addons/skin.confluence/media/epg-genres/32.png differ
diff --git a/addons/skin.confluence/media/epg-genres/48.png b/addons/skin.confluence/media/epg-genres/48.png
new file mode 100644 (file)
index 0000000..57b4dee
Binary files /dev/null and b/addons/skin.confluence/media/epg-genres/48.png differ
diff --git a/addons/skin.confluence/media/epg-genres/64.png b/addons/skin.confluence/media/epg-genres/64.png
new file mode 100644 (file)
index 0000000..d85eb05
Binary files /dev/null and b/addons/skin.confluence/media/epg-genres/64.png differ
diff --git a/addons/skin.confluence/media/epg-genres/80.png b/addons/skin.confluence/media/epg-genres/80.png
new file mode 100644 (file)
index 0000000..cdd6da3
Binary files /dev/null and b/addons/skin.confluence/media/epg-genres/80.png differ
diff --git a/addons/skin.confluence/media/epg-genres/96.png b/addons/skin.confluence/media/epg-genres/96.png
new file mode 100644 (file)
index 0000000..ba92ae3
Binary files /dev/null and b/addons/skin.confluence/media/epg-genres/96.png differ
diff --git a/addons/skin.confluence/media/epg-genres/genre-numbers.txt b/addons/skin.confluence/media/epg-genres/genre-numbers.txt
new file mode 100644 (file)
index 0000000..031732b
--- /dev/null
@@ -0,0 +1,18 @@
+Genre Numbers set internally by XBMC
+
+0 = other/unknown
+16 = moviedrama
+32 = news
+48 = show
+64 = sports
+80 = child
+96 = music
+112 = arts
+128 = social
+144 = science
+160 = hobby
+176 = special
+192 = other/unknown
+208 = other/unknown
+224 = other/unknown
+240 = other/unknown
index e7edf7e..7ec2ede 100644 (file)
@@ -1421,6 +1421,15 @@ OUTPUT_FILES="Makefile \
     xbmc/visualizations/WaveForm/Makefile \
     xbmc/visualizations/iTunes/Makefile \
     xbmc/FileSystem/Makefile \
+    xbmc/pvrclients/Makefile.include \
+    xbmc/pvrclients/MediaPortal/Makefile \
+    xbmc/pvrclients/mythtv/Makefile \
+    xbmc/pvrclients/tvheadend/Makefile \
+    xbmc/pvrclients/vdr-streamdev/Makefile \
+    xbmc/pvrclients/vdr-vnsi/Makefile \
+    lib/addons/library.xbmc.addon/Makefile \
+    lib/addons/library.xbmc.gui/Makefile \
+    lib/addons/library.xbmc.pvr/Makefile \
     tools/Linux/xbmc.sh \
     tools/Linux/xbmc-standalone.sh \
     tools/TexturePacker/Makefile \
index cd335cc..3d63388 100644 (file)
@@ -251,6 +251,7 @@ public:
     GUICONTAINER_LIST,
     GUICONTAINER_WRAPLIST,
     GUICONTAINER_FIXEDLIST,
+    GUICONTAINER_EPGGRID,
     GUICONTAINER_PANEL
   };
   GUICONTROLTYPES GetControlType() const { return ControlType; }
index 63bac83..c28893b 100644 (file)
@@ -50,6 +50,8 @@
 #include "GUIListContainer.h"
 #include "GUIFixedListContainer.h"
 #include "GUIWrappingListContainer.h"
+#include "GUIEPGGridContainer.h"
+#include "GUIEPGGridContainer.h"
 #include "GUIPanelContainer.h"
 #include "GUIMultiSelectText.h"
 #include "GUIListLabel.h"
@@ -108,6 +110,7 @@ static const ControlMapping controls[] =
     {"list",              CGUIControl::GUICONTAINER_LIST},
     {"wraplist",          CGUIControl::GUICONTAINER_WRAPLIST},
     {"fixedlist",         CGUIControl::GUICONTAINER_FIXEDLIST},
+    {"epggrid",           CGUIControl::GUICONTAINER_EPGGRID},
     {"panel",             CGUIControl::GUICONTAINER_PANEL}};
 
 CGUIControl::GUICONTROLTYPES CGUIControlFactory::TranslateControlType(const CStdString &type)
@@ -713,6 +716,8 @@ CGUIControl* CGUIControlFactory::Create(int parentID, const CRect &rect, TiXmlEl
 
   int focusPosition = 0;
   int scrollTime = 200;
+  int timeBlocks = 36;
+  int rulerUnit = 12;
   bool useControlCoords = false;
   bool renderFocusedLast = false;
 
@@ -933,6 +938,8 @@ CGUIControl* CGUIControlFactory::Create(int parentID, const CRect &rect, TiXmlEl
   GetAspectRatio(pControlNode, "aspectratio", aspect);
   XMLUtils::GetBoolean(pControlNode, "scroll", bScrollLabel);
   XMLUtils::GetBoolean(pControlNode,"pulseonselect", bPulse);
+  XMLUtils::GetInt(pControlNode, "timeblocks", timeBlocks);
+  XMLUtils::GetInt(pControlNode, "rulerunit", rulerUnit);
 
   GetInfoTexture(pControlNode, "imagepath", texture, texturePath);
 
@@ -1262,6 +1269,12 @@ CGUIControl* CGUIControlFactory::Create(int parentID, const CRect &rect, TiXmlEl
     ((CGUIWrappingListContainer *)control)->SetPageControl(pageControl);
     ((CGUIWrappingListContainer *)control)->SetRenderOffset(offset);
   }
+  else if (type == CGUIControl::GUICONTAINER_EPGGRID)
+  {
+    control = new CGUIEPGGridContainer(parentID, id, posX, posY, width, height, orientation, scrollTime, preloadItems, timeBlocks, rulerUnit);
+    ((CGUIEPGGridContainer *)control)->LoadLayout(pControlNode);
+//    ((CGUIEPGGridContainer *)control)->LoadContent(pControlNode); ///
+  }
   else if (type == CGUIControl::GUICONTAINER_FIXEDLIST)
   {
     control = new CGUIFixedListContainer(parentID, id, posX, posY, width, height, orientation, scrollTime, preloadItems, focusPosition, iMovementRange);
diff --git a/guilib/GUIEPGGridContainer.cpp b/guilib/GUIEPGGridContainer.cpp
new file mode 100644 (file)
index 0000000..67407a3
--- /dev/null
@@ -0,0 +1,1750 @@
+/*
+*      Copyright (C) 2005-2008 Team XBMC
+*      http://www.xbmc.org
+*
+*  This Program is free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2, or (at your option)
+*  any later version.
+*
+*  This Program is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+*  GNU General Public License for more details.
+*
+*  You should have received a copy of the GNU General Public License
+*  along with XBMC; see the file COPYING.  If not, write to
+*  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+*  http://www.gnu.org/copyleft/gpl.html
+*
+*/
+
+#include "Key.h"
+#include "GUIEPGGridContainer.h"
+#include "GUIControlFactory.h"
+#include "GUIListItem.h"
+#include "GUIFontManager.h"
+#include "utils/log.h"
+#include "utils/TimeUtils.h"
+#include "utils/GUIInfoManager.h"
+#include "LocalizeStrings.h"
+
+#include "pvr/PVREpgInfoTag.h"
+
+#define SHORTGAP     5 // how many blocks is considered a short-gap in nav logic
+#define MINSPERBLOCK 5 /// would be nice to offer zooming of busy schedules /// performance cost to increase resolution 5 fold?
+#define BLOCKJUMP    4 // how many blocks are jumped with each analogue scroll action
+
+CGUIEPGGridContainer::CGUIEPGGridContainer(int parentID, int controlID, float posX, float posY, float width,
+                                           float height, ORIENTATION orientation, int scrollTime,
+                                           int preloadItems, int timeBlocks, int rulerUnit)
+    : CGUIControl(parentID, controlID, posX, posY, width, height)
+{
+  ControlType             = GUICONTAINER_EPGGRID;
+  m_blocksPerPage         = timeBlocks;
+  m_rulerUnit             = rulerUnit;
+  m_channelCursor         = 0;
+  m_blockCursor           = 0;
+  m_channelOffset         = 0;
+  m_blockOffset           = 0;
+  m_channelScrollOffset   = 0;
+  m_channelScrollSpeed    = 0;
+  m_channelScrollLastTime = 0;
+  m_programmeScrollOffset = 0;
+  m_programmeScrollSpeed  = 0;
+  m_programmeScrollLastTime  = 0;
+  m_scrollTime            = scrollTime ? scrollTime : 1;
+  m_renderTime            = 0;
+  m_item                  = NULL;
+  m_lastItem              = NULL;
+  m_lastChannel           = NULL;
+  m_channelWrapAround     = true; /// get from settings?
+  m_orientation           = orientation;
+  m_programmeLayout       = NULL;
+  m_focusedProgrammeLayout= NULL;
+  m_channelLayout         = NULL;
+  m_focusedChannelLayout  = NULL;
+  m_rulerLayout           = NULL;
+  m_rulerPosX             = 0;
+  m_rulerPosY             = 0;
+  m_rulerHeight           = 0;
+  m_rulerWidth            = 0;
+  m_channelPosX           = 0;
+  m_channelPosY           = 0;
+  m_channelHeight         = 0;
+  m_channelWidth          = 0;
+  m_gridPosX              = 0;
+  m_gridPosY              = 0;
+  m_gridWidth             = 0;
+  m_gridHeight            = 0;
+  m_blockSize             = 0;
+  m_analogScrollCount     = 0;
+  m_cacheChannelItems     = preloadItems;
+  m_cacheRulerItems       = preloadItems;
+  m_cacheProgrammeItems   = preloadItems;
+  m_gridIndex             = NULL;
+}
+
+CGUIEPGGridContainer::~CGUIEPGGridContainer(void)
+{
+}
+
+void CGUIEPGGridContainer::Render()
+{
+  ValidateOffset();
+
+  if (m_bInvalidated)
+    UpdateLayout();
+
+  if (!m_focusedChannelLayout || !m_channelLayout || !m_rulerLayout || !m_focusedProgrammeLayout || !m_programmeLayout)
+    return;
+
+  UpdateScrollOffset();
+
+  int chanOffset  = (int)floorf(m_channelScrollOffset / m_programmeLayout->Size(m_orientation));
+  int blockOffset = (int)floorf(m_programmeScrollOffset / m_blockSize);
+  int rulerOffset = (int)floorf(m_programmeScrollOffset / m_blockSize);
+
+  /// Render channel names
+  int cacheBeforeChannel, cacheAfterChannel;
+  GetChannelCacheOffsets(cacheBeforeChannel, cacheAfterChannel);
+
+  // Free memory not used on screen
+  if ((int)m_channelItems.size() > m_channelsPerPage + cacheBeforeChannel + cacheAfterChannel)
+    FreeChannelMemory(CorrectOffset(chanOffset - cacheBeforeChannel, 0), CorrectOffset(chanOffset + m_channelsPerPage + 1 + cacheAfterChannel, 0));
+
+  if (m_orientation == VERTICAL)
+    g_graphicsContext.SetClipRegion(m_channelPosX, m_channelPosY, m_channelWidth, m_gridHeight);
+  else
+    g_graphicsContext.SetClipRegion(m_channelPosX, m_channelPosY, m_gridWidth, m_channelHeight);
+
+  CPoint originChannel = CPoint(m_channelPosX, m_channelPosY) + m_renderOffset;
+  float pos = (m_orientation == VERTICAL) ? originChannel.y : originChannel.x;
+  float end = (m_orientation == VERTICAL) ? m_posY + m_height : m_posX + m_width;
+
+  // we offset our draw position to take into account scrolling and whether or not our focused
+  // item is offscreen "above" the list.
+  float drawOffset = (chanOffset - cacheBeforeChannel) * m_channelLayout->Size(m_orientation) - m_channelScrollOffset;
+  if (m_channelOffset + m_channelCursor < chanOffset)
+    drawOffset += m_focusedChannelLayout->Size(m_orientation) - m_channelLayout->Size(m_orientation);
+  pos += drawOffset;
+  end += cacheAfterChannel * m_channelLayout->Size(m_orientation);
+
+  float focusedPos = 0;
+  CGUIListItemPtr focusedItem;
+  int current = chanOffset;// - cacheBeforeChannel;
+  while (pos < end && (int)m_channelItems.size())
+  {
+    int itemNo = CorrectOffset(current, 0);
+    if (itemNo >= (int)m_channelItems.size())
+      break;
+    bool focused = (current == m_channelOffset + m_channelCursor);
+    if (itemNo >= 0)
+    {
+      CGUIListItemPtr item = m_channelItems[itemNo];
+      // render our item
+      if (focused)
+      {
+        focusedPos = pos;
+        focusedItem = item;
+      }
+      else
+      {
+        if (m_orientation == VERTICAL)
+          RenderChannelItem(originChannel.x, pos, item.get(), false);
+        else
+          RenderChannelItem(pos, originChannel.y, item.get(), false);
+      }
+    }
+    // increment our position
+    pos += focused ? m_focusedChannelLayout->Size(m_orientation) : m_channelLayout->Size(m_orientation);
+    current++;
+  }
+  // render focused item last so it can overlap other items
+  if (focusedItem)
+  {
+    if (m_orientation == VERTICAL)
+      RenderChannelItem(originChannel.x, focusedPos, focusedItem.get(), true);
+    else
+      RenderChannelItem(focusedPos, originChannel.y, focusedItem.get(), true);
+  }
+  g_graphicsContext.RestoreClipRegion();
+
+  /// Render the ruler items
+  g_graphicsContext.SetClipRegion(m_posX, m_posY, m_width, m_height);
+  CGUIListItemPtr item = m_rulerItems[0];
+  g_graphicsContext.SetOrigin(m_posX, m_posY);
+  item->SetLabel(m_rulerItems[rulerOffset/m_rulerUnit+1]->GetLabel2());
+  if (!item->GetLayout())
+  {
+    CGUIListItemLayout *layout = new CGUIListItemLayout(*m_rulerLayout);
+    if (m_orientation == VERTICAL)
+      layout->SetWidth(m_channelWidth);
+    else
+      layout->SetHeight(m_channelHeight);
+    item->SetLayout(layout);
+  }
+  if (item->GetLayout())
+    item->GetLayout()->Render(item.get(), m_parentID, m_renderTime);
+  g_graphicsContext.RestoreOrigin();
+
+  int cacheBeforeRuler, cacheAfterRuler;
+  GetRulerCacheOffsets(cacheBeforeRuler, cacheAfterRuler);
+
+  g_graphicsContext.RestoreClipRegion();
+
+  // Free memory not used on screen
+  if ((int)m_rulerItems.size() > m_blocksPerPage + cacheBeforeRuler + cacheAfterRuler)
+    FreeRulerMemory(CorrectOffset(rulerOffset - cacheBeforeRuler, 0), CorrectOffset(rulerOffset + m_blocksPerPage + 1 + cacheAfterRuler, 0));
+
+  if (m_orientation == VERTICAL)
+    g_graphicsContext.SetClipRegion(m_rulerPosX, m_rulerPosY, m_gridWidth, m_rulerHeight);
+  else
+    g_graphicsContext.SetClipRegion(m_rulerPosX, m_rulerPosY, m_rulerWidth, m_gridHeight);
+
+  CPoint originRuler = CPoint(m_rulerPosX, m_rulerPosY) + m_renderOffset;
+  pos = (m_orientation == VERTICAL) ? originRuler.x : originRuler.y;
+  end = (m_orientation == VERTICAL) ? m_posX + m_width : m_posY + m_height;
+  drawOffset = (rulerOffset - cacheBeforeRuler) * m_blockSize - m_programmeScrollOffset;
+  pos += drawOffset;
+  end += cacheAfterRuler * m_rulerLayout->Size(m_orientation == VERTICAL ? HORIZONTAL : VERTICAL);
+
+  if (rulerOffset % m_rulerUnit != 0)
+  {
+    /* first ruler marker starts before current view */
+    int startBlock = rulerOffset - 1;
+
+    while (startBlock % m_rulerUnit != 0)
+      startBlock--;
+
+    int missingSection = rulerOffset - startBlock;
+
+    pos -= missingSection * m_blockSize;
+  }
+  while (pos < end && m_rulerItems.size())
+  {
+    item = m_rulerItems[rulerOffset/m_rulerUnit+1];
+    if (m_orientation == VERTICAL)
+    {
+      g_graphicsContext.SetOrigin(pos, originRuler.y);
+      pos += m_rulerWidth;
+    }
+    else
+    {
+      g_graphicsContext.SetOrigin(originRuler.x, pos);
+      pos += m_rulerHeight;
+    }
+    if (!item->GetLayout())
+    {
+      CGUIListItemLayout *layout = new CGUIListItemLayout(*m_rulerLayout);
+      if (m_orientation == VERTICAL)
+        layout->SetWidth(m_rulerWidth);
+      else
+        layout->SetHeight(m_rulerHeight);
+
+      item->SetLayout(layout);
+    }
+    if (item->GetLayout())
+      item->GetLayout()->Render(item.get(), m_parentID, m_renderTime);
+    g_graphicsContext.RestoreOrigin();
+
+    rulerOffset += m_rulerUnit;
+  }
+  g_graphicsContext.RestoreClipRegion();
+
+  /// Render programmes
+  int cacheBeforeProgramme, cacheAfterProgramme;
+  GetProgrammeCacheOffsets(cacheBeforeProgramme, cacheAfterProgramme);
+
+  // Free memory not used on screen
+  if ((int)m_programmeItems.size() > m_ProgrammesPerPage + cacheBeforeProgramme + cacheAfterProgramme)
+    FreeProgrammeMemory(CorrectOffset(blockOffset - cacheBeforeProgramme, 0), CorrectOffset(blockOffset + m_ProgrammesPerPage + 1 + cacheAfterProgramme, 0));
+
+  g_graphicsContext.SetClipRegion(m_gridPosX, m_gridPosY, m_gridWidth, m_gridHeight);
+  CPoint originProgramme = CPoint(m_gridPosX, m_gridPosY) + m_renderOffset;
+  float posA = (m_orientation != VERTICAL) ? originProgramme.y : originProgramme.x;
+  float endA = (m_orientation != VERTICAL) ? m_posY + m_height : m_posX + m_width;
+  float posB = (m_orientation == VERTICAL) ? originProgramme.y : originProgramme.x;
+  float endB = (m_orientation == VERTICAL) ? m_gridPosY + m_gridHeight : m_posX + m_width;
+  endA += cacheAfterProgramme * m_blockSize;
+
+  float DrawOffsetA = blockOffset * m_blockSize - m_programmeScrollOffset;
+  posA += DrawOffsetA;
+  float DrawOffsetB = (chanOffset - cacheBeforeProgramme) * m_channelLayout->Size(m_orientation) - m_channelScrollOffset;
+  posB += DrawOffsetB;
+
+  int channel = chanOffset;
+
+  float focusedPosX = 0;
+  float focusedPosY = 0;
+  float focusedwidth = 0;
+  float focusedheight = 0;
+  while (posB < endB && m_channelItems.size())
+  {
+    if (channel >= (int)m_channelItems.size())
+      break;
+
+    int block = blockOffset;
+    float posA2 = posA;
+
+    CGUIListItemPtr item = m_gridIndex[channel][block].item;
+    if (item == m_gridIndex[channel][blockOffset-1].item && blockOffset != 0)
+    {
+      /* first program starts before current view */
+      int startBlock = blockOffset - 1;
+      while (m_gridIndex[channel][startBlock].item == item)
+        startBlock--;
+
+      block = startBlock + 1;
+      int missingSection = blockOffset - block;
+      posA2 -= missingSection * m_blockSize;
+    }
+
+    while (posA2 < endA && m_programmeItems.size())   // FOR EACH ITEM ///////////////
+    {
+      item = m_gridIndex[channel][block].item;
+      if (!item || !item.get()->IsFileItem())
+        break;
+
+      bool focused = (channel == m_channelOffset + m_channelCursor) && (item == m_gridIndex[m_channelOffset + m_channelCursor][m_blockOffset + m_blockCursor].item);
+
+      // render our item
+      if (focused)
+      {
+        if (m_orientation == VERTICAL)
+        {
+          focusedPosX = posA2;
+          focusedPosY = posB;
+        }
+        else
+        {
+          focusedPosX = posB;
+          focusedPosY = posA2;
+        }
+        focusedItem = item;
+        focusedwidth = m_gridIndex[channel][block].width;
+        focusedheight = m_gridIndex[channel][block].height;
+      }
+      else
+      {
+        if (m_orientation == VERTICAL)
+          RenderProgrammeItem(posA2, posB, m_gridIndex[channel][block].width, m_gridIndex[channel][block].height, item.get(), focused);
+        else
+          RenderProgrammeItem(posB, posA2, m_gridIndex[channel][block].width, m_gridIndex[channel][block].height, item.get(), focused);
+      }
+
+      // increment our X position
+      if (m_orientation == VERTICAL)
+      {
+        posA2 += m_gridIndex[channel][block].width; // assumes focused & unfocused layouts have equal length
+        block += (int)(m_gridIndex[channel][block].width / m_blockSize);
+      }
+      else
+      {
+        posA2 += m_gridIndex[channel][block].height; // assumes focused & unfocused layouts have equal length
+        block += (int)(m_gridIndex[channel][block].height / m_blockSize);
+      }
+    }
+
+    // increment our Y position
+    channel++;
+    posB += m_orientation == VERTICAL ? m_channelHeight : m_channelWidth;
+  }
+
+  // and render the focused item last (for overlapping purposes)
+  if (focusedItem)
+    RenderProgrammeItem(focusedPosX, focusedPosY, focusedwidth, focusedheight, focusedItem.get(), true);
+
+  g_graphicsContext.RestoreClipRegion();
+
+  CGUIControl::Render();
+}
+
+void CGUIEPGGridContainer::RenderChannelItem(float posX, float posY, CGUIListItem *item, bool focused)
+{
+  if (!m_focusedChannelLayout || !m_channelLayout) return;
+
+  // set the origin
+  g_graphicsContext.SetOrigin(posX, posY);
+
+  if (m_bInvalidated)
+    item->SetInvalid();
+  if (focused)
+  {
+    if (!item->GetFocusedLayout())
+    {
+      CGUIListItemLayout *layout = new CGUIListItemLayout(*m_focusedChannelLayout);
+      item->SetFocusedLayout(layout);
+    }
+    if (item->GetFocusedLayout())
+    {
+      if (item != m_lastChannel || !HasFocus())
+      {
+        item->GetFocusedLayout()->SetFocusedItem(0);
+      }
+      if (item != m_lastChannel && HasFocus())
+      {
+        item->GetFocusedLayout()->ResetAnimation(ANIM_TYPE_UNFOCUS);
+        unsigned int subItem = 1;
+        if (m_lastChannel && m_lastChannel->GetFocusedLayout())
+          subItem = m_lastChannel->GetFocusedLayout()->GetFocusedItem();
+        item->GetFocusedLayout()->SetFocusedItem(subItem ? subItem : 1);
+      }
+      item->GetFocusedLayout()->Render(item, m_parentID, m_renderTime);
+    }
+    m_lastChannel = item;
+  }
+  else
+  {
+    if (item->GetFocusedLayout())
+      item->GetFocusedLayout()->SetFocusedItem(0);  // focus is not set
+    if (!item->GetLayout())
+    {
+      CGUIListItemLayout *layout = new CGUIListItemLayout(*m_channelLayout);
+      item->SetLayout(layout);
+    }
+    if (item->GetFocusedLayout() && item->GetFocusedLayout()->IsAnimating(ANIM_TYPE_UNFOCUS))
+      item->GetFocusedLayout()->Render(item, m_parentID, m_renderTime);
+    else if (item->GetLayout())
+      item->GetLayout()->Render(item, m_parentID, m_renderTime);
+  }
+  g_graphicsContext.RestoreOrigin();
+}
+
+void CGUIEPGGridContainer::RenderProgrammeItem(float posX, float posY, float width, float height, CGUIListItem *item, bool focused)
+{
+  if (!m_focusedProgrammeLayout || !m_programmeLayout) return;
+
+  // set the origin
+  g_graphicsContext.SetOrigin(posX, posY);
+
+  if (m_bInvalidated)
+    item->SetInvalid();
+  if (focused)
+  {
+    if (!item->GetFocusedLayout())
+    {
+      CGUIListItemLayout *layout = new CGUIListItemLayout(*m_focusedProgrammeLayout);
+      CFileItem *fileItem = item->IsFileItem() ? (CFileItem *)item : NULL;
+      if (fileItem)
+      {
+        const CPVREpgInfoTag* tag = fileItem->GetEPGInfoTag();
+        if (m_orientation == VERTICAL)
+          layout->SetWidth(width);
+        else
+          layout->SetHeight(height);
+
+        item->SetProperty("GenreType", tag->GenreType());
+      }
+      item->SetFocusedLayout(layout);
+    }
+    if (item->GetFocusedLayout())
+    {
+      if (item != m_lastItem || !HasFocus())
+      {
+        item->GetFocusedLayout()->SetFocusedItem(0);
+      }
+      if (item != m_lastItem && HasFocus())
+      {
+        item->GetFocusedLayout()->ResetAnimation(ANIM_TYPE_UNFOCUS);
+        unsigned int subItem = 1;
+        if (m_lastItem && m_lastItem->GetFocusedLayout())
+          subItem = m_lastItem->GetFocusedLayout()->GetFocusedItem();
+        item->GetFocusedLayout()->SetFocusedItem(subItem ? subItem : 1);
+      }
+      item->GetFocusedLayout()->Render(item, m_parentID, m_renderTime);
+    }
+    m_lastItem = item;
+  }
+  else
+  {
+    if (item->GetFocusedLayout())
+      item->GetFocusedLayout()->SetFocusedItem(0);  // focus is not set
+    if (!item->GetLayout())
+    {
+      CGUIListItemLayout *layout = new CGUIListItemLayout(*m_programmeLayout);
+      CFileItem *fileItem = item->IsFileItem() ? (CFileItem *)item : NULL;
+      if (fileItem)
+      {
+        const CPVREpgInfoTag* tag = fileItem->GetEPGInfoTag();
+        if (m_orientation == VERTICAL)
+          layout->SetWidth(width);
+        else
+          layout->SetHeight(height);
+
+        item->SetProperty("GenreType", tag->GenreType());
+      }
+      item->SetLayout(layout);
+    }
+    if (item->GetFocusedLayout() && item->GetFocusedLayout()->IsAnimating(ANIM_TYPE_UNFOCUS))
+      item->GetFocusedLayout()->Render(item, m_parentID, m_renderTime);
+    else if (item->GetLayout())
+      item->GetLayout()->Render(item, m_parentID, m_renderTime);
+  }
+  g_graphicsContext.RestoreOrigin();
+}
+
+bool CGUIEPGGridContainer::OnAction(const CAction &action)
+{
+  switch (action.GetID())
+  {
+  case ACTION_MOVE_LEFT:
+  case ACTION_MOVE_RIGHT:
+  case ACTION_MOVE_DOWN:
+  case ACTION_MOVE_UP:
+    { // use base class implementation
+
+      return CGUIControl::OnAction(action);
+    }
+
+    break;
+  case ACTION_PAGE_UP:
+    {
+      if (m_orientation == VERTICAL)
+      {
+        if (m_channelOffset == 0)
+        { // already on the first page, so move to the first item
+          SetChannel(0);
+        }
+        else
+        { // scroll up to the previous page
+          ChannelScroll(-m_channelsPerPage);
+        }
+      }
+      else
+        ProgrammesScroll(-m_blocksPerPage/4);
+
+      return true;
+    }
+
+    break;
+  case ACTION_PAGE_DOWN:
+    {
+      if (m_orientation == VERTICAL)
+      {
+        if (m_channelOffset == m_channels - m_channelsPerPage || m_channels < m_channelsPerPage)
+        { // already at the last page, so move to the last item.
+          SetChannel(m_channels - m_channelOffset - 1);
+        }
+        else
+        { // scroll down to the next page
+          ChannelScroll(m_channelsPerPage);
+        }
+      }
+      else
+        ProgrammesScroll(m_blocksPerPage/4);
+
+      return true;
+    }
+
+    break;
+
+    // smooth scrolling (for analog controls)
+  case ACTION_TELETEXT_RED:
+  case ACTION_TELETEXT_GREEN:
+  case ACTION_SCROLL_UP: // left horizontal scrolling
+    {
+      int blocksToJump = action.GetID() == ACTION_TELETEXT_RED ? m_blocksPerPage/2 : m_blocksPerPage/4;
+
+      m_analogScrollCount += action.GetAmount() * action.GetAmount();
+      bool handled = false;
+
+      while (m_analogScrollCount > 0.4)
+      {
+        handled = true;
+        m_analogScrollCount -= 0.4f;
+
+        if (m_blockOffset > 0 && m_blockCursor <= m_blocksPerPage / 2)
+        {
+          ProgrammesScroll(-blocksToJump);
+        }
+        else if (m_blockCursor > blocksToJump)
+        {
+          SetBlock(m_blockCursor - blocksToJump);
+        }
+      }
+
+      return handled;
+    }
+
+    break;
+
+  case ACTION_TELETEXT_BLUE:
+  case ACTION_TELETEXT_YELLOW:
+  case ACTION_SCROLL_DOWN: // right horizontal scrolling
+    {
+      int blocksToJump = action.GetID() == ACTION_TELETEXT_BLUE ? m_blocksPerPage/2 : m_blocksPerPage/4;
+
+      m_analogScrollCount += action.GetAmount() * action.GetAmount();
+      bool handled = false;
+
+      while (m_analogScrollCount > 0.4)
+      {
+        handled = true;
+        m_analogScrollCount -= 0.4f;
+
+        if (m_blockOffset + m_blocksPerPage < m_blocks && m_blockCursor >= m_blocksPerPage / 2)
+        {
+          ProgrammesScroll(blocksToJump);
+        }
+        else if (m_blockCursor < m_blocksPerPage - blocksToJump && m_blockOffset + m_blockCursor < m_blocks - blocksToJump)
+        {
+          SetBlock(m_blockCursor + blocksToJump);
+        }
+      }
+
+      return handled;
+    }
+
+    break;
+
+  default:
+
+    if (action.GetID())
+    {
+      return OnClick(action.GetID());
+    }
+  }
+
+  return false;
+}
+
+bool CGUIEPGGridContainer::OnMessage(CGUIMessage& message)
+{
+  if (message.GetControlId() == GetID())
+  {
+    if (message.GetMessage() == GUI_MSG_ITEM_SELECTED)
+    {
+      message.SetParam1(GetSelectedItem());
+      return true;
+    }
+    else if (message.GetMessage() == GUI_MSG_LABEL_BIND && message.GetPointer())
+    {
+      Reset();
+      CFileItemList *items = (CFileItemList *)message.GetPointer();
+
+      /* Create Channel items */
+      int ChannelLast = -1;
+      ItemsPtr itemsPointer;
+      itemsPointer.start = 0;
+      for (int i = 0; i < items->Size(); ++i)
+      {
+        const CPVREpgInfoTag* tag = items->Get(i)->GetEPGInfoTag();
+        int ChannelNow = tag->ChannelTag()->ChannelNumber();
+        if (ChannelNow != ChannelLast)
+        {
+          if (i > 0)
+          {
+            itemsPointer.stop     = i-1;
+            m_epgItemsPtr.push_back(itemsPointer);
+            itemsPointer.start    = i;
+          }
+          ChannelLast = ChannelNow;
+          CGUIListItemPtr item(new CFileItem(*tag->ChannelTag()));
+          m_channelItems.push_back(item);
+        }
+      }
+      if (m_epgItemsPtr.size() > 0)
+      {
+        itemsPointer.stop = items->Size()-1;
+        m_epgItemsPtr.push_back(itemsPointer);
+      }
+
+      /* Create programme items */
+      for (int i = 0; i < items->Size(); i++)
+        m_programmeItems.push_back(items->Get(i));
+
+      m_gridIndex = (struct GridItemsPtr **) calloc(1,m_channelItems.size()*sizeof(struct GridItemsPtr));
+      if (m_gridIndex != NULL)
+      {
+        for (unsigned int i = 0; i < m_channelItems.size(); i++)
+        {
+          m_gridIndex[i] = (struct GridItemsPtr*) calloc(1,MAXBLOCKS*sizeof(struct GridItemsPtr));
+        }
+      }
+
+      UpdateLayout(true); // true to refresh all items
+
+      /* Create Ruler items */
+      CDateTime ruler = m_gridStart;
+      CDateTimeSpan unit(0, 0, m_rulerUnit * MINSPERBLOCK, 0);
+      CGUIListItemPtr rulerItem(new CFileItem(ruler.GetAsLocalizedDate(true, true)));
+      rulerItem->SetProperty("DateLabel", true);
+      m_rulerItems.push_back(rulerItem);
+
+      for (; ruler < m_gridEnd; ruler += unit)
+      {
+        CGUIListItemPtr rulerItem(new CFileItem(ruler.GetAsLocalizedTime("", false)));
+        rulerItem->SetLabel2(ruler.GetAsLocalizedDate(true, true));
+        m_rulerItems.push_back(rulerItem);
+      }
+
+      UpdateItems();
+      //SelectItem(message.GetParam1());
+      return true;
+    }
+    else if (message.GetMessage() == GUI_MSG_REFRESH_LIST)
+    { // update our list contents
+      for (unsigned int i = 0; i < m_channelItems.size(); ++i)
+        m_channelItems[i]->SetInvalid();
+      for (unsigned int i = 0; i < m_programmeItems.size(); ++i)
+        m_programmeItems[i]->SetInvalid();
+      for (unsigned int i = 0; i < m_rulerItems.size(); ++i)
+        m_rulerItems[i]->SetInvalid();
+    }
+  }
+
+  return CGUIControl::OnMessage(message);
+}
+
+void CGUIEPGGridContainer::UpdateItems()
+{
+  CDateTimeSpan blockDuration, gridDuration;
+
+  gridDuration = m_gridEnd - m_gridStart;
+
+  m_blocks = (gridDuration.GetDays()*24*60 + gridDuration.GetHours()*60 + gridDuration.GetMinutes()) / MINSPERBLOCK;
+  if (m_blocks >= MAXBLOCKS)
+    m_blocks = MAXBLOCKS;
+
+  /* if less than one page, can't display grid */
+  if (m_blocks < m_blocksPerPage)
+  {
+    CLog::Log(LOGERROR, "(%s) - Less than one page of data available.", __FUNCTION__);
+    CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), GetParentID()); // message the window
+    SendWindowMessage(msg);
+    return;
+  }
+
+  blockDuration.SetDateTimeSpan(0, 0, MINSPERBLOCK, 0);
+
+  long tick(CTimeUtils::GetTimeMS());
+
+  for (unsigned int row = 0; row < m_channelItems.size(); ++row)
+  {
+    CDateTime gridCursor  = m_gridStart; //reset cursor for new channel
+    unsigned long progIdx = m_epgItemsPtr[row].start;
+    unsigned long lastIdx = m_epgItemsPtr[row].stop;
+    int channelnum        = ((CFileItem *)m_programmeItems[progIdx].get())->GetEPGInfoTag()->ChannelTag()->ChannelNumber();
+
+    /** FOR EACH BLOCK **********************************************************************/
+
+    for (int block = 0; block < m_blocks; block++)
+    {
+      while (progIdx <= lastIdx)
+      {
+        CGUIListItemPtr item = m_programmeItems[progIdx];
+        if (((CFileItem *)item.get())->GetEPGInfoTag()->ChannelTag()->ChannelNumber() != channelnum)
+          break;
+
+        const CPVREpgInfoTag* tag = ((CFileItem *)item.get())->GetEPGInfoTag();
+        if (tag == NULL)
+          progIdx++;
+
+        if (m_gridEnd <= tag->Start())
+        {
+          break;
+        }
+        else if (gridCursor >= tag->End())
+        {
+          progIdx++;
+        }
+        else if (gridCursor < tag->End())
+        {
+          m_gridIndex[row][block].item = item;
+          break;
+        }
+        else
+        {
+          progIdx++;
+        }
+      }
+
+      gridCursor += blockDuration;
+    }
+
+    /** FOR EACH BLOCK **********************************************************************/
+    int itemSize = 1; // size of the programme in blocks
+    int savedBlock = 0;
+
+    for (int block = 0; block < m_blocks; block++)
+    {
+      if (m_gridIndex[row][block].item != m_gridIndex[row][block+1].item)
+      {
+        if (!m_gridIndex[row][block].item)
+        {
+          CPVREpgInfoTag broadcast(NULL);
+          CFileItemPtr unknown(new CFileItem(broadcast));
+          for (int i = block ; i > block - itemSize; i--)
+          {
+            m_gridIndex[row][i].item = unknown;
+          }
+        }
+
+        CGUIListItemPtr item = m_gridIndex[row][block].item;
+        CFileItem *fileItem = (CFileItem *)item.get();
+
+        m_gridIndex[row][savedBlock].item->SetProperty("GenreType", fileItem->GetEPGInfoTag()->GenreType());
+        if (m_orientation == VERTICAL)
+        {
+          m_gridIndex[row][savedBlock].width   = itemSize*m_blockSize;
+          m_gridIndex[row][savedBlock].height  = m_channelHeight;
+        }
+        else
+        {
+          m_gridIndex[row][savedBlock].width   = m_channelWidth;
+          m_gridIndex[row][savedBlock].height  = itemSize*m_blockSize;
+        }
+
+        itemSize = 1;
+        savedBlock = block+1;
+      }
+      else
+      {
+        itemSize++;
+      }
+    }
+  }
+
+  /******************************************* END ******************************************/
+
+  CLog::Log(LOGDEBUG, "%s completed successfully in %u ms", __FUNCTION__, (unsigned int)(CTimeUtils::GetTimeMS()-tick));
+
+  m_channels = (int)m_epgItemsPtr.size();
+  m_item = GetItem(m_channelCursor);
+  m_blockCursor = GetBlock(m_item->item, m_channelCursor);
+
+  SetInvalid();
+}
+
+void CGUIEPGGridContainer::ChannelScroll(int amount)
+{
+  // increase or decrease the vertical offset
+  int offset = m_channelOffset + amount;
+
+  if (offset > m_channels - m_channelsPerPage)
+  {
+    offset = m_channels - m_channelsPerPage;
+  }
+
+  if (offset < 0) offset = 0;
+
+  ScrollToChannelOffset(offset);
+}
+
+void CGUIEPGGridContainer::ProgrammesScroll(int amount)
+{
+  // increase or decrease the horizontal offset
+  int offset = m_blockOffset + amount;
+
+  if (offset > m_blocks - m_blocksPerPage)
+  {
+    offset = m_blocks - m_blocksPerPage;
+  }
+
+  if (offset < 0) offset = 0;
+
+  ScrollToBlockOffset(offset);
+}
+
+bool CGUIEPGGridContainer::MoveChannel(bool direction)
+{
+  if (direction)
+  {
+    if (m_channelCursor > 0)
+    {
+      SetChannel(m_channelCursor - 1);
+    }
+    else if (m_channelCursor == 0 && m_channelOffset)
+    {
+      ScrollToChannelOffset(m_channelOffset - 1);
+      SetChannel(0);
+    }
+    else if (m_channelWrapAround)
+    {
+      int offset = m_channels - m_channelsPerPage;
+
+      if (offset < 0) offset = 0;
+
+      SetChannel(m_channels - offset - 1);
+
+      ScrollToChannelOffset(offset);
+    }
+    else
+      return false;
+  }
+  else
+  {
+    if (m_channelOffset + m_channelCursor + 1 < m_channels)
+    {
+      if (m_channelCursor + 1 < m_channelsPerPage)
+      {
+        SetChannel(m_channelCursor + 1);
+      }
+      else
+      {
+        ScrollToChannelOffset(m_channelOffset + 1);
+        SetChannel(m_channelsPerPage - 1);
+      }
+    }
+    else if (m_channelWrapAround)
+    {
+      SetChannel(0);
+      ScrollToChannelOffset(0);
+    }
+    else
+      return false;
+  }
+  return true;
+}
+
+bool CGUIEPGGridContainer::MoveProgrammes(bool direction)
+{
+  if (direction)
+  {
+    if (m_item->item != m_gridIndex[m_channelCursor + m_channelOffset][m_blockOffset].item)
+    {
+      // this is not first item on page
+      m_item = GetPrevItem(m_channelCursor);
+      m_blockCursor = GetBlock(m_item->item, m_channelCursor);
+    }
+    else if (m_blockCursor <= 0 && m_blockOffset)
+    {
+      // we're at the left edge and offset
+      int itemSize = GetItemSize(m_item);
+      int block = GetRealBlock(m_item->item, m_channelCursor);
+
+      if (block < m_blockOffset) /* current item begins before current offset, keep selected */
+      {
+        if (itemSize > m_blocksPerPage) /* current item is longer than one page, scroll one page left */
+        {
+          m_blockOffset < m_blocksPerPage ? block = 0 : block = m_blockOffset - m_blocksPerPage; // number blocks left < m_blocksPerPAge
+          ScrollToBlockOffset(block);
+          SetBlock(0);
+        }
+        else /* current item is shorter than one page, scroll left to start of item */
+        {
+          ScrollToBlockOffset(block); // -1?
+          SetBlock(0); // align cursor to left edge
+        }
+      }
+      else /* current item starts on this page's edge, select the previous item */
+      {
+        m_item = GetPrevItem(m_channelCursor);
+        itemSize = GetItemSize(m_item);
+
+        if (itemSize > m_blocksPerPage) // previous item is longer than one page, scroll left to last page of item */
+        {
+          ScrollToBlockOffset(m_blockOffset - m_blocksPerPage); // left one whole page
+          //SetBlock(m_blocksPerPage -1 ); // helps navigation by setting cursor to far right edge
+          SetBlock(0); // align cursor to left edge
+        }
+        else /* previous item is shorter than one page, scroll left to start of item */
+        {
+          ScrollToBlockOffset(m_blockOffset - itemSize);
+          SetBlock(0); //should be zero
+        }
+      }
+    }
+    else
+      return false;
+  }
+  else
+  {
+    if (m_item->item != m_gridIndex[m_channelCursor + m_channelOffset][m_blocksPerPage + m_blockOffset - 1].item)
+    {
+      // this is not last item on page
+      m_item = GetNextItem(m_channelCursor);
+      m_blockCursor = GetBlock(m_item->item, m_channelCursor);
+    }
+    else if ((m_blockOffset != m_blocks - m_blocksPerPage) && m_blocks > m_blocksPerPage)
+    {
+      // at right edge, more than one page and not at maximum offset
+      int itemSize = GetItemSize(m_item);
+      int block = GetRealBlock(m_item->item, m_channelCursor);
+
+      if (itemSize > m_blocksPerPage - m_blockCursor) // current item extends into next page, keep selected
+      {
+        if (itemSize > m_blocksPerPage) // current item is longer than one page, scroll one page right
+        {
+          if (m_blockOffset && m_blockOffset + m_blocksPerPage > m_blocks)
+            block = m_blocks - m_blocksPerPage;
+          else
+            block = m_blockOffset + m_blocksPerPage;
+
+          ScrollToBlockOffset(block);
+
+          SetBlock(0);
+        }
+        else // current item is shorter than one page, scroll so end of item sits on end of grid
+        {
+          ScrollToBlockOffset(block + itemSize - m_blocksPerPage);
+          SetBlock(GetBlock(m_item->item, m_channelCursor)); /// change to middle block of item?
+        }
+      }
+      else // current item finishes on this page's edge, select the next item
+      {
+        m_item = GetNextItem(m_channelCursor);
+        itemSize = GetItemSize(m_item);
+
+        if (itemSize > m_blocksPerPage) // next item is longer than one page, scroll to first page of this item
+        {
+          ScrollToBlockOffset(m_blockOffset + m_blocksPerPage);
+          SetBlock(0);
+        }
+        else // next item is shorter than one page, scroll so end of item sits on end of grid
+        {
+          ScrollToBlockOffset(m_blockOffset + itemSize);
+          SetBlock(m_blocksPerPage - itemSize); /// change to middle block of item?
+        }
+      }
+    }
+    else
+      return false;
+  }
+  return true;
+}
+
+void CGUIEPGGridContainer::OnUp()
+{
+  if (m_orientation == VERTICAL)
+  {
+    if (!MoveChannel(true))
+      CGUIControl::OnUp();
+  }
+  else
+  {
+    if (!MoveProgrammes(true))
+      CGUIControl::OnUp();
+  }
+}
+
+void CGUIEPGGridContainer::OnDown()
+{
+  if (m_orientation == VERTICAL)
+  {
+    if (!MoveChannel(false))
+      CGUIControl::OnDown();
+  }
+  else
+  {
+    if (!MoveProgrammes(false))
+      CGUIControl::OnDown();
+  }
+}
+
+void CGUIEPGGridContainer::OnLeft()
+{
+  if (m_orientation == VERTICAL)
+  {
+    if (!MoveProgrammes(true))
+      CGUIControl::OnLeft();
+  }
+  else
+  {
+    if (!MoveChannel(true))
+      CGUIControl::OnLeft();
+  }
+}
+
+void CGUIEPGGridContainer::OnRight()
+{
+  if (m_orientation == VERTICAL)
+  {
+    if (!MoveProgrammes(false))
+      CGUIControl::OnRight();
+  }
+  else
+  {
+    if (!MoveChannel(false))
+      CGUIControl::OnRight();
+  }
+}
+
+void CGUIEPGGridContainer::SetChannel(int channel)
+{
+  if (m_blockCursor + m_blockOffset == 0 || m_blockOffset + m_blockCursor + GetItemSize(m_item) == m_blocks)
+  {
+    m_item          = GetItem(channel);
+    m_blockCursor   = GetBlock(m_item->item, channel);
+    m_channelCursor = channel;
+    return;
+  }
+
+  /* basic checks failed, need to correctly identify nearest item */
+  m_item          = GetClosestItem(channel);
+  m_channelCursor = channel;
+  m_blockCursor   = GetBlock(m_item->item, m_channelCursor);
+}
+
+void CGUIEPGGridContainer::SetBlock(int block)
+{
+  m_blockCursor = block;
+  m_item        = GetItem(m_channelCursor);
+}
+
+CGUIListItemLayout *CGUIEPGGridContainer::GetFocusedLayout() const
+{
+  CGUIListItemPtr item = GetListItem(0);
+
+  if (item.get()) return item->GetFocusedLayout();
+
+  return NULL;
+}
+
+bool CGUIEPGGridContainer::SelectItemFromPoint(const CPoint &point)
+{
+  /* point has already had origin set to m_posX, m_posY */
+  if (!m_focusedProgrammeLayout || !m_programmeLayout)
+    return false;
+
+  int channel = (int)(point.y / m_channelHeight);
+  int block   = (int)(point.x / m_blockSize);
+
+  if (channel > m_channelsPerPage) channel = m_channelsPerPage - 1;
+  if (channel >= m_channels) channel = m_channels - 1;
+  if (channel < 0) channel = 0;
+  if (block > m_blocksPerPage) block = m_blocksPerPage - 1;
+  if (block < 0) block = 0;
+
+  SetChannel(channel);
+  SetBlock(block);
+  return true;
+}
+
+bool CGUIEPGGridContainer::OnMouseOver(const CPoint &point)
+{
+  // select the item under the pointer
+  SelectItemFromPoint(point - CPoint(m_gridPosX, m_posY + m_rulerHeight));
+  return CGUIControl::OnMouseOver(point);
+}
+
+bool CGUIEPGGridContainer::OnMouseClick(int dwButton, const CPoint &point)
+{
+  if (SelectItemFromPoint(point - CPoint(m_gridPosX, m_posY + m_rulerHeight)))
+  { // send click message to window
+    OnClick(ACTION_MOUSE_LEFT_CLICK + dwButton);
+    return true;
+  }
+
+  return false;
+}
+
+bool CGUIEPGGridContainer::OnMouseDoubleClick(int dwButton, const CPoint &point)
+{
+  if (SelectItemFromPoint(point - CPoint(m_gridPosX, m_posY + m_rulerHeight)))
+  { // send double click message to window
+    OnClick(ACTION_MOUSE_DOUBLE_CLICK + dwButton);
+    return true;
+  }
+
+  return false;
+}
+
+bool CGUIEPGGridContainer::OnClick(int actionID)
+{
+  int subItem = 0;
+
+  if (actionID == ACTION_SELECT_ITEM || actionID == ACTION_MOUSE_LEFT_CLICK)
+  {
+    // grab the currently focused subitem (if applicable)
+    CGUIListItemLayout *focusedLayout = GetFocusedLayout();
+
+    if (focusedLayout)
+      subItem = focusedLayout->GetFocusedItem();
+  }
+
+  // Don't know what to do, so send to our parent window.
+  CGUIMessage msg(GUI_MSG_CLICKED, GetID(), GetParentID(), actionID, subItem);
+  return SendWindowMessage(msg);
+}
+
+bool CGUIEPGGridContainer::OnMouseWheel(char wheel, const CPoint &point)
+{
+  ///doesn't work while an item is selected?
+  ProgrammesScroll(-wheel);
+  return true;
+}
+
+int CGUIEPGGridContainer::GetSelectedItem() const
+{
+  CGUIListItemPtr currentItem = m_gridIndex[m_channelCursor + m_channelOffset][m_blockCursor + m_blockOffset].item; ///
+  for (int i = 0; i < (int)m_programmeItems.size(); i++)
+  {
+    if (currentItem == m_programmeItems[i])
+      return i;
+  }
+  return 0;
+}
+
+CGUIListItemPtr CGUIEPGGridContainer::GetListItem(int offset) const
+{
+  if (!m_epgItemsPtr.size())
+    return CGUIListItemPtr();
+
+  return m_item->item;
+}
+
+GridItemsPtr *CGUIEPGGridContainer::GetClosestItem(const int &channel)
+{
+  GridItemsPtr *closest = GetItem(channel);
+  int block = GetBlock(closest->item, channel);
+  int left;   // num blocks to start of previous item
+  int right;  // num blocks to start of next item
+
+  if (block == m_blockCursor)
+    return closest; // item & m_item start together
+
+  if (block + GetItemSize(closest) == m_blockCursor + GetItemSize(m_item))
+    return closest; // closest item ends when current does
+
+  if (block > m_blockCursor)  // item starts after m_item
+  {
+    left = m_blockCursor - GetBlock(closest->item, channel);
+    right = block - m_blockCursor;
+  }
+  else
+  {
+    left  = m_blockCursor - block;
+    right = GetBlock(GetNextItem(channel)->item, channel) - m_blockCursor;
+  }
+
+  if (right <= SHORTGAP && right <= left && m_blockCursor + right < m_blocksPerPage)
+    return &m_gridIndex[channel + m_channelOffset][m_blockCursor + right + m_blockOffset];
+
+  return &m_gridIndex[channel + m_channelOffset][m_blockCursor - left  + m_blockOffset];
+}
+
+int CGUIEPGGridContainer::GetItemSize(GridItemsPtr *item)
+{
+  if (!item)
+    return (int) m_blockSize; /// stops it crashing
+
+  return (int) ((m_orientation == VERTICAL ? item->width : item->height) / m_blockSize);
+}
+
+int CGUIEPGGridContainer::GetBlock(const CGUIListItemPtr &item, const int &channel)
+{
+  return GetRealBlock(item, channel) - m_blockOffset;
+}
+
+int CGUIEPGGridContainer::GetRealBlock(const CGUIListItemPtr &item, const int &channel)
+{
+  int block = 0;
+
+  while (m_gridIndex[channel + m_channelOffset][block].item != item && block < m_blocks)
+    block++;
+
+  return block;
+}
+
+GridItemsPtr *CGUIEPGGridContainer::GetNextItem(const int &channel)
+{
+  int i = m_blockCursor;
+
+  while (m_gridIndex[channel + m_channelOffset][i + m_blockOffset].item == m_gridIndex[channel + m_channelOffset][m_blockCursor + m_blockOffset].item && i < m_blocksPerPage)
+    i++;
+
+  return &m_gridIndex[channel + m_channelOffset][i + m_blockOffset];
+}
+
+GridItemsPtr *CGUIEPGGridContainer::GetPrevItem(const int &channel)
+{
+  int i = m_blockCursor;
+
+  while (m_gridIndex[channel + m_channelOffset][i + m_blockOffset].item == m_gridIndex[channel + m_channelOffset][m_blockCursor + m_blockOffset].item && i > 0)
+    i--;
+
+  return &m_gridIndex[channel + m_channelOffset][i + m_blockOffset];
+
+//  return &m_gridIndex[channel + m_channelOffset][m_blockCursor + m_blockOffset - 1];
+}
+
+GridItemsPtr *CGUIEPGGridContainer::GetItem(const int &channel)
+{
+  if ( (channel >= 0) && (channel < m_channels) )
+    return &m_gridIndex[channel + m_channelOffset][m_blockCursor + m_blockOffset];
+  else
+    return NULL;
+}
+
+void CGUIEPGGridContainer::SetFocus(bool bOnOff)
+{
+  if (bOnOff != HasFocus())
+  {
+    SetInvalid();
+    /*m_lastItem.reset();
+    m_lastChannel.reset();*/
+  }
+
+  CGUIControl::SetFocus(bOnOff);
+}
+
+void CGUIEPGGridContainer::DoRender(unsigned int currentTime)
+{
+  m_renderTime = currentTime;
+  CGUIControl::DoRender(currentTime);
+  m_wasReset = false;
+}
+
+void CGUIEPGGridContainer::ScrollToChannelOffset(int offset)
+{
+  float size = m_programmeLayout->Size(VERTICAL);
+  int range = m_channelsPerPage / 4;
+
+  if (range <= 0) range = 1;
+
+  if (offset * size < m_channelScrollOffset &&  m_channelScrollOffset - offset * size > size * range)
+  { // scrolling up, and we're jumping more than 0.5 of a screen
+    m_channelScrollOffset = (offset + range) * size;
+  }
+
+  if (offset * size > m_channelScrollOffset && offset * size - m_channelScrollOffset > size * range)
+  { // scrolling down, and we're jumping more than 0.5 of a screen
+    m_channelScrollOffset = (offset - range) * size;
+  }
+
+  m_channelScrollSpeed = (offset * size - m_channelScrollOffset) / m_scrollTime;
+
+  m_channelOffset = offset;
+}
+
+void CGUIEPGGridContainer::ScrollToBlockOffset(int offset)
+{
+  float size = m_blockSize;
+  int range = m_blocksPerPage / 1;
+
+  if (range <= 0) range = 1;
+
+  if (offset * size < m_programmeScrollOffset &&  m_programmeScrollOffset - offset * size > size * range)
+  { // scrolling left, and we're jumping more than 0.5 of a screen
+    m_programmeScrollOffset = (offset + range) * size;
+  }
+
+  if (offset * size > m_programmeScrollOffset && offset * size - m_programmeScrollOffset > size * range)
+  { // scrolling right, and we're jumping more than 0.5 of a screen
+    m_programmeScrollOffset = (offset - range) * size;
+  }
+
+  m_programmeScrollSpeed = (offset * size - m_programmeScrollOffset) / m_scrollTime;
+
+  m_blockOffset = offset;
+}
+
+void CGUIEPGGridContainer::ValidateOffset()
+{
+  if (!m_programmeLayout)
+    return;
+
+  if (m_channelOffset > m_channels - m_channelsPerPage)
+  {
+    m_channelOffset = m_channels - m_channelsPerPage;
+    m_channelScrollOffset = m_channelOffset * m_channelHeight;
+  }
+
+  if (m_channelOffset < 0)
+  {
+    m_channelOffset = 0;
+    m_channelScrollOffset = 0;
+  }
+
+  if (m_blockOffset > m_blocks - m_blocksPerPage)
+  {
+    m_blockOffset = m_blocks - m_blocksPerPage;
+    m_programmeScrollOffset = m_blockOffset * m_blockSize;
+  }
+
+  if (m_blockOffset < 0)
+  {
+    m_blockOffset = 0;
+    m_programmeScrollOffset = 0;
+  }
+}
+
+void CGUIEPGGridContainer::LoadLayout(TiXmlElement *layout)
+{
+  /* layouts for the channel column */
+  TiXmlElement *itemElement = layout->FirstChildElement("channellayout");
+  while (itemElement)
+  { // we have a new item layout
+    CGUIListItemLayout itemLayout;
+    itemLayout.LoadLayout(itemElement, false);
+    m_channelLayouts.push_back(itemLayout);
+    itemElement = itemElement->NextSiblingElement("channellayout");
+  }
+  itemElement = layout->FirstChildElement("focusedchannellayout");
+  while (itemElement)
+  { // we have a new item layout
+    CGUIListItemLayout itemLayout;
+    itemLayout.LoadLayout(itemElement, true);
+    m_focusedChannelLayouts.push_back(itemLayout);
+    itemElement = itemElement->NextSiblingElement("focusedchannellayout");
+  }
+
+  /* layouts for the grid items */
+  itemElement = layout->FirstChildElement("focusedlayout");
+  while (itemElement)
+  {
+    CGUIListItemLayout itemLayout;
+    itemLayout.LoadLayout(itemElement, true);
+    m_focusedProgrammeLayouts.push_back(itemLayout);
+    itemElement = itemElement->NextSiblingElement("focusedlayout");
+  }
+  itemElement = layout->FirstChildElement("itemlayout");
+  while (itemElement)
+  {
+    CGUIListItemLayout itemLayout;
+    itemLayout.LoadLayout(itemElement, false);
+    m_programmeLayouts.push_back(itemLayout);
+    itemElement = itemElement->NextSiblingElement("itemlayout");
+  }
+
+  /* layout for the timeline above the grid */
+  itemElement = layout->FirstChildElement("rulerlayout");
+  while (itemElement)
+  {
+    CGUIListItemLayout itemLayout;
+    itemLayout.LoadLayout(itemElement, false);
+    m_rulerLayouts.push_back(itemLayout);
+    itemElement = itemElement->NextSiblingElement("rulerlayout");
+  }
+}
+
+void CGUIEPGGridContainer::UpdateLayout(bool updateAllItems)
+{
+  // if container is invalid, either new data has arrived, or m_blockSize has changed
+  //  need to run UpdateItems rather than CalculateLayout?
+  if (updateAllItems)
+  { // free memory of items
+    for (iItems it = m_channelItems.begin(); it != m_channelItems.end(); it++)
+      (*it)->FreeMemory();
+    for (iItems it = m_rulerItems.begin(); it != m_rulerItems.end(); it++)
+      (*it)->FreeMemory();
+    for (iItems it = m_programmeItems.begin(); it != m_programmeItems.end(); it++)
+      (*it)->FreeMemory();
+  }
+
+  // and recalculate the layout
+  CalculateLayout();
+}
+
+CStdString CGUIEPGGridContainer::GetDescription() const
+{
+  CStdString strLabel;
+  int item = GetSelectedItem();
+  if (item >= 0 && item < (int)m_programmeItems.size())
+  {
+    CGUIListItemPtr pItem = m_programmeItems[item];
+    strLabel = pItem->GetLabel();
+  }
+  return strLabel;
+}
+
+void CGUIEPGGridContainer::Reset()
+{
+  for (unsigned int i = 0; i < m_channelItems.size(); i++)
+    free(m_gridIndex[i]);
+  free(m_gridIndex);
+
+  m_wasReset = true;
+  m_channelItems.clear();
+  m_programmeItems.clear();
+  m_rulerItems.clear();
+  m_epgItemsPtr.clear();
+
+  m_lastItem    = NULL;
+  m_lastChannel = NULL;
+}
+
+void CGUIEPGGridContainer::GoToBegin()
+{
+  ScrollToBlockOffset(0);
+}
+
+void CGUIEPGGridContainer::GoToEnd()
+{
+  ScrollToBlockOffset(m_blocks - m_blocksPerPage);
+}
+
+void CGUIEPGGridContainer::SetStartEnd(CDateTime start, CDateTime end)
+{
+  m_gridStart = start;
+  m_gridEnd = end;
+}
+
+void CGUIEPGGridContainer::CalculateLayout()
+{
+  CGUIListItemLayout *oldFocusedChannelLayout   = m_focusedChannelLayout;
+  CGUIListItemLayout *oldChannelLayout          = m_channelLayout;
+  CGUIListItemLayout *oldFocusedProgrammeLayout = m_focusedProgrammeLayout;
+  CGUIListItemLayout *oldProgrammeLayout        = m_programmeLayout;
+  CGUIListItemLayout *oldRulerLayout            = m_rulerLayout;
+  GetCurrentLayouts();
+
+  if (!m_focusedProgrammeLayout || !m_programmeLayout || !m_focusedChannelLayout || !m_channelLayout || !m_rulerLayout)
+    return;
+
+  if (oldChannelLayout   == m_channelLayout   && oldFocusedChannelLayout   == m_focusedChannelLayout   &&
+      oldProgrammeLayout == m_programmeLayout && oldFocusedProgrammeLayout == m_focusedProgrammeLayout &&
+      oldRulerLayout     == m_rulerLayout)
+    return; // nothing has changed, so don't update stuff
+
+  m_channelHeight       = m_channelLayout->Size(VERTICAL);
+  m_channelWidth        = m_channelLayout->Size(HORIZONTAL);
+  if (m_orientation == VERTICAL)
+  {
+    m_rulerHeight       = m_rulerLayout->Size(VERTICAL);
+    m_gridPosX          = m_posX + m_channelWidth;
+    m_gridPosY          = m_posY + m_rulerHeight;
+    m_gridWidth         = m_width - m_channelWidth;
+    m_gridHeight        = m_height - m_rulerHeight;
+    m_blockSize         = m_gridWidth / m_blocksPerPage;
+    m_rulerWidth        = m_rulerUnit * m_blockSize;
+    m_channelPosX       = m_posX;
+    m_channelPosY       = m_posY + m_rulerHeight;
+    m_rulerPosX         = m_posX + m_channelWidth;
+    m_rulerPosY         = m_posY;
+    m_channelsPerPage   = (int)(m_gridHeight / m_channelHeight);
+    m_ProgrammesPerPage = (int)(m_gridWidth / m_blockSize) + 1;
+  }
+  else
+  {
+    m_rulerWidth        = m_rulerLayout->Size(HORIZONTAL);
+    m_gridPosX          = m_posX + m_rulerWidth;
+    m_gridPosY          = m_posY + m_channelHeight;
+    m_gridWidth         = m_width - m_rulerWidth;
+    m_gridHeight        = m_height - m_channelHeight;
+    m_blockSize         = m_gridHeight / m_blocksPerPage;
+    m_rulerHeight       = m_rulerUnit * m_blockSize;
+    m_channelPosX       = m_posX + m_rulerWidth;
+    m_channelPosY       = m_posY;
+    m_rulerPosX         = m_posX;
+    m_rulerPosY         = m_posY + m_channelHeight;
+    m_channelsPerPage   = (int)(m_gridWidth / m_channelWidth);
+    m_ProgrammesPerPage = (int)(m_gridHeight / m_blockSize) + 1;
+  }
+
+  // ensure that the scroll offsets are a multiple of our sizes
+  m_channelScrollOffset   = m_channelOffset * m_programmeLayout->Size(m_orientation);
+  m_programmeScrollOffset = m_blockOffset * m_blockSize;
+}
+
+void CGUIEPGGridContainer::UpdateScrollOffset()
+{
+  m_channelScrollOffset += m_channelScrollSpeed * (m_renderTime - m_channelScrollLastTime);
+  if ((m_channelScrollSpeed < 0 && m_channelScrollOffset < m_channelOffset * m_programmeLayout->Size(m_orientation)) ||
+      (m_channelScrollSpeed > 0 && m_channelScrollOffset > m_channelOffset * m_programmeLayout->Size(m_orientation)))
+  {
+    m_channelScrollOffset = m_channelOffset * m_programmeLayout->Size(m_orientation);
+    m_channelScrollSpeed = 0;
+  }
+  m_channelScrollLastTime = m_renderTime;
+
+  m_programmeScrollOffset += m_programmeScrollSpeed * (m_renderTime - m_programmeScrollLastTime);
+  if ((m_programmeScrollSpeed < 0 && m_programmeScrollOffset < m_blockOffset * m_blockSize) ||
+      (m_programmeScrollSpeed > 0 && m_programmeScrollOffset > m_blockOffset * m_blockSize))
+  {
+    m_programmeScrollOffset = m_blockOffset * m_blockSize;
+    m_programmeScrollSpeed = 0;
+  }
+  m_programmeScrollLastTime = m_renderTime;
+}
+
+void CGUIEPGGridContainer::GetCurrentLayouts()
+{
+  m_channelLayout = NULL;
+  for (unsigned int i = 0; i < m_channelLayouts.size(); i++)
+  {
+    int condition = m_channelLayouts[i].GetCondition();
+    if (!condition || g_infoManager.GetBool(condition, GetParentID()))
+    {
+      m_channelLayout = &m_channelLayouts[i];
+      break;
+    }
+  }
+  if (!m_channelLayout && m_channelLayouts.size())
+    m_channelLayout = &m_channelLayouts[0];  // failsafe
+
+  m_focusedChannelLayout = NULL;
+  for (unsigned int i = 0; i < m_focusedChannelLayouts.size(); i++)
+  {
+    int condition = m_focusedChannelLayouts[i].GetCondition();
+    if (!condition || g_infoManager.GetBool(condition, GetParentID()))
+    {
+      m_focusedChannelLayout = &m_focusedChannelLayouts[i];
+      break;
+    }
+  }
+  if (!m_focusedChannelLayout && m_focusedChannelLayouts.size())
+    m_focusedChannelLayout = &m_focusedChannelLayouts[0];  // failsafe
+
+  m_programmeLayout = NULL;
+  for (unsigned int i = 0; i < m_programmeLayouts.size(); i++)
+  {
+    int condition = m_programmeLayouts[i].GetCondition();
+    if (!condition || g_infoManager.GetBool(condition, GetParentID()))
+    {
+      m_programmeLayout = &m_programmeLayouts[i];
+      break;
+    }
+  }
+  if (!m_programmeLayout && m_programmeLayouts.size())
+    m_programmeLayout = &m_programmeLayouts[0];  // failsafe
+
+  m_focusedProgrammeLayout = NULL;
+  for (unsigned int i = 0; i < m_focusedProgrammeLayouts.size(); i++)
+  {
+    int condition = m_focusedProgrammeLayouts[i].GetCondition();
+    if (!condition || g_infoManager.GetBool(condition, GetParentID()))
+    {
+      m_focusedProgrammeLayout = &m_focusedProgrammeLayouts[i];
+      break;
+    }
+  }
+  if (!m_focusedProgrammeLayout && m_focusedProgrammeLayouts.size())
+    m_focusedProgrammeLayout = &m_focusedProgrammeLayouts[0];  // failsafe
+
+  m_rulerLayout = NULL;
+  for (unsigned int i = 0; i < m_rulerLayouts.size(); i++)
+  {
+    int condition = m_rulerLayouts[i].GetCondition();
+    if (!condition || g_infoManager.GetBool(condition, GetParentID()))
+    {
+      m_rulerLayout = &m_rulerLayouts[i];
+      break;
+    }
+  }
+  if (!m_rulerLayout && m_rulerLayouts.size())
+    m_rulerLayout = &m_rulerLayouts[0];  // failsafe
+}
+
+int CGUIEPGGridContainer::CorrectOffset(int offset, int cursor) const
+{
+  return offset + cursor;
+}
+
+void CGUIEPGGridContainer::SetRenderOffset(const CPoint &offset)
+{
+  m_renderOffset = offset;
+}
+
+void CGUIEPGGridContainer::FreeChannelMemory(int keepStart, int keepEnd)
+{
+  if (keepStart < keepEnd)
+  { // remove before keepStart and after keepEnd
+    for (int i = 0; i < keepStart && i < (int)m_channelItems.size(); ++i)
+      m_channelItems[i]->FreeMemory();
+    for (int i = keepEnd + 1; i < (int)m_channelItems.size(); ++i)
+      m_channelItems[i]->FreeMemory();
+  }
+  else
+  { // wrapping
+    for (int i = keepEnd + 1; i < keepStart && i < (int)m_channelItems.size(); ++i)
+      m_channelItems[i]->FreeMemory();
+  }
+}
+
+void CGUIEPGGridContainer::FreeProgrammeMemory(int keepStart, int keepEnd)
+{
+  if (keepStart < keepEnd)
+  { // remove before keepStart and after keepEnd
+    for (unsigned int i = 0; i < m_epgItemsPtr.size(); i++)
+    {
+      unsigned long progIdx = m_epgItemsPtr[i].start;
+      unsigned long lastIdx = m_epgItemsPtr[i].stop;
+
+      for (unsigned int j = progIdx; j < keepStart+progIdx && j < lastIdx; ++j)
+        m_programmeItems[j]->FreeMemory();
+      for (unsigned int j = keepEnd+progIdx + 1; j < lastIdx; ++j)
+        m_programmeItems[j]->FreeMemory();
+    }
+  }
+  else
+  { // wrapping
+    for (unsigned int i = 0; i < m_epgItemsPtr.size(); i++)
+    {
+      unsigned long progIdx = m_epgItemsPtr[i].start;
+      unsigned long lastIdx = m_epgItemsPtr[i].stop;
+
+      for (unsigned int j = keepEnd+progIdx + 1; j < keepStart+progIdx && j < lastIdx; ++j)
+        m_programmeItems[j]->FreeMemory();
+    }
+  }
+}
+
+void CGUIEPGGridContainer::FreeRulerMemory(int keepStart, int keepEnd)
+{
+  if (keepStart < keepEnd)
+  { // remove before keepStart and after keepEnd
+    for (int i = 1; i < keepStart && i < (int)m_rulerItems.size(); ++i)
+      m_rulerItems[i]->FreeMemory();
+    for (int i = keepEnd + 1; i < (int)m_rulerItems.size(); ++i)
+      m_rulerItems[i]->FreeMemory();
+  }
+  else
+  { // wrapping
+    for (int i = keepEnd + 1; i < keepStart && i < (int)m_rulerItems.size(); ++i)
+    {
+      if (i == 0)
+        continue;
+      m_rulerItems[i]->FreeMemory();
+    }
+  }
+}
+
+void CGUIEPGGridContainer::GetChannelCacheOffsets(int &cacheBefore, int &cacheAfter)
+{
+  if (m_channelScrollSpeed > 0)
+  {
+    cacheBefore = 0;
+    cacheAfter = m_cacheChannelItems;
+  }
+  else if (m_channelScrollSpeed < 0)
+  {
+    cacheBefore = m_cacheChannelItems;
+    cacheAfter = 0;
+  }
+  else
+  {
+    cacheBefore = m_cacheChannelItems / 2;
+    cacheAfter = m_cacheChannelItems / 2;
+  }
+}
+
+void CGUIEPGGridContainer::GetProgrammeCacheOffsets(int &cacheBefore, int &cacheAfter)
+{
+  if (m_programmeScrollSpeed > 0)
+  {
+    cacheBefore = 0;
+    cacheAfter = m_cacheProgrammeItems;
+  }
+  else if (m_programmeScrollSpeed < 0)
+  {
+    cacheBefore = m_cacheProgrammeItems;
+    cacheAfter = 0;
+  }
+  else
+  {
+    cacheBefore = m_cacheProgrammeItems / 2;
+    cacheAfter = m_cacheProgrammeItems / 2;
+  }
+}
+
+void CGUIEPGGridContainer::GetRulerCacheOffsets(int &cacheBefore, int &cacheAfter)
+{
+  if (m_programmeScrollSpeed > 0)
+  {
+    cacheBefore = 0;
+    cacheAfter = m_cacheRulerItems;
+  }
+  else if (m_programmeScrollSpeed < 0)
+  {
+    cacheBefore = m_cacheRulerItems;
+    cacheAfter = 0;
+  }
+  else
+  {
+    cacheBefore = m_cacheRulerItems / 2;
+    cacheAfter = m_cacheRulerItems / 2;
+  }
+}
diff --git a/guilib/GUIEPGGridContainer.h b/guilib/GUIEPGGridContainer.h
new file mode 100644 (file)
index 0000000..e797d2b
--- /dev/null
@@ -0,0 +1,215 @@
+#pragma once
+
+/*
+ *      Copyright (C) 2005-2008 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "DateTime.h"
+#include "FileItem.h"
+#include "GUIControl.h"
+#include "GUIListItemLayout.h"
+
+#define MAXCHANNELS 20
+#define MAXBLOCKS   2304 //! !!_EIGHT_!! days of 5 minute blocks
+
+struct GridItemsPtr
+{
+  CGUIListItemPtr item;
+  float width;
+  float height;
+};
+
+class CGUIEPGGridContainer : public CGUIControl
+{
+public:
+  CGUIEPGGridContainer(int parentID, int controlID, float posX, float posY, float width, float height,
+                       ORIENTATION orientation, int scrollTime, int preloadItems, int minutesPerPage,
+                       int rulerUnit);
+  virtual ~CGUIEPGGridContainer(void);
+  virtual CGUIEPGGridContainer *Clone() const { return new CGUIEPGGridContainer(*this); };
+
+  virtual bool OnAction(const CAction &action);
+  virtual void OnDown();
+  virtual void OnUp();
+  virtual void OnLeft();
+  virtual void OnRight();
+  virtual bool OnMouseOver(const CPoint &point);
+  virtual bool OnMouseClick(int dwButton, const CPoint &point);
+  virtual bool OnMouseDoubleClick(int dwButton, const CPoint &point);
+  virtual bool OnMouseWheel(char wheel, const CPoint &point);
+  virtual bool OnMessage(CGUIMessage& message);
+  virtual void SetFocus(bool bOnOff);
+
+  virtual CStdString GetDescription() const;
+  const int GetNumChannels()   { return m_channels; };
+  virtual int GetSelectedItem() const;
+  const int GetSelectedChannel() { return m_channelCursor + m_channelOffset; }
+
+  void DoRender(unsigned int currentTime);
+  void Render();
+  void LoadLayout(TiXmlElement *layout);
+  void LoadContent(TiXmlElement *content);
+
+  virtual bool IsContainer() const { return true; };
+  CGUIListItemPtr GetListItem(int offset) const;
+
+  virtual int  CorrectOffset(int offset, int cursor) const;
+
+  /*! \brief Set the offset of the first item in the container from the container's position
+   Useful for lists/panels where the focused item may be larger than the non-focused items and thus
+   normally cut off from the clipping window defined by the container's position + size.
+   \param offset CPoint holding the offset in skin coordinates.
+   */
+  void SetRenderOffset(const CPoint &offset);
+
+  void GoToBegin();
+  void GoToEnd();
+  void SetStartEnd(CDateTime start, CDateTime end);
+
+protected:
+  bool OnClick(int actionID);
+  bool SelectItemFromPoint(const CPoint &point);
+
+  void UpdateItems();
+
+  void SetChannel(int channel);
+  void SetBlock(int block);
+  void ChannelScroll(int amount);
+  void ProgrammesScroll(int amount);
+  void ValidateOffset();
+  void UpdateLayout(bool refreshAllItems = false);
+  void CalculateLayout();
+  void Reset();
+
+  GridItemsPtr *GetItem(const int &channel);
+  GridItemsPtr *GetNextItem(const int &channel);
+  GridItemsPtr *GetPrevItem(const int &channel);
+  GridItemsPtr *GetClosestItem(const int &channel);
+
+  int  GetItemSize(GridItemsPtr *item);
+  int  GetBlock(const CGUIListItemPtr &item, const int &channel);
+  int  GetRealBlock(const CGUIListItemPtr &item, const int &channel);
+  void MoveToRow(int row);
+  bool MoveChannel(bool direction);
+  bool MoveProgrammes(bool direction);
+
+  CGUIListItemLayout *GetFocusedLayout() const;
+
+  void ScrollToBlockOffset(int offset);
+  void ScrollToChannelOffset(int offset);
+  void UpdateScrollOffset();
+  void RenderChannelItem(float posX, float posY, CGUIListItem *item, bool focused);
+  void RenderProgrammeItem(float posX, float posY, float width, float height, CGUIListItem *item, bool focused);
+  void GetCurrentLayouts();
+
+  CPoint m_renderOffset; ///< \brief render offset of the first item in the list \sa SetRenderOffset
+
+  ORIENTATION m_orientation;
+
+  struct ItemsPtr
+  {
+    long start;
+    long stop;
+  };
+  std::vector< ItemsPtr > m_epgItemsPtr;
+  std::vector< CGUIListItemPtr > m_channelItems;
+  std::vector< CGUIListItemPtr > m_rulerItems;
+  std::vector< CGUIListItemPtr > m_programmeItems;
+  typedef std::vector<CGUIListItemPtr> ::iterator iItems;
+
+  std::vector<CGUIListItemLayout> m_channelLayouts;
+  std::vector<CGUIListItemLayout> m_focusedChannelLayouts;
+  std::vector<CGUIListItemLayout> m_focusedProgrammeLayouts;
+  std::vector<CGUIListItemLayout> m_programmeLayouts;
+  std::vector<CGUIListItemLayout> m_rulerLayouts;
+
+  CGUIListItemLayout *m_channelLayout;
+  CGUIListItemLayout *m_focusedChannelLayout;
+  CGUIListItemLayout *m_programmeLayout;
+  CGUIListItemLayout *m_focusedProgrammeLayout;
+  CGUIListItemLayout *m_rulerLayout;
+
+  bool m_wasReset;  // true if we've received a Reset message until we've rendered once.  Allows
+                    // us to make sure we don't tell the infomanager that we've been moving when
+                    // the "movement" was simply due to the list being repopulated (thus cursor position
+                    // changing around)
+
+  void FreeChannelMemory(int keepStart, int keepEnd);
+  void FreeProgrammeMemory(int keepStart, int keepEnd);
+  void FreeRulerMemory(int keepStart, int keepEnd);
+
+  void GetChannelCacheOffsets(int &cacheBefore, int &cacheAfter);
+  void GetProgrammeCacheOffsets(int &cacheBefore, int &cacheAfter);
+  void GetRulerCacheOffsets(int &cacheBefore, int &cacheAfter);
+
+private:
+  int   m_rulerUnit; //! number of blocks that makes up one element of the ruler
+  int   m_channels;
+  int   m_channelsPerPage;
+  int   m_ProgrammesPerPage;
+  int   m_channelCursor;
+  int   m_channelOffset;
+  int   m_blocks;
+  int   m_blocksPerPage;
+  int   m_blockCursor;
+  int   m_blockOffset;
+  int   m_cacheChannelItems;
+  int   m_cacheProgrammeItems;
+  int   m_cacheRulerItems;
+
+  float m_rulerPosX;      //! X position of first ruler item
+  float m_rulerPosY;      //! Y position of first ruler item
+  float m_rulerHeight;    //! height of the scrolling timeline above the ruler items
+  float m_rulerWidth;     //! width of each element of the ruler
+  float m_channelPosX;    //! Y position of first channel row
+  float m_channelPosY;    //! Y position of first channel row
+  float m_channelHeight;  //! height of each channel row (& every grid item)
+  float m_channelWidth;   //! width of the channel item
+  float m_gridPosX;       //! X position of first grid item
+  float m_gridPosY;       //! Y position of first grid item
+  float m_gridWidth;
+  float m_gridHeight;
+  float m_blockSize;      //! a block's width in pixels
+  float m_analogScrollCount;
+
+  CDateTime m_gridStart;
+  CDateTime m_gridEnd;
+
+  struct GridItemsPtr **m_gridIndex;
+  GridItemsPtr *m_item;
+  CGUIListItem *m_lastItem;
+  CGUIListItem *m_lastChannel;
+
+  unsigned int m_renderTime;
+
+  int   m_scrollTime;
+  bool  m_channelWrapAround;
+  bool  m_gridWrapAround; //! only when no more data available should this be true
+
+  int m_programmeScrollLastTime;
+  float m_programmeScrollSpeed;
+  float m_programmeScrollOffset;
+
+  int m_channelScrollLastTime;
+  float m_channelScrollSpeed;
+  float m_channelScrollOffset;
+
+  CStdString m_label;
+};
index ded9e1f..a66cd86 100644 (file)
@@ -255,6 +255,20 @@ void CGUIEditControl::OnClick()
     case INPUT_TYPE_SECONDS:
       textChanged = CGUIDialogNumeric::ShowAndGetSeconds(utf8, g_localizeStrings.Get(21420));
       break;
+    case INPUT_TYPE_TIME:
+    {
+      CDateTime dateTime;
+      dateTime.SetFromDBTime(utf8);
+      SYSTEMTIME time;
+      dateTime.GetAsSystemTime(time);
+      if (CGUIDialogNumeric::ShowAndGetTime(time, heading > 0 ? heading : g_localizeStrings.Get(21420)))
+      {
+        dateTime = CDateTime(time);
+        utf8 = dateTime.GetAsLocalizedTime("", false);
+        textChanged = true;
+      }
+      break;
+    }
     case INPUT_TYPE_DATE:
     {
       CDateTime dateTime;
@@ -263,7 +277,7 @@ void CGUIEditControl::OnClick()
         dateTime = CDateTime(2000, 1, 1, 0, 0, 0);
       SYSTEMTIME date;
       dateTime.GetAsSystemTime(date);
-      if (CGUIDialogNumeric::ShowAndGetDate(date, g_localizeStrings.Get(21420)))
+      if (CGUIDialogNumeric::ShowAndGetDate(date, heading > 0 ? heading : g_localizeStrings.Get(21420)))
       {
         dateTime = CDateTime(date);
         utf8 = dateTime.GetAsDBDate();
index ada7bd8..63e0c79 100644 (file)
@@ -44,6 +44,7 @@ public:
                     INPUT_TYPE_TEXT = 0,
                     INPUT_TYPE_NUMBER,
                     INPUT_TYPE_SECONDS,
+                    INPUT_TYPE_TIME,
                     INPUT_TYPE_DATE,
                     INPUT_TYPE_IPADDRESS,
                     INPUT_TYPE_PASSWORD,
index f7e53b3..9ef7ec3 100644 (file)
@@ -139,6 +139,13 @@ float CGUILabelControl::GetWidth() const
   return m_width;
 }
 
+void CGUILabelControl::SetWidth(float width)
+{
+  m_width = width;
+  m_label.SetMaxRect(m_posX, m_posY, m_width, m_height);
+  CGUIControl::SetWidth(m_width);
+}
+
 bool CGUILabelControl::OnMessage(CGUIMessage& message)
 {
   if ( message.GetControlId() == GetID() )
index c82e89c..11a4c11 100644 (file)
@@ -50,6 +50,7 @@ public:
   virtual bool OnMessage(CGUIMessage& message);
   virtual CStdString GetDescription() const;
   virtual float GetWidth() const;
+  virtual void SetWidth(float width);
  
   const CLabelInfo& GetLabelInfo() const { return m_label.GetLabelInfo(); };
   void SetLabel(const std::string &strLabel);
index 215e221..c27a6db 100644 (file)
@@ -111,6 +111,50 @@ void CGUIListGroup::UpdateInfo(const CGUIListItem *item)
   }
 }
 
+void CGUIListGroup::EnlargeWidth(float difference)
+{
+  // Alters the width of the controls that have an ID of 1
+  for (iControls it = m_children.begin(); it != m_children.end(); it++)
+  {
+    CGUIControl *child = *it;
+    if (child->GetID() >= 1 && child->GetID() <= 14)
+    {
+      if (child->GetID() == 1) // label
+      {
+        child->SetWidth(child->GetWidth() + difference - 10);
+        child->SetVisible(child->GetWidth() > 10); ///
+      }
+      else
+      {
+        child->SetWidth(child->GetWidth() + difference);
+      }
+    }
+  }
+  SetInvalid();
+}
+
+void CGUIListGroup::EnlargeHeight(float difference)
+{
+  // Alters the width of the controls that have an ID of 1
+  for (iControls it = m_children.begin(); it != m_children.end(); it++)
+  {
+    CGUIControl *child = *it;
+    if (child->GetID() >= 1 && child->GetID() <= 14)
+    {
+      if (child->GetID() == 1) // label
+      {
+        child->SetHeight(child->GetHeight() + difference);
+        child->SetVisible(child->GetHeight() > 10); ///
+      }
+      else
+      {
+        child->SetHeight(child->GetHeight() + difference);
+      }
+    }
+  }
+  SetInvalid();
+}
+
 void CGUIListGroup::SetFocusedItem(unsigned int focus)
 {
   for (iControls it = m_children.begin(); it != m_children.end(); it++)
index 241ca22..c8ad9d5 100644 (file)
@@ -47,6 +47,8 @@ public:
   virtual void UpdateVisibility(const CGUIListItem *item = NULL);
   virtual void UpdateInfo(const CGUIListItem *item);
 
+  void EnlargeWidth(float difference);
+  void EnlargeHeight(float difference);
   void SetFocusedItem(unsigned int subfocus);
   unsigned int GetFocusedItem() const;
   bool MoveLeft();
index ac44392..198b2b2 100644 (file)
@@ -103,6 +103,20 @@ unsigned int CGUIListItemLayout::GetFocusedItem() const
   return m_group.GetFocusedItem();
 }
 
+void CGUIListItemLayout::SetWidth(float width)
+{
+  m_group.EnlargeWidth(width - m_width);
+  m_width = width;
+  SetInvalid();
+}
+
+void CGUIListItemLayout::SetHeight(float height)
+{
+  m_group.EnlargeHeight(height - m_height);
+  m_height = height;
+  SetInvalid();
+}
+
 void CGUIListItemLayout::SelectItemFromPoint(const CPoint &point)
 {
   m_group.SelectItemFromPoint(point);
index e934841..b941ba5 100644 (file)
@@ -48,6 +48,8 @@ public:
   void CreateListControlLayouts(float width, float height, bool focused, const CLabelInfo &labelInfo, const CLabelInfo &labelInfo2, const CTextureInfo &texture, const CTextureInfo &textureFocus, float texHeight, float iconWidth, float iconHeight, int nofocusCondition, int focusCondition);
 //#endif
 
+  void SetWidth(float width);
+  void SetHeight(float height);
   void SelectItemFromPoint(const CPoint &point);
   bool MoveLeft();
   bool MoveRight();
index 28439cf..42561e4 100644 (file)
@@ -89,6 +89,18 @@ void CGUIListLabel::SetInvalid()
   CGUIControl::SetInvalid();
 }
 
+void CGUIListLabel::SetWidth(float width)
+{
+  m_width = width;
+  if (m_label.GetLabelInfo().align & XBFONT_RIGHT)
+    m_label.SetMaxRect(m_posX - m_width, m_posY, m_width, m_height);
+  else if (m_label.GetLabelInfo().align & XBFONT_CENTER_X)
+    m_label.SetMaxRect(m_posX - m_width*0.5f, m_posY, m_width, m_height);
+  else
+    m_label.SetMaxRect(m_posX, m_posY, m_posX + m_width, m_posY + m_height);
+  CGUIControl::SetWidth(m_width);
+}
+
 void CGUIListLabel::SetLabel(const CStdString &label)
 {
   m_label.SetText(label);
index 5c82fe1..a39e35e 100644 (file)
@@ -46,6 +46,7 @@ public:
   virtual void UpdateInfo(const CGUIListItem *item = NULL);
   virtual void SetFocus(bool focus);
   virtual void SetInvalid();
+  virtual void SetWidth(float width);
 
   void SetLabel(const CStdString &label);
   void SetSelected(bool selected);
index d50f60a..ffc7a7c 100644 (file)
 #define ACTION_AUDIO_DELAY            161
 #define ACTION_SUBTITLE_DELAY         162
 
+#define ACTION_RECORD                 170
+
 #define ACTION_PASTE                  180
 #define ACTION_NEXT_CONTROL           181
 #define ACTION_PREV_CONTROL           182
 #define WINDOW_SETTINGS_MYVIDEOS          10017
 #define WINDOW_SETTINGS_NETWORK           10018
 #define WINDOW_SETTINGS_APPEARANCE        10019
-
-#define WINDOW_SCRIPTS                    10020 // virtual window for backward compatibility
+#define WINDOW_SETTINGS_MYTV              10020
 
 #define WINDOW_VIDEO_FILES                10024
 #define WINDOW_VIDEO_NAV                  10025
 #define WINDOW_VIDEO_PLAYLIST             10028
 
 #define WINDOW_LOGIN_SCREEN               10029
+#define WINDOW_SCRIPTS                    10030
 #define WINDOW_SETTINGS_PROFILES          10034
 
 #define WINDOW_ADDON_BROWSER              10040
 #define WINDOW_MUSIC_NAV                  10502
 #define WINDOW_MUSIC_PLAYLIST_EDITOR      10503
 
-#define WINDOW_DIALOG_OSD_TELETEXT        10600
+// PVR related Window and Dialog ID's
+#define WINDOW_TV                         10600
+#define WINDOW_DIALOG_PVR_GUIDE_INFO      10601
+#define WINDOW_DIALOG_PVR_RECORDING_INFO  10602
+#define WINDOW_DIALOG_PVR_TIMER_SETTING   10603
+#define WINDOW_DIALOG_PVR_GROUP_MANAGER   10604
+#define WINDOW_DIALOG_PVR_CHANNEL_MANAGER 10605
+#define WINDOW_DIALOG_PVR_GUIDE_SEARCH    10606
+#define WINDOW_DIALOG_PVR_CHANNEL_SCAN    10607
+#define WINDOW_DIALOG_PVR_UPDATE_PROGRESS 10608
+#define WINDOW_DIALOG_PVR_OSD_CHANNELS    10609
+#define WINDOW_DIALOG_PVR_OSD_GUIDE       10610
+#define WINDOW_DIALOG_PVR_OSD_DIRECTOR    10611
+#define WINDOW_DIALOG_PVR_OSD_CUTTER      10612
+#define WINDOW_DIALOG_OSD_TELETEXT        10613
+#define WINDOW_DIALOG_EPG_SCAN            10614
 
 //#define WINDOW_VIRTUAL_KEYBOARD           11000
 #define WINDOW_DIALOG_SELECT              12000
 #define WINDOW_PYTHON_START               13000
 #define WINDOW_PYTHON_END                 13099
 
+// WINDOW_ID's from 14000 to 14099 reserved for Addons
+
+#define WINDOW_ADDON_START                14000
+#define WINDOW_ADDON_END                  14099
+
 #define ICON_TYPE_NONE          101
 #define ICON_TYPE_PROGRAMS      102
 #define ICON_TYPE_MUSIC         103
index 7b34605..58b9cd7 100644 (file)
@@ -19,6 +19,7 @@ SRCS=AnimatedGif.cpp \
      GUIControlGroupList.cpp \
      GUIDialog.cpp \
      GUIEditControl.cpp \
+     GUIEPGGridContainer.cpp \
      GUIFadeLabelControl.cpp \
      GUIFixedListContainer.cpp \
      GUIFont.cpp \
index 7ad7a1c..73c2244 100644 (file)
@@ -42,6 +42,7 @@
 #define HAS_UPNP
 #define HAS_VIDEO_PLAYBACK
 #define HAS_VISUALISATION
+#define HAS_PVRCLIENTS
 
 #ifdef HAVE_LIBMICROHTTPD
 #define HAS_WEB_SERVER
index c8d47a4..b9ed629 100644 (file)
@@ -1,10 +1,16 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
 <!--Language file translated with Team XBMC Translator-->
+<<<<<<< HEAD
+<!--Translator: Lars Op den Kamp-->
+<!--Email: lars@opdenkamp.eu-->
+<!--Date of translation: 11/15/2010-->
+=======
 <!--Translator: idioteque-->
 <!--Email: ronaldloggen@gmail.com-->
 <!--Date of translation: 12/28/2010-->
+>>>>>>> upstream/master
 <!--$Revision$-->
-<!--Based on english strings version 35208-->
+<!--Based on english strings version 32586-->
 <strings>
   <string id="0">Programma's</string>
   <string id="1">Afbeeldingen</string>
   <string id="13013">Opnieuw opstarten</string>
   <string id="13014">Minimaliseren</string>
   <string id="13015">Actie voor aan/uitknop</string>
+  <string id="13016">Uitschakelen</string>
   <string id="13020">Er is een andere sessie actief, wellicht SSH?</string>
   <string id="13021">Verwisselbare harde schijf aankoppelen?</string>
   <string id="13022">Apparaat onveilig verwijderd</string>
   <string id="13506">Laag (snel)</string>
   <string id="13507">Medium</string>
   <string id="13508">Hoog</string>
+<<<<<<< HEAD
+  <string id="13509">Echt hoog</string>
+  <string id="13510">Afspelen video naar beeldscherm synchroniseren</string>
+  <string id="13550">Pauzeer tijdens aanpassen verversingssnelheid</string>
+  <string id="13551">Uit</string>
+  <string id="13552">%.1f Seconde</string>
+  <string id="13553">%.1f Seconden</string>
+  <string id="13600">Apple afstandsbediening</string>
+  <string id="13602">Altijd actief</string>
+=======
   <string id="13509">Echt hoog (traag!)</string>
   <string id="13510">Synchroniseer video met beeldschermfrequentie</string>
   <string id="13600">Type Apple afstandsbediening</string>
   <string id="13602">Gebruik afstandsbediening om XBMC zelf op te staren</string>
+>>>>>>> upstream/master
   <string id="13603">Ondertitel eerder/later starten</string>
   <string id="13610">Uitgeschakeld</string>
   <string id="13611">Standaard</string>
   <string id="16317">Temporal (Half)</string>
   <string id="16318">Temporal/Spatial (Half)</string>
   <string id="16319">DXVA</string>
+<<<<<<< HEAD
+  <string id="16320">Auto - ION geoptimaliseerd</string>
+  <string id="16400">Video post-processing</string>
+=======
   <string id="16400">Kwaliteitsverbetering video</string>
+>>>>>>> upstream/master
   <string id="16401">Uitgeschakeld</string>
-  <string id="16402">Ingeschakeld voor SD beelden</string>
+  <string id="16402">Ingeschakeld voor SD content</string>
   <string id="16403">Altijd ingeschakeld</string>
   <string id="17500">Uitschakeltijd beeldscherm</string>
+  <string id="17997">%i MB</string>
+  <string id="17998">%i uren</string>
+  <string id="17999">%i dagen</string>
   <string id="19000">Kanaal wijzigen</string>
+<<<<<<< HEAD
+  <string id="19001">Scheid de zoekwoorden door middel van AND, OR en/of NOT om de zoekopdracht te specificeren.</string>
+  <string id="19002">Of gebruik zinnen om op een tekst te zoeken, bijv. "Toen was geluk heel gewoon".</string>
+  <string id="19003">Zoek vergelijkbaar programma</string>
+  <string id="19004">Gids van clients wordt geladen</string>
+  <string id="19005">PVR stream informatie</string>
+  <string id="19006">Ontvanger</string>
+  <string id="19007">Status ontvanger</string>
+  <string id="19008">Signaal kwaliteit</string>
+  <string id="19009">SNR</string>
+  <string id="19010">BER</string>
+  <string id="19011">UNC</string>
+  <string id="19012">Server</string>
+  <string id="19013">Ongecodeerd</string>
+  <string id="19014">Vast</string>
+  <string id="19015">Versleuteling</string>
+  <string id="19016">Server %i - %s</string>
+  <string id="19017">TV Opnames</string>
+  <string id="19018">Standaard map voor TV thumbnails</string>
+  <string id="19019">Kanalen</string>
+  <string id="19020">TV</string>
+  <string id="19021">Radio</string>
+  <string id="19022">Verborgen</string>
+  <string id="19023">TV kanalen</string>
+  <string id="19024">Radio kanalen</string>
+  <string id="19025">Toekomstige opnames</string>
+  <string id="19026">Voeg opname toe...</string>
+  <string id="19027">Geen zoekresultaten</string>
+  <string id="19028">Geen gids aanwezig</string>
+  <string id="19029">TV gids</string>
+  <string id="19030">Nu</string>
+  <string id="19031">Straks</string>
+  <string id="19032">Tijdslijn</string>
+  <string id="19033">Informatie</string>
+  <string id="19034">Opname is al gestart</string>
+  <string id="19035">Kanaal kan niet worden weergegeven</string>
+  <string id="19036">Opname kan niet worden weergegeven</string>
+  <string id="19037">Toon signaal kwaliteit</string>
+  <string id="19038">Op dit moment niet ondersteund!</string>
+  <string id="19039">Bevestig verbergen van het kanaal</string>
+  <string id="19040">Planning</string>
+  <string id="19041">Hernoem opname?</string>
+  <string id="19042">Hernoem geplande opname?</string>
+  <string id="19043">Opname</string>
+  <string id="19044">Controleer de instellingen van de server</string>
+  <string id="19045">Geen PVR clients beschikbaar</string>
+  <string id="19046">Nieuw kanaal</string>
+  <string id="19047">Programma info</string>
+  <string id="19048">Beheer groepen</string>
+  <string id="19049">Toon kanaal</string>
+  <string id="19050">Toon normale kanalen</string>
+  <string id="19051">Toon verborgen kanalen</string>
+  <string id="19052">Verplaats kanaal naar:</string>
+  <string id="19053">Opname informatie</string>
+  <string id="19054">Verberg kanaal</string>
+  <string id="19055">Geen informatie beschikbaar!</string>
+  <string id="19056">Nieuwe geplande opname</string>
+  <string id="19057">Pas geplande opname aan</string>
+  <string id="19058">Geplande opname aan/uit</string>
+  <string id="19059">Stop opname</string>
+  <string id="19060">Annuleer opname</string>
+  <string id="19061">Opnemen</string>
+  <string id="19062">Sorteer op: Kanaal</string>
+  <string id="19063">Ga naar begin</string>
+  <string id="19064">Ga naar einde</string>
+  <string id="19065">Standaard gids</string>
+  <string id="19066">Laadt opname informatie van clients</string>
+  <string id="19067">Dit programma wordt al opgenomen</string>
+  <string id="19068">Kon de opname niet verwijderen</string>
+  <string id="19069">TV gids</string>
+  <string id="19070">TV gids scan timeout</string>
+  <string id="19071">TV gids update timeout</string>
+  <string id="19072">Negeer database voor PVR-Server gids</string>
+  <string id="19073">Kanaal timeout</string>
+  <string id="19074">Actief:</string>
+  <string id="19075">Naam:</string>
+  <string id="19076">Map:</string>
+  <string id="19077">Radio:</string>
+  <string id="19078">Kanaal:</string>
+  <string id="19079">Dag:</string>
+  <string id="19080">Begin:</string>
+  <string id="19081">Einde:</string>
+  <string id="19082">Prioriteit:</string>
+  <string id="19083">Levensduur (dagen):</string>
+  <string id="19084">Eerste dag:</string>
+  <string id="19085">Onbekend kanaal: %u</string>
+  <string id="19086">Ma-__-__-__-__-__-__</string>
+  <string id="19087">__-Di-__-__-__-__-__</string>
+  <string id="19088">__-__-Wo-__-__-__-__</string>
+  <string id="19089">__-__-__-Do-__-__-__</string>
+  <string id="19090">__-__-__-__-Vr-__-__</string>
+  <string id="19091">__-__-__-__-__-Za-__</string>
+  <string id="19092">__-__-__-__-__-__-Zo</string>
+  <string id="19093">Ma-Di-Wo-Do-Vr-__-__</string>
+  <string id="19094">Ma-Di-Wo-Do-Vr-Za-__</string>
+  <string id="19095">Ma-Di-Wo-Do-Vr-Za-Zo</string>
+  <string id="19096">__-__-__-__-__-Za-Zo</string>
+  <string id="19097">Naam van de opname</string>
+  <string id="19098">Waarschuwing</string>
+  <string id="19099">Opname aanwezig</string>
+  <string id="19100">Verwijder kanaal en opname?</string>
+  <string id="19101">Kanaal wordt afgespeeld</string>
+  <string id="19102">Schakel over naar een ander kanaal!</string>
+  <string id="19103">Zoek missende iconen</string>
+  <string id="19104">Geef naam van de opname map</string>
+  <string id="19105">Grootte:</string>
+  <string id="19106">Volgende opname op</string>
+  <string id="19107">om</string>
+  <string id="19108">Opnames niet gesynchroniseerd</string>
+  <string id="19109">Kon de opname niet opslaan!</string>
+  <string id="19110">Probeer nog eens...</string>
+  <string id="19111">Server fout!</string>
+  <string id="19112">Opnames niet synchroon!</string>
+  <string id="19113">Volgende</string>
+  <string id="19114">Versie</string>
+  <string id="19115">Adres</string>
+  <string id="19116">Schijf grootte</string>
+  <string id="19117">Zoek naar kanalen</string>
+  <string id="19118">Kan de instellingen van de TV niet aanpassen tijdens het zoeken!</string>
+  <string id="19119">Op welke server wilt u zoeken?</string>
+  <string id="19120">Client nummer</string>
+  <string id="19121">Vermijd herhalingen</string>
+  <string id="19122">Opname loopt nog. Wilt u deze verwijderen?</string>
+  <string id="19123">Alleen ongecodeerd</string>
+  <string id="19124">Negeer bij lopende opnames</string>
+  <string id="19125">Negeer bij opgenomen programma's</string>
+  <string id="19126">Starttijd</string>
+  <string id="19127">Eindtijd</string>
+  <string id="19128">Startdatum</string>
+  <string id="19129">Einddatum</string>
+  <string id="19130">Minimale lengte</string>
+  <string id="19131">Maximale lengte</string>
+  <string id="19132">Neem onbekende genres op</string>
+  <string id="19133">Zoekopdracht</string>
+  <string id="19134">Neem omschrijving op</string>
+  <string id="19135">Hoofdlettergevoelig</string>
+  <string id="19136">Kanaal niet beschikbaar</string>
+  <string id="19137">Geen groepen gedifinieerd</string>
+  <string id="19138">Maak er eerst een aan</string>
+  <string id="19139">Naam nieuwe groep</string>
+  <string id="19140">Alle kanalen</string>
+  <string id="19141">Groep</string>
+  <string id="19142">Zoek in gids</string>
+  <string id="19143">Beheer groepen</string>
+  <string id="19144">Geen groepen</string>
+  <string id="19145">Gegroepeerd</string>
+  <string id="19146">Groepen</string>
+  <string id="19147">PVR-Server is niet compatibel!</string>
+  <string id="19148">Kanaal</string>
+  <string id="19149">Ma</string>
+  <string id="19150">Di</string>
+  <string id="19151">Wo</string>
+  <string id="19152">Do</string>
+  <string id="19153">Vr</string>
+  <string id="19154">Za</string>
+  <string id="19155">Zo</string>
+  <string id="19156">van</string>
+  <string id="19157">Volgende opname</string>
+  <string id="19158">Wordt nu opgenomen</string>
+  <string id="19159">van</string>
+  <string id="19160">tot</string>
+  <string id="19161">op</string>
+  <string id="19162">Wordt opgenomen</string>
+  <string id="19163">Opnames</string>
+  <string id="19164">Kan de opname niet starten</string>
+  <string id="19165">Wijzig kanaal</string>
+  <string id="19166">PVR informatie</string>
+  <string id="19167">Zoek missende iconen</string>
+  <string id="19168">Wijzig kanaal zonder op OK te drukken</string>
+  <string id="19169">Verberg video lengte informatie</string>
+  <string id="19170">Scan timeout voor kanaal afspelen</string>
+  <string id="19171">Start afspelen geminimaliseerd</string>
+  <string id="19172">Lengte directe opname</string>
+  <string id="19173">Standaard prioriteit</string>
+  <string id="19174">Standaard levensduur</string>
+  <string id="19175">Marge opname begin</string>
+  <string id="19176">Marge opname einde</string>
+  <string id="19177">Afspelen</string>
+  <string id="19178">Info bij wisselen kanaal</string>
+  <string id="19179">Timeout kanaal informatie</string>
+  <string id="19180">TV</string>
+  <string id="19181">Menu/OSD</string>
+  <string id="19182">Weergegeven dagen in de TV gids</string>
+  <string id="19183">TV gids wachttijd</string>
+  <string id="19184">Kanaal info tijd</string>
+  <string id="19185">Wis TV database</string>
+  <string id="19186">Alle gegevens in de TV database worden gewist</string>
+  <string id="19187">Wis TV gids en lees nieuwe in</string>
+  <string id="19188">TV gids wordt gewist</string>
+  <string id="19189">Hervat laatste kanaal bij opstarten</string>
+  <string id="19190">Geminimaliseerd</string>
+  <string id="19191">PVR service</string>
+  <string id="19192">Geen server ondersteunt het scannen naar kanalen</string>
+  <string id="19193">Kon het scannen naar kanalen niet starten</string>
+  <string id="19194">Hervatten?</string>
+  <string id="19195">Client acties</string>
+  <string id="19196">PVR client specifieke acties</string>
+  <string id="19197">Opname gestart om: %s</string>
+  <string id="19198">Opname beeindigd om: %s</string>
+  <string id="19199">Beheer kanalen</string>
+  <string id="19200">TV gids bron:</string>
+  <string id="19201">Naam kanaal:</string>
+  <string id="19202">Kanaal icoon:</string>
+  <string id="19203">Pas kanaal aan</string>
+  <string id="19204">Nieuw kanaal</string>
+  <string id="19205">Beheer groepen</string>
+  <string id="19206">Activeer TV gids:</string>
+  <string id="19207">Groep:</string>
+  <string id="19208">Voer de naam van het nieuwe kanaal in</string>
+  <string id="19209">XBMC intern virtueel kanaal</string>
+  <string id="19210">Client</string>
+  <string id="19211">Verwijder kanaal</string>
+  <string id="19212">Lijst bevat wijzigigen</string>
+  <string id="19213">Selecteer client voor het kanaal</string>
+  <string id="19214">Voer een geldige URL in voor het kanaal</string>
+  <string id="19215">De PVR server ondersteunt geen timers!</string>
+  <string id="19499">Anders/Onbekend</string>
+  <string id="19500">Film/Drama</string>
+  <string id="19501">Detective/Thriller</string>
+  <string id="19502">Avontuur/Western/Oorlog</string>
+  <string id="19503">Science Fiction/Fantasie/Horror</string>
+  <string id="19504">Komedie</string>
+  <string id="19505">Soap/Melodrama/Folklore</string>
+  <string id="19506">Romantisch</string>
+  <string id="19507">Serieus/Klassiek/Religie/Historische Film/Drama</string>
+  <string id="19508">Volwassenen Film/Drama</string>
+  <string id="19509">
+  </string>
+  <string id="19510">
+  </string>
+  <string id="19511">
+  </string>
+  <string id="19512">
+  </string>
+  <string id="19513">
+  </string>
+  <string id="19514">
+  </string>
+  <string id="19515">
+  </string>
+  <string id="19516">Nieuws/Actualiteiten</string>
+  <string id="19517">Nieuws/Weer</string>
+  <string id="19518">Nieuws magazine</string>
+  <string id="19519">Documentaire</string>
+  <string id="19520">Discussie/Interview/Debat</string>
+  <string id="19521">
+  </string>
+  <string id="19522">
+  </string>
+  <string id="19523">
+  </string>
+  <string id="19524">
+  </string>
+  <string id="19525">
+  </string>
+  <string id="19526">
+  </string>
+  <string id="19527">
+  </string>
+  <string id="19528">
+  </string>
+  <string id="19529">
+  </string>
+  <string id="19530">
+  </string>
+  <string id="19531">
+  </string>
+  <string id="19532">Show/Spelshow</string>
+  <string id="19533">Spelshow/Quiz/Wedstrijd</string>
+  <string id="19534">Variété</string>
+  <string id="19535">Praatprogramma</string>
+  <string id="19536">
+  </string>
+  <string id="19537">
+  </string>
+  <string id="19538">
+  </string>
+  <string id="19539">
+  </string>
+  <string id="19540">
+  </string>
+  <string id="19541">
+  </string>
+  <string id="19542">
+  </string>
+  <string id="19543">
+  </string>
+  <string id="19544">
+  </string>
+  <string id="19545">
+  </string>
+  <string id="19546">
+  </string>
+  <string id="19547">
+  </string>
+  <string id="19548">Sport</string>
+  <string id="19549">Speciaal</string>
+  <string id="19550">Sport Magazine</string>
+  <string id="19551">Voetbal</string>
+  <string id="19552">Tennis/Squash</string>
+  <string id="19553">Team Sport</string>
+  <string id="19554">Atletiek</string>
+  <string id="19555">Motorsport</string>
+  <string id="19556">Watersport</string>
+  <string id="19557">Wintersport</string>
+  <string id="19558">Ruitersport</string>
+  <string id="19559">Vechtsport</string>
+  <string id="19560">
+  </string>
+  <string id="19561">
+  </string>
+  <string id="19562">
+  </string>
+  <string id="19563">
+  </string>
+  <string id="19564">Kinderen/jeugdprogramma's</string>
+  <string id="19565">Peuter programma's</string>
+  <string id="19566">Entertainment programma's voor 6 tot 14</string>
+  <string id="19567">Entertainment programma's voor 10 tot 16</string>
+  <string id="19568">Informatie/Educatie/School Programma's</string>
+  <string id="19569">Cartoons/Marionetten</string>
+  <string id="19570">
+  </string>
+  <string id="19571">
+  </string>
+  <string id="19572">
+  </string>
+  <string id="19573">
+  </string>
+  <string id="19574">
+  </string>
+  <string id="19575">
+  </string>
+  <string id="19576">
+  </string>
+  <string id="19577">
+  </string>
+  <string id="19578">
+  </string>
+  <string id="19579">
+  </string>
+  <string id="19580">Muziek/Ballet/Dans</string>
+  <string id="19581">Rock/Pop</string>
+  <string id="19582">Serieus/Klassieke muziek</string>
+  <string id="19583">Folklore/Tradionele muziek</string>
+  <string id="19584">Muziek/Opera</string>
+  <string id="19585">Ballet</string>
+  <string id="19586">
+  </string>
+  <string id="19587">
+  </string>
+  <string id="19588">
+  </string>
+  <string id="19589">
+  </string>
+  <string id="19590">
+  </string>
+  <string id="19591">
+  </string>
+  <string id="19592">
+  </string>
+  <string id="19593">
+  </string>
+  <string id="19594">
+  </string>
+  <string id="19595">
+  </string>
+  <string id="19596">Kunst/Cultuur</string>
+  <string id="19597">Uitvoerende kunsten</string>
+  <string id="19598">Fijne kunsten</string>
+  <string id="19599">Religie</string>
+  <string id="19600">Populaire cultuur/Tradionele kunst</string>
+  <string id="19601">Literatuur</string>
+  <string id="19602">Film/Cinema</string>
+  <string id="19603">Experimentele film/Video</string>
+  <string id="19604">Uitzending/Pers</string>
+  <string id="19605">Nieuwe media</string>
+  <string id="19606">Kunst/Cultuur magazines</string>
+  <string id="19607">Mode</string>
+  <string id="19608">
+  </string>
+  <string id="19609">
+  </string>
+  <string id="19610">
+  </string>
+  <string id="19611">
+  </string>
+  <string id="19612">Sociaal/Politiek/Economisch</string>
+  <string id="19613">Magazines/Verslagen/Documentaires</string>
+  <string id="19614">Economisch/Maatschappelijk advies</string>
+  <string id="19615">Opmerkelijke personen</string>
+  <string id="19616">
+  </string>
+  <string id="19617">
+  </string>
+  <string id="19618">
+  </string>
+  <string id="19619">
+  </string>
+  <string id="19620">
+  </string>
+  <string id="19621">
+  </string>
+  <string id="19622">
+  </string>
+  <string id="19623">
+  </string>
+  <string id="19624">
+  </string>
+  <string id="19625">
+  </string>
+  <string id="19626">
+  </string>
+  <string id="19627">
+  </string>
+  <string id="19628">Educatie/Wetenschap/Feiten</string>
+  <string id="19629">Natuur/Dieren/Milieu</string>
+  <string id="19630">Technologie/Natuurwetenschappen</string>
+  <string id="19631">Geneeskunde/Physiologie/Psychologie</string>
+  <string id="19632">Buitenland/Expedities</string>
+  <string id="19633">Sociale/Spirituele wetenschap</string>
+  <string id="19634">Verder leren</string>
+  <string id="19635">Talen</string>
+  <string id="19636">
+  </string>
+  <string id="19637">
+  </string>
+  <string id="19638">
+  </string>
+  <string id="19639">
+  </string>
+  <string id="19640">
+  </string>
+  <string id="19641">
+  </string>
+  <string id="19642">
+  </string>
+  <string id="19643">
+  </string>
+  <string id="19644">Vrije tijd/Hobbies</string>
+  <string id="19645">Tourisme/Reizen</string>
+  <string id="19646">Handwerk</string>
+  <string id="19647">Autorijden</string>
+  <string id="19648">Fitness &amp; Gezondheid</string>
+  <string id="19649">Koken</string>
+  <string id="19650">Reclame/Winkelen</string>
+  <string id="19651">Tuinieren</string>
+  <string id="19652">
+  </string>
+  <string id="19653">
+  </string>
+  <string id="19654">
+  </string>
+  <string id="19655">
+  </string>
+  <string id="19656">
+  </string>
+  <string id="19657">
+  </string>
+  <string id="19658">
+  </string>
+  <string id="19659">
+  </string>
+  <string id="19660">Bijzondere kenmerken</string>
+  <string id="19661">Oude talen</string>
+  <string id="19662">Zwart/Wit</string>
+  <string id="19663">Niet gepubliceerd</string>
+  <string id="19664">Live uitzending</string>
+  <string id="19665">
+  </string>
+  <string id="19666">
+  </string>
+  <string id="19667">
+  </string>
+  <string id="19668">
+  </string>
+  <string id="19669">
+  </string>
+  <string id="19670">
+  </string>
+  <string id="19671">
+  </string>
+  <string id="19672">
+  </string>
+  <string id="19673">
+  </string>
+  <string id="19674">
+  </string>
+  <string id="19675">
+  </string>
+  <string id="19676">Drama</string>
+  <string id="19677">Detective/Thriller</string>
+  <string id="19678">Avontuur/Western/Oorlog</string>
+  <string id="19679">Science Fiction/Fantasie/Horror</string>
+  <string id="19680">Comedy</string>
+  <string id="19681">Soap/Melodrama/Folklore</string>
+  <string id="19682">Romantisch</string>
+  <string id="19683">Serieus/Klassiek-Regionaal/Historisch</string>
+  <string id="19684">Volwassenen</string>
+  <string id="19685">
+  </string>
+  <string id="19686">
+  </string>
+  <string id="19687">
+  </string>
+  <string id="19688">
+  </string>
+  <string id="19689">
+  </string>
+  <string id="19690">
+  </string>
+  <string id="19691">
+  </string>
+  <string id="20000">Geimporteerde muziek map</string>
+=======
   <string id="20000">Opgeslagen muziek map...</string>
+>>>>>>> upstream/master
   <string id="20001">Andere DVD-speler gebruiken</string>
   <string id="20002">- Locatie van de DVD-speler</string>
   <string id="20003">Trainersmap</string>
   <string id="20255">Eerste login, profiel bijwerken</string>
   <string id="20256">HTS tvheadend client</string>
   <string id="20257">VDR streamdev client</string>
+  <string id="20258">MythTV client</string>
   <string id="20300">Webservermap (HTTP)</string>
   <string id="20301">Webservermap (HTTPS)</string>
   <string id="20302">Kan niet schrijven naar map:</string>
   <string id="20451">Landen</string>
   <string id="20452">Aflevering</string>
   <string id="20453">Afleveringen</string>
+  <string id="20454">Luisteraar</string> 
+  <string id="20455">Luisteraars</string>
   <string id="21330">Verborgen bestanden en mappen weergeven</string>
   <string id="21331">TuxBox-client</string>
   <string id="21332">WAARSCHUWING: TuxBox is aan het opnemen.</string>
   <string id="24015">Muziekvideoinformatie</string>
   <string id="24016">Albuminformatie</string>
   <string id="24017">Artiestinformatie</string>
+  <string id="24019">Services</string>
+  <string id="24019">PVR clients</string>
   <string id="24020">Configureren</string>
   <string id="24021">Uitschakelen</string>
   <string id="24022">Inschakelen</string>
index 7b07c96..e6930af 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>
-<!--$Revision$-->
+<!--$Revision: 32586 $-->
 <strings>
   <string id="0">Programs</string>
   <string id="1">Pictures</string>
   <string id="16317">Temporal (Half)</string>
   <string id="16318">Temporal/Spatial (Half)</string>
   <string id="16319">DXVA</string>
+  <string id="16320">Auto - ION Optimized</string>
 
   <string id="16400">Post-processing</string>
 
   <string id="17500">Display sleep timeout</string>
 
+  <string id="17997">%i MByte</string>
+  <string id="17998">%i hours</string>
+  <string id="17999">%i days</string>
+
+  <!-- strings 19000 thru 19999 used for tv interface -->
   <string id="19000">Switch to channel</string>
+  <string id="19001">Separate different search words using AND, OR and/or NOT to specify your search.</string>
+  <string id="19002">Or use Phrases to find a full text e.G.: "The wizard of Oz".</string>
+  <string id="19003">Find similar program</string>
+  <string id="19004">Loading EPG from clients</string>
+  <string id="19005">PVR Stream Information</string>
+  <string id="19006">Receiving Device</string>
+  <string id="19007">Receiving Status</string>
+  <string id="19008">Signal Quality</string>
+  <string id="19009">SNR</string>
+  <string id="19010">BER</string>
+  <string id="19011">UNC</string>
+  <string id="19012">Backend</string>
+  <string id="19013">Free To Air</string>
+  <string id="19014">Fixed</string>
+  <string id="19015">Encryption</string>
+  <string id="19016">Server %i - %s</string>
+  <string id="19017">TV Recordings</string>
+  <string id="19018">Default folder for TV-Thumbnails</string>
+  <string id="19019">Channels</string>
+  <string id="19020">TV</string>
+  <string id="19021">Radio</string>
+  <string id="19022">Hidden</string>
+  <string id="19023">TV Channels</string>
+  <string id="19024">Radio Channels</string>
+  <string id="19025">Upcoming Recordings</string>
+  <string id="19026">Add Timer...</string>
+  <string id="19027">No search results present</string>
+  <string id="19028">No EPG entries present</string>
+  <string id="19029">TV Guide</string>
+  <string id="19030">Now</string>
+  <string id="19031">Next</string>
+  <string id="19032">Timeline</string>
+  <string id="19033">Information</string>
+  <string id="19034">Recording already started</string>
+  <string id="19035">Channel can not be played</string>
+  <string id="19036">Recording can not be played</string>
+  <string id="19037">Show Signal Qualitiy</string>
+  <string id="19038">Not Supported at the moment!</string>
+  <string id="19039">Confirm Channel hide</string>
+  <string id="19040">Timer</string>
+  <string id="19041">Rename recording?</string>
+  <string id="19042">Rename timer?</string>
+  <string id="19043">Recording</string>
+  <string id="19044">Please check your Settings or the Backend</string>
+  <string id="19045">No PVR Clients available</string>
+  <string id="19046">New Channel</string>
+  <string id="19047">Programme info</string>
+  <string id="19048">Group Managment</string>
+  <string id="19049">Show Channel</string>
+  <string id="19050">Show normal Channels</string>
+  <string id="19051">Show hidden Channels</string>
+  <string id="19052">Move channel to:</string>
+  <string id="19053">Recording information</string>
+  <string id="19054">Hide Channel</string>
+  <string id="19055">No Information available!</string>
+  <string id="19056">New Timer</string>
+  <string id="19057">Edit Timer</string>
+  <string id="19058">On/Off Timer</string>
+  <string id="19059">Stop Recording</string>
+  <string id="19060">Delete Timer</string>
+  <string id="19061">Add Timer</string>
+  <string id="19062">Sort by: Channel</string>
+  <string id="19063">Go to begin</string>
+  <string id="19064">Go to end</string>
+  <string id="19065">Default Programm Guide</string>
+  <string id="19066">Loading Recording Information from clients</string>
+  <string id="19067">Timer already set for this event</string>
+  <string id="19068">Couldn't delete recording!</string>
+  <string id="19069">EPG</string>
+  <string id="19070">EPG scan timeout</string>
+  <string id="19071">EPG update timeout</string>
+  <string id="19072">Ignore Database for PVR-Server EPG</string>
+  <string id="19073">Channel entry timeout</string>
+  <string id="19074">Active:</string>
+  <string id="19075">Name:</string>
+  <string id="19076">Directory:</string>
+  <string id="19077">Radio:</string>
+  <string id="19078">Channel:</string>
+  <string id="19079">Day:</string>
+  <string id="19080">Begin:</string>
+  <string id="19081">End:</string>
+  <string id="19082">Priority:</string>
+  <string id="19083">Lifetime (days):</string>
+  <string id="19084">First day:</string>
+  <string id="19085">Unknown Channel %u</string>
+  <string id="19086">Mo-__-__-__-__-__-__</string>
+  <string id="19087">__-Tu-__-__-__-__-__</string>
+  <string id="19088">__-__-We-__-__-__-__</string>
+  <string id="19089">__-__-__-Th-__-__-__</string>
+  <string id="19090">__-__-__-__-Fr-__-__</string>
+  <string id="19091">__-__-__-__-__-Sa-__</string>
+  <string id="19092">__-__-__-__-__-__-Su</string>
+  <string id="19093">Mo-Tu-We-Th-Fr-__-__</string>
+  <string id="19094">Mo-Tu-We-Th-Fr-Sa-__</string>
+  <string id="19095">Mo-Tu-We-Th-Fr-Sa-Su</string>
+  <string id="19096">__-__-__-__-__-Sa-Su</string>
+  <string id="19097">Enter the recording name</string>
+  <string id="19098">Warning</string>
+  <string id="19099">Timer present</string>
+  <string id="19100">Delete Channel and Timer?</string>
+  <string id="19101">Channel is playing</string>
+  <string id="19102">Please switch to another Channel!</string>
+  <string id="19103">Rescan for missing icons</string>
+  <string id="19104">Enter the recording directory</string>
+  <string id="19105">Size:</string>
+  <string id="19106">Next timer on</string>
+  <string id="19107">at</string>
+  <string id="19108">Recordings not in sync!</string>
+  <string id="19109">Couldn't save timer!</string>
+  <string id="19110">Try again...</string>
+  <string id="19111">Server error!</string>
+  <string id="19112">Timers not in sync!</string>
+  <string id="19113">Next</string>
+  <string id="19114">Version</string>
+  <string id="19115">Address</string>
+  <string id="19116">Disksize</string>
+  <string id="19117">Search for Channels</string>
+  <string id="19118">TV specific action are during search not possible!</string>
+  <string id="19119">On which server you want to search?</string>
+  <string id="19120">Client number</string>
+  <string id="19121">Avoid Repeats</string>
+  <string id="19122">Timer still recording - really delete?</string>
+  <string id="19123">Free-To-Air only</string>
+  <string id="19124">Ignore for by present Timers</string>
+  <string id="19125">Ignore for by present Recordings</string>
+  <string id="19126">Start Time</string>
+  <string id="19127">Stop Time</string>
+  <string id="19128">Start Date</string>
+  <string id="19129">Stop Date</string>
+  <string id="19130">Minimal Duration</string>
+  <string id="19131">Maximal Duration</string>
+  <string id="19132">Inluding unknown Genres</string>
+  <string id="19133">Search string</string>
+  <string id="19134">Include Description</string>
+  <string id="19135">Case Sensitive</string>
+  <string id="19136">Channel Unavailable</string>
+  <string id="19137">No Groups defined</string>
+  <string id="19138">Please create first one</string>
+  <string id="19139">New Group name</string>
+  <string id="19140">All Channels</string>
+  <string id="19141">Group</string>
+  <string id="19142">Search Guide</string>
+  <string id="19143">Group Managment</string>
+  <string id="19144">No Groups</string>
+  <string id="19145">Grouped</string>
+  <string id="19146">Groups</string>
+  <string id="19147">PVR-Server is not compatible!</string>
+  <string id="19148">Channel</string>
+  <string id="19149">Mo</string>
+  <string id="19150">Tu</string>
+  <string id="19151">We</string>
+  <string id="19152">Th</string>
+  <string id="19153">Fr</string>
+  <string id="19154">Sa</string>
+  <string id="19155">Su</string>
+  <string id="19156">from</string>
+  <string id="19157">Next Recording</string>
+  <string id="19158">Now Recording</string>
+  <string id="19159">from</string>
+  <string id="19160">to</string>
+  <string id="19161">On</string>
+  <string id="19162">Recording running</string>
+  <string id="19163">Recordings</string>
+  <string id="19164">Can not start recording</string>
+  <string id="19165">Switch</string>
+  <string id="19166">PVR information</string>
+  <string id="19167">Rescan for missing icons</string>
+  <string id="19168">Switch channel without pressing OK</string>
+  <string id="19169">Hide Video-Length info box</string>
+  <string id="19170">Scan Timeout before channel playback</string>
+  <string id="19171">Start playback Minimized</string>
+  <string id="19172">Instant record time</string>
+  <string id="19173">Default priority</string>
+  <string id="19174">Default lifetime</string>
+  <string id="19175">Margin at start</string>
+  <string id="19176">Margin at stop</string>
+  <string id="19177">Replay</string>
+  <string id="19178">Info on channel switch</string>
+  <string id="19179">Timeout requested channel info</string>
+  <string id="19180">TV</string>
+  <string id="19181">Menu/OSD</string>
+  <string id="19182">Days to display in TV Guide</string>
+  <string id="19183">TV Guide linger time</string>
+  <string id="19184">Channel info time</string>
+  <string id="19185">Clear TV Database</string>
+  <string id="19186">All data in the Database is being erased</string>
+  <string id="19187">Delete EPG and read new</string>
+  <string id="19188">EPG is being erased</string>
+  <string id="19189">Continue last channel after startup</string>
+  <string id="19190">Minimized</string>
+  <string id="19191">PVR service</string>
+  <string id="19192">No Server support channel scanning</string>
+  <string id="19193">Channel scan can't be started</string>
+  <string id="19194">Continue?</string>
+  <string id="19195">Client actions</string>
+  <string id="19196">PVR client specific actions</string>
+  <string id="19197">Recording started on: %s</string>
+  <string id="19198">Recording finished on: %s</string>
+  <string id="19199">Channel manager</string>
+  <string id="19200">EPG source:</string>
+  <string id="19201">Channel name:</string>
+  <string id="19202">Channel icon:</string>
+  <string id="19203">Edit channel</string>
+  <string id="19204">New channel</string>
+  <string id="19205">Group Managment</string>
+  <string id="19206">Activate EPG:</string>
+  <string id="19207">Group:</string>
+  <string id="19208">Enter new channel name</string>
+  <string id="19209">XBMC internal virtual channel</string>
+  <string id="19210">Client</string>
+  <string id="19211">Delete Channel</string>
+  <string id="19212">List contains changes</string>
+  <string id="19213">Select channel client</string>
+  <string id="19214">Enter a valid URL for the new channel</string>
+  <string id="19215">The PVR-Server don't support Timers!</string>
+
+  <string id="19499">Other/Unknown</string>
+  <string id="19500">Movie/Drama</string>
+  <string id="19501">Detective/Thriller</string>
+  <string id="19502">Adventure/Western/War</string>
+  <string id="19503">Science Fiction/Fantasy/Horror</string>
+  <string id="19504">Comedy</string>
+  <string id="19505">Soap/Melodrama/Folkloric</string>
+  <string id="19506">Romance</string>
+  <string id="19507">Serious/Classical/Religious/Historical Movie/Drama</string>
+  <string id="19508">Adult Movie/Drama</string>
+  <string id="19509"></string>
+  <string id="19510"></string>
+  <string id="19511"></string>
+  <string id="19512"></string>
+  <string id="19513"></string>
+  <string id="19514"></string>
+  <string id="19515"></string>
+  <string id="19516">News/Current Affairs</string>
+  <string id="19517">News/Weather Report</string>
+  <string id="19518">News Magazine</string>
+  <string id="19519">Documentary</string>
+  <string id="19520">Discussion/Inverview/Debate</string>
+  <string id="19521"></string>
+  <string id="19522"></string>
+  <string id="19523"></string>
+  <string id="19524"></string>
+  <string id="19525"></string>
+  <string id="19526"></string>
+  <string id="19527"></string>
+  <string id="19528"></string>
+  <string id="19529"></string>
+  <string id="19530"></string>
+  <string id="19531"></string>
+  <string id="19532">Show/Game Show</string>
+  <string id="19533">Game Show/Quiz/Contest</string>
+  <string id="19534">Variety Show</string>
+  <string id="19535">Talk Show</string>
+  <string id="19536"></string>
+  <string id="19537"></string>
+  <string id="19538"></string>
+  <string id="19539"></string>
+  <string id="19540"></string>
+  <string id="19541"></string>
+  <string id="19542"></string>
+  <string id="19543"></string>
+  <string id="19544"></string>
+  <string id="19545"></string>
+  <string id="19546"></string>
+  <string id="19547"></string>
+  <string id="19548">Sports</string>
+  <string id="19549">Special Event</string>
+  <string id="19550">Sport Magazine</string>
+  <string id="19551">Football</string>
+  <string id="19552">Tennis/Squash</string>
+  <string id="19553">Team Sports</string>
+  <string id="19554">Athletics</string>
+  <string id="19555">Motor Sport</string>
+  <string id="19556">Water Sport</string>
+  <string id="19557">Winter Sports</string>
+  <string id="19558">Equestrian</string>
+  <string id="19559">Martial Sports</string>
+  <string id="19560"></string>
+  <string id="19561"></string>
+  <string id="19562"></string>
+  <string id="19563"></string>
+  <string id="19564">Children's/Youth Programmes</string>
+  <string id="19565">Pre-school Children's Programmes</string>
+  <string id="19566">Entertainment Programmes for 6 to 14</string>
+  <string id="19567">Entertainment Programmes for 10 to 16</string>
+  <string id="19568">Informational/Educational/School Programme</string>
+  <string id="19569">Cartoons/Puppets</string>
+  <string id="19570"></string>
+  <string id="19571"></string>
+  <string id="19572"></string>
+  <string id="19573"></string>
+  <string id="19574"></string>
+  <string id="19575"></string>
+  <string id="19576"></string>
+  <string id="19577"></string>
+  <string id="19578"></string>
+  <string id="19579"></string>
+  <string id="19580">Music/Ballet/Dance</string>
+  <string id="19581">Rock/Pop</string>
+  <string id="19582">Serious/Classical Music</string>
+  <string id="19583">Folk/Tradional Music</string>
+  <string id="19584">Musical/Opera</string>
+  <string id="19585">Ballet</string>
+  <string id="19586"></string>
+  <string id="19587"></string>
+  <string id="19588"></string>
+  <string id="19589"></string>
+  <string id="19590"></string>
+  <string id="19591"></string>
+  <string id="19592"></string>
+  <string id="19593"></string>
+  <string id="19594"></string>
+  <string id="19595"></string>
+  <string id="19596">Arts/Culture</string>
+  <string id="19597">Performing Arts</string>
+  <string id="19598">Fine Arts</string>
+  <string id="19599">Religion</string>
+  <string id="19600">Popular Culture/Traditional Arts</string>
+  <string id="19601">Literature</string>
+  <string id="19602">Film/Cinema</string>
+  <string id="19603">Experimental Film/Video</string>
+  <string id="19604">Broadcasting/Press</string>
+  <string id="19605">New Media</string>
+  <string id="19606">Arts/Culture Magazines</string>
+  <string id="19607">Fashion</string>
+  <string id="19608"></string>
+  <string id="19609"></string>
+  <string id="19610"></string>
+  <string id="19611"></string>
+  <string id="19612">Social/Political/Economics</string>
+  <string id="19613">Magazines/Reports/Documentary</string>
+  <string id="19614">Economics/Social Advisory</string>
+  <string id="19615">Remarkable People</string>
+  <string id="19616"></string>
+  <string id="19617"></string>
+  <string id="19618"></string>
+  <string id="19619"></string>
+  <string id="19620"></string>
+  <string id="19621"></string>
+  <string id="19622"></string>
+  <string id="19623"></string>
+  <string id="19624"></string>
+  <string id="19625"></string>
+  <string id="19626"></string>
+  <string id="19627"></string>
+  <string id="19628">Education/Science/Factual</string>
+  <string id="19629">Nature/Animals/Environment</string>
+  <string id="19630">Technology/Natural Sciences</string>
+  <string id="19631">Medicine/Physiology/Psychology</string>
+  <string id="19632">Foreign Countries/Expeditions</string>
+  <string id="19633">Social/Spiritual Sciences</string>
+  <string id="19634">Further Education</string>
+  <string id="19635">Languages</string>
+  <string id="19636"></string>
+  <string id="19637"></string>
+  <string id="19638"></string>
+  <string id="19639"></string>
+  <string id="19640"></string>
+  <string id="19641"></string>
+  <string id="19642"></string>
+  <string id="19643"></string>
+  <string id="19644">Leisure/Hobbies</string>
+  <string id="19645">Tourism/Travel</string>
+  <string id="19646">Handicraft</string>
+  <string id="19647">Motoring</string>
+  <string id="19648">Fitness &amp; Health</string>
+  <string id="19649">Cooking</string>
+  <string id="19650">Advertisement/Shopping</string>
+  <string id="19651">Gardening</string>
+  <string id="19652"></string>
+  <string id="19653"></string>
+  <string id="19654"></string>
+  <string id="19655"></string>
+  <string id="19656"></string>
+  <string id="19657"></string>
+  <string id="19658"></string>
+  <string id="19659"></string>
+  <string id="19660">Special Characteristics</string>
+  <string id="19661">Original Language</string>
+  <string id="19662">Black &amp; White</string>
+  <string id="19663">Unpublished</string>
+  <string id="19664">Live Broadcast</string>
+  <string id="19665"></string>
+  <string id="19666"></string>
+  <string id="19667"></string>
+  <string id="19668"></string>
+  <string id="19669"></string>
+  <string id="19670"></string>
+  <string id="19671"></string>
+  <string id="19672"></string>
+  <string id="19673"></string>
+  <string id="19674"></string>
+  <string id="19675"></string>
+  <string id="19676">Drama</string>
+  <string id="19677">Detective/Thriller</string>
+  <string id="19678">Adventure/Western/War</string>
+  <string id="19679">Science Fiction/Fantasy/Horror</string>
+  <string id="19680">Comedy</string>
+  <string id="19681">Soap/Melodrama/Folkloric</string>
+  <string id="19682">Romance</string>
+  <string id="19683">Serious/ClassicalReligion/Historical</string>
+  <string id="19684">Adult</string>
+  <string id="19685"></string>
+  <string id="19686"></string>
+  <string id="19687"></string>
+  <string id="19688"></string>
+  <string id="19689"></string>
+  <string id="19690"></string>
+  <string id="19691"></string>
 
   <string id="20000">Saved music folder</string>
   <string id="20001">Use external DVD player</string>
   <string id="24016">Album information</string>
   <string id="24017">Artist information</string>
   <string id="24018">Services</string>
+  <string id="24019">PVR clients</string>
 
   <string id="24020">Configure</string>
   <string id="24021">Disable</string>
   <string id="24059">Would you like to enable this Add-on?</string>
   <string id="24060">Would you like to disable this Add-on?</string>
   <string id="24061">Add-on update available!</string>
-  <string id="24062">Enabled Add-ons</string>
+  <string id="24062">Installed Add-ons</string>
   <string id="24063">Auto update</string>
   <string id="24064">Add-on enabled</string>
   <string id="24065">Add-on updated</string>
index 79b6c60..645f410 100644 (file)
 
   <string id="17500">Bildschirm Sleep Timeout</string>
 
+  <!-- strings 19000 thru 19999 used for tv interface -->
   <string id="19000">Zum Kanal wechseln</string>
+  <string id="19001">Benutzen Sie AND, OR und NOT in Verbindung mit Ihren Suchbegriffen, um detaillierter zu suchen.</string>
+  <string id="19002">Oder benutzen Sie ganze Sätze in Ausrufezeichen z.B.: "Der Zauberer von Oz".</string>
+  <string id="19003">Finde ähnliche Sendung</string>
+  <string id="19004">Lade EPG von den PVR-Klienten</string>
+  <string id="19005">PVR Empfang-Informationen</string>
+  <string id="19006">Empfangsgerät</string>
+  <string id="19007">Empfangsstatus</string>
+  <string id="19008">Signal Qualität</string>
+  <string id="19009">SNR</string>
+  <string id="19010">BER</string>
+  <string id="19011">UNC</string>
+  <string id="19012">Backend</string>
+  <string id="19013">Frei empfangbar</string>
+  <string id="19014">Fest</string>
+  <string id="19015">Verschlüsselung</string>
+  <string id="19016">Server %i - %s</string>
+  <string id="19017">TV Aufnahmen</string>
+  <string id="19018">Standardordner für TV Thumbnails</string>
+  <string id="19019">Kanäle</string>
+  <string id="19020">TV</string>
+  <string id="19021">Radio</string>
+  <string id="19022">Versteckte</string>
+  <string id="19023">TV Kanäle</string>
+  <string id="19024">Radio Kanäle</string>
+  <string id="19025">Bevorstehende Aufnahmen</string>
+  <string id="19026">Timer hinzufügen...</string>
+  <string id="19027">Es liegen keine Suchergebnisse vor</string>
+  <string id="19028">Es sind keine EPG Einträge vorhanden</string>
+  <string id="19029">Programm</string>
+  <string id="19030">Jetzt</string>
+  <string id="19031">Nächstes</string>
+  <string id="19032">Zeitleiste</string>
+  <string id="19033">Information</string>
+  <string id="19034">Aufnahme läuft bereits</string>
+  <string id="19035">Kanal konnte nicht wiedergegeben werden</string>
+  <string id="19036">Aufnahme konnte nicht wiedergegeben werden</string>
+  <string id="19037">Signalqualität anzeigen</string>
+  <string id="19038">Wird derzeit nicht unterstützt!</string>
+  <string id="19039">Verstecken bestätigen</string>
+  <string id="19040">Timer</string>
+  <string id="19041">Aufnahme umbenennen?</string>
+  <string id="19042">Timer umbenennen?</string>
+  <string id="19043">Aufnahme</string>
+  <string id="19044">Bitte überprüfen Sie ihre Einstellungen oder den Server</string>
+  <string id="19045">Keine PVR Klienten verfügbar</string>
+  <string id="19046">Neuer Kanal</string>
+  <string id="19047">Titel-Informationen</string>
+  <string id="19048">Gruppenverwaltung</string>
+  <string id="19049">Zeige Kanal</string>
+  <string id="19050">Zeige normale Kanäle</string>
+  <string id="19051">Zeige versteckte Kanäle</string>
+  <string id="19052">Kanal verschieben nach:</string>
+  <string id="19053">Aufnahme-Informationen</string>
+  <string id="19054">Kanal verstecken</string>
+  <string id="19055">Keine Informationen verfügbar!</string>
+  <string id="19056">Neuer Timer</string>
+  <string id="19057">Timer bearbeiten</string>
+  <string id="19058">Timer aktivieren/deaktivieren</string>
+  <string id="19059">Aufnahme abbrechen</string>
+  <string id="19060">Timer löschen</string>
+  <string id="19061">Timer hinzufügen</string>
+  <string id="19062">Nach Kanal</string>
+  <string id="19063">Gehe zum Anfang</string>
+  <string id="19064">Gehe zum Ende</string>
+  <string id="19065">Standard Programmanzeige</string>
+  <string id="19066">Lade Aufnahmeinformationen von den PVR-Klienten</string>
+  <string id="19067">Es ist bereits ein Timer vorhanden</string>
+  <string id="19068">Konnte Aufnahme nicht löschen!</string>
+  <string id="19069">EPG</string>
+  <string id="19070">Zeit bis zur EPG-Suche</string>
+  <string id="19071">Zeit bis zur EPG-Aktualisierung</string>
+  <string id="19072">Ignoriere Datenbank für PVR-Server EPG</string>
+  <string id="19073">Zeitlimit für Kanaleingabe</string>
+  <string id="19074">Aktiv:</string>
+  <string id="19075">Name:</string>
+  <string id="19076">Ordner:</string>
+  <string id="19077">Radio:</string>
+  <string id="19078">Kanal:</string>
+  <string id="19079">Tag:</string>
+  <string id="19080">Anfang:</string>
+  <string id="19081">Ende:</string>
+  <string id="19082">Priorität:</string>
+  <string id="19083">Lebensdauer (Tage):</string>
+  <string id="19084">Erster Tag:</string>
+  <string id="19085">Unbekannter Kanal %u</string>
+  <string id="19086">Mo-__-__-__-__-__-__</string>
+  <string id="19087">__-Di-__-__-__-__-__</string>
+  <string id="19088">__-__-Mi-__-__-__-__</string>
+  <string id="19089">__-__-__-Do-__-__-__</string>
+  <string id="19090">__-__-__-__-Fr-__-__</string>
+  <string id="19091">__-__-__-__-__-Sa-__</string>
+  <string id="19092">__-__-__-__-__-__-So</string>
+  <string id="19093">Mo-Di-Mi-Do-Fr-__-__</string>
+  <string id="19094">Mo-Di-Mi-Do-Fr-Sa-__</string>
+  <string id="19095">Mo-Di-Mi-Do-Fr-Sa-So</string>
+  <string id="19096">__-__-__-__-__-Sa-So</string>
+  <string id="19097">Bitte geben Sie den Aufnahmenamen ein</string>
+  <string id="19098">Warnung</string>
+  <string id="19099">Timer vorhanden</string>
+  <string id="19100">Kanal und Timer löschen?</string>
+  <string id="19101">Kanalwiedergabe läuft</string>
+  <string id="19102">Bitte schalten Sie auf einen anderen Kanal!</string>
+  <string id="19103">Suche fehlende Kanallogos</string>
+  <string id="19104">Bitte geben Sie den Aufnahmeordner ein</string>
+  <string id="19105">Größe:</string>
+  <string id="19106">Nächster Timer am</string>
+  <string id="19107">um</string>
+  <string id="19108">Aufnahmen nicht synchron!</string>
+  <string id="19109">Konnte Timer nicht speichern!</string>
+  <string id="19110">Erneut versuchen...</string>
+  <string id="19111">Server Fehler!</string>
+  <string id="19112">Timers nicht synchron!</string>
+  <string id="19113">Danach</string>
+  <string id="19114">Version</string>
+  <string id="19115">Addresse</string>
+  <string id="19116">Festplattengröße</string>
+  <string id="19117">Kanäle suchen</string>
+  <string id="19118">TV ist während der Kanalsuche nicht möglich!</string>
+  <string id="19119">Auf welchem Server soll gesucht werden?</string>
+  <string id="19120">Klientnummer</string>
+  <string id="19121">Keine Wiederholungen</string>
+  <string id="19122">Timer zeichnet auf - trotzdem löschen?</string>
+  <string id="19123">Nur frei empfangbare Kanäle</string>
+  <string id="19124">Bei vorhandenem Timer ignorieren</string>
+  <string id="19125">Bei vorhandener Aufnahmen ignorieren</string>
+  <string id="19126">Start Uhrzeit</string>
+  <string id="19127">Stop Uhrzeit</string>
+  <string id="19128">Start Datum</string>
+  <string id="19129">Stop Datum</string>
+  <string id="19130">Minimale Dauer</string>
+  <string id="19131">Maximale Dauer</string>
+  <string id="19132">Inklusive unbekannter Genres</string>
+  <string id="19133">Suchbegriff</string>
+  <string id="19134">Inklusive Beschreibung</string>
+  <string id="19135">Groß-/Kleinschreibung</string>
+  <string id="19136">Kanal nicht verfügbar</string>
+  <string id="19137">Keine Gruppen definiert</string>
+  <string id="19138">Bitte erstellen Sie zuerst eine</string>
+  <string id="19139">Neuer Gruppenname</string>
+  <string id="19140">Alle Kanäle</string>
+  <string id="19141">Gruppe</string>
+  <string id="19142">EPG durchsuchen</string>
+  <string id="19143">Gruppenverwaltung</string>
+  <string id="19144">Ungruppiert</string>
+  <string id="19145">Keine Gruppen</string>
+  <string id="19146">Gruppen</string>
+  <string id="19147">PVR-Server ist nicht kompatibel!</string>
+  <string id="19148">Kanal</string>
+  <string id="19149">Mo</string>
+  <string id="19150">Di</string>
+  <string id="19151">Mi</string>
+  <string id="19152">Do</string>
+  <string id="19153">Fr</string>
+  <string id="19154">Sa</string>
+  <string id="19155">So</string>
+  <string id="19156">ab</string>
+  <string id="19157">Nächste Aufnahme</string>
+  <string id="19158">Jetzige Aufnahme</string>
+  <string id="19159">von</string>
+  <string id="19160">bis</string>
+  <string id="19161">Auf</string>
+  <string id="19162">Aufnahme läuft</string>
+  <string id="19163">Aufnahmen</string>
+  <string id="19164">Konnte Aufnahme nicht starten</string>
+  <string id="19165">Umschalten</string>
+  <string id="19166">PVR-Informationen</string>
+  <string id="19167">Suche fehlende Kanallogos</string>
+  <string id="19168">Beim umschalten auf OK verzichten</string>
+  <string id="19169">Infobox für Videolänge verbergen</string>
+  <string id="19170">Suchüberlauf vor Kanalwiedergabe</string>
+  <string id="19171">Wiedergabe minimiert starten</string>
+  <string id="19172">Dauer der Direktaufzeichnung</string>
+  <string id="19173">Standard-Priorität</string>
+  <string id="19174">Standard-Lebensdauer</string>
+  <string id="19175">Vorlauf zum Timer-Beginn</string>
+  <string id="19176">Nachlauf am Timer-Ende</string>
+  <string id="19177">Wiedergabe</string>
+  <string id="19178">Info beim Kanalwechsel</string>
+  <string id="19179">Angeforderte Kanalinfo schließen</string>
+  <string id="19180">TV</string>
+  <string id="19181">Menü/OSD</string>
+  <string id="19182">Tage für TV-Programm Anzeige</string>
+  <string id="19183">Alte EPG-Daten anzeigen</string>
+  <string id="19184">Anzeigedauer für Kanalinfo</string>
+  <string id="19185">TV Datenbank zurücksetzten</string>
+  <string id="19186">Alle Daten in der Datenbank werden gelöscht</string>
+  <string id="19187">EPG Daten löschen und neu einlesen</string>
+  <string id="19188">Alle EPG Daten werden gelöscht</string>
+  <string id="19189">Nach Start letzten Kanal wiedergeben</string>
+  <string id="19190">Minimiert</string>
+  <string id="19191">PVR-Service</string>
+  <string id="19192">Keiner der PVR-Server unterstützt die Kanalsuche</string>
+  <string id="19193">Kanalsuche konnte nicht durchgeführt werden</string>
+  <string id="19194">Fortfahren?</string>
+  <string id="19195">Klientaktionen</string>
+  <string id="19196">PVR Klient spezifische Aktionen</string>
+  <string id="19197">Aufnahme gestarted auf: %s</string>
+  <string id="19198">Aufnahme beendet auf: %s</string>
+  <string id="19199">Kanalverwaltung</string>
+  <string id="19200">EPG Quelle:</string>
+  <string id="19201">Kanalname:</string>
+  <string id="19202">Kanallogo:</string>
+  <string id="19203">Kanal bearbeiten</string>
+  <string id="19204">Neuer Kanal</string>
+  <string id="19205">Gruppenverwaltung</string>
+  <string id="19206">EPG Aktivieren:</string>
+  <string id="19207">Gruppe:</string>
+  <string id="19208">Bitte geben Sie den neuen Kanalnamen ein</string>
+  <string id="19209">XBMC interner virtueller Kanal</string>
+  <string id="19210">Klient</string>
+  <string id="19211">Kanal löschen</string>
+  <string id="19212">Liste enthält Änderungen</string>
+  <string id="19213">Wählen Sie die Kanalquelle</string>
+  <string id="19214">Bitte geben Sie eine URL für den Kanal ein</string>
+  <string id="19215">Der PVR-Server unterstützt keine Timer!</string>
+
+  <string id="19499">Andere/Unbekannt</string>
+  <string id="19500">Movie/Drama</string>
+  <string id="19501">Detektivfilm/Thriller</string>
+  <string id="19502">Abenteuer/Western/Krieg</string>
+  <string id="19503">Science Fiction/Fantasy/Horror</string>
+  <string id="19504">Komödie</string>
+  <string id="19505">Seifenoper/Melodram/Folklore</string>
+  <string id="19506">Romanze</string>
+  <string id="19507">Serie/Klassik/Religion/Historienfilm</string>
+  <string id="19508">Erwachsenenfilm</string>
+  <string id="19509"></string>
+  <string id="19510"></string>
+  <string id="19511"></string>
+  <string id="19512"></string>
+  <string id="19513"></string>
+  <string id="19514"></string>
+  <string id="19515"></string>
+  <string id="19516">Nachrichten/Aktuelle Meldungen</string>
+  <string id="19517">Nachrichten/Wettervorhersage</string>
+  <string id="19518">Nachrichtenmagazin</string>
+  <string id="19519">Dokumentation</string>
+  <string id="19520">Diskussion/Interview/Debatte</string>
+  <string id="19521"></string>
+  <string id="19522"></string>
+  <string id="19523"></string>
+  <string id="19524"></string>
+  <string id="19525"></string>
+  <string id="19526"></string>
+  <string id="19527"></string>
+  <string id="19528"></string>
+  <string id="19529"></string>
+  <string id="19530"></string>
+  <string id="19531"></string>
+  <string id="19532">Show/Game Show</string>
+  <string id="19533">Game Show/Quiz/Contest</string>
+  <string id="19534">Varietevorführung</string>
+  <string id="19535">Talk Show</string>
+  <string id="19536"></string>
+  <string id="19537"></string>
+  <string id="19538"></string>
+  <string id="19539"></string>
+  <string id="19540"></string>
+  <string id="19541"></string>
+  <string id="19542"></string>
+  <string id="19543"></string>
+  <string id="19544"></string>
+  <string id="19545"></string>
+  <string id="19546"></string>
+  <string id="19547"></string>
+  <string id="19548">Sport</string>
+  <string id="19549">Sportveranstaltung</string>
+  <string id="19550">Sportmagazin</string>
+  <string id="19551">Fußball</string>
+  <string id="19552">Tennis/Squash</string>
+  <string id="19553">Team Sportarten</string>
+  <string id="19554">Athletik</string>
+  <string id="19555">Motorsport</string>
+  <string id="19556">Wassersport</string>
+  <string id="19557">Wintersport</string>
+  <string id="19558">Pferdesport</string>
+  <string id="19559">Kampfsport</string>
+  <string id="19560"></string>
+  <string id="19561"></string>
+  <string id="19562"></string>
+  <string id="19563"></string>
+  <string id="19564">Kinder- &amp; Jugendprogramm</string>
+  <string id="19565">Unterhaltungsprogramm für Vorschulkinder</string>
+  <string id="19566">Unterhaltungsprogramm für 6 bis 14 Jährige</string>
+  <string id="19567">Unterhaltungsprogramm für 10 bis 16 Jährige</string>
+  <string id="19568">Information/Educational/Schulprogramm</string>
+  <string id="19569">Zeichentrick/Puppen</string>
+  <string id="19570"></string>
+  <string id="19571"></string>
+  <string id="19572"></string>
+  <string id="19573"></string>
+  <string id="19574"></string>
+  <string id="19575"></string>
+  <string id="19576"></string>
+  <string id="19577"></string>
+  <string id="19578"></string>
+  <string id="19579"></string>
+  <string id="19580">Musik/Ballet/Tanzen</string>
+  <string id="19581">Rock/Pop</string>
+  <string id="19582">Serious/Klassische Musik</string>
+  <string id="19583">Volksmusik/Tradionelle Musik</string>
+  <string id="19584">Musical/Oper</string>
+  <string id="19585">Ballet</string>
+  <string id="19586"></string>
+  <string id="19587"></string>
+  <string id="19588"></string>
+  <string id="19589"></string>
+  <string id="19590"></string>
+  <string id="19591"></string>
+  <string id="19592"></string>
+  <string id="19593"></string>
+  <string id="19594"></string>
+  <string id="19595"></string>
+  <string id="19596">Kunst/Kultur</string>
+  <string id="19597">Performing Arts</string>
+  <string id="19598">Bildende Kunst</string>
+  <string id="19599">Religion</string>
+  <string id="19600">Volkskultur/Traditionelle Kunst</string>
+  <string id="19601">Literatur</string>
+  <string id="19602">Film/Kino</string>
+  <string id="19603">Experimental Film/Video</string>
+  <string id="19604">Rundfunk/Presse</string>
+  <string id="19605">Neue Medien</string>
+  <string id="19606">Kunst/Kultur Magazin</string>
+  <string id="19607">Mode</string>
+  <string id="19608"></string>
+  <string id="19609"></string>
+  <string id="19610"></string>
+  <string id="19611"></string>
+  <string id="19612">Soziales/Politik/Wirtschaft</string>
+  <string id="19613">Magazin/Report/Dokumentation</string>
+  <string id="19614">Wirtschaft- &amp; Sozialberatung</string>
+  <string id="19615">Bemerkenswerte Personen</string>
+  <string id="19616"></string>
+  <string id="19617"></string>
+  <string id="19618"></string>
+  <string id="19619"></string>
+  <string id="19620"></string>
+  <string id="19621"></string>
+  <string id="19622"></string>
+  <string id="19623"></string>
+  <string id="19624"></string>
+  <string id="19625"></string>
+  <string id="19626"></string>
+  <string id="19627"></string>
+  <string id="19628">Bildung/Wissenschaft/Fakten</string>
+  <string id="19629">Natur/Tiere/Umwelt</string>
+  <string id="19630">Technologie/Naturwissenschaften</string>
+  <string id="19631">Medizin/Physiologie/Psychologie</string>
+  <string id="19632">Ausland/Expeditionen</string>
+  <string id="19633">Sozial- &amp; Geisteswissenschaften</string>
+  <string id="19634">Weiterbildung</string>
+  <string id="19635">Sprachen</string>
+  <string id="19636"></string>
+  <string id="19637"></string>
+  <string id="19638"></string>
+  <string id="19639"></string>
+  <string id="19640"></string>
+  <string id="19641"></string>
+  <string id="19642"></string>
+  <string id="19643"></string>
+  <string id="19644">Freizeit/Hobbys</string>
+  <string id="19645">Tourismus/Reisen</string>
+  <string id="19646">Handwerk</string>
+  <string id="19647">Auto</string>
+  <string id="19648">Fitness &amp; Gesundheit</string>
+  <string id="19649">Kochen</string>
+  <string id="19650">Werbung/Einkaufen</string>
+  <string id="19651">Garten</string>
+  <string id="19652"></string>
+  <string id="19653"></string>
+  <string id="19654"></string>
+  <string id="19655"></string>
+  <string id="19656"></string>
+  <string id="19657"></string>
+  <string id="19658"></string>
+  <string id="19659"></string>
+  <string id="19660">Besondere Merkmale</string>
+  <string id="19661">Originalsprache</string>
+  <string id="19662">Schwarz/Weiß</string>
+  <string id="19663">Bisher unveröffentlicht</string>
+  <string id="19664">Livesendung</string>
+  <string id="19665"></string>
+  <string id="19666"></string>
+  <string id="19667"></string>
+  <string id="19668"></string>
+  <string id="19669"></string>
+  <string id="19670"></string>
+  <string id="19671"></string>
+  <string id="19672"></string>
+  <string id="19673"></string>
+  <string id="19674"></string>
+  <string id="19675"></string>
+  <string id="19676">Drama</string>
+  <string id="19677">Detektivfilm/Thriller</string>
+  <string id="19678">Abenteuer/Western/Krieg</string>
+  <string id="19679">Science Fiction/Fantasy/Horror</string>
+  <string id="19680">Komödie</string>
+  <string id="19681">Seifenoper/Melodram/Folklore</string>
+  <string id="19682">Romanze</string>
+  <string id="19683">Serie/Klassisch/Religion/Geschichte</string>
+  <string id="19684">Erwachsenenfilm</string>
+  <string id="19685"></string>
+  <string id="19686"></string>
+  <string id="19687"></string>
+  <string id="19688"></string>
+  <string id="19689"></string>
+  <string id="19690"></string>
+  <string id="19691"></string>
 
   <string id="20000">Ordner für CD-Kopien</string>
   <string id="20001">Externen DVD-Player verwenden</string>
diff --git a/lib/addons/library.xbmc.addon/Makefile.in b/lib/addons/library.xbmc.addon/Makefile.in
new file mode 100644 (file)
index 0000000..56afe6c
--- /dev/null
@@ -0,0 +1,27 @@
+ARCH=@ARCH@
+INCLUDES=-I. -I../../../xbmc/addons/include -I../../../xbmc
+DEFINES+=
+CXXFLAGS=-fPIC
+LIBNAME=libXBMC_addon
+OBJS=$(LIBNAME).o
+
+LIB_SHARED=../../../addons/library.xbmc.addon/$(LIBNAME)-$(ARCH).so
+
+all: $(LIB_SHARED)
+
+$(LIB_SHARED): $(OBJS)
+ifeq ($(findstring osx,$(ARCH)), osx)
+       @export MACOSX_DEPLOYMENT_TARGET=10.4
+       $(CXX) -bundle -shared -flat_namespace -undefined suppress -o $(LIB_SHARED) $(OBJS)
+       ../../../tools/Mach5/wrapper.rb $@;mv output.so $@
+else
+       $(CXX) $(CFLAGS) $(LDFLAGS) -shared -g -o $(LIB_SHARED) $(OBJS)
+endif
+
+CLEAN_FILES = \
+       $(LIBNAME).so \
+
+DISTCLEAN_FILES= \
+       Makefile \
+
+include ../../../Makefile.include
diff --git a/lib/addons/library.xbmc.addon/libXBMC_addon.cpp b/lib/addons/library.xbmc.addon/libXBMC_addon.cpp
new file mode 100644 (file)
index 0000000..22651af
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string>
+#include "../../../addons/library.xbmc.addon/libXBMC_addon.h"
+#include "../../../xbmc/addons/AddonHelpers_local.h"
+
+#ifdef _WIN32
+#include <windows.h>
+#define DLLEXPORT __declspec(dllexport)
+#else
+#define DLLEXPORT
+#endif
+
+
+using namespace std;
+
+AddonCB *m_Handle = NULL;
+CB_AddOnLib *m_cb = NULL;
+
+extern "C"
+{
+
+DLLEXPORT int XBMC_register_me(void *hdl)
+{
+  if (!hdl)
+    fprintf(stderr, "libXBMC_addon-ERROR: XBMC_register_me is called with NULL handle !!!\n");
+  else
+  {
+    m_Handle = (AddonCB*) hdl;
+    m_cb     = m_Handle->AddOnLib_RegisterMe(m_Handle->addonData);
+    if (!m_cb)
+      fprintf(stderr, "libXBMC_addon-ERROR: XBMC_register_me can't get callback table from XBMC !!!\n");
+    else
+      return 1;
+  }
+  return 0;
+}
+
+DLLEXPORT void XBMC_unregister_me()
+{
+  if (m_Handle && m_cb)
+    m_Handle->AddOnLib_UnRegisterMe(m_Handle->addonData, m_cb);
+}
+
+DLLEXPORT void XBMC_log(const addon_log_t loglevel, const char *format, ... )
+{
+  if (m_cb == NULL)
+    return;
+
+  char buffer[16384];
+  va_list args;
+  va_start (args, format);
+  vsprintf (buffer, format, args);
+  va_end (args);
+  m_cb->Log(m_Handle->addonData, loglevel, buffer);
+}
+
+DLLEXPORT bool XBMC_get_setting(const char* settingName, void *settingValue)
+{
+  if (m_cb == NULL)
+    return false;
+
+  return m_cb->GetSetting(m_Handle->addonData, settingName, settingValue);
+}
+
+DLLEXPORT void XBMC_queue_notification(const queue_msg_t type, const char *format, ... )
+{
+  if (m_cb == NULL)
+    return;
+
+  char buffer[16384];
+  va_list args;
+  va_start (args, format);
+  vsprintf (buffer, format, args);
+  va_end (args);
+  m_cb->QueueNotification(m_Handle->addonData, type, buffer);
+}
+
+DLLEXPORT void XBMC_unknown_to_utf8(string &str)
+{
+  if (m_cb == NULL)
+    return;
+
+  string buffer = m_cb->UnknownToUTF8(str.c_str());
+  str = buffer;
+}
+
+DLLEXPORT const char* XBMC_get_localized_string(int dwCode)
+{
+  if (m_cb == NULL)
+    return "";
+
+  string buffer = m_cb->GetLocalizedString(m_Handle->addonData, dwCode);
+  return buffer.c_str();
+}
+
+DLLEXPORT const char* XBMC_get_dvd_menu_language()
+{
+  if (m_cb == NULL)
+    return "";
+
+  string buffer = m_cb->GetDVDMenuLanguage(m_Handle->addonData);
+  return buffer.c_str();
+}
+
+};
diff --git a/lib/addons/library.xbmc.addon/project/VS2008Express/libXBMC_addon.sln b/lib/addons/library.xbmc.addon/project/VS2008Express/libXBMC_addon.sln
new file mode 100644 (file)
index 0000000..b282dec
--- /dev/null
@@ -0,0 +1,20 @@
+\r
+Microsoft Visual Studio Solution File, Format Version 10.00\r
+# Visual C++ Express 2008\r
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libXBMC_addon", "libXBMC_addon.vcproj", "{D450FE9A-CE56-4496-B4AB-379094E642F2}"\r
+EndProject\r
+Global\r
+       GlobalSection(SolutionConfigurationPlatforms) = preSolution\r
+               Debug|Win32 = Debug|Win32\r
+               Release|Win32 = Release|Win32\r
+       EndGlobalSection\r
+       GlobalSection(ProjectConfigurationPlatforms) = postSolution\r
+               {D450FE9A-CE56-4496-B4AB-379094E642F2}.Debug|Win32.ActiveCfg = Debug|Win32\r
+               {D450FE9A-CE56-4496-B4AB-379094E642F2}.Debug|Win32.Build.0 = Debug|Win32\r
+               {D450FE9A-CE56-4496-B4AB-379094E642F2}.Release|Win32.ActiveCfg = Release|Win32\r
+               {D450FE9A-CE56-4496-B4AB-379094E642F2}.Release|Win32.Build.0 = Release|Win32\r
+       EndGlobalSection\r
+       GlobalSection(SolutionProperties) = preSolution\r
+               HideSolutionNode = FALSE\r
+       EndGlobalSection\r
+EndGlobal\r
diff --git a/lib/addons/library.xbmc.addon/project/VS2008Express/libXBMC_addon.vcproj b/lib/addons/library.xbmc.addon/project/VS2008Express/libXBMC_addon.vcproj
new file mode 100644 (file)
index 0000000..11330f0
--- /dev/null
@@ -0,0 +1,177 @@
+<?xml version="1.0" encoding="Windows-1252"?>\r
+<VisualStudioProject\r
+       ProjectType="Visual C++"\r
+       Version="9,00"\r
+       Name="libXBMC_addon"\r
+       ProjectGUID="{2DCEA60B-4EBC-4DB7-9FBD-297C1EFD95D7}"\r
+       RootNamespace="XBMC_VDR"\r
+       Keyword="Win32Proj"\r
+       TargetFrameworkVersion="131072"\r
+       >\r
+       <Platforms>\r
+               <Platform\r
+                       Name="Win32"\r
+               />\r
+       </Platforms>\r
+       <ToolFiles>\r
+       </ToolFiles>\r
+       <Configurations>\r
+               <Configuration\r
+                       Name="Debug|Win32"\r
+                       OutputDirectory="Debug"\r
+                       IntermediateDirectory="Debug"\r
+                       ConfigurationType="2"\r
+                       InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"\r
+                       CharacterSet="2"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               Optimization="0"\r
+                               AdditionalIncludeDirectories="..\..\..\..\..\xbmc\addons\include;..\..\..\..\..\xbmc\addons;..\..\..\..\..\xbmc\cores\dvdplayer\DVDDemuxers;..\..\..\..\..\xbmc"\r
+                               PreprocessorDefinitions="_WIN32;_DEBUG;_WINDOWS;VDR_EXPORTS;_WIN32PC;_WINSOCKAPI_;_CRT_SECURE_NO_WARNINGS"\r
+                               MinimalRebuild="true"\r
+                               ExceptionHandling="1"\r
+                               BasicRuntimeChecks="3"\r
+                               RuntimeLibrary="3"\r
+                               UsePrecompiledHeader="0"\r
+                               WarningLevel="3"\r
+                               Detect64BitPortabilityProblems="false"\r
+                               DebugInformationFormat="3"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               OutputFile="../../../../../addons/library.xbmc.addon/$(ProjectName).dll"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManifestTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCAppVerifierTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                       />\r
+               </Configuration>\r
+               <Configuration\r
+                       Name="Release|Win32"\r
+                       OutputDirectory="Release"\r
+                       IntermediateDirectory="Release"\r
+                       ConfigurationType="2"\r
+                       InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"\r
+                       CharacterSet="2"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               AdditionalIncludeDirectories="..\..\..\..\..\xbmc\addons\include;..\..\..\..\..\xbmc\addons;..\..\..\..\..\xbmc\cores\dvdplayer\DVDDemuxers;..\..\..\..\..\xbmc"\r
+                               PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;HAS_SDL_OPENGL;HAS_SDL;_USRDLL;XBMC_VDR_EXPORTS;_WIN32PC;_CRT_SECURE_NO_WARNINGS"\r
+                               ExceptionHandling="1"\r
+                               RuntimeLibrary="2"\r
+                               UsePrecompiledHeader="0"\r
+                               WarningLevel="3"\r
+                               Detect64BitPortabilityProblems="false"\r
+                               DebugInformationFormat="3"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               OutputFile="../../../../../addons/library.xbmc.addon/$(ProjectName).dll"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManifestTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCAppVerifierTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                       />\r
+               </Configuration>\r
+       </Configurations>\r
+       <References>\r
+       </References>\r
+       <Files>\r
+               <Filter\r
+                       Name="Source Files"\r
+                       Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"\r
+                       UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"\r
+                       >\r
+                       <File\r
+                               RelativePath="..\..\libXBMC_addon.cpp"\r
+                               >\r
+                       </File>\r
+               </Filter>\r
+       </Files>\r
+       <Globals>\r
+       </Globals>\r
+</VisualStudioProject>\r
diff --git a/lib/addons/library.xbmc.addon/project/VS2010Express/libXBMC_addon.vcxproj b/lib/addons/library.xbmc.addon/project/VS2010Express/libXBMC_addon.vcxproj
new file mode 100644 (file)
index 0000000..af5c26f
--- /dev/null
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{2DCEA60B-4EBC-4DB7-9FBD-297C1EFD95D7}</ProjectGuid>
+    <RootNamespace>XBMC_VDR</RootNamespace>
+    <Keyword>Win32Proj</Keyword>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup>
+    <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\..\..\..\addons\library.xbmc.addon\</OutDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Debug\</IntDir>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\..\..\..\addons\library.xbmc.addon\</OutDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Release\</IntDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <Optimization>Disabled</Optimization>
+      <AdditionalIncludeDirectories>..\..\..\..\..\xbmc\addons\include;..\..\..\..\..\xbmc\addons;..\..\..\..\..\xbmc\cores\dvdplayer\DVDDemuxers;..\..\..\..\..\xbmc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>_WIN32;_DEBUG;_WINDOWS;VDR_EXPORTS;_WIN32PC;_WINSOCKAPI_;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <MinimalRebuild>true</MinimalRebuild>
+      <ExceptionHandling>Sync</ExceptionHandling>
+      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+    </ClCompile>
+    <Link>
+      <OutputFile>..\..\..\..\..\addons\library.xbmc.addon\$(ProjectName).dll</OutputFile>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <AdditionalIncludeDirectories>..\..\..\..\..\xbmc\addons\include;..\..\..\..\..\xbmc\addons;..\..\..\..\..\xbmc\cores\dvdplayer\DVDDemuxers;..\..\..\..\..\xbmc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;HAS_SDL_OPENGL;HAS_SDL;_USRDLL;XBMC_VDR_EXPORTS;_WIN32PC;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ExceptionHandling>Sync</ExceptionHandling>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+    </ClCompile>
+    <Link>
+      <OutputFile>..\..\..\..\..\addons\library.xbmc.addon\$(ProjectName).dll</OutputFile>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\libXBMC_addon.cpp" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\..\..\..\addons\library.xbmc.addon\libXBMC_addon.h" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/lib/addons/library.xbmc.addon/project/VS2010Express/libXBMC_addon.vcxproj.filters b/lib/addons/library.xbmc.addon/project/VS2010Express/libXBMC_addon.vcxproj.filters
new file mode 100644 (file)
index 0000000..916673c
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\libXBMC_addon.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\..\..\..\addons\library.xbmc.addon\libXBMC_addon.h">
+      <Filter>Source Files</Filter>
+    </ClInclude>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/lib/addons/library.xbmc.gui/Makefile.in b/lib/addons/library.xbmc.gui/Makefile.in
new file mode 100644 (file)
index 0000000..6921066
--- /dev/null
@@ -0,0 +1,27 @@
+ARCH=@ARCH@
+INCLUDES=-I. -I../../../xbmc/addons/include -I../../../xbmc -I../../../xbmc/cores/dvdplayer/DVDDemuxers
+DEFINES+=
+CXXFLAGS=-fPIC
+LIBNAME=libXBMC_gui
+OBJS=$(LIBNAME).o
+
+LIB_SHARED=../../../addons/library.xbmc.gui/$(LIBNAME)-$(ARCH).so
+
+all: $(LIB_SHARED)
+
+$(LIB_SHARED): $(OBJS)
+ifeq ($(findstring osx,$(ARCH)), osx)
+       @export MACOSX_DEPLOYMENT_TARGET=10.4
+       $(CXX) -bundle -shared -flat_namespace -undefined suppress -o $(LIB_SHARED) $(OBJS)
+       ../../../tools/Mach5/wrapper.rb $@;mv output.so $@
+else
+       $(CXX) $(CFLAGS) $(LDFLAGS) -shared -g -o $(LIB_SHARED) $(OBJS)
+endif
+
+CLEAN_FILES = \
+       $(LIB_SHARED) \
+
+DISTCLEAN_FILES= \
+       Makefile \
+
+include ../../../Makefile.include
diff --git a/lib/addons/library.xbmc.gui/libXBMC_gui.cpp b/lib/addons/library.xbmc.gui/libXBMC_gui.cpp
new file mode 100644 (file)
index 0000000..3c2898d
--- /dev/null
@@ -0,0 +1,555 @@
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string>
+#include "../../../addons/library.xbmc.gui/libXBMC_gui.h"
+#include "addons/AddonHelpers_local.h"
+
+#ifdef _WIN32
+#include <windows.h>
+#define DLLEXPORT __declspec(dllexport)
+#else
+#define DLLEXPORT
+#endif
+
+using namespace std;
+
+AddonCB *m_Handle = NULL;
+CB_GUILib *m_cb   = NULL;
+
+extern "C"
+{
+
+DLLEXPORT int GUI_register_me(void *hdl)
+{
+  if (!hdl)
+    fprintf(stderr, "libXBMC_gui-ERROR: GUILib_register_me is called with NULL handle !!!\n");
+  else
+  {
+    m_Handle = (AddonCB*) hdl;
+    m_cb     = m_Handle->GUILib_RegisterMe(m_Handle->addonData);
+    if (!m_cb)
+      fprintf(stderr, "libXBMC_gui-ERROR: GUILib_register_me can't get callback table from XBMC !!!\n");
+    else
+      return 1;
+  }
+  return 0;
+}
+
+DLLEXPORT void GUI_unregister_me()
+{
+  if (m_Handle && m_cb)
+    m_Handle->GUILib_UnRegisterMe(m_Handle->addonData, m_cb);
+}
+
+DLLEXPORT void GUI_lock()
+{
+  m_cb->Lock();
+}
+
+DLLEXPORT void GUI_unlock()
+{
+  m_cb->Unlock();
+}
+
+DLLEXPORT int GUI_get_screen_height()
+{
+  return m_cb->GetScreenHeight();
+}
+
+DLLEXPORT int GUI_get_screen_width()
+{
+  return m_cb->GetScreenWidth();
+}
+
+DLLEXPORT int GUI_get_video_resolution()
+{
+  return m_cb->GetVideoResolution();
+}
+
+DLLEXPORT cGUIWindow* GUI_Window_create(const char *xmlFilename, const char *defaultSkin, bool forceFallback, bool asDialog)
+{
+  return new cGUIWindow(xmlFilename, defaultSkin, forceFallback, asDialog);
+}
+
+DLLEXPORT void GUI_Window_destroy(cGUIWindow* p)
+{
+  delete p;
+}
+
+DLLEXPORT bool GUI_Window_OnClick(GUIHANDLE handle, int controlId)
+{
+  cGUIWindow *window = (cGUIWindow*) handle;
+  return window->OnClick(controlId);
+}
+
+DLLEXPORT bool GUI_Window_OnFocus(GUIHANDLE handle, int controlId)
+{
+  cGUIWindow *window = (cGUIWindow*) handle;
+  return window->OnFocus(controlId);
+}
+
+DLLEXPORT bool GUI_Window_OnInit(GUIHANDLE handle)
+{
+  cGUIWindow *window = (cGUIWindow*) handle;
+  return window->OnInit();
+}
+
+DLLEXPORT bool GUI_Window_OnAction(GUIHANDLE handle, int actionId)
+{
+  cGUIWindow *window = (cGUIWindow*) handle;
+  return window->OnAction(actionId);
+}
+
+
+cGUIWindow::cGUIWindow(const char *xmlFilename, const char *defaultSkin, bool forceFallback, bool asDialog)
+{
+  CBOnInit = NULL;
+  CBOnClick = NULL;
+  CBOnFocus = NULL;
+  if (m_Handle && m_cb)
+  {
+    m_WindowHandle = m_cb->Window_New(m_Handle->addonData, xmlFilename, defaultSkin, forceFallback, asDialog);
+    if (!m_WindowHandle)
+      fprintf(stderr, "libXBMC_gui-ERROR: cGUIWindow can't create window class from XBMC !!!\n");
+
+    m_cb->Window_SetCallbacks(m_Handle->addonData, m_WindowHandle, this, GUI_Window_OnInit, GUI_Window_OnClick, GUI_Window_OnFocus, GUI_Window_OnAction);
+  }
+}
+
+cGUIWindow::~cGUIWindow()
+{
+  if (m_Handle && m_cb && m_WindowHandle)
+  {
+    m_cb->Window_Delete(m_Handle->addonData, m_WindowHandle);
+    m_WindowHandle = NULL;
+  }
+}
+
+bool cGUIWindow::Show()
+{
+  return m_cb->Window_Show(m_Handle->addonData, m_WindowHandle);
+}
+
+void cGUIWindow::Close()
+{
+  m_cb->Window_Close(m_Handle->addonData, m_WindowHandle);
+}
+
+void cGUIWindow::DoModal()
+{
+  m_cb->Window_DoModal(m_Handle->addonData, m_WindowHandle);
+}
+
+bool cGUIWindow::OnInit()
+{
+  if (!CBOnInit)
+    return false;
+
+  return CBOnInit(m_cbhdl);
+}
+
+bool cGUIWindow::OnClick(int controlId)
+{
+  if (!CBOnClick)
+    return false;
+
+  return CBOnClick(m_cbhdl, controlId);
+}
+
+bool cGUIWindow::OnFocus(int controlId)
+{
+  if (!CBOnFocus)
+    return false;
+
+  return CBOnFocus(m_cbhdl, controlId);
+}
+
+bool cGUIWindow::OnAction(int actionId)
+{
+  if (!CBOnAction)
+    return false;
+
+  return CBOnAction(m_cbhdl, actionId);
+}
+
+bool cGUIWindow::SetFocusId(int iControlId)
+{
+  return m_cb->Window_SetFocusId(m_Handle->addonData, m_WindowHandle, iControlId);
+}
+
+int cGUIWindow::GetFocusId()
+{
+  return m_cb->Window_GetFocusId(m_Handle->addonData, m_WindowHandle);
+}
+
+bool cGUIWindow::SetCoordinateResolution(int res)
+{
+  return m_cb->Window_SetCoordinateResolution(m_Handle->addonData, m_WindowHandle, res);
+}
+
+void cGUIWindow::SetProperty(const char *key, const char *value)
+{
+  m_cb->Window_SetProperty(m_Handle->addonData, m_WindowHandle, key, value);
+}
+
+void cGUIWindow::SetPropertyInt(const char *key, int value)
+{
+  m_cb->Window_SetPropertyInt(m_Handle->addonData, m_WindowHandle, key, value);
+}
+
+void cGUIWindow::SetPropertyBool(const char *key, bool value)
+{
+  m_cb->Window_SetPropertyBool(m_Handle->addonData, m_WindowHandle, key, value);
+}
+
+void cGUIWindow::SetPropertyDouble(const char *key, double value)
+{
+  m_cb->Window_SetPropertyDouble(m_Handle->addonData, m_WindowHandle, key, value);
+}
+
+const char *cGUIWindow::GetProperty(const char *key) const
+{
+  return m_cb->Window_GetProperty(m_Handle->addonData, m_WindowHandle, key);
+}
+
+int cGUIWindow::GetPropertyInt(const char *key) const
+{
+  return m_cb->Window_GetPropertyInt(m_Handle->addonData, m_WindowHandle, key);
+}
+
+bool cGUIWindow::GetPropertyBool(const char *key) const
+{
+  return m_cb->Window_GetPropertyBool(m_Handle->addonData, m_WindowHandle, key);
+}
+
+double cGUIWindow::GetPropertyDouble(const char *key) const
+{
+  return m_cb->Window_GetPropertyDouble(m_Handle->addonData, m_WindowHandle, key);
+}
+
+void cGUIWindow::ClearProperties()
+{
+  m_cb->Window_ClearProperties(m_Handle->addonData, m_WindowHandle);
+}
+
+int cGUIWindow::GetListSize()
+{
+  return m_cb->Window_GetListSize(m_Handle->addonData, m_WindowHandle);
+}
+
+void cGUIWindow::ClearList()
+{
+  m_cb->Window_ClearList(m_Handle->addonData, m_WindowHandle);
+}
+
+GUIHANDLE cGUIWindow::AddStringItem(const char *name, int itemPosition)
+{
+  return m_cb->Window_AddStringItem(m_Handle->addonData, m_WindowHandle, name, itemPosition);
+}
+
+void cGUIWindow::AddItem(GUIHANDLE item, int itemPosition)
+{
+  m_cb->Window_AddItem(m_Handle->addonData, m_WindowHandle, item, itemPosition);
+}
+
+void cGUIWindow::AddItem(cListItem *item, int itemPosition)
+{
+  m_cb->Window_AddItem(m_Handle->addonData, m_WindowHandle, item->m_ListItemHandle, itemPosition);
+}
+
+void cGUIWindow::RemoveItem(int itemPosition)
+{
+  m_cb->Window_RemoveItem(m_Handle->addonData, m_WindowHandle, itemPosition);
+}
+
+GUIHANDLE cGUIWindow::GetListItem(int listPos)
+{
+  return m_cb->Window_GetListItem(m_Handle->addonData, m_WindowHandle, listPos);
+}
+
+void cGUIWindow::SetCurrentListPosition(int listPos)
+{
+  m_cb->Window_SetCurrentListPosition(m_Handle->addonData, m_WindowHandle, listPos);
+}
+
+int cGUIWindow::GetCurrentListPosition()
+{
+  return m_cb->Window_GetCurrentListPosition(m_Handle->addonData, m_WindowHandle);
+}
+
+void cGUIWindow::SetControlLabel(int controlId, const char *label)
+{
+  m_cb->Window_SetControlLabel(m_Handle->addonData, m_WindowHandle, controlId, label);
+}
+
+///-------------------------------------
+/// cGUISpinControl
+
+DLLEXPORT cGUISpinControl* GUI_control_get_spin(cGUIWindow *window, int controlId)
+{
+  return new cGUISpinControl(window, controlId);
+}
+
+DLLEXPORT void GUI_control_release_spin(cGUISpinControl* p)
+{
+  delete p;
+}
+
+cGUISpinControl::cGUISpinControl(cGUIWindow *window, int controlId)
+ : m_Window(window)
+ , m_ControlId(controlId)
+{
+  m_SpinHandle = m_cb->Window_GetControl_Spin(m_Handle->addonData, m_Window->m_WindowHandle, controlId);
+}
+
+void cGUISpinControl::SetVisible(bool yesNo)
+{
+  if (m_SpinHandle)
+    m_cb->Control_Spin_SetVisible(m_Handle->addonData, m_SpinHandle, yesNo);
+}
+
+void cGUISpinControl::SetText(const char *label)
+{
+  if (m_SpinHandle)
+    m_cb->Control_Spin_SetText(m_Handle->addonData, m_SpinHandle, label);
+}
+
+void cGUISpinControl::Clear()
+{
+  if (m_SpinHandle)
+    m_cb->Control_Spin_Clear(m_Handle->addonData, m_SpinHandle);
+}
+
+void cGUISpinControl::AddLabel(const char *label, int iValue)
+{
+  if (m_SpinHandle)
+    m_cb->Control_Spin_AddLabel(m_Handle->addonData, m_SpinHandle, label, iValue);
+}
+
+int cGUISpinControl::GetValue()
+{
+  if (!m_SpinHandle)
+    return -1;
+
+  return m_cb->Control_Spin_GetValue(m_Handle->addonData, m_SpinHandle);
+}
+
+void cGUISpinControl::SetValue(int iValue)
+{
+  if (m_SpinHandle)
+    m_cb->Control_Spin_SetValue(m_Handle->addonData, m_SpinHandle, iValue);
+}
+
+///-------------------------------------
+/// cGUIRadioButton
+
+DLLEXPORT cGUIRadioButton* GUI_control_get_radiobutton(cGUIWindow *window, int controlId)
+{
+  return new cGUIRadioButton(window, controlId);
+}
+
+DLLEXPORT void GUI_control_release_radiobutton(cGUIRadioButton* p)
+{
+  delete p;
+}
+
+cGUIRadioButton::cGUIRadioButton(cGUIWindow *window, int controlId)
+ : m_Window(window)
+ , m_ControlId(controlId)
+{
+  m_ButtonHandle = m_cb->Window_GetControl_RadioButton(m_Handle->addonData, m_Window->m_WindowHandle, controlId);
+}
+
+void cGUIRadioButton::SetVisible(bool yesNo)
+{
+  if (m_ButtonHandle)
+    m_cb->Control_RadioButton_SetVisible(m_Handle->addonData, m_ButtonHandle, yesNo);
+}
+
+void cGUIRadioButton::SetText(const char *label)
+{
+  if (m_ButtonHandle)
+    m_cb->Control_RadioButton_SetText(m_Handle->addonData, m_ButtonHandle, label);
+}
+
+void cGUIRadioButton::SetSelected(bool yesNo)
+{
+  if (m_ButtonHandle)
+    m_cb->Control_RadioButton_SetSelected(m_Handle->addonData, m_ButtonHandle, yesNo);
+}
+
+bool cGUIRadioButton::IsSelected()
+{
+  if (!m_ButtonHandle)
+    return false;
+
+  return m_cb->Control_RadioButton_IsSelected(m_Handle->addonData, m_ButtonHandle);
+}
+
+
+///-------------------------------------
+/// cGUIProgressControl
+
+DLLEXPORT cGUIProgressControl* GUI_control_get_progress(cGUIWindow *window, int controlId)
+{
+  return new cGUIProgressControl(window, controlId);
+}
+
+DLLEXPORT void GUI_control_release_progress(cGUIProgressControl* p)
+{
+  delete p;
+}
+
+cGUIProgressControl::cGUIProgressControl(cGUIWindow *window, int controlId)
+ : m_Window(window)
+ , m_ControlId(controlId)
+{
+  m_ProgressHandle = m_cb->Window_GetControl_Progress(m_Handle->addonData, m_Window->m_WindowHandle, controlId);
+}
+
+void cGUIProgressControl::SetPercentage(float fPercent)
+{
+  if (m_ProgressHandle)
+    m_cb->Control_Progress_SetPercentage(m_Handle->addonData, m_ProgressHandle, fPercent);
+}
+
+float cGUIProgressControl::GetPercentage() const
+{
+  if (!m_ProgressHandle)
+    return 0.0;
+
+  return m_cb->Control_Progress_GetPercentage(m_Handle->addonData, m_ProgressHandle);
+}
+
+void cGUIProgressControl::SetInfo(int iInfo)
+{
+  if (m_ProgressHandle)
+    m_cb->Control_Progress_SetInfo(m_Handle->addonData, m_ProgressHandle, iInfo);
+}
+
+int cGUIProgressControl::GetInfo() const
+{
+  if (!m_ProgressHandle)
+    return -1;
+
+  return m_cb->Control_Progress_GetInfo(m_Handle->addonData, m_ProgressHandle);
+}
+
+string cGUIProgressControl::GetDescription() const
+{
+  if (!m_ProgressHandle)
+    return "";
+
+  return m_cb->Control_Progress_GetDescription(m_Handle->addonData, m_ProgressHandle);
+}
+
+
+///-------------------------------------
+/// cListItem
+
+DLLEXPORT cListItem* GUI_ListItem_create(const char *label, const char *label2, const char *iconImage, const char *thumbnailImage, const char *path)
+{
+  return new cListItem(label, label2, iconImage, thumbnailImage, path);
+}
+
+DLLEXPORT void GUI_ListItem_destroy(cListItem* p)
+{
+  delete p;
+}
+
+
+cListItem::cListItem(const char *label, const char *label2, const char *iconImage, const char *thumbnailImage, const char *path)
+{
+  m_ListItemHandle = m_cb->ListItem_Create(m_Handle->addonData, label, label2, iconImage, thumbnailImage, path);
+}
+
+const char *cListItem::GetLabel()
+{
+  if (!m_ListItemHandle)
+    return "";
+
+  return m_cb->ListItem_GetLabel(m_Handle->addonData, m_ListItemHandle);
+}
+
+void cListItem::SetLabel(const char *label)
+{
+  if (m_ListItemHandle)
+    m_cb->ListItem_SetLabel(m_Handle->addonData, m_ListItemHandle, label);
+}
+
+const char *cListItem::GetLabel2()
+{
+  if (!m_ListItemHandle)
+    return "";
+
+  return m_cb->ListItem_GetLabel2(m_Handle->addonData, m_ListItemHandle);
+}
+
+void cListItem::SetLabel2(const char *label)
+{
+  if (m_ListItemHandle)
+    m_cb->ListItem_SetLabel2(m_Handle->addonData, m_ListItemHandle, label);
+}
+
+void cListItem::SetIconImage(const char *image)
+{
+  if (m_ListItemHandle)
+    m_cb->ListItem_SetIconImage(m_Handle->addonData, m_ListItemHandle, image);
+}
+
+void cListItem::SetThumbnailImage(const char *image)
+{
+  if (m_ListItemHandle)
+    m_cb->ListItem_SetThumbnailImage(m_Handle->addonData, m_ListItemHandle, image);
+}
+
+void cListItem::SetInfo(const char *Info)
+{
+  if (m_ListItemHandle)
+    m_cb->ListItem_SetInfo(m_Handle->addonData, m_ListItemHandle, Info);
+}
+
+void cListItem::SetProperty(const char *key, const char *value)
+{
+  if (m_ListItemHandle)
+    m_cb->ListItem_SetProperty(m_Handle->addonData, m_ListItemHandle, key, value);
+}
+
+const char *cListItem::GetProperty(const char *key) const
+{
+  if (!m_ListItemHandle)
+    return "";
+
+  return m_cb->ListItem_GetProperty(m_Handle->addonData, m_ListItemHandle, key);
+}
+
+void cListItem::SetPath(const char *Path)
+{
+  if (m_ListItemHandle)
+    m_cb->ListItem_SetPath(m_Handle->addonData, m_ListItemHandle, Path);
+}
+
+
+};
diff --git a/lib/addons/library.xbmc.gui/project/VS2008Express/libXBMC_gui.sln b/lib/addons/library.xbmc.gui/project/VS2008Express/libXBMC_gui.sln
new file mode 100644 (file)
index 0000000..7b05929
--- /dev/null
@@ -0,0 +1,20 @@
+\r
+Microsoft Visual Studio Solution File, Format Version 10.00\r
+# Visual C++ Express 2008\r
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libXBMC_gui", "libXBMC_gui.vcproj", "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}"\r
+EndProject\r
+Global\r
+       GlobalSection(SolutionConfigurationPlatforms) = preSolution\r
+               Debug|Win32 = Debug|Win32\r
+               Release|Win32 = Release|Win32\r
+       EndGlobalSection\r
+       GlobalSection(ProjectConfigurationPlatforms) = postSolution\r
+               {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Debug|Win32.ActiveCfg = Debug|Win32\r
+               {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Debug|Win32.Build.0 = Debug|Win32\r
+               {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Release|Win32.ActiveCfg = Release|Win32\r
+               {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Release|Win32.Build.0 = Release|Win32\r
+       EndGlobalSection\r
+       GlobalSection(SolutionProperties) = preSolution\r
+               HideSolutionNode = FALSE\r
+       EndGlobalSection\r
+EndGlobal\r
diff --git a/lib/addons/library.xbmc.gui/project/VS2008Express/libXBMC_gui.vcproj b/lib/addons/library.xbmc.gui/project/VS2008Express/libXBMC_gui.vcproj
new file mode 100644 (file)
index 0000000..60b62b0
--- /dev/null
@@ -0,0 +1,183 @@
+<?xml version="1.0" encoding="Windows-1252"?>\r
+<VisualStudioProject\r
+       ProjectType="Visual C++"\r
+       Version="9,00"\r
+       Name="libXBMC_gui"\r
+       ProjectGUID="{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}"\r
+       RootNamespace="XBMC_VDR"\r
+       Keyword="Win32Proj"\r
+       TargetFrameworkVersion="131072"\r
+       >\r
+       <Platforms>\r
+               <Platform\r
+                       Name="Win32"\r
+               />\r
+       </Platforms>\r
+       <ToolFiles>\r
+       </ToolFiles>\r
+       <Configurations>\r
+               <Configuration\r
+                       Name="Debug|Win32"\r
+                       OutputDirectory="Debug"\r
+                       IntermediateDirectory="Debug"\r
+                       ConfigurationType="2"\r
+                       InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"\r
+                       CharacterSet="2"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               Optimization="0"\r
+                               AdditionalIncludeDirectories="..\..\..\..\..\xbmc;..\..\..\..\..\xbmc\addons\include;..\..\..\..\..\xbmc\cores\dvdplayer\DVDDemuxers"\r
+                               PreprocessorDefinitions="_WIN32;_DEBUG;_WINDOWS;VDR_EXPORTS;_WIN32PC;_WINSOCKAPI_"\r
+                               MinimalRebuild="true"\r
+                               ExceptionHandling="1"\r
+                               BasicRuntimeChecks="3"\r
+                               RuntimeLibrary="3"\r
+                               UsePrecompiledHeader="0"\r
+                               WarningLevel="3"\r
+                               Detect64BitPortabilityProblems="false"\r
+                               DebugInformationFormat="3"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               OutputFile="../../../../../addons/library.xbmc.gui/$(ProjectName).dll"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManifestTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCAppVerifierTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                       />\r
+               </Configuration>\r
+               <Configuration\r
+                       Name="Release|Win32"\r
+                       OutputDirectory="Release"\r
+                       IntermediateDirectory="Release"\r
+                       ConfigurationType="2"\r
+                       InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"\r
+                       CharacterSet="2"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               AdditionalIncludeDirectories="..\..\..\..\..\xbmc;..\..\..\..\..\xbmc\addons\include;..\..\..\..\..\xbmc\cores\dvdplayer\DVDDemuxers"\r
+                               PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;HAS_SDL_OPENGL;HAS_SDL;_USRDLL;XBMC_VDR_EXPORTS;_WIN32PC"\r
+                               ExceptionHandling="1"\r
+                               RuntimeLibrary="2"\r
+                               UsePrecompiledHeader="0"\r
+                               WarningLevel="3"\r
+                               Detect64BitPortabilityProblems="false"\r
+                               DebugInformationFormat="3"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               OutputFile="../../../../../addons/library.xbmc.gui/$(ProjectName).dll"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManifestTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCAppVerifierTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                       />\r
+               </Configuration>\r
+       </Configurations>\r
+       <References>\r
+       </References>\r
+       <Files>\r
+               <Filter\r
+                       Name="Source Files"\r
+                       Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"\r
+                       UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"\r
+                       >\r
+                       <File\r
+                               RelativePath="..\..\libXBMC_gui.cpp"\r
+                               >\r
+                       </File>\r
+               </Filter>\r
+               <Filter\r
+                       Name="Header Files"\r
+                       Filter="h;hpp;hxx;hm;inl;inc;xsd"\r
+                       UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"\r
+                       >\r
+               </Filter>\r
+       </Files>\r
+       <Globals>\r
+       </Globals>\r
+</VisualStudioProject>\r
diff --git a/lib/addons/library.xbmc.gui/project/VS2010Express/libXBMC_gui.vcxproj b/lib/addons/library.xbmc.gui/project/VS2010Express/libXBMC_gui.vcxproj
new file mode 100644 (file)
index 0000000..1a09551
--- /dev/null
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}</ProjectGuid>
+    <RootNamespace>XBMC_VDR</RootNamespace>
+    <Keyword>Win32Proj</Keyword>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup>
+    <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\..\..\..\addons\library.xbmc.gui\</OutDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Debug\</IntDir>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\..\..\..\addons\library.xbmc.gui\</OutDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Release\</IntDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <Optimization>Disabled</Optimization>
+      <AdditionalIncludeDirectories>..\..\..\..\..\xbmc;..\..\..\..\..\xbmc\addons\include;..\..\..\..\..\xbmc\cores\dvdplayer\DVDDemuxers;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>_WIN32;_DEBUG;_WINDOWS;VDR_EXPORTS;_WIN32PC;_WINSOCKAPI_;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <MinimalRebuild>true</MinimalRebuild>
+      <ExceptionHandling>Sync</ExceptionHandling>
+      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+    </ClCompile>
+    <Link>
+      <OutputFile>..\..\..\..\..\addons\library.xbmc.gui\$(ProjectName).dll</OutputFile>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <AdditionalIncludeDirectories>..\..\..\..\..\xbmc;..\..\..\..\..\xbmc\addons\include;..\..\..\..\..\xbmc\cores\dvdplayer\DVDDemuxers;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;HAS_SDL_OPENGL;HAS_SDL;_USRDLL;XBMC_VDR_EXPORTS;_WIN32PC;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ExceptionHandling>Sync</ExceptionHandling>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+    </ClCompile>
+    <Link>
+      <OutputFile>../../../../../addons/library.xbmc.gui/$(ProjectName).dll</OutputFile>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\libXBMC_gui.cpp" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/lib/addons/library.xbmc.gui/project/VS2010Express/libXBMC_gui.vcxproj.filters b/lib/addons/library.xbmc.gui/project/VS2010Express/libXBMC_gui.vcxproj.filters
new file mode 100644 (file)
index 0000000..1667196
--- /dev/null
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\libXBMC_gui.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/lib/addons/library.xbmc.pvr/Makefile.in b/lib/addons/library.xbmc.pvr/Makefile.in
new file mode 100644 (file)
index 0000000..cbba189
--- /dev/null
@@ -0,0 +1,27 @@
+ARCH=@ARCH@
+INCLUDES=-I. -I../../../xbmc/addons/include -I../../../xbmc -I../../../xbmc/cores/dvdplayer/DVDDemuxers
+DEFINES+=
+CXXFLAGS=-fPIC
+LIBNAME=libXBMC_pvr
+OBJS=$(LIBNAME).o
+
+LIB_SHARED=../../../addons/library.xbmc.pvr/$(LIBNAME)-$(ARCH).so
+
+all: $(LIB_SHARED)
+
+$(LIB_SHARED): $(OBJS)
+ifeq ($(findstring osx,$(ARCH)), osx)
+       @export MACOSX_DEPLOYMENT_TARGET=10.4
+       $(CXX) -bundle -shared -flat_namespace -undefined suppress -o $(LIB_SHARED) $(OBJS)
+       ../../../tools/Mach5/wrapper.rb $@;mv output.so $@
+else
+       $(CXX) $(CFLAGS) $(LDFLAGS) -shared -g -o $(LIB_SHARED) $(OBJS)
+endif
+
+CLEAN_FILES = \
+       $(LIB_SHARED) \
+
+DISTCLEAN_FILES= \
+       Makefile \
+
+include ../../../Makefile.include
diff --git a/lib/addons/library.xbmc.pvr/libXBMC_pvr.cpp b/lib/addons/library.xbmc.pvr/libXBMC_pvr.cpp
new file mode 100644 (file)
index 0000000..693b704
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#define USE_DEMUX // enable including of the demuxer packet structure
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string>
+#include "../../../addons/library.xbmc.pvr/libXBMC_pvr.h"
+#include "addons/AddonHelpers_local.h"
+#include "cores/dvdplayer/DVDDemuxers/DVDDemuxPacket.h"
+
+#ifdef _WIN32
+#include <windows.h>
+#define DLLEXPORT __declspec(dllexport)
+#else
+#define DLLEXPORT
+#endif
+
+using namespace std;
+
+AddonCB *m_Handle = NULL;
+CB_PVRLib *m_cb   = NULL;
+
+extern "C"
+{
+
+DLLEXPORT int PVR_register_me(void *hdl)
+{
+  if (!hdl)
+    fprintf(stderr, "libXBMC_pvr-ERROR: PVRLib_register_me is called with NULL handle !!!\n");
+  else
+  {
+    m_Handle = (AddonCB*) hdl;
+    m_cb     = m_Handle->PVRLib_RegisterMe(m_Handle->addonData);
+    if (!m_cb)
+      fprintf(stderr, "libXBMC_pvr-ERROR: PVRLib_register_me can't get callback table from XBMC !!!\n");
+    else
+      return 1;
+  }
+  return 0;
+}
+
+DLLEXPORT void PVR_unregister_me()
+{
+  if (m_Handle && m_cb)
+    m_Handle->PVRLib_UnRegisterMe(m_Handle->addonData, m_cb);
+}
+
+DLLEXPORT void PVR_transfer_epg_entry(const PVRHANDLE handle, const PVR_PROGINFO *epgentry)
+{
+  if (m_cb == NULL)
+    return;
+
+  m_cb->TransferEpgEntry(m_Handle->addonData, handle, epgentry);
+}
+
+DLLEXPORT void PVR_transfer_channel_entry(const PVRHANDLE handle, const PVR_CHANNEL *chan)
+{
+  if (m_cb == NULL)
+    return;
+
+  m_cb->TransferChannelEntry(m_Handle->addonData, handle, chan);
+}
+
+DLLEXPORT void PVR_transfer_timer_entry(const PVRHANDLE handle, const PVR_TIMERINFO *timer)
+{
+  if (m_cb == NULL)
+    return;
+
+  m_cb->TransferTimerEntry(m_Handle->addonData, handle, timer);
+}
+
+DLLEXPORT void PVR_transfer_recording_entry(const PVRHANDLE handle, const PVR_RECORDINGINFO *recording)
+{
+  if (m_cb == NULL)
+    return;
+
+  m_cb->TransferRecordingEntry(m_Handle->addonData, handle, recording);
+}
+
+DLLEXPORT void PVR_add_menu_hook(PVR_MENUHOOK *hook)
+{
+  if (m_cb == NULL)
+    return;
+
+  m_cb->AddMenuHook(m_Handle->addonData, hook);
+}
+
+DLLEXPORT void PVR_recording(const char *Name, const char *FileName, bool On)
+{
+  if (m_cb == NULL)
+    return;
+
+  m_cb->Recording(m_Handle->addonData, Name, FileName, On);
+}
+
+DLLEXPORT void PVR_trigger_timer_update()
+{
+  if (m_cb == NULL)
+    return;
+
+  m_cb->TriggerTimerUpdate(m_Handle->addonData);
+}
+
+DLLEXPORT void PVR_trigger_recording_update()
+{
+  if (m_cb == NULL)
+    return;
+
+  m_cb->TriggerTimerUpdate(m_Handle->addonData);
+}
+
+DLLEXPORT void PVR_free_demux_packet(DemuxPacket* pPacket)
+{
+  if (m_cb == NULL)
+    return;
+
+  m_cb->FreeDemuxPacket(m_Handle->addonData, pPacket);
+}
+
+DLLEXPORT DemuxPacket* PVR_allocate_demux_packet(int iDataSize)
+{
+  if (m_cb == NULL)
+    return NULL;
+
+  return m_cb->AllocateDemuxPacket(m_Handle->addonData, iDataSize);
+}
+
+};
diff --git a/lib/addons/library.xbmc.pvr/project/VS2008Express/libXBMC_pvr.sln b/lib/addons/library.xbmc.pvr/project/VS2008Express/libXBMC_pvr.sln
new file mode 100644 (file)
index 0000000..e69868a
--- /dev/null
@@ -0,0 +1,20 @@
+\r
+Microsoft Visual Studio Solution File, Format Version 10.00\r
+# Visual C++ Express 2008\r
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libXBMC_pvr", "libXBMC_pvr.vcproj", "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}"\r
+EndProject\r
+Global\r
+       GlobalSection(SolutionConfigurationPlatforms) = preSolution\r
+               Debug|Win32 = Debug|Win32\r
+               Release|Win32 = Release|Win32\r
+       EndGlobalSection\r
+       GlobalSection(ProjectConfigurationPlatforms) = postSolution\r
+               {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Debug|Win32.ActiveCfg = Debug|Win32\r
+               {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Debug|Win32.Build.0 = Debug|Win32\r
+               {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Release|Win32.ActiveCfg = Release|Win32\r
+               {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Release|Win32.Build.0 = Release|Win32\r
+       EndGlobalSection\r
+       GlobalSection(SolutionProperties) = preSolution\r
+               HideSolutionNode = FALSE\r
+       EndGlobalSection\r
+EndGlobal\r
diff --git a/lib/addons/library.xbmc.pvr/project/VS2008Express/libXBMC_pvr.vcproj b/lib/addons/library.xbmc.pvr/project/VS2008Express/libXBMC_pvr.vcproj
new file mode 100644 (file)
index 0000000..12c11f2
--- /dev/null
@@ -0,0 +1,183 @@
+<?xml version="1.0" encoding="Windows-1252"?>\r
+<VisualStudioProject\r
+       ProjectType="Visual C++"\r
+       Version="9,00"\r
+       Name="libXBMC_pvr"\r
+       ProjectGUID="{6D8C91F8-992F-4C83-9DE3-485D64EF8420}"\r
+       RootNamespace="XBMC_VDR"\r
+       Keyword="Win32Proj"\r
+       TargetFrameworkVersion="131072"\r
+       >\r
+       <Platforms>\r
+               <Platform\r
+                       Name="Win32"\r
+               />\r
+       </Platforms>\r
+       <ToolFiles>\r
+       </ToolFiles>\r
+       <Configurations>\r
+               <Configuration\r
+                       Name="Debug|Win32"\r
+                       OutputDirectory="Debug"\r
+                       IntermediateDirectory="Debug"\r
+                       ConfigurationType="2"\r
+                       InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"\r
+                       CharacterSet="2"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               Optimization="0"\r
+                               AdditionalIncludeDirectories="..\..\..\..\..\xbmc;..\..\..\..\..\xbmc\addons\include;..\..\..\..\..\xbmc\cores\dvdplayer\DVDDemuxers"\r
+                               PreprocessorDefinitions="_WIN32;_DEBUG;_WINDOWS;VDR_EXPORTS;_WIN32PC;_WINSOCKAPI_"\r
+                               MinimalRebuild="true"\r
+                               ExceptionHandling="1"\r
+                               BasicRuntimeChecks="3"\r
+                               RuntimeLibrary="3"\r
+                               UsePrecompiledHeader="0"\r
+                               WarningLevel="3"\r
+                               Detect64BitPortabilityProblems="false"\r
+                               DebugInformationFormat="3"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               OutputFile="../../../../../addons/library.xbmc.pvr/$(ProjectName).dll"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManifestTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCAppVerifierTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                       />\r
+               </Configuration>\r
+               <Configuration\r
+                       Name="Release|Win32"\r
+                       OutputDirectory="Release"\r
+                       IntermediateDirectory="Release"\r
+                       ConfigurationType="2"\r
+                       InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"\r
+                       CharacterSet="2"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               AdditionalIncludeDirectories="..\..\..\..\..\xbmc;..\..\..\..\..\xbmc\addons\include;..\..\..\..\..\xbmc\cores\dvdplayer\DVDDemuxers"\r
+                               PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;HAS_SDL_OPENGL;HAS_SDL;_USRDLL;XBMC_VDR_EXPORTS;_WIN32PC"\r
+                               ExceptionHandling="1"\r
+                               RuntimeLibrary="2"\r
+                               UsePrecompiledHeader="0"\r
+                               WarningLevel="3"\r
+                               Detect64BitPortabilityProblems="false"\r
+                               DebugInformationFormat="3"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               OutputFile="../../../../../addons/library.xbmc.pvr/$(ProjectName).dll"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManifestTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCAppVerifierTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                       />\r
+               </Configuration>\r
+       </Configurations>\r
+       <References>\r
+       </References>\r
+       <Files>\r
+               <Filter\r
+                       Name="Source Files"\r
+                       Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"\r
+                       UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"\r
+                       >\r
+                       <File\r
+                               RelativePath="..\..\libXBMC_pvr.cpp"\r
+                               >\r
+                       </File>\r
+               </Filter>\r
+               <Filter\r
+                       Name="Header Files"\r
+                       Filter="h;hpp;hxx;hm;inl;inc;xsd"\r
+                       UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"\r
+                       >\r
+               </Filter>\r
+       </Files>\r
+       <Globals>\r
+       </Globals>\r
+</VisualStudioProject>\r
diff --git a/lib/addons/library.xbmc.pvr/project/VS2010Express/libXBMC_pvr.vcxproj b/lib/addons/library.xbmc.pvr/project/VS2010Express/libXBMC_pvr.vcxproj
new file mode 100644 (file)
index 0000000..3e386a0
--- /dev/null
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{6D8C91F8-992F-4C83-9DE3-485D64EF8420}</ProjectGuid>
+    <RootNamespace>XBMC_VDR</RootNamespace>
+    <Keyword>Win32Proj</Keyword>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup>
+    <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\..\..\..\addons\library.xbmc.pvr\</OutDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Debug\</IntDir>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\..\..\..\addons\library.xbmc.pvr\</OutDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Release\</IntDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <Optimization>Disabled</Optimization>
+      <AdditionalIncludeDirectories>..\..\..\..\..\xbmc;..\..\..\..\..\xbmc\addons\include;..\..\..\..\..\xbmc\cores\dvdplayer\DVDDemuxers;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>_WIN32;_DEBUG;_WINDOWS;VDR_EXPORTS;_WIN32PC;_WINSOCKAPI_;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <MinimalRebuild>true</MinimalRebuild>
+      <ExceptionHandling>Sync</ExceptionHandling>
+      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+    </ClCompile>
+    <Link>
+      <OutputFile>..\..\..\..\..\addons\library.xbmc.pvr\$(ProjectName).dll</OutputFile>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <AdditionalIncludeDirectories>..\..\..\..\..\xbmc;..\..\..\..\..\xbmc\addons\include;..\..\..\..\..\xbmc\cores\dvdplayer\DVDDemuxers;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;HAS_SDL_OPENGL;HAS_SDL;_USRDLL;XBMC_VDR_EXPORTS;_WIN32PC;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ExceptionHandling>Sync</ExceptionHandling>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+    </ClCompile>
+    <Link>
+      <OutputFile>..\..\..\..\..\addons\library.xbmc.pvr\$(ProjectName).dll</OutputFile>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\libXBMC_pvr.cpp" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/lib/addons/library.xbmc.pvr/project/VS2010Express/libXBMC_pvr.vcxproj.filters b/lib/addons/library.xbmc.pvr/project/VS2010Express/libXBMC_pvr.vcxproj.filters
new file mode 100644 (file)
index 0000000..db93c59
--- /dev/null
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\libXBMC_pvr.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+</Project>
\ No newline at end of file
index e6a0115..73298f0 100644 (file)
@@ -1,5 +1,5 @@
 Microsoft Visual Studio Solution File, Format Version 11.00
-# Visual Studio 2010
+# Visual C++ Express 2010
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "XBMC", "XBMC.vcxproj", "{3A68081D-E8F9-4523-9436-530DE9E5530A}"
        ProjectSection(ProjectDependencies) = postProject
                {78B079BD-9FC7-4B9E-B4A6-96DA0F00248B} = {78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}
@@ -131,6 +131,21 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "python_bz2.pyd", "..\..\xbm
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libhdhomerun_dll", "..\..\xbmc\lib\libhdhomerun\hdhomerun\hdhomerun.vcxproj", "{1E2FB608-3DD2-4021-A598-90008FA6DE85}"
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libXBMC_addon", "..\..\lib\addons\library.xbmc.addon\project\VS2010Express\libXBMC_addon.vcxproj", "{2DCEA60B-4EBC-4DB7-9FBD-297C1EFD95D7}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libXBMC_gui", "..\..\lib\addons\library.xbmc.gui\project\VS2010Express\libXBMC_gui.vcxproj", "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libXBMC_pvr", "..\..\lib\addons\library.xbmc.pvr\project\VS2010Express\libXBMC_pvr.vcxproj", "{6D8C91F8-992F-4C83-9DE3-485D64EF8420}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pvrclient_mptv", "..\..\xbmc\pvrclients\MediaPortal\project\VS2010Express\XBMC_MPTV.vcxproj", "{74C9642E-1988-48DC-8404-11717C355378}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pvrclient_tvheadend", "..\..\xbmc\pvrclients\tvheadend\project\VS2010Express\XBMC_tvheadend.vcxproj", "{C04B0FB1-667D-4F1C-BDAE-A07CDFFA74A3}"
+       ProjectSection(ProjectDependencies) = postProject
+               {00700E12-A63B-4E54-B962-4011A90584BD} = {00700E12-A63B-4E54-B962-4011A90584BD}
+       EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pvrclient_vdr", "..\..\xbmc\pvrclients\vdr-streamdev\project\VS2010Express\XBMC_VDR.vcxproj", "{838CDE27-AFDF-449F-9981-A78903C9C71E}"
+EndProject
 Global
        GlobalSection(SolutionConfigurationPlatforms) = preSolution
                Debug (DirectX)|Win32 = Debug (DirectX)|Win32
@@ -623,6 +638,54 @@ Global
                {1E2FB608-3DD2-4021-A598-90008FA6DE85}.Release (DirectX)|Win32.Build.0 = Release|Win32
                {1E2FB608-3DD2-4021-A598-90008FA6DE85}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
                {1E2FB608-3DD2-4021-A598-90008FA6DE85}.Release (OpenGL)|Win32.Build.0 = Release|Win32
+               {2DCEA60B-4EBC-4DB7-9FBD-297C1EFD95D7}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
+               {2DCEA60B-4EBC-4DB7-9FBD-297C1EFD95D7}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
+               {2DCEA60B-4EBC-4DB7-9FBD-297C1EFD95D7}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
+               {2DCEA60B-4EBC-4DB7-9FBD-297C1EFD95D7}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32
+               {2DCEA60B-4EBC-4DB7-9FBD-297C1EFD95D7}.Release (DirectX)|Win32.ActiveCfg = Release|Win32
+               {2DCEA60B-4EBC-4DB7-9FBD-297C1EFD95D7}.Release (DirectX)|Win32.Build.0 = Release|Win32
+               {2DCEA60B-4EBC-4DB7-9FBD-297C1EFD95D7}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
+               {2DCEA60B-4EBC-4DB7-9FBD-297C1EFD95D7}.Release (OpenGL)|Win32.Build.0 = Release|Win32
+               {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
+               {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
+               {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
+               {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32
+               {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Release (DirectX)|Win32.ActiveCfg = Release|Win32
+               {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Release (DirectX)|Win32.Build.0 = Release|Win32
+               {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
+               {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}.Release (OpenGL)|Win32.Build.0 = Release|Win32
+               {6D8C91F8-992F-4C83-9DE3-485D64EF8420}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
+               {6D8C91F8-992F-4C83-9DE3-485D64EF8420}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
+               {6D8C91F8-992F-4C83-9DE3-485D64EF8420}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
+               {6D8C91F8-992F-4C83-9DE3-485D64EF8420}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32
+               {6D8C91F8-992F-4C83-9DE3-485D64EF8420}.Release (DirectX)|Win32.ActiveCfg = Release|Win32
+               {6D8C91F8-992F-4C83-9DE3-485D64EF8420}.Release (DirectX)|Win32.Build.0 = Release|Win32
+               {6D8C91F8-992F-4C83-9DE3-485D64EF8420}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
+               {6D8C91F8-992F-4C83-9DE3-485D64EF8420}.Release (OpenGL)|Win32.Build.0 = Release|Win32
+               {74C9642E-1988-48DC-8404-11717C355378}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
+               {74C9642E-1988-48DC-8404-11717C355378}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
+               {74C9642E-1988-48DC-8404-11717C355378}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
+               {74C9642E-1988-48DC-8404-11717C355378}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32
+               {74C9642E-1988-48DC-8404-11717C355378}.Release (DirectX)|Win32.ActiveCfg = Release|Win32
+               {74C9642E-1988-48DC-8404-11717C355378}.Release (DirectX)|Win32.Build.0 = Release|Win32
+               {74C9642E-1988-48DC-8404-11717C355378}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
+               {74C9642E-1988-48DC-8404-11717C355378}.Release (OpenGL)|Win32.Build.0 = Release|Win32
+               {C04B0FB1-667D-4F1C-BDAE-A07CDFFA74A3}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
+               {C04B0FB1-667D-4F1C-BDAE-A07CDFFA74A3}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
+               {C04B0FB1-667D-4F1C-BDAE-A07CDFFA74A3}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
+               {C04B0FB1-667D-4F1C-BDAE-A07CDFFA74A3}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32
+               {C04B0FB1-667D-4F1C-BDAE-A07CDFFA74A3}.Release (DirectX)|Win32.ActiveCfg = Release|Win32
+               {C04B0FB1-667D-4F1C-BDAE-A07CDFFA74A3}.Release (DirectX)|Win32.Build.0 = Release|Win32
+               {C04B0FB1-667D-4F1C-BDAE-A07CDFFA74A3}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
+               {C04B0FB1-667D-4F1C-BDAE-A07CDFFA74A3}.Release (OpenGL)|Win32.Build.0 = Release|Win32
+               {838CDE27-AFDF-449F-9981-A78903C9C71E}.Debug (DirectX)|Win32.ActiveCfg = Debug|Win32
+               {838CDE27-AFDF-449F-9981-A78903C9C71E}.Debug (DirectX)|Win32.Build.0 = Debug|Win32
+               {838CDE27-AFDF-449F-9981-A78903C9C71E}.Debug (OpenGL)|Win32.ActiveCfg = Debug|Win32
+               {838CDE27-AFDF-449F-9981-A78903C9C71E}.Debug (OpenGL)|Win32.Build.0 = Debug|Win32
+               {838CDE27-AFDF-449F-9981-A78903C9C71E}.Release (DirectX)|Win32.ActiveCfg = Release|Win32
+               {838CDE27-AFDF-449F-9981-A78903C9C71E}.Release (DirectX)|Win32.Build.0 = Release|Win32
+               {838CDE27-AFDF-449F-9981-A78903C9C71E}.Release (OpenGL)|Win32.ActiveCfg = Release|Win32
+               {838CDE27-AFDF-449F-9981-A78903C9C71E}.Release (OpenGL)|Win32.Build.0 = Release|Win32
        EndGlobalSection
        GlobalSection(SolutionProperties) = preSolution
                HideSolutionNode = FALSE
index 81fed4f..2841fa8 100644 (file)
   </ItemDefinitionGroup>
   <ItemGroup>
     <ClCompile Include="..\..\xbmc\AddonDatabase.cpp" />
+    <ClCompile Include="..\..\xbmc\addons\AddonHelpers_Addon.cpp" />
+    <ClCompile Include="..\..\xbmc\addons\AddonHelpers_GUI.cpp" />
+    <ClCompile Include="..\..\xbmc\addons\AddonHelpers_local.cpp" />
+    <ClCompile Include="..\..\xbmc\addons\AddonHelpers_PVR.cpp" />
     <ClCompile Include="..\..\xbmc\addons\PluginSource.cpp" />
+    <ClCompile Include="..\..\xbmc\addons\PVRClient.cpp" />
     <ClCompile Include="..\..\xbmc\addons\Repository.cpp" />
     <ClCompile Include="..\..\xbmc\addons\Service.cpp" />
     <ClCompile Include="..\..\xbmc\addons\Skin.cpp" />
     <ClCompile Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Video\CrystalHD.cpp" />
+    <ClCompile Include="..\..\xbmc\cores\dvdplayer\DVDDemuxers\DVDDemuxPVRClient.cpp" />
     <ClCompile Include="..\..\xbmc\cores\dvdplayer\DVDInputStreams\DVDInputStreamBluray.cpp" />
+    <ClCompile Include="..\..\xbmc\cores\dvdplayer\DVDInputStreams\DVDInputStreamPVRManager.cpp" />
     <ClCompile Include="..\..\xbmc\cores\VideoRenderers\VideoShaders\WinVideoFilter.cpp" />
     <ClCompile Include="..\..\xbmc\FileSystem\FileUDF.cpp" />
+    <ClCompile Include="..\..\xbmc\FileSystem\PVRDirectory.cpp" />
+    <ClCompile Include="..\..\xbmc\FileSystem\PVRFile.cpp" />
     <ClCompile Include="..\..\xbmc\FileSystem\udf25.cpp" />
     <ClCompile Include="..\..\xbmc\FileSystem\UDFDirectory.cpp" />
     <ClCompile Include="..\..\xbmc\FileSystem\VideoDatabaseDirectory\DirectoryNodeCountry.cpp" />
     <ClCompile Include="..\..\xbmc\GUIDialogAddonInfo.cpp" />
+    <ClCompile Include="..\..\xbmc\GUIDialogPVRChannelManager.cpp" />
+    <ClCompile Include="..\..\xbmc\GUIDialogPVRChannelsOSD.cpp" />
+    <ClCompile Include="..\..\xbmc\GUIDialogPVRCutterOSD.cpp" />
+    <ClCompile Include="..\..\xbmc\GUIDialogPVRDirectorOSD.cpp" />
+    <ClCompile Include="..\..\xbmc\GUIDialogPVRGroupManager.cpp" />
+    <ClCompile Include="..\..\xbmc\GUIDialogPVRGuideInfo.cpp" />
+    <ClCompile Include="..\..\xbmc\GUIDialogPVRGuideOSD.cpp" />
+    <ClCompile Include="..\..\xbmc\GUIDialogPVRGuideSearch.cpp" />
+    <ClCompile Include="..\..\xbmc\GUIDialogPVRRecordingInfo.cpp" />
+    <ClCompile Include="..\..\xbmc\GUIDialogPVRTimerSettings.cpp" />
+    <ClCompile Include="..\..\xbmc\GUIDialogPVRUpdateProgressBar.cpp" />
     <ClCompile Include="..\..\xbmc\GUIDialogTextViewer.cpp" />
     <ClCompile Include="..\..\xbmc\GUIViewStateAddonBrowser.cpp" />
+    <ClCompile Include="..\..\xbmc\GUIViewStateTV.cpp" />
     <ClCompile Include="..\..\xbmc\GUIWindowSystemInfo.cpp" />
+    <ClCompile Include="..\..\xbmc\GUIWindowTV.cpp" />
     <ClCompile Include="..\..\xbmc\lib\libjsonrpc\AudioLibrary.cpp" />
     <ClCompile Include="..\..\xbmc\lib\libPython\xbmcmodule\PythonAddon.cpp" />
     <ClCompile Include="..\..\xbmc\lib\libPython\xbmcmodule\xbmcaddonmodule.cpp" />
+    <ClCompile Include="..\..\xbmc\pvr\PVRChannel.cpp" />
+    <ClCompile Include="..\..\xbmc\pvr\PVRChannelGroup.cpp" />
+    <ClCompile Include="..\..\xbmc\pvr\PVRChannelGroups.cpp" />
+    <ClCompile Include="..\..\xbmc\pvr\PVRChannels.cpp" />
+    <ClCompile Include="..\..\xbmc\pvr\PVRChannelsContainer.cpp" />
+    <ClCompile Include="..\..\xbmc\pvr\PVRDatabase.cpp" />
+    <ClCompile Include="..\..\xbmc\pvr\PVREpg.cpp" />
+    <ClCompile Include="..\..\xbmc\pvr\PVREpgInfoTag.cpp" />
+    <ClCompile Include="..\..\xbmc\pvr\PVREpgs.cpp" />
+    <ClCompile Include="..\..\xbmc\pvr\PVREpgSearchFilter.cpp" />
+    <ClCompile Include="..\..\xbmc\pvr\PVRManager.cpp" />
+    <ClCompile Include="..\..\xbmc\pvr\PVRRecordings.cpp" />
+    <ClCompile Include="..\..\xbmc\pvr\PVRTimerInfoTag.cpp" />
+    <ClCompile Include="..\..\xbmc\pvr\PVRTimers.cpp" />
     <ClCompile Include="..\..\xbmc\RenderSystemGLES.cpp" />
     <ClCompile Include="..\..\xbmc\SystemGlobals.cpp" />
     <ClCompile Include="..\..\xbmc\utils\fstrcmp.c">
       <CompileAs Condition="'$(Configuration)|$(Platform)'=='Release (OpenGL)|Win32'">CompileAsCpp</CompileAs>
       <CompileAs Condition="'$(Configuration)|$(Platform)'=='Template|Win32'">CompileAsCpp</CompileAs>
     </ClCompile>
+    <ClCompile Include="..\..\xbmc\utils\Observer.cpp" />
+    <ClCompile Include="..\..\xbmc\utils\TextSearch.cpp" />
     <ClCompile Include="..\..\xbmc\utils\Variant.cpp" />
     <ClCompile Include="..\..\xbmc\win32\NetworkWin32.cpp" />
     <ClCompile Include="..\..\xbmc\win32\pch.cpp">
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\..\xbmc\AddonDatabase.h" />
+    <ClInclude Include="..\..\xbmc\addons\AddonHelpers_Addon.h" />
+    <ClInclude Include="..\..\xbmc\addons\AddonHelpers_GUI.h" />
+    <ClInclude Include="..\..\xbmc\addons\AddonHelpers_local.h" />
+    <ClInclude Include="..\..\xbmc\addons\AddonHelpers_PVR.h" />
+    <ClInclude Include="..\..\xbmc\addons\DllPVRClient.h" />
     <ClInclude Include="..\..\xbmc\addons\PluginSource.h" />
+    <ClInclude Include="..\..\xbmc\addons\PVRClient.h" />
     <ClInclude Include="..\..\xbmc\addons\Repository.h" />
     <ClInclude Include="..\..\xbmc\addons\Service.h" />
     <ClInclude Include="..\..\xbmc\addons\Skin.h" />
     <ClInclude Include="..\..\xbmc\cores\dvdplayer\DVDCodecs\Video\CrystalHD.h" />
+    <ClInclude Include="..\..\xbmc\cores\dvdplayer\DVDDemuxers\DVDDemuxPVRClient.h" />
     <ClInclude Include="..\..\xbmc\cores\dvdplayer\DVDInputStreams\DVDInputStreamBluray.h" />
+    <ClInclude Include="..\..\xbmc\cores\dvdplayer\DVDInputStreams\DVDInputStreamPVRManager.h" />
     <ClInclude Include="..\..\xbmc\cores\VideoRenderers\VideoShaders\WinVideoFilter.h" />
     <ClInclude Include="..\..\xbmc\FileSystem\FileUDF.h" />
+    <ClInclude Include="..\..\xbmc\FileSystem\PVRDirectory.h" />
+    <ClInclude Include="..\..\xbmc\FileSystem\PVRFile.h" />
     <ClInclude Include="..\..\xbmc\FileSystem\udf25.h" />
     <ClInclude Include="..\..\xbmc\FileSystem\UDFDirectory.h" />
     <ClInclude Include="..\..\xbmc\FileSystem\VideoDatabaseDirectory\DirectoryNodeCountry.h" />
     <ClInclude Include="..\..\xbmc\GUIDialogAddonInfo.h" />
+    <ClInclude Include="..\..\xbmc\GUIDialogPVRChannelManager.h" />
+    <ClInclude Include="..\..\xbmc\GUIDialogPVRChannelsOSD.h" />
+    <ClInclude Include="..\..\xbmc\GUIDialogPVRCutterOSD.h" />
+    <ClInclude Include="..\..\xbmc\GUIDialogPVRDirectorOSD.h" />
+    <ClInclude Include="..\..\xbmc\GUIDialogPVRGroupManager.h" />
+    <ClInclude Include="..\..\xbmc\GUIDialogPVRGuideInfo.h" />
+    <ClInclude Include="..\..\xbmc\GUIDialogPVRGuideOSD.h" />
+    <ClInclude Include="..\..\xbmc\GUIDialogPVRGuideSearch.h" />
+    <ClInclude Include="..\..\xbmc\GUIDialogPVRRecordingInfo.h" />
+    <ClInclude Include="..\..\xbmc\GUIDialogPVRTimerSettings.h" />
+    <ClInclude Include="..\..\xbmc\GUIDialogPVRUpdateProgressBar.h" />
     <ClInclude Include="..\..\xbmc\GUIDialogTextViewer.h" />
     <ClInclude Include="..\..\xbmc\GUIViewStateAddonBrowser.h" />
+    <ClInclude Include="..\..\xbmc\GUIViewStateTV.h" />
+    <ClInclude Include="..\..\xbmc\GUIWindowTV.h" />
     <ClInclude Include="..\..\xbmc\lib\libjsonrpc\AudioLibrary.h" />
     <ClInclude Include="..\..\xbmc\lib\libPython\xbmcmodule\PythonAddon.h" />
+    <ClInclude Include="..\..\xbmc\pvr\PVRChannel.h" />
+    <ClInclude Include="..\..\xbmc\pvr\PVRChannelGroup.h" />
+    <ClInclude Include="..\..\xbmc\pvr\PVRChannelGroups.h" />
+    <ClInclude Include="..\..\xbmc\pvr\PVRChannels.h" />
+    <ClInclude Include="..\..\xbmc\pvr\PVRChannelsContainer.h" />
+    <ClInclude Include="..\..\xbmc\pvr\PVRDatabase.h" />
+    <ClInclude Include="..\..\xbmc\pvr\PVREpg.h" />
+    <ClInclude Include="..\..\xbmc\pvr\PVREpgInfoTag.h" />
+    <ClInclude Include="..\..\xbmc\pvr\PVREpgs.h" />
+    <ClInclude Include="..\..\xbmc\pvr\PVREpgSearchFilter.h" />
+    <ClInclude Include="..\..\xbmc\pvr\PVRManager.h" />
+    <ClInclude Include="..\..\xbmc\pvr\PVRRecordings.h" />
+    <ClInclude Include="..\..\xbmc\pvr\PVRTimerInfoTag.h" />
+    <ClInclude Include="..\..\xbmc\pvr\PVRTimers.h" />
     <ClInclude Include="..\..\xbmc\RenderSystemGLES.h" />
     <ClInclude Include="..\..\xbmc\utils\ISerializable.h" />
+    <ClInclude Include="..\..\xbmc\utils\Observer.h" />
+    <ClInclude Include="..\..\xbmc\utils\TextSearch.h" />
     <ClInclude Include="..\..\xbmc\utils\Variant.h" />
     <ClInclude Include="..\..\xbmc\win32\NetworkWin32.h" />
     <ClInclude Include="..\..\xbmc\win32\pch.h" />
index 9858303..0db4733 100644 (file)
     <Filter Include="libraries\jsoncpp">
       <UniqueIdentifier>{b27f9ce9-0b2e-4456-9bce-8313af739c2d}</UniqueIdentifier>
     </Filter>
+    <Filter Include="Source Files\PVR">
+      <UniqueIdentifier>{d3ac1ca7-da89-4fa6-9d06-072df2d8fc04}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\..\xbmc\win32\NetworkWin32.cpp">
     <ClCompile Include="..\..\xbmc\FileSystem\UDFDirectory.cpp">
       <Filter>Source Files\Filesystem</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\xbmc\pvr\PVRDatabase.cpp">
+      <Filter>Source Files\PVR</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\xbmc\pvr\PVREpg.cpp">
+      <Filter>Source Files\PVR</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\xbmc\pvr\PVREpgInfoTag.cpp">
+      <Filter>Source Files\PVR</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\xbmc\pvr\PVREpgs.cpp">
+      <Filter>Source Files\PVR</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\xbmc\pvr\PVREpgSearchFilter.cpp">
+      <Filter>Source Files\PVR</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\xbmc\pvr\PVRManager.cpp">
+      <Filter>Source Files\PVR</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\xbmc\pvr\PVRRecordings.cpp">
+      <Filter>Source Files\PVR</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\xbmc\pvr\PVRTimerInfoTag.cpp">
+      <Filter>Source Files\PVR</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\xbmc\pvr\PVRTimers.cpp">
+      <Filter>Source Files\PVR</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\xbmc\pvr\PVRChannel.cpp">
+      <Filter>Source Files\PVR</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\xbmc\pvr\PVRChannelGroup.cpp">
+      <Filter>Source Files\PVR</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\xbmc\pvr\PVRChannelGroups.cpp">
+      <Filter>Source Files\PVR</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\xbmc\pvr\PVRChannels.cpp">
+      <Filter>Source Files\PVR</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\xbmc\pvr\PVRChannelsContainer.cpp">
+      <Filter>Source Files\PVR</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\xbmc\GUIDialogPVRChannelManager.cpp">
+      <Filter>Source Files\GUI Dialog</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\xbmc\GUIDialogPVRChannelsOSD.cpp">
+      <Filter>Source Files\GUI Dialog</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\xbmc\GUIDialogPVRCutterOSD.cpp">
+      <Filter>Source Files\GUI Dialog</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\xbmc\GUIDialogPVRDirectorOSD.cpp">
+      <Filter>Source Files\GUI Dialog</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\xbmc\GUIDialogPVRGroupManager.cpp">
+      <Filter>Source Files\GUI Dialog</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\xbmc\GUIDialogPVRGuideInfo.cpp">
+      <Filter>Source Files\GUI Dialog</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\xbmc\GUIDialogPVRGuideOSD.cpp">
+      <Filter>Source Files\GUI Dialog</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\xbmc\GUIDialogPVRGuideSearch.cpp">
+      <Filter>Source Files\GUI Dialog</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\xbmc\GUIDialogPVRRecordingInfo.cpp">
+      <Filter>Source Files\GUI Dialog</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\xbmc\GUIDialogPVRTimerSettings.cpp">
+      <Filter>Source Files\GUI Dialog</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\xbmc\GUIDialogPVRUpdateProgressBar.cpp">
+      <Filter>Source Files\GUI Dialog</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\xbmc\addons\PVRClient.cpp">
+      <Filter>Source Files\Addons</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\xbmc\addons\AddonHelpers_PVR.cpp">
+      <Filter>Source Files\Addons</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\xbmc\FileSystem\PVRDirectory.cpp">
+      <Filter>Source Files\Filesystem</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\xbmc\FileSystem\PVRFile.cpp">
+      <Filter>Source Files\Filesystem</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\xbmc\GUIWindowTV.cpp">
+      <Filter>Source Files\GUI Other</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\xbmc\cores\dvdplayer\DVDInputStreams\DVDInputStreamPVRManager.cpp">
+      <Filter>cores\dvdplayer\DVDInputStreams</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\xbmc\cores\dvdplayer\DVDDemuxers\DVDDemuxPVRClient.cpp">
+      <Filter>cores\dvdplayer\DVDDemuxers</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\xbmc\GUIViewStateTV.cpp">
+      <Filter>Source Files\GUI Other</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\xbmc\utils\Observer.cpp">
+      <Filter>Source Files\Utils</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\xbmc\addons\AddonHelpers_Addon.cpp">
+      <Filter>Source Files\Addons</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\xbmc\addons\AddonHelpers_GUI.cpp">
+      <Filter>Source Files\Addons</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\xbmc\addons\AddonHelpers_local.cpp">
+      <Filter>Source Files\Addons</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\xbmc\utils\TextSearch.cpp">
+      <Filter>Source Files\Utils</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\..\xbmc\win32\NetworkWin32.h">
     <ClInclude Include="..\..\xbmc\FileSystem\UDFDirectory.h">
       <Filter>Source Files\Filesystem</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\xbmc\pvr\PVRDatabase.h">
+      <Filter>Source Files\PVR</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\xbmc\pvr\PVREpg.h">
+      <Filter>Source Files\PVR</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\xbmc\pvr\PVREpgInfoTag.h">
+      <Filter>Source Files\PVR</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\xbmc\pvr\PVREpgs.h">
+      <Filter>Source Files\PVR</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\xbmc\pvr\PVREpgSearchFilter.h">
+      <Filter>Source Files\PVR</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\xbmc\pvr\PVRManager.h">
+      <Filter>Source Files\PVR</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\xbmc\pvr\PVRRecordings.h">
+      <Filter>Source Files\PVR</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\xbmc\pvr\PVRTimerInfoTag.h">
+      <Filter>Source Files\PVR</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\xbmc\pvr\PVRTimers.h">
+      <Filter>Source Files\PVR</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\xbmc\pvr\PVRChannel.h">
+      <Filter>Source Files\PVR</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\xbmc\pvr\PVRChannelGroup.h">
+      <Filter>Source Files\PVR</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\xbmc\pvr\PVRChannelGroups.h">
+      <Filter>Source Files\PVR</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\xbmc\pvr\PVRChannels.h">
+      <Filter>Source Files\PVR</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\xbmc\pvr\PVRChannelsContainer.h">
+      <Filter>Source Files\PVR</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\xbmc\GUIDialogPVRUpdateProgressBar.h">
+      <Filter>Source Files\GUI Dialog</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\xbmc\GUIDialogPVRChannelManager.h">
+      <Filter>Source Files\GUI Dialog</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\xbmc\GUIDialogPVRChannelsOSD.h">
+      <Filter>Source Files\GUI Dialog</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\xbmc\GUIDialogPVRCutterOSD.h">
+      <Filter>Source Files\GUI Dialog</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\xbmc\GUIDialogPVRDirectorOSD.h">
+      <Filter>Source Files\GUI Dialog</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\xbmc\GUIDialogPVRGroupManager.h">
+      <Filter>Source Files\GUI Dialog</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\xbmc\GUIDialogPVRGuideInfo.h">
+      <Filter>Source Files\GUI Dialog</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\xbmc\GUIDialogPVRGuideOSD.h">
+      <Filter>Source Files\GUI Dialog</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\xbmc\GUIDialogPVRGuideSearch.h">
+      <Filter>Source Files\GUI Dialog</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\xbmc\GUIDialogPVRRecordingInfo.h">
+      <Filter>Source Files\GUI Dialog</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\xbmc\GUIDialogPVRTimerSettings.h">
+      <Filter>Source Files\GUI Dialog</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\xbmc\addons\DllPVRClient.h">
+      <Filter>Source Files\Addons</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\xbmc\addons\PVRClient.h">
+      <Filter>Source Files\Addons</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\xbmc\addons\AddonHelpers_PVR.h">
+      <Filter>Source Files\Addons</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\xbmc\FileSystem\PVRFile.h">
+      <Filter>Source Files\Filesystem</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\xbmc\FileSystem\PVRDirectory.h">
+      <Filter>Source Files\Filesystem</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\xbmc\GUIWindowTV.h">
+      <Filter>Source Files\GUI Other</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\xbmc\cores\dvdplayer\DVDInputStreams\DVDInputStreamPVRManager.h">
+      <Filter>cores\dvdplayer\DVDInputStreams</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\xbmc\cores\dvdplayer\DVDDemuxers\DVDDemuxPVRClient.h">
+      <Filter>cores\dvdplayer\DVDDemuxers</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\xbmc\GUIViewStateTV.h">
+      <Filter>Source Files\GUI Other</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\xbmc\utils\Observer.h">
+      <Filter>Source Files\Utils</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\xbmc\addons\AddonHelpers_local.h">
+      <Filter>Source Files\Addons</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\xbmc\addons\AddonHelpers_Addon.h">
+      <Filter>Source Files\Addons</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\xbmc\addons\AddonHelpers_GUI.h">
+      <Filter>Source Files\Addons</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\xbmc\utils\TextSearch.h">
+      <Filter>Source Files\Utils</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="..\..\xbmc\win32\XBMC.ico">
index 422457d..88564ab 100644 (file)
     <ClCompile Include="..\..\guilib\GUIControlProfiler.cpp" />
     <ClCompile Include="..\..\guilib\GUIDialog.cpp" />
     <ClCompile Include="..\..\guilib\GUIEditControl.cpp" />
+    <ClCompile Include="..\..\guilib\GUIEPGGridContainer.cpp" />
     <ClCompile Include="..\..\guilib\GUIFadeLabelControl.cpp" />
     <ClCompile Include="..\..\guilib\GUIFixedListContainer.cpp" />
     <ClCompile Include="..\..\guilib\GUIFont.cpp" />
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\..\guilib\D3DResource.h" />
+    <ClInclude Include="..\..\guilib\GUIEPGGridContainer.h" />
     <ClInclude Include="..\..\guilib\GUIRenderingControl.h" />
     <ClInclude Include="..\..\guilib\GUITextureGLES.h" />
     <ClInclude Include="..\..\guilib\TextureBundleXBT.h" />
index c5669b3..b3f840a 100644 (file)
     <ClCompile Include="..\..\guilib\GUITextureGLES.cpp">
       <Filter>Rendering\GL</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\guilib\GUIEPGGridContainer.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\..\guilib\D3DResource.h">
     <ClInclude Include="..\..\guilib\GUITextureGLES.h">
       <Filter>Rendering\GL</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\guilib\GUIEPGGridContainer.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <CustomBuild Include="..\..\xbmc\win32\PlatformInclude.h">
index 29a9fa3..c0a8a79 100644 (file)
       <backspace>Backspace</backspace>
     </keyboard>
   </VirtualKeyboard>
+  <MyTV>
+    <keyboard>
+      <delete>Delete</delete>
+      <m>Move</m>
+      <r>Rename</r>
+    </keyboard>
+  </MyTV>
   <MyFiles>
     <keyboard>
       <space>Highlight</space>
       <down>BigStepBack</down>
       <a>AudioDelay</a>
       <escape>Fullscreen</escape>
+      <c>Playlist</c>
       <v>XBMC.ActivateWindow(Teletext)</v>
     </keyboard>
   </FullscreenVideo>
       <o>CodecInfo</o>
       <l>LockPreset</l>
       <escape>FullScreen</escape>
+      <g>XBMC.ActivateWindow(PVROSDGuide)</g>
+      <c>XBMC.ActivateWindow(PVROSDChannels)</c>
     </keyboard>
   </Visualisation>
   <MusicOSD>
       <backspace>Close</backspace>
     </keyboard>
   </NumericInput>
+  <PVROSDChannels>
+    <keyboard>
+      <backspace>Close</backspace>
+      <escape>Close</escape>
+      <c>Close</c>
+    </keyboard>
+  </PVROSDChannels>
+  <PVROSDGuide>
+    <keyboard>
+      <backspace>Close</backspace>
+      <escape>Close</escape>
+    </keyboard>
+  </PVROSDGuide>
+  <PVROSDDirector>
+    <keyboard>
+      <backspace>Close</backspace>
+      <escape>Close</escape>
+    </keyboard>
+  </PVROSDDirector>
+  <PVROSDCutter>
+    <keyboard>
+      <backspace>Close</backspace>
+      <escape>Close</escape>
+    </keyboard>
+  </PVROSDCutter>
+  <MyTVSettings>
+    <keyboard>
+      <backspace>PreviousMenu</backspace>
+    </keyboard>
+  </MyTVSettings>
   <FileBrowser>
     <keyboard>
       <space>Highlight</space>
index 3a2b8f5..7a1e2f8 100644 (file)
@@ -66,8 +66,8 @@
       <myvideo>XBMC.ActivateWindow(MyVideos)</myvideo>
       <mymusic>XBMC.ActivateWindow(MyMusic)</mymusic>
       <mypictures>XBMC.ActivateWindow(MyPictures)</mypictures>
-      <mytv>XBMC.ActivateWindow(VideoLibrary,TvShows)</mytv>
-      <red>XBMC.ActivateWindow(Home)</red>
+      <mytv>XBMC.ActivateWindow(MyTV)</mytv>
+      <red>XBMC.ActivateWindow(MyTV)</red>
       <green>XBMC.ActivateWindow(MyVideos)</green>
       <yellow>XBMC.ActivateWindow(MyMusic)</yellow>
       <blue>XBMC.ActivateWindow(MyPictures)</blue>
       <hash>XBMC.ActivateWindow(Settings)</hash>
     </remote>
   </Home>
+  <MyTV>
+    <remote>
+      <clear>Delete</clear>
+    </remote>
+  </MyTV>
   <MyFiles>
     <remote>
       <clear>Delete</clear>
       <select>AspectRatio</select>
       <title>CodecInfo</title>
       <info>Info</info>
+      <guide>XBMC.ActivateWindow(PVROSDGuide)</guide>
       <teletext>XBMC.ActivateWindow(Teletext)</teletext>
       <subtitle>ShowSubtitles</subtitle>
       <star>ShowSubtitles</star>
       <language>AudioNextLanguage</language>
+      <playlist>Playlist</playlist>
+      <language>Language</language>
       <hash>AudioNextLanguage</hash>
     </remote>
   </FullscreenVideo>
       <menu>XBMC.ActivateWindow(MusicOSD)</menu>
       <start>XBMC.ActivateWindow(MusicOSD)</start>
       <info>Info</info>
+      <guide>XBMC.ActivateWindow(PVROSDGuide)</guide>
+      <playlist>XBMC.ActivateWindow(PVROSDChannels)</playlist>
     </remote>
   </Visualisation>
   <MusicOSD>
       <back>PreviousMenu</back>
     </remote>
   </Weather>
+  <TV>
+    <remote>
+      <red>Red</red>
+      <green>Green</green>
+      <yellow>Yellow</yellow>
+      <blue>Blue</blue>
+      <back>PreviousMenu</back>
+    </remote>
+  </TV>
   <Settings>
     <remote>
       <back>PreviousMenu</back>
       <back>Close</back>
     </remote>
   </Favourites>
+  <PVROSDChannels>
+    <remote>
+      <back>Close</back>
+      <menu>Close</menu>
+      <start>Close</start>
+      <playlist>Close</playlist>
+    </remote>
+  </PVROSDChannels>
+  <PVROSDGuide>
+    <remote>
+      <back>Close</back>
+      <menu>Close</menu>
+      <start>Close</start>
+      <guide>Close</guide>
+    </remote>
+  </PVROSDGuide>
+  <PVROSDDirector>
+    <remote>
+      <back>Close</back>
+      <menu>Close</menu>
+    </remote>
+  </PVROSDDirector>
+  <PVROSDCutter>
+    <remote>
+      <back>Close</back>
+      <menu>Close</menu>
+    </remote>
+  </PVROSDCutter>
+  <MyTVSettings>
+    <remote>
+      <back>PreviousMenu</back>
+    </remote>
+  </MyTVSettings>
 </keymap>
index 66784de..6fc8c20 100644 (file)
@@ -33,5 +33,8 @@
 
     <!-- Pass these to dvdplayer as we do not know if they are audio or video -->
     <rule name="nsv" filetypes="nsv" player="DVDPlayer" />
+
+    <!-- pvr radio channels should be played by dvdplayer because they need buffering -->
+    <rule name="radio" filetypes="pvr" filename=".*/radio/.*" player="DVDPlayer" />
   </rules>
 </playercorefactory>
index 15bc0f5..85ed2c1 100644 (file)
@@ -76,6 +76,10 @@ bool CAddonDatabase::CreateTables()
     m_pDS->exec("CREATE TABLE disabled (id integer primary key, addonID text)\n");
     m_pDS->exec("CREATE UNIQUE INDEX idxDisabled ON disabled(addonID)");
 
+    CLog::Log(LOGINFO, "create pvrenabled table");
+    m_pDS->exec("CREATE TABLE pvrenabled (id integer primary key, addonID text)\n");
+    m_pDS->exec("CREATE UNIQUE INDEX idxPVREnabled ON pvrenabled(addonID)");
+
     CLog::Log(LOGINFO, "create broken table");
     m_pDS->exec("CREATE TABLE broken (id integer primary key, addonID text, reason text)\n");
     m_pDS->exec("CREATE UNIQUE INDEX idxBroken ON broken(addonID)");
@@ -151,6 +155,11 @@ bool CAddonDatabase::UpdateOldVersion(int version)
     {
       m_pDS->exec("alter table addon add disclaimer text");
     }
+    if (version < 13)
+    {
+      m_pDS->exec("CREATE TABLE pvrenabled (id integer primary key, addonID text)\n");
+      m_pDS->exec("CREATE INDEX idxPVREnabled ON pvrenabled(addonID)");
+    }
   }
   catch (...)
   {
@@ -617,6 +626,40 @@ bool CAddonDatabase::DisableAddon(const CStdString &addonID, bool disable /* = t
   return false;
 }
 
+bool CAddonDatabase::EnableSystemPVRAddon(const CStdString &addonID, bool enable)
+{
+  try
+  {
+    if (NULL == m_pDB.get()) return false;
+    if (NULL == m_pDS.get()) return false;
+
+    if (enable)
+    {
+      CStdString sql = PrepareSQL("select id from pvrenabled where addonID='%s'", addonID.c_str());
+      m_pDS->query(sql.c_str());
+      if (m_pDS->eof()) // not found
+      {
+        m_pDS->close();
+        sql = PrepareSQL("insert into pvrenabled(id, addonID) values(NULL, '%s')", addonID.c_str());
+        m_pDS->exec(sql);
+        return true;
+      }
+      return false; // already enabled or failed query
+    }
+    else
+    {
+      CStdString sql = PrepareSQL("delete from pvrenabled where addonID='%s'", addonID.c_str());
+      m_pDS->exec(sql);
+    }
+    return true;
+  }
+  catch (...)
+  {
+    CLog::Log(LOGERROR, "%s failed on addon '%s'", __FUNCTION__, addonID.c_str());
+  }
+  return false;
+}
+
 bool CAddonDatabase::BreakAddon(const CStdString &addonID, bool broken /* = true */, const CStdString& reason)
 {
   try
@@ -671,6 +714,26 @@ bool CAddonDatabase::IsAddonDisabled(const CStdString &addonID)
   return false;
 }
 
+bool CAddonDatabase::IsSystemPVRAddonEnabled(const CStdString &addonID)
+{
+  try
+  {
+    if (NULL == m_pDB.get()) return false;
+    if (NULL == m_pDS.get()) return false;
+
+    CStdString sql = PrepareSQL("select id from pvrenabled where addonID='%s'", addonID.c_str());
+    m_pDS->query(sql.c_str());
+    bool ret = !m_pDS->eof(); // in the pvrenabled table -> enabled
+    m_pDS->close();
+    return ret;
+  }
+  catch (...)
+  {
+    CLog::Log(LOGERROR, "%s failed on addon %s", __FUNCTION__, addonID.c_str());
+  }
+  return false;
+}
+
 CStdString CAddonDatabase::IsAddonBroken(const CStdString &addonID)
 {
   try
index 5fd182c..deb557d 100644 (file)
@@ -57,9 +57,11 @@ public:
    \sa SetRepoTimestamp */
   CDateTime GetRepoTimestamp(const CStdString& id);
 
+  bool GetSystemEnabled(const CStdString& id);
+  bool SetSystemEnabled(const CStdString& id, bool enabled);
   bool Search(const CStdString& search, ADDON::VECADDONS& items);
   bool SearchTitle(const CStdString& strSearch, ADDON::VECADDONS& items);
-  static void SetPropertiesFromAddon(const ADDON::AddonPtr& addon, CFileItemPtr& item); 
+  static void SetPropertiesFromAddon(const ADDON::AddonPtr& addon, CFileItemPtr& item);
 
   /*! \brief Disable an addon.
    Sets a flag that this addon has been disabled.  If disabled, it is usually still available on disk.
@@ -80,11 +82,25 @@ public:
    \sa DisableAddon, IsAddonDisabled */
   bool HasDisabledAddons();
 
+  /*! \brief Enable an system PVR addon.
+   Sets a flag that this PVR addon has been enabled.  If disabled, it is usually still available on disk.
+   \param addonID id of the addon to enable
+   \param disable whether to enable or disable.  Defaults to false (disable)
+   \return true on success, false on failure
+   \sa IsSystemPVRAddonEnabled */
+  bool EnableSystemPVRAddon(const CStdString &addonID, bool enable = false);
+
+  /*! \brief Check whether an system PVR addon has been enabled via EnableSystemPVRAddon.
+   \param addonID id of the addon to check
+   \return true if the addon is disabled, false otherwise
+   \sa EnableSystemPVRAddon */
+  bool IsSystemPVRAddonEnabled(const CStdString &addonID);
+
   /*! \brief Mark an addon as broken
-   Sets a flag that this addon has been marked as broken in the repository. 
+   Sets a flag that this addon has been marked as broken in the repository.
    \param addonID id of the addon to mark as broken
    \param broken whether to mark or not.  Defaults to true
-   \param reason why it is broken.  Defaults to blank 
+   \param reason why it is broken.  Defaults to blank
    \return true on success, false on failure
    \sa IsAddonBroken */
   bool BreakAddon(const CStdString &addonID, bool broken = true, const CStdString& reason="");
@@ -98,7 +114,7 @@ public:
 protected:
   virtual bool CreateTables();
   virtual bool UpdateOldVersion(int version);
-  virtual int GetMinVersion() const { return 12; }
+  virtual int GetMinVersion() const { return 13; }
   const char *GetDefaultDBName() const { return "Addons"; }
 };
 
index 3a84f1b..28fce47 100644 (file)
@@ -269,6 +269,9 @@ void CAdvancedSettings::Initialize()
 
   m_bgInfoLoaderMaxThreads = 5;
 
+  m_bDisableEPGTimeCorrection = false;
+  m_iUserDefinedEPGTimeCorrection = 0;
+
   m_measureRefreshrate = false;
 
   m_cacheMemBufferSize = (1048576 * 5);
@@ -831,6 +834,9 @@ bool CAdvancedSettings::Load()
   XMLUtils::GetInt(pRootElement, "bginfoloadermaxthreads", m_bgInfoLoaderMaxThreads);
   m_bgInfoLoaderMaxThreads = std::max(1, m_bgInfoLoaderMaxThreads);
 
+  XMLUtils::GetBoolean(pRootElement, "noepgtimecorrection", m_bDisableEPGTimeCorrection);
+  XMLUtils::GetInt(pRootElement, "userepgtimecorrection", m_iUserDefinedEPGTimeCorrection, 0, 1440);
+
   XMLUtils::GetBoolean(pRootElement, "measurerefreshrate", m_measureRefreshrate);
 
   TiXmlElement* pDatabase = pRootElement->FirstChildElement("videodatabase");
@@ -856,6 +862,17 @@ bool CAdvancedSettings::Load()
     XMLUtils::GetString(pDatabase, "name", m_databaseMusic.name);
   }
 
+  pDatabase = pRootElement->FirstChildElement("tvdatabase");
+  if (pDatabase)
+  {
+    XMLUtils::GetString(pDatabase, "type", m_databaseTV.type);
+    XMLUtils::GetString(pDatabase, "host", m_databaseTV.host);
+    XMLUtils::GetString(pDatabase, "port", m_databaseTV.port);
+    XMLUtils::GetString(pDatabase, "user", m_databaseTV.user);
+    XMLUtils::GetString(pDatabase, "pass", m_databaseTV.pass);
+    XMLUtils::GetString(pDatabase, "name", m_databaseTV.name);
+  }
+
   // load in the GUISettings overrides:
   g_guiSettings.LoadXML(pRootElement, true);  // true to hide the settings we read in
 
index 77cea9b..2023d16 100644 (file)
@@ -275,11 +275,16 @@ class CAdvancedSettings
     CStdString m_gpuTempCmd;
     int m_bgInfoLoaderMaxThreads;
 
+    /* PVR/TV related advanced settings */
+    bool m_bDisableEPGTimeCorrection;
+    int m_iUserDefinedEPGTimeCorrection;
+
     bool m_measureRefreshrate; //when true the videoreferenceclock will measure the refreshrate when direct3d is used
                                //otherwise it will use the windows refreshrate
 
     DatabaseSettings m_databaseMusic; // advanced music database setup
     DatabaseSettings m_databaseVideo; // advanced video database setup
+    DatabaseSettings m_databaseTV;    // advanced tv database setup
 
     unsigned int m_cacheMemBufferSize;
 };
index a8a58a0..e89340f 100644 (file)
 #include "GUIDialogAccessPoints.h"
 #endif
 #include "GUIDialogFullScreenInfo.h"
-#include "GUIDialogTeletext.h"
 #include "GUIDialogSlider.h"
+
+/* PVR related include Files */
+#include "pvr/PVRManager.h"
+#include "GUIWindowTV.h"
+#include "GUIDialogPVRChannelManager.h"
+#include "GUIDialogPVRChannelsOSD.h"
+#include "GUIDialogPVRCutterOSD.h"
+#include "GUIDialogPVRDirectorOSD.h"
+#include "GUIDialogPVRGroupManager.h"
+#include "GUIDialogPVRGuideInfo.h"
+#include "GUIDialogPVRGuideOSD.h"
+#include "GUIDialogPVRGuideSearch.h"
+#include "GUIDialogPVRRecordingInfo.h"
+#include "GUIDialogPVRTimerSettings.h"
+#include "GUIDialogPVRUpdateProgressBar.h"
+#include "GUIDialogTeletext.h"
+
 #include "GUIControlFactory.h"
 #include "cores/dlgcache.h"
 
@@ -1027,7 +1043,6 @@ bool CApplication::Initialize()
 #ifdef HAS_DX
   g_windowManager.Add(new CGUIWindowTestPatternDX);      // window id = 8
 #endif
-  g_windowManager.Add(new CGUIDialogTeletext);               // window id =
   g_windowManager.Add(new CGUIWindowSettingsScreenCalibration); // window id = 11
   g_windowManager.Add(new CGUIWindowSettingsCategory);         // window id = 12 slideshow:window id 2007
   g_windowManager.Add(new CGUIWindowVideoNav);                 // window id = 36
@@ -1074,7 +1089,6 @@ bool CApplication::Initialize()
 #ifdef HAS_LINUX_NETWORK
   g_windowManager.Add(new CGUIDialogAccessPoints);      // window id = 141
 #endif
-
   g_windowManager.Add(new CGUIDialogLockSettings); // window id = 131
 
   g_windowManager.Add(new CGUIDialogContentSettings);        // window id = 132
@@ -1084,6 +1098,21 @@ bool CApplication::Initialize()
   g_windowManager.Add(new CGUIWindowMusicNav);               // window id = 502
   g_windowManager.Add(new CGUIWindowMusicPlaylistEditor);    // window id = 503
 
+  /* Load PVR related Windows and Dialogs */
+  g_windowManager.Add(new CGUIWindowTV);                       // window id = 600
+  g_windowManager.Add(new CGUIDialogPVRGuideInfo);             // window id = 601
+  g_windowManager.Add(new CGUIDialogPVRRecordingInfo);         // window id = 602
+  g_windowManager.Add(new CGUIDialogPVRTimerSettings);         // window id = 603
+  g_windowManager.Add(new CGUIDialogPVRGroupManager);          // window id = 604
+  g_windowManager.Add(new CGUIDialogPVRChannelManager);        // window id = 605
+  g_windowManager.Add(new CGUIDialogPVRGuideSearch);           // window id = 606
+  g_windowManager.Add(new CGUIDialogPVRUpdateProgressBar);     // window id = 608
+  g_windowManager.Add(new CGUIDialogPVRChannelsOSD);           // window id = 609
+  g_windowManager.Add(new CGUIDialogPVRGuideOSD);              // window id = 610
+  g_windowManager.Add(new CGUIDialogPVRDirectorOSD);           // window id = 611
+  g_windowManager.Add(new CGUIDialogPVRCutterOSD);             // window id = 612
+  g_windowManager.Add(new CGUIDialogTeletext);                 // window id = 613
+
   g_windowManager.Add(new CGUIDialogSelect);             // window id = 2000
   g_windowManager.Add(new CGUIWindowMusicInfo);                // window id = 2001
   g_windowManager.Add(new CGUIDialogOK);                 // window id = 2002
@@ -1113,6 +1142,8 @@ bool CApplication::Initialize()
       FatalErrorHandler(true, true, true);
   }
 
+  StartPVRManager();
+
   SAFE_DELETE(m_splash);
 
   if (g_guiSettings.GetBool("masterlock.startuplock") &&
@@ -1426,6 +1457,21 @@ void CApplication::StopZeroconf()
 #endif
 }
 
+void CApplication::StartPVRManager()
+{
+  if (g_guiSettings.GetBool("pvrmanager.enabled"))
+  {
+    CLog::Log(LOGINFO, "starting PVRManager");
+    g_PVRManager.Start();
+  }
+}
+
+void CApplication::StopPVRManager()
+{
+  CLog::Log(LOGINFO, "stopping PVRManager");
+  g_PVRManager.Stop();
+}
+
 void CApplication::DimLCDOnPlayback(bool dim)
 {
 #ifdef HAS_LCD
@@ -2485,81 +2531,84 @@ bool CApplication::OnAction(const CAction &action)
 
   if ( IsPlaying())
   {
-    // pause : pauses current audio song
-    if (action.GetID() == ACTION_PAUSE && m_iPlaySpeed == 1)
+    if (!CurrentFileItem().IsLiveTV())
     {
-      m_pPlayer->Pause();
+      // pause : pauses current audio song
+      if (action.GetID() == ACTION_PAUSE && m_iPlaySpeed == 1)
+      {
+        m_pPlayer->Pause();
 #ifdef HAS_KARAOKE
-      m_pKaraokeMgr->SetPaused( m_pPlayer->IsPaused() );
+        m_pKaraokeMgr->SetPaused( m_pPlayer->IsPaused() );
 #endif
-      if (!m_pPlayer->IsPaused())
-      { // unpaused - set the playspeed back to normal
-        SetPlaySpeed(1);
-      }
-      g_audioManager.Enable(m_pPlayer->IsPaused() && !g_audioContext.IsPassthroughActive());
-      return true;
-    }
-    if (!m_pPlayer->IsPaused())
-    {
-      // if we do a FF/RW in my music then map PLAY action togo back to normal speed
-      // if we are playing at normal speed, then allow play to pause
-      if (action.GetID() == ACTION_PLAYER_PLAY || action.GetID() == ACTION_PAUSE)
-      {
-        if (m_iPlaySpeed != 1)
-        {
+        if (!m_pPlayer->IsPaused())
+        { // unpaused - set the playspeed back to normal
           SetPlaySpeed(1);
         }
-        else
-        {
-          m_pPlayer->Pause();
-        }
+        g_audioManager.Enable(m_pPlayer->IsPaused() && !g_audioContext.IsPassthroughActive());
         return true;
       }
-      if (action.GetID() == ACTION_PLAYER_FORWARD || action.GetID() == ACTION_PLAYER_REWIND)
+      if (!m_pPlayer->IsPaused())
       {
-        int iPlaySpeed = m_iPlaySpeed;
-        if (action.GetID() == ACTION_PLAYER_REWIND && iPlaySpeed == 1) // Enables Rewinding
-          iPlaySpeed *= -2;
-        else if (action.GetID() == ACTION_PLAYER_REWIND && iPlaySpeed > 1) //goes down a notch if you're FFing
-          iPlaySpeed /= 2;
-        else if (action.GetID() == ACTION_PLAYER_FORWARD && iPlaySpeed < 1) //goes up a notch if you're RWing
-          iPlaySpeed /= 2;
-        else
-          iPlaySpeed *= 2;
+        // if we do a FF/RW in my music then map PLAY action togo back to normal speed
+        // if we are playing at normal speed, then allow play to pause
+        if (action.GetID() == ACTION_PLAYER_PLAY || action.GetID() == ACTION_PAUSE)
+        {
+          if (m_iPlaySpeed != 1)
+          {
+            SetPlaySpeed(1);
+          }
+          else
+          {
+            m_pPlayer->Pause();
+          }
+          return true;
+        }
+        if (action.GetID() == ACTION_PLAYER_FORWARD || action.GetID() == ACTION_PLAYER_REWIND)
+        {
+          int iPlaySpeed = m_iPlaySpeed;
+          if (action.GetID() == ACTION_PLAYER_REWIND && iPlaySpeed == 1) // Enables Rewinding
+            iPlaySpeed *= -2;
+          else if (action.GetID() == ACTION_PLAYER_REWIND && iPlaySpeed > 1) //goes down a notch if you're FFing
+            iPlaySpeed /= 2;
+          else if (action.GetID() == ACTION_PLAYER_FORWARD && iPlaySpeed < 1) //goes up a notch if you're RWing
+            iPlaySpeed /= 2;
+          else
+            iPlaySpeed *= 2;
 
-        if (action.GetID() == ACTION_PLAYER_FORWARD && iPlaySpeed == -1) //sets iSpeed back to 1 if -1 (didn't plan for a -1)
-          iPlaySpeed = 1;
-        if (iPlaySpeed > 32 || iPlaySpeed < -32)
-          iPlaySpeed = 1;
+          if (action.GetID() == ACTION_PLAYER_FORWARD && iPlaySpeed == -1) //sets iSpeed back to 1 if -1 (didn't plan for a -1)
+            iPlaySpeed = 1;
+          if (iPlaySpeed > 32 || iPlaySpeed < -32)
+            iPlaySpeed = 1;
 
-        SetPlaySpeed(iPlaySpeed);
-        return true;
-      }
-      else if ((action.GetAmount() || GetPlaySpeed() != 1) && (action.GetID() == ACTION_ANALOG_REWIND || action.GetID() == ACTION_ANALOG_FORWARD))
-      {
-        // calculate the speed based on the amount the button is held down
-        int iPower = (int)(action.GetAmount() * MAX_FFWD_SPEED + 0.5f);
-        // returns 0 -> MAX_FFWD_SPEED
-        int iSpeed = 1 << iPower;
-        if (iSpeed != 1 && action.GetID() == ACTION_ANALOG_REWIND)
-          iSpeed = -iSpeed;
-        g_application.SetPlaySpeed(iSpeed);
-        if (iSpeed == 1)
-          CLog::Log(LOGDEBUG,"Resetting playspeed");
-        return true;
+          SetPlaySpeed(iPlaySpeed);
+          return true;
+        }
+        else if ((action.GetAmount() || GetPlaySpeed() != 1) && (action.GetID() == ACTION_ANALOG_REWIND || action.GetID() == ACTION_ANALOG_FORWARD))
+        {
+          // calculate the speed based on the amount the button is held down
+          int iPower = (int)(action.GetAmount() * MAX_FFWD_SPEED + 0.5f);
+          // returns 0 -> MAX_FFWD_SPEED
+          int iSpeed = 1 << iPower;
+          if (iSpeed != 1 && action.GetID() == ACTION_ANALOG_REWIND)
+            iSpeed = -iSpeed;
+          g_application.SetPlaySpeed(iSpeed);
+          if (iSpeed == 1)
+            CLog::Log(LOGDEBUG,"Resetting playspeed");
+          return true;
+        }
       }
-    }
-    // allow play to unpause
-    else
-    {
-      if (action.GetID() == ACTION_PLAYER_PLAY)
+      // allow play to unpause
+      else
       {
-        // unpause, and set the playspeed back to normal
-        m_pPlayer->Pause();
-        g_audioManager.Enable(m_pPlayer->IsPaused() && !g_audioContext.IsPassthroughActive());
+        if (action.GetID() == ACTION_PLAYER_PLAY)
+        {
+          // unpause, and set the playspeed back to normal
+          m_pPlayer->Pause();
+          g_audioManager.Enable(m_pPlayer->IsPaused() && !g_audioContext.IsPassthroughActive());
 
-        g_application.SetPlaySpeed(1);
-        return true;
+          g_application.SetPlaySpeed(1);
+          return true;
+        }
       }
     }
   }
@@ -3125,6 +3174,20 @@ bool CApplication::Cleanup()
     g_windowManager.Delete(WINDOW_DIALOG_ACCESS_POINTS);
     g_windowManager.Delete(WINDOW_DIALOG_SLIDER);
 
+    /* Delete PVR related windows and dialogs */
+    g_windowManager.Delete(WINDOW_TV);
+    g_windowManager.Delete(WINDOW_DIALOG_PVR_GUIDE_INFO);
+    g_windowManager.Delete(WINDOW_DIALOG_PVR_RECORDING_INFO);
+    g_windowManager.Delete(WINDOW_DIALOG_PVR_TIMER_SETTING);
+    g_windowManager.Delete(WINDOW_DIALOG_PVR_GROUP_MANAGER);
+    g_windowManager.Delete(WINDOW_DIALOG_PVR_CHANNEL_MANAGER);
+    g_windowManager.Delete(WINDOW_DIALOG_PVR_GUIDE_SEARCH);
+    g_windowManager.Delete(WINDOW_DIALOG_PVR_CHANNEL_SCAN);
+    g_windowManager.Delete(WINDOW_DIALOG_PVR_UPDATE_PROGRESS);
+    g_windowManager.Delete(WINDOW_DIALOG_PVR_OSD_CHANNELS);
+    g_windowManager.Delete(WINDOW_DIALOG_PVR_OSD_GUIDE);
+    g_windowManager.Delete(WINDOW_DIALOG_PVR_OSD_DIRECTOR);
+    g_windowManager.Delete(WINDOW_DIALOG_PVR_OSD_CUTTER);
     g_windowManager.Delete(WINDOW_DIALOG_OSD_TELETEXT);
     g_windowManager.Delete(WINDOW_DIALOG_TEXT_VIEWER);
 
@@ -3143,6 +3206,7 @@ bool CApplication::Cleanup()
     g_windowManager.Delete(WINDOW_MUSIC_OVERLAY);
     g_windowManager.Delete(WINDOW_VIDEO_OVERLAY);
     g_windowManager.Delete(WINDOW_SLIDESHOW);
+    g_windowManager.Delete(WINDOW_ADDON_BROWSER);
 
     g_windowManager.Delete(WINDOW_HOME);
     g_windowManager.Delete(WINDOW_PROGRAMS);
@@ -3157,6 +3221,7 @@ bool CApplication::Cleanup()
     g_windowManager.Remove(WINDOW_SETTINGS_MYVIDEOS);
     g_windowManager.Remove(WINDOW_SETTINGS_NETWORK);
     g_windowManager.Remove(WINDOW_SETTINGS_APPEARANCE);
+    g_windowManager.Remove(WINDOW_SETTINGS_MYTV);
     g_windowManager.Remove(WINDOW_DIALOG_KAI_TOAST);
 
     g_windowManager.Remove(WINDOW_DIALOG_SEEK_BAR);
@@ -3272,6 +3337,7 @@ void CApplication::Stop()
 
     m_applicationMessenger.Cleanup();
 
+    StopPVRManager();
     StopServices();
     //Sleep(5000);
 
@@ -4312,7 +4378,7 @@ void CApplication::ActivateScreenSaver(bool forceType /*= false */)
   if (!forceType)
   {
     // set to Dim in the case of a dialog on screen or playing video
-    if (g_windowManager.HasModalDialog() || (IsPlayingVideo() && g_guiSettings.GetBool("screensaver.usedimonpause")))
+    if (g_windowManager.HasModalDialog() || (IsPlayingVideo() && g_guiSettings.GetBool("screensaver.usedimonpause")) || g_PVRManager.ChannelScanRunning())
     {
       if (!CAddonMgr::Get().GetAddon("screensaver.xbmc.builtin.dim", m_screenSaver))
         m_screenSaver.reset(new CScreenSaver(""));
index 7e6519a..b470422 100644 (file)
@@ -115,6 +115,8 @@ public:
   void StopUPnPRenderer();
   void StartUPnPServer();
   void StopUPnPServer();
+  void StartPVRManager();
+  void StopPVRManager();
   void StartEventServer();
   bool StopEventServer(bool bWait, bool promptuser);
   void RefreshEventServer();
index a4e3cb6..ce3b301 100644 (file)
@@ -55,6 +55,7 @@
 #include "SingleLock.h"
 #include "lib/libPython/xbmcmodule/GUIPythonWindowDialog.h"
 #include "lib/libPython/xbmcmodule/GUIPythonWindowXMLDialog.h"
+#include "addons/AddonHelpers_GUI.h"
 
 #ifdef HAS_HTTPAPI
 #include "lib/libhttpapi/XBMChttp.h"
@@ -633,6 +634,15 @@ case TMSG_POWERDOWN:
       }
       break;
 
+    case TMSG_GUI_ADDON_DIALOG:
+      {
+        if (pMsg->lpVoid)
+        { // TODO: This is ugly - really these python dialogs should just be normal XBMC dialogs
+          ((ADDON::CGUIAddonWindowDialog *)pMsg->lpVoid)->Show_Internal(pMsg->dwParam2 > 0);
+        }
+      }
+      break;
+
     case TMSG_GUI_PYTHON_DIALOG:
       {
         if (pMsg->lpVoid)
index 5dd1058..d821478 100644 (file)
@@ -82,8 +82,9 @@ class CGUIDialog;
 #define TMSG_GUI_PYTHON_DIALOG        605
 #define TMSG_GUI_DIALOG_CLOSE         606
 #define TMSG_GUI_ACTION               607
-#define TMSG_GUI_INFOLABEL            608
-#define TMSG_GUI_INFOBOOL             609
+#define TMSG_GUI_ADDON_DIALOG         608
+#define TMSG_GUI_INFOLABEL            609
+#define TMSG_GUI_INFOBOOL             610
 
 #define TMSG_OPTICAL_MOUNT        700
 #define TMSG_OPTICAL_UNMOUNT      701
index 650bb11..95026a6 100644 (file)
@@ -203,6 +203,7 @@ static const ActionMapping windows[] =
         {"music"                    , WINDOW_MUSIC},
         {"video"                    , WINDOW_VIDEOS},
         {"videos"                   , WINDOW_VIDEOS}, // backward compat
+        {"tv"                       , WINDOW_TV},
         {"systeminfo"               , WINDOW_SYSTEM_INFORMATION},
         {"testpattern"              , WINDOW_TEST_PATTERN},
         {"screencalibration"        , WINDOW_SCREEN_CALIBRATION},
@@ -215,6 +216,7 @@ static const ActionMapping windows[] =
         {"videossettings"           , WINDOW_SETTINGS_MYVIDEOS},
         {"networksettings"          , WINDOW_SETTINGS_NETWORK},
         {"appearancesettings"       , WINDOW_SETTINGS_APPEARANCE},
+        {"tvsettings"               , WINDOW_SETTINGS_MYTV},
         {"scripts"                  , WINDOW_PROGRAMS}, // backward compat
         {"videofiles"               , WINDOW_VIDEO_FILES},
         {"videolibrary"             , WINDOW_VIDEO_NAV},
@@ -227,6 +229,10 @@ static const ActionMapping windows[] =
         {"virtualkeyboard"          , WINDOW_DIALOG_KEYBOARD},
         {"volumebar"                , WINDOW_DIALOG_VOLUME_BAR},
         {"submenu"                  , WINDOW_DIALOG_SUB_MENU},
+        {"pvrosdchannels"           , WINDOW_DIALOG_PVR_OSD_CHANNELS},
+        {"pvrosdguide"              , WINDOW_DIALOG_PVR_OSD_GUIDE},
+        {"pvrosddirector"           , WINDOW_DIALOG_PVR_OSD_DIRECTOR},
+        {"pvrosdcutter"             , WINDOW_DIALOG_PVR_OSD_CUTTER},
         {"favourites"               , WINDOW_DIALOG_FAVOURITES},
         {"contextmenu"              , WINDOW_DIALOG_CONTEXT_MENU},
         {"infodialog"               , WINDOW_DIALOG_KAI_TOAST},
@@ -1001,13 +1007,15 @@ uint32_t CButtonTranslator::TranslateRemoteString(const char *szButton)
   else if (strButton.Equals("pageminus")) buttonCode = XINPUT_IR_REMOTE_CHANNEL_MINUS;
   else if (strButton.Equals("mute")) buttonCode = XINPUT_IR_REMOTE_MUTE;
   else if (strButton.Equals("recordedtv")) buttonCode = XINPUT_IR_REMOTE_RECORDED_TV;
-  else if (strButton.Equals("guide")) buttonCode = XINPUT_IR_REMOTE_TITLE;   // same as title
+  else if (strButton.Equals("guide")) buttonCode = XINPUT_IR_REMOTE_GUIDE;
   else if (strButton.Equals("livetv")) buttonCode = XINPUT_IR_REMOTE_LIVE_TV;
   else if (strButton.Equals("star")) buttonCode = XINPUT_IR_REMOTE_STAR;
   else if (strButton.Equals("hash")) buttonCode = XINPUT_IR_REMOTE_HASH;
   else if (strButton.Equals("clear")) buttonCode = XINPUT_IR_REMOTE_CLEAR;
   else if (strButton.Equals("enter")) buttonCode = XINPUT_IR_REMOTE_ENTER;
   else if (strButton.Equals("xbox")) buttonCode = XINPUT_IR_REMOTE_DISPLAY; // same as display
+  else if (strButton.Equals("playlist")) buttonCode = XINPUT_IR_REMOTE_PLAYLIST;
+  else if (strButton.Equals("guide")) buttonCode = XINPUT_IR_REMOTE_GUIDE;
   else if (strButton.Equals("teletext")) buttonCode = XINPUT_IR_REMOTE_TELETEXT;
   else if (strButton.Equals("red")) buttonCode = XINPUT_IR_REMOTE_RED;
   else if (strButton.Equals("green")) buttonCode = XINPUT_IR_REMOTE_GREEN;
index eb0a1d3..f3f2cae 100644 (file)
@@ -38,6 +38,7 @@ CDatabase::CDatabase(void)
   m_bOpen = false;
   m_iRefCount = 0;
   m_sqlite = true;
+  m_bMultiWrite = false;
 }
 
 CDatabase::~CDatabase(void)
@@ -106,6 +107,146 @@ CStdString CDatabase::PrepareSQL(CStdString strStmt, ...) const
   return strResult;
 }
 
+CStdString CDatabase::GetSingleValue(const CStdString &strTable, const CStdString &strColumn, const CStdString &strWhereClause /* = CStdString() */, const CStdString &strOrderBy /* = CStdString() */)
+{
+  CStdString strReturn;
+
+  try
+  {
+    if (NULL == m_pDB.get()) return strReturn;
+    if (NULL == m_pDS.get()) return strReturn;
+
+    CStdString strQueryBase = "SELECT %s FROM %s";
+    if (!strWhereClause.IsEmpty())
+      strQueryBase.AppendFormat(" WHERE %s", strWhereClause.c_str());
+    if (!strOrderBy.IsEmpty())
+      strQueryBase.AppendFormat(" ORDER BY %s", strOrderBy.c_str());
+    strQueryBase.append(" LIMIT 1");
+
+    CStdString strQuery = PrepareSQL(strQueryBase,
+        strColumn.c_str(), strTable.c_str());
+
+    if (!m_pDS->query(strQuery.c_str())) return strReturn;
+
+    if (m_pDS->num_rows() > 0)
+    {
+      strReturn = m_pDS->fv(0).get_asString();
+    }
+
+    m_pDS->close();
+  }
+  catch(...)
+  {
+    CLog::Log(LOGERROR, "%s - failed to get value '%s' from table '%s'",
+        __FUNCTION__, strColumn.c_str(), strTable.c_str());
+  }
+
+  return strReturn;
+}
+
+bool CDatabase::DeleteValues(const CStdString &strTable, const CStdString &strWhereClause /* = CStdString() */)
+{
+  bool bReturn = true;
+
+  CStdString strQueryBase = "DELETE FROM %s";
+  if (!strWhereClause.IsEmpty())
+    strQueryBase.AppendFormat(" WHERE %s", strWhereClause.c_str());
+
+  CStdString strQuery = FormatSQL(strQueryBase, strTable.c_str());
+
+  bReturn = ExecuteQuery(strQuery);
+
+  return bReturn;
+}
+
+bool CDatabase::ExecuteQuery(const CStdString &strQuery)
+{
+  bool bReturn = false;
+
+  try
+  {
+    if (NULL == m_pDB.get()) return false;
+    if (NULL == m_pDS.get()) return false;
+    m_pDS->exec(strQuery.c_str());
+    bReturn = true;
+  }
+  catch (...)
+  {
+    CLog::Log(LOGERROR, "%s - failed to execute query '%s'",
+        __FUNCTION__, strQuery.c_str());
+  }
+
+  return bReturn;
+}
+
+int CDatabase::ResultQuery(const CStdString &strQuery)
+{
+  int iReturn = -1;
+
+  try
+  {
+    if (NULL == m_pDB.get()) return iReturn;
+    if (NULL == m_pDS.get()) return iReturn;
+
+    CStdString strPreparedQuery = PrepareSQL(strQuery.c_str());
+
+    if (m_pDS->query(strPreparedQuery.c_str()))
+      iReturn = m_pDS->num_rows();
+  }
+  catch (...)
+  {
+    CLog::Log(LOGERROR, "%s - failed to execute query '%s'",
+        __FUNCTION__, strQuery.c_str());
+  }
+
+  return iReturn;
+}
+
+bool CDatabase::QueueInsertQuery(const CStdString &strQuery)
+{
+  bool bReturn = false;
+
+  if (strQuery.IsEmpty())
+    return bReturn;
+
+  if (!m_bMultiWrite)
+  {
+    if (NULL == m_pDB.get()) return bReturn;
+    if (NULL == m_pDS2.get()) return bReturn;
+
+    m_bMultiWrite = true;
+    m_pDS2->insert();
+  }
+
+  m_pDS2->add_insert_sql(strQuery);
+  bReturn = true;
+
+  return bReturn;
+}
+
+bool CDatabase::CommitInsertQueries()
+{
+  bool bReturn = false;
+
+  if (m_bMultiWrite)
+  {
+    try
+    {
+      m_bMultiWrite = false;
+      m_pDS2->post();
+      m_pDS2->clear_insert_sql();
+      bReturn = true;
+    }
+    catch(...)
+    {
+      CLog::Log(LOGERROR, "%s - failed to execute queries",
+          __FUNCTION__);
+    }
+  }
+
+  return bReturn;
+}
+
 bool CDatabase::Open()
 {
   DatabaseSettings db_fallback;
index 53020e1..1cc29d5 100644 (file)
@@ -49,6 +49,30 @@ public:
   static CStdString FormatSQL(CStdString strStmt, ...);
   CStdString PrepareSQL(CStdString strStmt, ...) const;
 
+  /**
+   * Get a single value from a table
+   */
+  CStdString GetSingleValue(const CStdString &strTable, const CStdString &strColumn, const CStdString &strWhereClause = CStdString(), const CStdString &strOrderBy = CStdString());
+
+  /**
+   * Delete values from a table
+   */
+  bool DeleteValues(const CStdString &strTable, const CStdString &strWhereClause = CStdString());
+
+  /**
+   * Executes a query
+   */
+  bool ExecuteQuery(const CStdString &strQuery);
+  int ResultQuery(const CStdString &strQuery);
+
+  /**
+   * Open a new dataset
+   */
+  bool OpenDS();
+
+  bool QueueInsertQuery(const CStdString &strQuery);
+  bool CommitInsertQueries();
+
 protected:
   void Split(const CStdString& strFileNameAndPath, CStdString& strPath, CStdString& strFileName);
   uint32_t ComputeCRC(const CStdString &text);
@@ -70,5 +94,6 @@ protected:
 private:
   bool UpdateVersionNumber();
 
+  bool m_bMultiWrite;
   int m_iRefCount;
 };
index d73f696..6bf3928 100644 (file)
 #include "SortFileItem.h"
 #include "utils/TuxBoxUtil.h"
 #include "VideoInfoTag.h"
+#include "pvr/PVREpg.h"
+#include "pvr/PVREpgInfoTag.h"
+#include "pvr/PVRChannel.h"
+#include "pvr/PVRRecordings.h"
+#include "pvr/PVRTimerInfoTag.h"
 #include "utils/SingleLock.h"
+#include "utils/Observer.h"
 #include "MusicInfoTag.h"
 #include "PictureInfoTag.h"
 #include "Artist.h"
@@ -65,6 +71,10 @@ CFileItem::CFileItem(const CSong& song)
 {
   m_musicInfoTag = NULL;
   m_videoInfoTag = NULL;
+  m_epgInfoTag = NULL;
+  m_pvrChannelInfoTag = NULL;
+  m_pvrRecordingInfoTag = NULL;
+  m_pvrTimerInfoTag = NULL;
   m_pictureInfoTag = NULL;
   Reset();
   SetLabel(song.strTitle);
@@ -79,6 +89,10 @@ CFileItem::CFileItem(const CStdString &path, const CAlbum& album)
 {
   m_musicInfoTag = NULL;
   m_videoInfoTag = NULL;
+  m_epgInfoTag = NULL;
+  m_pvrChannelInfoTag = NULL;
+  m_pvrRecordingInfoTag = NULL;
+  m_pvrTimerInfoTag = NULL;
   m_pictureInfoTag = NULL;
   Reset();
   SetLabel(album.strAlbum);
@@ -99,6 +113,10 @@ CFileItem::CFileItem(const CVideoInfoTag& movie)
 {
   m_musicInfoTag = NULL;
   m_videoInfoTag = NULL;
+  m_epgInfoTag = NULL;
+  m_pvrChannelInfoTag = NULL;
+  m_pvrRecordingInfoTag = NULL;
+  m_pvrTimerInfoTag = NULL;
   m_pictureInfoTag = NULL;
   Reset();
   SetLabel(movie.m_strTitle);
@@ -118,10 +136,89 @@ CFileItem::CFileItem(const CVideoInfoTag& movie)
   SetCachedVideoThumb();
 }
 
+CFileItem::CFileItem(const CPVREpgInfoTag& programme)
+{
+  m_musicInfoTag = NULL;
+  m_videoInfoTag = NULL;
+  m_epgInfoTag = NULL;
+  m_pvrChannelInfoTag = NULL;
+  m_pvrRecordingInfoTag = NULL;
+  m_pvrTimerInfoTag = NULL;
+  m_pictureInfoTag = NULL;
+  Reset();
+  m_strPath = programme.Path();
+  m_bIsFolder = false;
+  *GetEPGInfoTag() = programme;
+  SetLabel(programme.Title());
+  SetThumbnailImage(programme.Icon());
+  m_strLabel2 = programme.Plot();
+  SetInvalid();
+}
+
+CFileItem::CFileItem(const CPVRChannel& channel)
+{
+  m_musicInfoTag = NULL;
+  m_videoInfoTag = NULL;
+  m_epgInfoTag = NULL;
+  m_pvrChannelInfoTag = NULL;
+  m_pvrRecordingInfoTag = NULL;
+  m_pvrTimerInfoTag = NULL;
+  m_pictureInfoTag = NULL;
+  Reset();
+  m_strPath = channel.Path();
+  m_bIsFolder = false;
+  *GetPVRChannelInfoTag() = channel;
+  SetLabel(channel.ChannelName());
+  m_strLabel2 = channel.GetEPGNow()->Title();
+  SetThumbnailImage(channel.IconPath());
+
+  SetInvalid();
+}
+
+CFileItem::CFileItem(const CPVRRecordingInfoTag& record)
+{
+  m_musicInfoTag = NULL;
+  m_videoInfoTag = NULL;
+  m_epgInfoTag   = NULL;
+  m_pvrChannelInfoTag = NULL;
+  m_pvrRecordingInfoTag = NULL;
+  m_pvrTimerInfoTag = NULL;
+  m_pictureInfoTag = NULL;
+  Reset();
+  m_strPath = record.Path();
+  m_bIsFolder = false;
+  *GetPVRRecordingInfoTag() = record;
+  SetLabel(record.m_strTitle);
+  m_strLabel2 = record.Plot();
+  SetInvalid();
+}
+
+CFileItem::CFileItem(const CPVRTimerInfoTag& timer)
+{
+  m_musicInfoTag = NULL;
+  m_videoInfoTag = NULL;
+  m_epgInfoTag = NULL;
+  m_pvrChannelInfoTag = NULL;
+  m_pvrRecordingInfoTag = NULL;
+  m_pvrTimerInfoTag = NULL;
+  m_pictureInfoTag = NULL;
+  Reset();
+  m_strPath = timer.Path();
+  m_bIsFolder = false;
+  *GetPVRTimerInfoTag() = timer;
+  SetLabel(timer.Title());
+  m_strLabel2 = timer.Summary();
+  SetInvalid();
+}
+
 CFileItem::CFileItem(const CArtist& artist)
 {
   m_musicInfoTag = NULL;
   m_videoInfoTag = NULL;
+  m_epgInfoTag = NULL;
+  m_pvrChannelInfoTag = NULL;
+  m_pvrRecordingInfoTag = NULL;
+  m_pvrTimerInfoTag = NULL;
   m_pictureInfoTag = NULL;
   Reset();
   SetLabel(artist.strArtist);
@@ -135,6 +232,10 @@ CFileItem::CFileItem(const CGenre& genre)
 {
   m_musicInfoTag = NULL;
   m_videoInfoTag = NULL;
+  m_epgInfoTag = NULL;
+  m_pvrChannelInfoTag = NULL;
+  m_pvrRecordingInfoTag = NULL;
+  m_pvrTimerInfoTag = NULL;
   m_pictureInfoTag = NULL;
   Reset();
   SetLabel(genre.strGenre);
@@ -148,6 +249,10 @@ CFileItem::CFileItem(const CFileItem& item): CGUIListItem()
 {
   m_musicInfoTag = NULL;
   m_videoInfoTag = NULL;
+  m_epgInfoTag = NULL;
+  m_pvrChannelInfoTag = NULL;
+  m_pvrRecordingInfoTag = NULL;
+  m_pvrTimerInfoTag = NULL;
   m_pictureInfoTag = NULL;
   *this = item;
 }
@@ -156,6 +261,10 @@ CFileItem::CFileItem(const CGUIListItem& item)
 {
   m_musicInfoTag = NULL;
   m_videoInfoTag = NULL;
+  m_epgInfoTag = NULL;
+  m_pvrChannelInfoTag = NULL;
+  m_pvrRecordingInfoTag = NULL;
+  m_pvrTimerInfoTag = NULL;
   m_pictureInfoTag = NULL;
   Reset();
   // not particularly pretty, but it gets around the issue of Reset() defaulting
@@ -167,6 +276,10 @@ CFileItem::CFileItem(void)
 {
   m_musicInfoTag = NULL;
   m_videoInfoTag = NULL;
+  m_epgInfoTag = NULL;
+  m_pvrChannelInfoTag = NULL;
+  m_pvrRecordingInfoTag = NULL;
+  m_pvrTimerInfoTag = NULL;
   m_pictureInfoTag = NULL;
   Reset();
 }
@@ -176,6 +289,10 @@ CFileItem::CFileItem(const CStdString& strLabel)
 {
   m_musicInfoTag = NULL;
   m_videoInfoTag = NULL;
+  m_epgInfoTag = NULL;
+  m_pvrChannelInfoTag = NULL;
+  m_pvrRecordingInfoTag = NULL;
+  m_pvrTimerInfoTag = NULL;
   m_pictureInfoTag = NULL;
   Reset();
   SetLabel(strLabel);
@@ -185,6 +302,10 @@ CFileItem::CFileItem(const CStdString& strPath, bool bIsFolder)
 {
   m_musicInfoTag = NULL;
   m_videoInfoTag = NULL;
+  m_epgInfoTag = NULL;
+  m_pvrChannelInfoTag = NULL;
+  m_pvrRecordingInfoTag = NULL;
+  m_pvrTimerInfoTag = NULL;
   m_pictureInfoTag = NULL;
   Reset();
   m_strPath = strPath;
@@ -198,6 +319,10 @@ CFileItem::CFileItem(const CMediaSource& share)
 {
   m_musicInfoTag = NULL;
   m_videoInfoTag = NULL;
+  m_epgInfoTag = NULL;
+  m_pvrChannelInfoTag = NULL;
+  m_pvrRecordingInfoTag = NULL;
+  m_pvrTimerInfoTag = NULL;
   m_pictureInfoTag = NULL;
   Reset();
   m_bIsFolder = true;
@@ -221,10 +346,18 @@ CFileItem::~CFileItem(void)
 {
   delete m_musicInfoTag;
   delete m_videoInfoTag;
+  delete m_epgInfoTag;
+  delete m_pvrChannelInfoTag;
+  delete m_pvrRecordingInfoTag;
+  delete m_pvrTimerInfoTag;
   delete m_pictureInfoTag;
 
   m_musicInfoTag = NULL;
   m_videoInfoTag = NULL;
+  m_epgInfoTag = NULL;
+  m_pvrChannelInfoTag = NULL;
+  m_pvrRecordingInfoTag = NULL;
+  m_pvrTimerInfoTag = NULL;
   m_pictureInfoTag = NULL;
 }
 
@@ -264,6 +397,62 @@ const CFileItem& CFileItem::operator=(const CFileItem& item)
     m_videoInfoTag = NULL;
   }
 
+  if (item.HasEPGInfoTag())
+  {
+    m_epgInfoTag = GetEPGInfoTag();
+    if (m_epgInfoTag)
+      *m_epgInfoTag = *item.m_epgInfoTag;
+  }
+  else
+  {
+    if (m_epgInfoTag)
+      delete m_epgInfoTag;
+
+    m_epgInfoTag = NULL;
+  }
+
+  if (item.HasPVRChannelInfoTag())
+  {
+    m_pvrChannelInfoTag = GetPVRChannelInfoTag();
+    if (m_pvrChannelInfoTag)
+      *m_pvrChannelInfoTag = *item.m_pvrChannelInfoTag;
+  }
+  else
+  {
+    if (m_pvrChannelInfoTag)
+      delete m_pvrChannelInfoTag;
+
+    m_pvrChannelInfoTag = NULL;
+  }
+
+  if (item.HasPVRRecordingInfoTag())
+  {
+    m_pvrRecordingInfoTag = GetPVRRecordingInfoTag();
+    if (m_pvrRecordingInfoTag)
+      *m_pvrRecordingInfoTag = *item.m_pvrRecordingInfoTag;
+  }
+  else
+  {
+    if (m_pvrRecordingInfoTag)
+      delete m_pvrRecordingInfoTag;
+
+    m_pvrRecordingInfoTag = NULL;
+  }
+
+  if (item.HasPVRTimerInfoTag())
+  {
+    m_pvrTimerInfoTag = GetPVRTimerInfoTag();
+    if (m_pvrTimerInfoTag)
+      *m_pvrTimerInfoTag = *item.m_pvrTimerInfoTag;
+  }
+  else
+  {
+    if (m_pvrTimerInfoTag)
+      delete m_pvrTimerInfoTag;
+
+    m_pvrTimerInfoTag = NULL;
+  }
+
   if (item.HasPictureInfoTag())
   {
     m_pictureInfoTag = GetPictureInfoTag();
@@ -325,6 +514,14 @@ void CFileItem::Reset()
   m_musicInfoTag=NULL;
   delete m_videoInfoTag;
   m_videoInfoTag=NULL;
+  delete m_epgInfoTag;
+  m_epgInfoTag=NULL;
+  delete m_pvrChannelInfoTag;
+  m_pvrChannelInfoTag=NULL;
+  delete m_pvrRecordingInfoTag;
+  m_pvrRecordingInfoTag=NULL;
+  delete m_pvrTimerInfoTag;
+  m_pvrTimerInfoTag=NULL;
   delete m_pictureInfoTag;
   m_pictureInfoTag=NULL;
   m_extrainfo.Empty();
@@ -509,6 +706,30 @@ bool CFileItem::IsVideo() const
   return (g_settings.m_videoExtensions.Find(extension) != -1);
 }
 
+bool CFileItem::IsEPG() const
+{
+  if (HasEPGInfoTag()) return true; /// is this enough?
+  return false;
+}
+
+bool CFileItem::IsPVRChannel() const
+{
+  if (HasPVRChannelInfoTag()) return true; /// is this enough?
+  return false;
+}
+
+bool CFileItem::IsPVRRecording() const
+{
+  if (HasPVRRecordingInfoTag()) return true; /// is this enough?
+  return false;
+}
+
+bool CFileItem::IsPVRTimer() const
+{
+  if (HasPVRTimerInfoTag()) return true; /// is this enough?
+  return false;
+}
+
 bool CFileItem::IsAudio() const
 {
   /* check preset mime type */
@@ -795,6 +1016,11 @@ bool CFileItem::IsVTP() const
   return CUtil::IsVTP(m_strPath);
 }
 
+bool CFileItem::IsPVR() const
+{
+  return CUtil::IsPVR(m_strPath);
+}
+
 bool CFileItem::IsLiveTV() const
 {
   return CUtil::IsLiveTV(m_strPath);
@@ -859,6 +1085,11 @@ void CFileItem::FillInDefaultIcon()
        * in mind the complexity of the code behind the check in the
        * case of IsWhatater() returns false.
        */
+      if ( IsLiveTV() )
+      {
+        // Live TV Channel
+        return;
+      }
       if ( IsAudio() )
       {
         // audio
@@ -1015,6 +1246,12 @@ void CFileItem::SetLabel(const CStdString &strLabel)
   CGUIListItem::SetLabel(strLabel);
 }
 
+void CFileItem::SetLabel2(const CStdString &strLabel)
+{
+  m_strLabel2 = strLabel;
+}
+
+
 void CFileItem::SetFileSizeLabel()
 {
   if( m_bIsFolder && m_dwSize == 0 )
@@ -1567,6 +1804,9 @@ void CFileItemList::Sort(SORT_METHOD sortMethod, SORT_ORDER sortOrder)
   case SORT_METHOD_LISTENERS:
     FillSortFields(SSortFileItem::ByListeners);
     break;    
+  case SORT_METHOD_CHANNEL:
+    FillSortFields(SSortFileItem::ByChannel);
+    break;
   default:
     break;
   }
@@ -2293,6 +2533,8 @@ bool CFileItemList::AlwaysCache() const
     return CMusicDatabaseDirectory::CanCache(m_strPath);
   if (IsVideoDb())
     return CVideoDatabaseDirectory::CanCache(m_strPath);
+//  if (IsEPG())
+//    return true; // always cache
   return false;
 }
 
@@ -2932,6 +3174,38 @@ CVideoInfoTag* CFileItem::GetVideoInfoTag()
   return m_videoInfoTag;
 }
 
+CPVREpgInfoTag* CFileItem::GetEPGInfoTag()
+{
+  if (!m_epgInfoTag)
+    m_epgInfoTag = new CPVREpgInfoTag;
+
+  return m_epgInfoTag;
+}
+
+CPVRChannel* CFileItem::GetPVRChannelInfoTag()
+{
+  if (!m_pvrChannelInfoTag)
+    m_pvrChannelInfoTag = new CPVRChannel;
+
+  return m_pvrChannelInfoTag;
+}
+
+CPVRRecordingInfoTag* CFileItem::GetPVRRecordingInfoTag()
+{
+  if (!m_pvrRecordingInfoTag)
+    m_pvrRecordingInfoTag = new CPVRRecordingInfoTag;
+
+  return m_pvrRecordingInfoTag;
+}
+
+CPVRTimerInfoTag* CFileItem::GetPVRTimerInfoTag()
+{
+  if (!m_pvrTimerInfoTag)
+    m_pvrTimerInfoTag = new CPVRTimerInfoTag;
+
+  return m_pvrTimerInfoTag;
+}
+
 CPictureInfoTag* CFileItem::GetPictureInfoTag()
 {
   if (!m_pictureInfoTag)
index 9fb7135..c1baec3 100644 (file)
@@ -43,6 +43,10 @@ namespace MUSIC_INFO
   class CMusicInfoTag;
 }
 class CVideoInfoTag;
+class CPVREpgInfoTag;
+class CPVRChannel;
+class CPVRRecordingInfoTag;
+class CPVRTimerInfoTag;
 class CPictureInfoTag;
 
 class CAlbum;
@@ -75,6 +79,10 @@ public:
   CFileItem(const CArtist& artist);
   CFileItem(const CGenre& genre);
   CFileItem(const CVideoInfoTag& movie);
+  CFileItem(const CPVREpgInfoTag& programme);
+  CFileItem(const CPVRChannel& channel);
+  CFileItem(const CPVRRecordingInfoTag& record);
+  CFileItem(const CPVRTimerInfoTag& timer);
   CFileItem(const CMediaSource& share);
   virtual ~CFileItem(void);
   virtual CGUIListItem *Clone() const { return new CFileItem(*this); };
@@ -125,6 +133,10 @@ public:
   bool IsMultiPath() const;
   bool IsMusicDb() const;
   bool IsVideoDb() const;
+  bool IsEPG() const;
+  bool IsPVRChannel() const;
+  bool IsPVRRecording() const;
+  bool IsPVRTimer() const;
   bool IsType(const char *ext) const;
   bool IsVirtualDirectoryRoot() const;
   bool IsReadOnly() const;
@@ -137,6 +149,7 @@ public:
   bool IsMythTV() const;
   bool IsHDHomeRun() const;
   bool IsVTP() const;
+  bool IsPVR() const;
   bool IsLiveTV() const;
   bool IsRSS() const;
 
@@ -146,6 +159,7 @@ public:
   void SetMusicThumb(bool alwaysCheckRemote = false);
   void SetFileSizeLabel();
   virtual void SetLabel(const CStdString &strLabel);
+  virtual void SetLabel2(const CStdString &strLabel);
   CURL GetAsUrl() const;
   VIDEODB_CONTENT_TYPE GetVideoContentType() const;
   bool IsLabelPreformated() const { return m_bLabelPreformated; }
@@ -178,6 +192,54 @@ public:
     return m_videoInfoTag;
   }
 
+  inline bool HasEPGInfoTag() const
+  {
+    return m_epgInfoTag != NULL;
+  }
+
+  CPVREpgInfoTag* GetEPGInfoTag();
+
+  inline const CPVREpgInfoTag* GetEPGInfoTag() const
+  {
+    return m_epgInfoTag;
+  }
+
+  inline bool HasPVRChannelInfoTag() const
+  {
+    return m_pvrChannelInfoTag != NULL;
+  }
+
+  CPVRChannel* GetPVRChannelInfoTag();
+
+  inline const CPVRChannel* GetPVRChannelInfoTag() const
+  {
+    return m_pvrChannelInfoTag;
+  }
+
+  inline bool HasPVRRecordingInfoTag() const
+  {
+    return m_pvrRecordingInfoTag != NULL;
+  }
+
+  CPVRRecordingInfoTag* GetPVRRecordingInfoTag();
+
+  inline const CPVRRecordingInfoTag* GetPVRRecordingInfoTag() const
+  {
+    return m_pvrRecordingInfoTag;
+  }
+
+  inline bool HasPVRTimerInfoTag() const
+  {
+    return m_pvrTimerInfoTag != NULL;
+  }
+
+  CPVRTimerInfoTag* GetPVRTimerInfoTag();
+
+  inline const CPVRTimerInfoTag* GetPVRTimerInfoTag() const
+  {
+    return m_pvrTimerInfoTag;
+  }
+
   inline bool HasPictureInfoTag() const
   {
     return m_pictureInfoTag != NULL;
@@ -288,6 +350,10 @@ private:
   CStdString m_extrainfo;
   MUSIC_INFO::CMusicInfoTag* m_musicInfoTag;
   CVideoInfoTag* m_videoInfoTag;
+  CPVREpgInfoTag* m_epgInfoTag;
+  CPVRChannel* m_pvrChannelInfoTag;
+  CPVRRecordingInfoTag* m_pvrRecordingInfoTag;
+  CPVRTimerInfoTag * m_pvrTimerInfoTag;
   CPictureInfoTag* m_pictureInfoTag;
   bool m_bIsAlbum;
 };
index c353e16..cbda466 100644 (file)
@@ -31,6 +31,7 @@
 #include "StringUtils.h"
 #include "TextureManager.h"
 #include "File.h"
+#include "SpecialProtocol.h"
 
 using namespace ADDON;
 
@@ -175,6 +176,7 @@ bool CAddonsDirectory::GetDirectory(const CStdString& strPath, CFileItemList &it
 
 void CAddonsDirectory::GenerateListing(CURL &path, VECADDONS& addons, CFileItemList &items, bool reposAsFolders)
 {
+  CStdString xbmcPath = _P("special://xbmc/addons");
   items.ClearItems();
   for (unsigned i=0; i < addons.size(); i++)
   {
@@ -187,6 +189,9 @@ void CAddonsDirectory::GenerateListing(CURL &path, VECADDONS& addons, CFileItemL
     AddonPtr addon2;
     if (CAddonMgr::Get().GetAddon(addon->ID(),addon2))
       pItem->SetProperty("Addon.Status",g_localizeStrings.Get(305));
+    else if (pItem->GetProperty("Addon.Path").Left(xbmcPath.size()).Equals(xbmcPath))
+      pItem->SetProperty("Addon.Status",g_localizeStrings.Get(24095));
+
     if (!addon->Props().broken.IsEmpty())
       pItem->SetProperty("Addon.Status",g_localizeStrings.Get(24098));
     if (addon2 && addon2->Version() < addon->Version())
index 1a39b24..940778a 100644 (file)
@@ -38,11 +38,17 @@ public:
   virtual cmyth_recorder_t conn_get_free_recorder   (cmyth_conn_t conn)=0;
   virtual cmyth_recorder_t conn_get_recorder_from_num(cmyth_conn_t conn, int num)=0;
 
+  virtual int              conn_get_freespace       (cmyth_conn_t control,long long *total, long long *used)=0;
+  virtual int              conn_hung                (cmyth_conn_t control)=0;
 
   virtual cmyth_event_t    event_get                (cmyth_conn_t conn, char * data, int len)=0;
   virtual int              event_select             (cmyth_conn_t conn, struct timeval *timeout)=0;
 
   virtual cmyth_proglist_t proglist_get_all_recorded(cmyth_conn_t control)=0;
+  virtual cmyth_proglist_t proglist_get_all_scheduled(cmyth_conn_t control)=0;
+  virtual cmyth_proglist_t proglist_get_all_pending  (cmyth_conn_t control)=0;
+  virtual cmyth_proglist_t proglist_get_conflicting  (cmyth_conn_t control)=0;
+
   virtual int              mysql_get_guide(cmyth_database_t db, cmyth_program_t **prog, time_t starttime, time_t endtime) = 0;
   virtual cmyth_proginfo_t proglist_get_item        (cmyth_proglist_t pl, int index)=0;
   virtual int              proglist_get_count       (cmyth_proglist_t pl)=0;
@@ -106,6 +112,7 @@ public:
   virtual cmyth_timestamp_t proginfo_rec_start      (cmyth_proginfo_t prog)=0;
   virtual cmyth_timestamp_t proginfo_rec_end        (cmyth_proginfo_t prog)=0;
   virtual cmyth_proginfo_rec_status_t proginfo_rec_status(cmyth_proginfo_t prog)=0;
+  virtual long              proginfo_card_id        (cmyth_proginfo_t prog)=0;
   virtual char*             proginfo_prodyear       (cmyth_proginfo_t prog)=0;
   virtual cmyth_proginfo_t  proginfo_get_from_basename   (cmyth_conn_t control, const char* basename)=0;
   virtual int               proginfo_delete_recording(cmyth_conn_t control, cmyth_proginfo_t prog)=0;
@@ -140,11 +147,17 @@ class DllLibCMyth : public DllDynamic, DllLibCMythInterface
 
   DEFINE_METHOD1(cmyth_recorder_t,    conn_get_free_recorder,   (cmyth_conn_t p1))
   DEFINE_METHOD2(cmyth_recorder_t,    conn_get_recorder_from_num,(cmyth_conn_t p1, int p2))
+  DEFINE_METHOD3(int,                 conn_get_freespace,       (cmyth_conn_t p1, long long *p2, long long *p3))
+  DEFINE_METHOD1(int,                 conn_hung,                (cmyth_conn_t p1))
 
   DEFINE_METHOD3(cmyth_event_t,       event_get,                (cmyth_conn_t p1, char * p2, int p3))
   DEFINE_METHOD2(int,                 event_select,             (cmyth_conn_t p1, struct timeval *p2))
 
   DEFINE_METHOD1(cmyth_proglist_t,    proglist_get_all_recorded, (cmyth_conn_t p1))
+  DEFINE_METHOD1(cmyth_proglist_t,    proglist_get_all_scheduled, (cmyth_conn_t p1))
+  DEFINE_METHOD1(cmyth_proglist_t,    proglist_get_all_pending, (cmyth_conn_t p1))
+  DEFINE_METHOD1(cmyth_proglist_t,    proglist_get_conflicting, (cmyth_conn_t p1))
+
   DEFINE_METHOD4(int,                 mysql_get_guide,          (cmyth_database_t p1, cmyth_program_t **p2, time_t p3, time_t p4))
   DEFINE_METHOD2(cmyth_proginfo_t,    proglist_get_item,        (cmyth_proglist_t p1, int p2))
   DEFINE_METHOD1(int,                 proglist_get_count,       (cmyth_proglist_t p1))
@@ -206,6 +219,7 @@ class DllLibCMyth : public DllDynamic, DllLibCMythInterface
   DEFINE_METHOD1(cmyth_timestamp_t,   proginfo_rec_start,       (cmyth_proginfo_t p1))
   DEFINE_METHOD1(cmyth_timestamp_t,   proginfo_rec_end,         (cmyth_proginfo_t p1))
   DEFINE_METHOD1(cmyth_proginfo_rec_status_t, proginfo_rec_status, (cmyth_proginfo_t p1))
+  DEFINE_METHOD1(long,                proginfo_card_id,         (cmyth_proginfo_t p1))
   DEFINE_METHOD1(char*,               proginfo_prodyear,        (cmyth_proginfo_t p1))
   DEFINE_METHOD2(cmyth_proginfo_t,    proginfo_get_from_basename,    (cmyth_conn_t p1, const char* p2))
   DEFINE_METHOD2(int,                 proginfo_delete_recording, (cmyth_conn_t p1, cmyth_proginfo_t p2))
@@ -235,10 +249,15 @@ class DllLibCMyth : public DllDynamic, DllLibCMythInterface
     RESOLVE_METHOD_RENAME(cmyth_conn_connect_path, conn_connect_path)
     RESOLVE_METHOD_RENAME(cmyth_conn_get_free_recorder, conn_get_free_recorder)
     RESOLVE_METHOD_RENAME(cmyth_conn_get_recorder_from_num, conn_get_recorder_from_num)
+    RESOLVE_METHOD_RENAME(cmyth_conn_get_freespace, conn_get_freespace)
+    RESOLVE_METHOD_RENAME(cmyth_conn_hung, conn_hung)
 
     RESOLVE_METHOD_RENAME(cmyth_event_get, event_get)
     RESOLVE_METHOD_RENAME(cmyth_event_select, event_select)
     RESOLVE_METHOD_RENAME(cmyth_proglist_get_all_recorded, proglist_get_all_recorded)
+    RESOLVE_METHOD_RENAME(cmyth_proglist_get_all_scheduled, proglist_get_all_scheduled)
+    RESOLVE_METHOD_RENAME(cmyth_proglist_get_all_pending, proglist_get_all_pending)
+    RESOLVE_METHOD_RENAME(cmyth_proglist_get_conflicting, proglist_get_conflicting)
     RESOLVE_METHOD_RENAME(cmyth_mysql_get_guide, mysql_get_guide)
     RESOLVE_METHOD_RENAME(cmyth_proglist_get_item, proglist_get_item)
     RESOLVE_METHOD_RENAME(cmyth_proglist_get_count, proglist_get_count)
@@ -300,6 +319,7 @@ class DllLibCMyth : public DllDynamic, DllLibCMythInterface
     RESOLVE_METHOD_RENAME(cmyth_proginfo_rec_start, proginfo_rec_start)
     RESOLVE_METHOD_RENAME(cmyth_proginfo_rec_end, proginfo_rec_end)
     RESOLVE_METHOD_RENAME(cmyth_proginfo_rec_status, proginfo_rec_status)
+    RESOLVE_METHOD_RENAME(cmyth_proginfo_card_id, proginfo_card_id)
     RESOLVE_METHOD_RENAME(cmyth_proginfo_prodyear, proginfo_prodyear)
     RESOLVE_METHOD_RENAME(cmyth_proginfo_get_from_basename, proginfo_get_from_basename)
     RESOLVE_METHOD_RENAME(cmyth_proginfo_delete_recording, proginfo_delete_recording)
index 0b111f2..2fb9678 100644 (file)
@@ -80,6 +80,9 @@
 #ifdef HAS_FILESYSTEM_HTSP
 #include "HTSPDirectory.h"
 #endif
+#ifdef HAS_PVRCLIENTS
+#include "PVRDirectory.h"
+#endif
 #include "../utils/Network.h"
 #include "ZipDirectory.h"
 #ifdef HAS_FILESYSTEM_RAR
@@ -187,6 +190,9 @@ IDirectory* CFactoryDirectory::Create(const CStdString& strPath)
 #ifdef HAS_FILESYSTEM_HTSP
     if (strProtocol == "htsp") return new CHTSPDirectory();
 #endif
+#ifdef HAS_PVRCLIENTS
+    if (strProtocol == "pvr") return new CPVRDirectory();
+#endif
 #ifdef HAS_ZEROCONF
     if (strProtocol == "zeroconf") return new CZeroconfDirectory();
 #endif
index 9256893..bc636dd 100644 (file)
@@ -58,6 +58,9 @@
 #ifdef HAS_FILESYSTEM_VTP
 #include "VTPFile.h"
 #endif
+#ifdef HAS_PVRCLIENTS
+#include "PVRFile.h"
+#endif
 #include "FileZip.h"
 #ifdef HAS_FILESYSTEM_RAR
 #include "FileRar.h"
@@ -160,6 +163,9 @@ IFile* CFileFactory::CreateLoader(const CURL& url)
 #ifdef HAS_FILESYSTEM_VTP
     else if (strProtocol == "vtp") return new CVTPFile();
 #endif
+#ifdef HAS_PVRCLIENTS
+    else if (strProtocol == "pvr") return new CPVRFile();
+#endif
   }
 
   CLog::Log(LOGWARNING, "%s - Unsupported protocol(%s) in %s", __FUNCTION__, strProtocol.c_str(), url.Get().c_str() );
index 36ac07d..3b2277b 100644 (file)
@@ -28,8 +28,8 @@ class ILiveTVInterface
 {
 public:
   virtual ~ILiveTVInterface() {}
-  virtual bool           NextChannel() = 0;
-  virtual bool           PrevChannel() = 0;
+  virtual bool           NextChannel(bool preview = false) = 0;
+  virtual bool           PrevChannel(bool preview = false) = 0;
   virtual bool           SelectChannel(unsigned int channel) = 0;
 
   virtual int            GetTotalTime() = 0;
index 643d406..e502b72 100644 (file)
@@ -69,6 +69,8 @@ SRCS=AddonsDirectory.cpp \
      PlaylistDirectory.cpp \
      PlaylistFileDirectory.cpp \
      PluginDirectory.cpp \
+     PVRFile.cpp \
+     PVRDirectory.cpp \
      RSSDirectory.cpp \
      RTVDirectory.cpp \
      SAPDirectory.cpp \
index 3b1a8f7..75658bf 100644 (file)
@@ -632,12 +632,12 @@ bool CMythFile::ChangeChannel(int direction, const CStdString &channel)
   return true;
 }
 
-bool CMythFile::NextChannel()
+bool CMythFile::NextChannel(bool preview)
 {
   return ChangeChannel(CHANNEL_DIRECTION_UP, "");
 }
 
-bool CMythFile::PrevChannel()
+bool CMythFile::PrevChannel(bool preview)
 {
   return ChangeChannel(CHANNEL_DIRECTION_DOWN, "");
 }
index b70b9dc..0d16284 100644 (file)
@@ -63,8 +63,8 @@ public:
 
   virtual ILiveTVInterface* GetLiveTV() {return (ILiveTVInterface*)this;}
 
-  virtual bool           NextChannel();
-  virtual bool           PrevChannel();
+  virtual bool           NextChannel(bool preview = false);
+  virtual bool           PrevChannel(bool preview = false);
   virtual bool           SelectChannel(unsigned int channel);
 
   virtual int            GetTotalTime();
diff --git a/xbmc/FileSystem/PVRDirectory.cpp b/xbmc/FileSystem/PVRDirectory.cpp
new file mode 100644 (file)
index 0000000..0cf263c
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "PVRDirectory.h"
+#include "FileItem.h"
+#include "Util.h"
+#include "URL.h"
+#include "utils/log.h"
+#include "LocalizeStrings.h"
+
+#include "pvr/PVRManager.h"
+#include "pvr/PVRChannels.h"
+#include "pvr/PVRRecordings.h"
+#include "pvr/PVRTimers.h"
+
+using namespace std;
+using namespace XFILE;
+
+CPVRDirectory::CPVRDirectory()
+{
+}
+
+CPVRDirectory::~CPVRDirectory()
+{
+}
+
+bool CPVRDirectory::GetDirectory(const CStdString& strPath, CFileItemList &items)
+{
+  CStdString base(strPath);
+  CUtil::RemoveSlashAtEnd(base);
+
+  CURL url(strPath);
+  CStdString fileName = url.GetFileName();
+  CUtil::RemoveSlashAtEnd(fileName);
+  CLog::Log(LOGDEBUG, "CPVRDirectory::GetDirectory(%s)", base.c_str());
+
+  if (fileName == "")
+  {
+    CFileItemPtr item;
+
+    item.reset(new CFileItem(base + "/channels/", true));
+    item->SetLabel(g_localizeStrings.Get(19019));
+    item->SetLabelPreformated(true);
+    items.Add(item);
+
+    item.reset(new CFileItem(base + "/recordings/", true));
+    item->SetLabel(g_localizeStrings.Get(19017));
+    item->SetLabelPreformated(true);
+    items.Add(item);
+
+    item.reset(new CFileItem(base + "/timers/", true));
+    item->SetLabel(g_localizeStrings.Get(19040));
+    item->SetLabelPreformated(true);
+    items.Add(item);
+
+    item.reset(new CFileItem(base + "/guide/", true));
+    item->SetLabel(g_localizeStrings.Get(19029));
+    item->SetLabelPreformated(true);
+    items.Add(item);
+
+    // Sort by name only. Labels are preformated.
+    items.AddSortMethod(SORT_METHOD_LABEL, 551 /* Name */, LABEL_MASKS("%L", "", "%L", ""));
+
+    return true;
+  }
+  else if (fileName.Left(10) == "recordings")
+  {
+    return PVRRecordings.GetDirectory(strPath, items) > 0;
+  }
+  else if (fileName.Left(8) == "channels")
+  {
+    return CPVRChannels::GetDirectory(strPath, items) > 0;
+  }
+  else if (fileName.Left(6) == "timers")
+  {
+    return PVRTimers.GetDirectory(strPath, items) > 0;
+  }
+
+  return false;
+}
+
+bool CPVRDirectory::SupportsFileOperations(const CStdString& strPath)
+{
+  CURL url(strPath);
+  CStdString filename = url.GetFileName();
+  CUtil::RemoveSlashAtEnd(filename);
+
+  if (filename.Left(11) == "recordings/" && filename.Right(4) == ".pvr")
+     return true;
+
+  return false;
+}
+
+bool CPVRDirectory::IsLiveTV(const CStdString& strPath)
+{
+  CURL url(strPath);
+  return url.GetFileName().Left(9) == "channelstv/";
+}
+
+bool CPVRDirectory::HasRecordings()
+{
+  return PVRRecordings.GetNumRecordings() > 0;
+}
diff --git a/xbmc/FileSystem/PVRDirectory.h b/xbmc/FileSystem/PVRDirectory.h
new file mode 100644 (file)
index 0000000..90ce917
--- /dev/null
@@ -0,0 +1,46 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "IDirectory.h"
+
+class CPVRSession;
+
+namespace XFILE {
+
+class CPVRDirectory
+  : public IDirectory
+{
+public:
+  CPVRDirectory();
+  virtual ~CPVRDirectory();
+
+  virtual bool GetDirectory(const CStdString& strPath, CFileItemList &items);
+  virtual bool IsAllowed(const CStdString &strFile) const { return true; };
+
+  static bool SupportsFileOperations(const CStdString& strPath);
+  static bool IsLiveTV(const CStdString& strPath);
+  static bool HasRecordings();
+
+private:
+};
+
+}
diff --git a/xbmc/FileSystem/PVRFile.cpp b/xbmc/FileSystem/PVRFile.cpp
new file mode 100644 (file)
index 0000000..c3053e3
--- /dev/null
@@ -0,0 +1,325 @@
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "PVRFile.h"
+#include "Util.h"
+#include "pvr/PVRManager.h"
+#include "utils/log.h"
+
+using namespace XFILE;
+using namespace std;
+
+CPVRFile::CPVRFile()
+{
+  m_isPlayRecording = false;
+  m_playingItem     = -1;
+}
+
+CPVRFile::~CPVRFile()
+{
+}
+
+bool CPVRFile::Open(const CURL& url)
+{
+  Close();
+
+  CStdString strURL = url.Get();
+
+  if (strURL.Left(18) == "pvr://channels/tv/")
+  {
+    CPVRChannel *tag = CPVRChannels::GetByPath(strURL);
+    if (tag)
+    {
+      if (!g_PVRManager.OpenLiveStream(tag))
+        return false;
+
+      m_isPlayRecording = false;
+      CLog::Log(LOGDEBUG, "%s - TV Channel has started on filename %s", __FUNCTION__, strURL.c_str());
+    }
+    else
+    {
+      CLog::Log(LOGERROR, "PVRFile - TV Channel not found with filename %s", strURL.c_str());
+      return false;
+    }
+  }
+  else if (strURL.Left(21) == "pvr://channels/radio/")
+  {
+    CPVRChannel *tag = CPVRChannels::GetByPath(strURL);
+    if (tag)
+    {
+      if (!g_PVRManager.OpenLiveStream(tag))
+        return false;
+
+      m_isPlayRecording = false;
+      CLog::Log(LOGDEBUG, "%s - Radio Channel has started on filename %s", __FUNCTION__, strURL.c_str());
+    }
+    else
+    {
+      CLog::Log(LOGERROR, "PVRFile - Radio Channel not found with filename %s", strURL.c_str());
+      return false;
+    }
+  }
+  else if (strURL.Left(17) == "pvr://recordings/")
+  {
+    CPVRRecordingInfoTag *tag = PVRRecordings.GetByPath(strURL);
+    if (tag)
+    {
+      if (!g_PVRManager.OpenRecordedStream(tag))
+        return false;
+
+      m_isPlayRecording = true;
+      CLog::Log(LOGDEBUG, "%s - Recording has started on filename %s", __FUNCTION__, strURL.c_str());
+    }
+    else
+    {
+      CLog::Log(LOGERROR, "PVRFile - Recording not found with filename %s", strURL.c_str());
+      return false;
+    }
+  }
+  else
+  {
+    CLog::Log(LOGERROR, "%s - invalid path specified %s", __FUNCTION__, strURL.c_str());
+    return false;
+  }
+
+  return true;
+}
+
+void CPVRFile::Close()
+{
+  g_PVRManager.CloseStream();
+}
+
+unsigned int CPVRFile::Read(void* buffer, int64_t size)
+{
+  return g_PVRManager.ReadStream((BYTE*)buffer, size);
+}
+
+int64_t CPVRFile::GetLength()
+{
+  return g_PVRManager.LengthStream();
+}
+
+int64_t CPVRFile::Seek(int64_t pos, int whence)
+{
+  if (whence == SEEK_POSSIBLE)
+  {
+    int64_t ret = g_PVRManager.SeekStream(pos, whence);
+
+    if (ret >= 0)
+    {
+      return ret;
+    }
+    else
+    {
+      if (g_PVRManager.LengthStream() && g_PVRManager.SeekStream(0, SEEK_CUR) >= 0)
+        return 1;
+      else
+        return 0;
+    }
+  }
+  else
+  {
+    return g_PVRManager.SeekStream(pos, whence);
+  }
+  return 0;
+}
+
+int64_t CPVRFile::GetPosition()
+{
+  return g_PVRManager.GetStreamPosition();
+}
+
+int CPVRFile::GetTotalTime()
+{
+  return g_PVRManager.GetTotalTime();
+}
+
+int CPVRFile::GetStartTime()
+{
+  return g_PVRManager.GetStartTime();
+}
+
+bool CPVRFile::NextChannel(bool preview/* = false*/)
+{
+  unsigned int newchannel;
+
+  if (m_isPlayRecording)
+  {
+    /* We are inside a recording, skip channelswitch */
+    return true;
+  }
+
+  /* Do channel switch and save new channel number, it is not always
+   * increased by one in a case if next channel is encrypted or we
+   * on the beginning or end of the channel list!
+   */
+  if (g_PVRManager.ChannelUp(&newchannel, preview))
+  {
+    m_playingItem = newchannel;
+    return true;
+  }
+  else
+  {
+    return false;
+  }
+}
+
+bool CPVRFile::PrevChannel(bool preview/* = false*/)
+{
+  unsigned int newchannel;
+
+  if (m_isPlayRecording)
+  {
+    /* We are inside a recording, skip channelswitch */
+    return true;
+  }
+
+  /* Do channel switch and save new channel number, it is not always
+   * increased by one in a case if next channel is encrypted or we
+   * on the beginning or end of the channel list!
+   */
+  if (g_PVRManager.ChannelDown(&newchannel, preview))
+  {
+    m_playingItem = newchannel;
+    return true;
+  }
+  else
+  {
+    return false;
+  }
+}
+
+bool CPVRFile::SelectChannel(unsigned int channel)
+{
+  if (m_isPlayRecording)
+  {
+    /* We are inside a recording, skip channelswitch */
+    /** TODO:
+     ** Add support for cutting keys (functions becomes the numeric keys as integer)
+     **/
+    return true;
+  }
+
+  if (g_PVRManager.ChannelSwitch(channel))
+  {
+    m_playingItem = channel;
+    return true;
+  }
+  else
+  {
+    return false;
+  }
+}
+
+bool CPVRFile::UpdateItem(CFileItem& item)
+{
+  if (m_isPlayRecording)
+  {
+    /* We are inside a recording, skip item update */
+    return true;
+  }
+
+  return g_PVRManager.UpdateItem(item);
+}
+
+CStdString CPVRFile::TranslatePVRFilename(const CStdString& pathFile)
+{
+  CStdString FileName = pathFile;
+
+  if (FileName.substr(0, 14) == "pvr://channels")
+  {
+    CPVRChannel *tag = CPVRChannels::GetByPath(FileName);
+    if (tag)
+    {
+      CStdString stream = tag->StreamURL();
+      if(!stream.IsEmpty())
+      {
+        if (stream.compare(6, 7, "stream/") == 0)
+        {
+          // pvr://stream
+          // This function was added to retrieve the stream URL for this item
+          // Is is used for the MediaPortal PVR addon
+          // see PVRManager.cpp
+          return g_PVRManager.GetLiveStreamURL(tag);
+        }
+        else
+        {
+          return stream;
+        }
+      }
+    }
+  }
+  return FileName;
+}
+
+bool CPVRFile::CanRecord()
+{
+  if (m_isPlayRecording)
+  {
+    return false;
+  }
+
+  return g_PVRManager.CanRecordInstantly();
+}
+
+bool CPVRFile::IsRecording()
+{
+  return g_PVRManager.IsRecordingOnPlayingChannel();
+}
+
+bool CPVRFile::Record(bool bOnOff)
+{
+  return g_PVRManager.StartRecordingOnPlayingChannel(bOnOff);
+}
+
+bool CPVRFile::Delete(const CURL& url)
+{
+  CStdString path(url.GetFileName());
+
+  if (path.Left(11) == "recordings/" && path[path.size()-1] != '/')
+  {
+    CStdString strURL = url.Get();
+    CPVRRecordingInfoTag *tag = PVRRecordings.GetByPath(strURL);
+    if (tag)
+      return tag->Delete();
+  }
+  return false;
+}
+
+bool CPVRFile::Rename(const CURL& url, const CURL& urlnew)
+{
+  CStdString path(url.GetFileName());
+  CStdString newname(urlnew.GetFileName());
+
+  size_t found = newname.find_last_of("/");
+  if (found != CStdString::npos)
+    newname = newname.substr(found+1);
+
+  if (path.Left(11) == "recordings/" && path[path.size()-1] != '/')
+  {
+    CStdString strURL = url.Get();
+    CPVRRecordingInfoTag *tag = PVRRecordings.GetByPath(strURL);
+    if (tag)
+      return tag->Rename(newname);
+  }
+  return false;
+}
diff --git a/xbmc/FileSystem/PVRFile.h b/xbmc/FileSystem/PVRFile.h
new file mode 100644 (file)
index 0000000..d81637e
--- /dev/null
@@ -0,0 +1,79 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "IFile.h"
+#include "ILiveTV.h"
+#include "VideoInfoTag.h"
+
+
+class CPVRSession;
+
+namespace XFILE {
+
+class CPVRFile
+  : public  IFile
+  ,         ILiveTVInterface
+  ,         IRecordable
+{
+public:
+  CPVRFile();
+  virtual ~CPVRFile();
+  virtual bool          Open(const CURL& url);
+  virtual int64_t       Seek(int64_t pos, int whence=SEEK_SET);
+  virtual int64_t       GetPosition();
+  virtual int64_t       GetLength();
+  virtual int           Stat(const CURL& url, struct __stat64* buffer) { return -1; }
+  virtual void          Close();
+  virtual unsigned int  Read(void* buffer, int64_t size);
+  virtual CStdString    GetContent()                                   { return ""; }
+  virtual bool          SkipNext()                                     { return true; }
+
+  virtual bool          Delete(const CURL& url);
+  virtual bool          Rename(const CURL& url, const CURL& urlnew);
+  virtual bool          Exists(const CURL& url)                        { return false; }
+
+  virtual ILiveTVInterface* GetLiveTV() {return (ILiveTVInterface*)this;}
+
+  virtual bool           NextChannel(bool preview = false);
+  virtual bool           PrevChannel(bool preview = false);
+  virtual bool           SelectChannel(unsigned int channel);
+
+  virtual int            GetTotalTime();
+  virtual int            GetStartTime();
+  virtual bool           UpdateItem(CFileItem& item);
+
+  virtual IRecordable* GetRecordable() {return (IRecordable*)this;}
+
+  virtual bool           CanRecord();
+  virtual bool           IsRecording();
+  virtual bool           Record(bool bOnOff);
+
+  static CStdString      TranslatePVRFilename(const CStdString& pathFile);
+
+protected:
+  bool            m_isPlayRecording;
+  int             m_playingItem;
+};
+
+}
+
+
index 6194de0..9bbd40b 100644 (file)
@@ -147,7 +147,7 @@ int64_t CVTPFile::Seek(int64_t pos, int whence)
   return -1;
 }
 
-bool CVTPFile::NextChannel()
+bool CVTPFile::NextChannel(bool preview/* = false*/)
 {
   if(m_session == NULL)
     return false;
@@ -173,7 +173,7 @@ bool CVTPFile::NextChannel()
   return false;
 }
 
-bool CVTPFile::PrevChannel()
+bool CVTPFile::PrevChannel(bool preview/* = false*/)
 {
   if(m_session == NULL)
     return false;
index 42401ac..eb2c553 100644 (file)
@@ -51,8 +51,8 @@ public:
 
   virtual ILiveTVInterface* GetLiveTV() {return (ILiveTVInterface*)this;}
 
-  virtual bool           NextChannel();
-  virtual bool           PrevChannel();
+  virtual bool           NextChannel(bool preview = false);
+  virtual bool           PrevChannel(bool preview = false);
   virtual bool           SelectChannel(unsigned int channel);
 
   virtual int            GetTotalTime()              { return 0; }
index 746d9fd..a9f8b84 100644 (file)
@@ -34,6 +34,7 @@
 #include "URL.h"
 #include "utils/JobManager.h"
 #include "utils/FileOperationJob.h"
+#include "Application.h"
 
 #define CONTROL_BTN_INSTALL          6
 #define CONTROL_BTN_ENABLE           7
@@ -126,7 +127,7 @@ void CGUIDialogAddonInfo::UpdateControls()
   bool isEnabled = isInstalled && m_item->GetProperty("Addon.Enabled").Equals("true");
   bool isUpdatable = isInstalled && m_item->GetProperty("Addon.UpdateAvail").Equals("true");
   // TODO: System addons should be able to be disabled
-  bool canDisable = isInstalled && !isSystem && !m_localAddon->IsInUse();
+  bool canDisable = isInstalled && (!isSystem || m_localAddon->Type() == ADDON_PVRDLL) && !m_localAddon->IsInUse();
   bool canInstall = !isInstalled && m_item->GetProperty("Addon.Broken").IsEmpty();
                      
   CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_INSTALL, canDisable || canInstall);
@@ -178,9 +179,17 @@ void CGUIDialogAddonInfo::OnEnable(bool enable)
   if (!m_localAddon.get())
     return;
 
+  CStdString xbmcPath = _P("special://xbmc/addons");
   CAddonDatabase database;
   database.Open();
-  database.DisableAddon(m_localAddon->ID(), !enable);
+  if (m_localAddon->Type() == ADDON_PVRDLL && m_localAddon->Path().Left(xbmcPath.size()).Equals(xbmcPath))
+    database.EnableSystemPVRAddon(m_localAddon->ID(), enable);
+  else
+    database.DisableAddon(m_localAddon->ID(), !enable);
+
+  if (m_localAddon->Type() == ADDON_PVRDLL && enable)
+    g_application.StartPVRManager();
+
   SetItem(m_item);
   UpdateControls();
   g_windowManager.SendMessage(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE);
index 1ed9992..381eaed 100644 (file)
@@ -99,8 +99,25 @@ enum CONTEXT_BUTTON { CONTEXT_BUTTON_CANCELLED = 0,
                       CONTEXT_BUTTON_SCRIPT_SETTINGS,
                       CONTEXT_BUTTON_LASTFM_UNLOVE_ITEM,
                       CONTEXT_BUTTON_LASTFM_UNBAN_ITEM,
+                      CONTEXT_BUTTON_HIDE,
+                      CONTEXT_BUTTON_SHOW_HIDDEN,
+                      CONTEXT_BUTTON_ADD,
+                      CONTEXT_BUTTON_ACTIVATE,
+                      CONTEXT_BUTTON_START_RECORD,
+                      CONTEXT_BUTTON_STOP_RECORD,
+                      CONTEXT_BUTTON_GROUP_MANAGER,
+                      CONTEXT_BUTTON_CHANNEL_MANAGER,
                       CONTEXT_BUTTON_SET_MOVIESET_THUMB,
+                      CONTEXT_BUTTON_BEGIN,
+                      CONTEXT_BUTTON_END,
+                      CONTEXT_BUTTON_FIND,
                       CONTEXT_BUTTON_DELETE_PLUGIN,
+                      CONTEXT_BUTTON_SORTASC,
+                      CONTEXT_BUTTON_SORTBY,
+                      CONTEXT_BUTTON_SORTBY_CHANNEL,
+                      CONTEXT_BUTTON_SORTBY_NAME,
+                      CONTEXT_BUTTON_SORTBY_DATE,
+                      CONTEXT_BUTTON_MENU_HOOKS,
                       CONTEXT_BUTTON_USER1,
                       CONTEXT_BUTTON_USER2,
                       CONTEXT_BUTTON_USER3,
@@ -141,7 +158,6 @@ public:
    */
   static int ShowAndGetChoice(const CContextButtons &choices);
 
-protected:
   void SetupButtons();
 
   /*! \brief Position the context menu in the middle of the focused control.
@@ -151,6 +167,7 @@ protected:
 
   float GetWidth();
   float GetHeight();
+protected:
   virtual void OnInitWindow();
   virtual void OnWindowLoaded();
   virtual void OnWindowUnload();
index e90c078..4e7ae30 100644 (file)
@@ -27,6 +27,7 @@
 #include "GUIWindowManager.h"
 #include "Util.h"
 #include "FileSystem/PluginDirectory.h"
+#include "FileSystem/PVRDirectory.h"
 #include "GUIDialogYesNo.h"
 #include "FileSystem/File.h"
 #include "FileItem.h"
@@ -301,6 +302,14 @@ void CGUIDialogMediaSource::OnPathBrowse(int item)
     share1.strPath = "zeroconf://";
     share1.strName = "Zeroconf Browser";
     extraShares.push_back(share1);
+
+    // add the recordings dir as needed
+    if (CPVRDirectory::HasRecordings())
+    {
+      share1.strPath = "pvr://recordings/";
+      share1.strName = g_localizeStrings.Get(19017); // TV Recordings
+      extraShares.push_back(share1);
+    }
   }
   else if (m_type == "pictures")
   {
index dd4c243..aa29b1b 100644 (file)
@@ -42,6 +42,7 @@ CGUIDialogNumeric::CGUIDialogNumeric(void)
 {
   m_bConfirmed = false;
   m_bCanceled = false;
+  m_autoCloseTime = 0;
 
   m_mode = INPUT_PASSWORD;
   m_block = 0;
@@ -55,6 +56,9 @@ CGUIDialogNumeric::~CGUIDialogNumeric(void)
 
 bool CGUIDialogNumeric::OnAction(const CAction &action)
 {
+  if (action.GetID() >= ACTION_MOVE_LEFT &&  action.GetID() <= ACTION_MOVE_DOWN)
+    m_autoClosing = false;
+
   if (action.GetID() == ACTION_CLOSE_DIALOG || action.GetID() == ACTION_PREVIOUS_MENU)
     OnCancel();
   else if (action.GetID() == ACTION_NEXT_ITEM)
@@ -98,6 +102,7 @@ bool CGUIDialogNumeric::OnMessage(CGUIMessage& message)
       m_bConfirmed = false;
       m_bCanceled = false;
       m_dirty = false;
+      m_autoCloseTime = 0;
       return CGUIDialog::OnMessage(message);
     }
     break;
@@ -278,6 +283,11 @@ void CGUIDialogNumeric::FrameMove()
 
 void CGUIDialogNumeric::OnNumber(unsigned int num)
 {
+  if (m_autoCloseTime)
+  {
+    SetAutoClose(m_autoCloseTime);
+  }
+
   if (m_mode == INPUT_NUMBER || m_mode == INPUT_PASSWORD)
   {
     m_number += num + '0';
@@ -561,16 +571,22 @@ bool CGUIDialogNumeric::ShowAndGetIPAddress(CStdString &IPAddress, const CStdStr
   return true;
 }
 
-bool CGUIDialogNumeric::ShowAndGetNumber(CStdString& strInput, const CStdString &strHeading)
+bool CGUIDialogNumeric::ShowAndGetNumber(CStdString& strInput, const CStdString &strHeading, unsigned int autoCloseTime)
 {
   // Prompt user for password input
   CGUIDialogNumeric *pDialog = (CGUIDialogNumeric *)g_windowManager.GetWindow(WINDOW_DIALOG_NUMERIC);
   pDialog->SetHeading( strHeading );
 
   pDialog->SetMode(INPUT_NUMBER, (void *)&strInput);
+  if (autoCloseTime)
+  {
+    pDialog->m_autoCloseTime = autoCloseTime;
+    pDialog->SetAutoClose(autoCloseTime);
+  }
+
   pDialog->DoModal();
 
-  if (!pDialog->IsConfirmed() || pDialog->IsCanceled())
+  if (!autoCloseTime && (!pDialog->IsConfirmed() || pDialog->IsCanceled()))
     return false;
   pDialog->GetOutput(&strInput);
   return true;
index ff96265..196b98b 100644 (file)
@@ -48,7 +48,7 @@ public:
   static bool ShowAndGetTime(SYSTEMTIME &time, const CStdString &heading);
   static bool ShowAndGetDate(SYSTEMTIME &date, const CStdString &heading);
   static bool ShowAndGetIPAddress(CStdString &IPAddress, const CStdString &heading);
-  static bool ShowAndGetNumber(CStdString& strInput, const CStdString &strHeading);
+  static bool ShowAndGetNumber(CStdString& strInput, const CStdString &strHeading, unsigned int autoCloseTime = 0);
   static bool ShowAndGetSeconds(CStdString& timeString, const CStdString &heading);
 
 protected:
@@ -69,5 +69,7 @@ protected:
   unsigned int m_block;             // for time, date, and IP methods.
   unsigned int m_lastblock;
   bool m_dirty;                     // true if the current block has been changed.
+  int m_autoCloseTime;
+  CStdString m_password;            // for password input
   CStdString m_number;              ///< for number or password input
 };
diff --git a/xbmc/GUIDialogPVRChannelManager.cpp b/xbmc/GUIDialogPVRChannelManager.cpp
new file mode 100644 (file)
index 0000000..1cb2f3e
--- /dev/null
@@ -0,0 +1,845 @@
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "GUIDialogPVRChannelManager.h"
+#include "Application.h"
+#include "GUISettings.h"
+#include "GUIWindowManager.h"
+#include "LocalizeStrings.h"
+#include "MediaManager.h"
+#include "Picture.h"
+#include "Settings.h"
+#include "utils/log.h"
+#include "GUISpinControlEx.h"
+#include "GUIEditControl.h"
+#include "GUIRadioButtonControl.h"
+#include "GUIImage.h"
+#include "GUIDialogYesNo.h"
+#include "GUIDialogFileBrowser.h"
+#include "GUIDialogPVRGroupManager.h"
+#include "GUIDialogProgress.h"
+#include "GUIDialogSelect.h"
+#include "GUIDialogOK.h"
+#include "GUIDialogKeyboard.h"
+
+#include "pvr/PVRChannelGroups.h"
+#include "pvr/PVRChannelGroup.h"
+#include "pvr/PVRChannelsContainer.h"
+#include "pvr/PVREpg.h"
+#include "pvr/PVRManager.h"
+#include "pvr/PVRDatabase.h"
+
+#define BUTTON_OK                 4
+#define BUTTON_APPLY              5
+#define BUTTON_CANCEL             6
+#define RADIOBUTTON_ACTIVE        7
+#define EDIT_NAME                 8
+#define BUTTON_CHANNEL_LOGO       9
+#define IMAGE_CHANNEL_LOGO        10
+#define SPIN_GROUP_SELECTION      11
+#define RADIOBUTTON_USEEPG        12
+#define SPIN_EPGSOURCE_SELECTION  13
+#define CONTROL_LIST_CHANNELS     20
+#define BUTTON_GROUP_MANAGER      30
+#define BUTTON_EDIT_CHANNEL       31
+#define BUTTON_DELETE_CHANNEL     32
+#define BUTTON_NEW_CHANNEL        33
+#define BUTTON_RADIO_TV           34
+
+using namespace std;
+
+CGUIDialogPVRChannelManager::CGUIDialogPVRChannelManager()
+    : CGUIDialog(WINDOW_DIALOG_PVR_CHANNEL_MANAGER, "DialogPVRChannelManager.xml")
+{
+  m_channelItems  = new CFileItemList;
+  m_bIsRadio      = false;
+}
+
+CGUIDialogPVRChannelManager::~CGUIDialogPVRChannelManager()
+{
+  delete m_channelItems;
+}
+
+bool CGUIDialogPVRChannelManager::OnAction(const CAction& action)
+{
+  int actionID = action.GetID();
+  if (actionID == ACTION_PREVIOUS_MENU || actionID == ACTION_CLOSE_DIALOG)
+  {
+    Close();
+    return true;
+  }
+  else if (actionID == ACTION_MOVE_DOWN || actionID == ACTION_MOVE_UP ||
+           actionID == ACTION_PAGE_DOWN || actionID == ACTION_PAGE_UP)
+  {
+    if (GetFocusedControlID() == CONTROL_LIST_CHANNELS)
+    {
+      if (!m_bMovingMode)
+      {
+        CGUIDialog::OnAction(action);
+        int iSelected = m_viewControl.GetSelectedItem();
+        if (iSelected != m_iSelected)
+        {
+          m_iSelected = iSelected;
+          SetData(m_iSelected);
+        }
+        return true;
+      }
+      else
+      {
+        CStdString number;
+        CGUIDialog::OnAction(action);
+        if (actionID == ACTION_MOVE_UP)
+        {
+          unsigned int newSelect = m_iSelected == 0 ? m_channelItems->Size()-1 : m_iSelected == 0 ? m_iSelected : m_iSelected-1;
+          if (m_channelItems->Get(newSelect)->GetProperty("Number") != "-")
+          {
+            number.Format("%i", m_iSelected+1);
+            m_channelItems->Get(newSelect)->SetProperty("Number", number);
+            number.Format("%i", newSelect+1);
+            m_channelItems->Get(m_iSelected)->SetProperty("Number", number);
+          }
+          m_channelItems->Swap(newSelect, m_iSelected);
+          m_iSelected = newSelect;
+        }
+        else if (actionID == ACTION_MOVE_DOWN)
+        {
+          int newSelect = m_iSelected >= m_channelItems->Size()-1 ? 0 : m_iSelected+1;
+          if (m_channelItems->Get(newSelect)->GetProperty("Number") != "-")
+          {
+            number.Format("%i", m_iSelected+1);
+            m_channelItems->Get(newSelect)->SetProperty("Number", number);
+            number.Format("%i", newSelect+1);
+            m_channelItems->Get(m_iSelected)->SetProperty("Number", number);
+          }
+          m_channelItems->Swap(newSelect, m_iSelected);
+          m_iSelected = newSelect;
+        }
+        else if (actionID == ACTION_PAGE_UP)
+        {
+          unsigned int lines = m_iSelected-m_viewControl.GetSelectedItem();
+          for (unsigned int i = 0; i < lines; i++)
+          {
+            unsigned int newSelect = m_iSelected == 0 ? m_channelItems->Size()-1 : m_iSelected == 0 ? m_iSelected : m_iSelected-1;
+            if (m_channelItems->Get(newSelect)->GetProperty("Number") != "-")
+            {
+              number.Format("%i", m_iSelected+1);
+              m_channelItems->Get(newSelect)->SetProperty("Number", number);
+              number.Format("%i", newSelect+1);
+              m_channelItems->Get(m_iSelected)->SetProperty("Number", number);
+            }
+            m_channelItems->Swap(newSelect, m_iSelected);
+            m_iSelected = newSelect;
+          }
+        }
+        else if (actionID == ACTION_PAGE_DOWN)
+        {
+          unsigned int lines = m_viewControl.GetSelectedItem()-m_iSelected;
+          for (unsigned int i = 0; i < lines; i++)
+          {
+            int newSelect = m_iSelected >= m_channelItems->Size()-1 ? 0 : m_iSelected+1;
+            if (m_channelItems->Get(newSelect)->GetProperty("Number") != "-")
+            {
+              number.Format("%i", m_iSelected+1);
+              m_channelItems->Get(newSelect)->SetProperty("Number", number);
+              number.Format("%i", newSelect+1);
+              m_channelItems->Get(m_iSelected)->SetProperty("Number", number);
+            }
+            m_channelItems->Swap(newSelect, m_iSelected);
+            m_iSelected = newSelect;
+          }
+        }
+        m_viewControl.SetItems(*m_channelItems);
+        m_viewControl.SetSelectedItem(m_iSelected);
+        return true;
+      }
+    }
+  }
+
+  return CGUIDialog::OnAction(action);
+}
+
+bool CGUIDialogPVRChannelManager::OnMessage(CGUIMessage& message)
+{
+  unsigned int iControl = 0;
+  unsigned int iMessage = message.GetMessage();
+
+  switch (iMessage)
+  {
+    case GUI_MSG_WINDOW_DEINIT:
+    {
+      Clear();
+    }
+    break;
+
+    case GUI_MSG_WINDOW_INIT:
+    {
+      CGUIWindow::OnMessage(message);
+      m_iSelected = 0;
+      m_bIsRadio = false;
+      m_bMovingMode = false;
+      m_bContainsChanges = false;
+      SetProperty("IsRadio", "");
+      Update();
+      SetData(m_iSelected);
+      return true;
+    }
+    break;
+
+    case GUI_MSG_CLICKED:
+    {
+      iControl = message.GetSenderId();
+      if (iControl == CONTROL_LIST_CHANNELS)
+      {
+        if (!m_bMovingMode)
+        {
+          int iAction = message.GetParam1();
+          int iItem = m_viewControl.GetSelectedItem();
+
+          /* Check file item is in list range and get his pointer */
+          if (iItem < 0 || iItem >= (int)m_channelItems->Size()) return true;
+
+          CFileItemPtr pItem = m_channelItems->Get(iItem);
+
+          /* Process actions */
+          if (iAction == ACTION_SELECT_ITEM || iAction == ACTION_CONTEXT_MENU || iAction == ACTION_MOUSE_RIGHT_CLICK)
+          {
+            /* Show Contextmenu */
+            OnPopupMenu(iItem);
+          }
+        }
+        else
+        {
+          CFileItemPtr pItem = m_channelItems->Get(m_iSelected);
+          pItem->SetProperty("Changed", true);
+          pItem->Select(false);
+          m_bMovingMode = false;
+          m_bContainsChanges = true;
+          return true;
+        }
+      }
+      else if (iControl == BUTTON_OK)
+      {
+        SaveList();
+        Close();
+        return true;
+      }
+      else if (iControl == BUTTON_APPLY)
+      {
+        SaveList();
+        return true;
+      }
+      else if (iControl == BUTTON_CANCEL)
+      {
+        Close();
+        return true;
+      }
+      else if (iControl == BUTTON_RADIO_TV)
+      {
+        if (m_bContainsChanges)
+        {
+          // prompt user for confirmation of channel record
+          CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
+          if (!pDialog)
+            return true;
+
+          pDialog->SetHeading(20052);
+          pDialog->SetLine(0, "");
+          pDialog->SetLine(1, 19212);
+          pDialog->SetLine(2, 20103);
+          pDialog->DoModal();
+
+          if (pDialog->IsConfirmed())
+            SaveList();
+        }
+
+        m_iSelected = 0;
+        m_bMovingMode = false;
+        m_bContainsChanges = false;
+        m_bIsRadio = !m_bIsRadio;
+        SetProperty("IsRadio", m_bIsRadio ? "true" : "");
+        Update();
+        SetData(m_iSelected);
+        return true;
+      }
+      else if (iControl == RADIOBUTTON_ACTIVE)
+      {
+        CGUIRadioButtonControl *pRadioButton = (CGUIRadioButtonControl *)GetControl(RADIOBUTTON_ACTIVE);
+        if (pRadioButton)
+        {
+          CFileItemPtr pItem = m_channelItems->Get(m_iSelected);
+          pItem->SetProperty("Changed", true);
+          pItem->SetProperty("ActiveChannel", pRadioButton->IsSelected());
+          m_bContainsChanges = true;
+          Renumber();
+        }
+      }
+      else if (iControl == EDIT_NAME)
+      {
+        CGUIEditControl *pEdit = (CGUIEditControl *)GetControl(EDIT_NAME);
+        if (pEdit)
+        {
+          CFileItemPtr pItem = m_channelItems->Get(m_iSelected);
+          pItem->SetProperty("Changed", true);
+          pItem->SetProperty("Name", pEdit->GetLabel2());
+          m_bContainsChanges = true;
+        }
+      }
+      else if (iControl == BUTTON_CHANNEL_LOGO)
+      {
+        CFileItemPtr pItem = m_channelItems->Get(m_iSelected);
+
+        if (g_settings.GetCurrentProfile().canWriteSources() && !g_passwordManager.IsProfileLockUnlocked())
+          return false;
+        else if (!g_passwordManager.IsMasterLockUnlocked(true))
+          return false;
+
+        // setup our thumb list
+        CFileItemList items;
+
+        // add the current thumb, if available
+        if (!pItem->GetProperty("Icon").IsEmpty())
+        {
+          CFileItemPtr current(new CFileItem("thumb://Current", false));
+          current->SetThumbnailImage(pItem->GetPVRChannelInfoTag()->IconPath());
+          current->SetLabel(g_localizeStrings.Get(20016));
+          items.Add(current);
+        }
+        else if (pItem->HasThumbnail())
+        { // already have a thumb that the share doesn't know about - must be a local one, so we mayaswell reuse it.
+          CFileItemPtr current(new CFileItem("thumb://Current", false));
+          current->SetThumbnailImage(pItem->GetThumbnailImage());
+          current->SetLabel(g_localizeStrings.Get(20016));
+          items.Add(current);
+        }
+
+        // and add a "no thumb" entry as well
+        CFileItemPtr nothumb(new CFileItem("thumb://None", false));
+        nothumb->SetIconImage(pItem->GetIconImage());
+        nothumb->SetLabel(g_localizeStrings.Get(20018));
+        items.Add(nothumb);
+
+        CStdString strThumb;
+        VECSOURCES shares;
+        if (g_guiSettings.GetString("pvrmenu.iconpath") != "")
+        {
+          CMediaSource share1;
+          share1.strPath = g_guiSettings.GetString("pvrmenu.iconpath");
+          share1.strName = g_localizeStrings.Get(19018);
+          shares.push_back(share1);
+        }
+        g_mediaManager.GetLocalDrives(shares);
+        if (!CGUIDialogFileBrowser::ShowAndGetImage(items, shares, g_localizeStrings.Get(1030), strThumb))
+          return false;
+
+        if (strThumb == "thumb://Current")
+          return true;
+
+        if (strThumb == "thumb://None")
+          strThumb = "";
+
+        pItem->SetProperty("Icon", strThumb);
+        pItem->SetProperty("Changed", true);
+        m_bContainsChanges = true;
+        return true;
+      }
+      else if (iControl == SPIN_GROUP_SELECTION)
+      {
+        CGUISpinControlEx *pSpin = (CGUISpinControlEx *)GetControl(SPIN_GROUP_SELECTION);
+        if (pSpin)
+        {
+          if (!m_bIsRadio && PVRChannelGroupsTV.size() == 0)
+            return true;
+          else if (m_bIsRadio && PVRChannelGroupsRadio.size() == 0)
+            return true;
+
+          CFileItemPtr pItem = m_channelItems->Get(m_iSelected);
+          pItem->SetProperty("GroupId", (int)pSpin->GetValue());
+          pItem->SetProperty("Changed", true);
+          m_bContainsChanges = true;
+          return true;
+        }
+      }
+      else if (iControl == RADIOBUTTON_USEEPG)
+      {
+        CGUIRadioButtonControl *pRadioButton = (CGUIRadioButtonControl *)GetControl(RADIOBUTTON_USEEPG);
+        if (pRadioButton)
+        {
+          CFileItemPtr pItem = m_channelItems->Get(m_iSelected);
+          pItem->SetProperty("Changed", true);
+          pItem->SetProperty("UseEPG", pRadioButton->IsSelected());
+          m_bContainsChanges = true;
+        }
+      }
+      else if (iControl == SPIN_EPGSOURCE_SELECTION)
+      {
+        /// TODO: Add EPG scraper support
+        return true;
+        CGUISpinControlEx *pSpin = (CGUISpinControlEx *)GetControl(SPIN_EPGSOURCE_SELECTION);
+        if (pSpin)
+        {
+          CFileItemPtr pItem = m_channelItems->Get(m_iSelected);
+          pItem->SetProperty("EPGSource", (int)0);
+          pItem->SetProperty("Changed", true);
+          m_bContainsChanges = true;
+          return true;
+        }
+      }
+      else if (iControl == BUTTON_GROUP_MANAGER)
+      {
+        /* Load group manager dialog */
+        CGUIDialogPVRGroupManager* pDlgInfo = (CGUIDialogPVRGroupManager*)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_GROUP_MANAGER);
+        if (!pDlgInfo)
+          return false;
+
+        pDlgInfo->SetRadio(m_bIsRadio);
+
+        /* Open dialog window */
+        pDlgInfo->DoModal();
+
+        CGUISpinControlEx *pSpin = (CGUISpinControlEx *)GetControl(SPIN_GROUP_SELECTION);
+        if (pSpin)
+        {
+          pSpin->Clear();
+          pSpin->AddLabel(g_localizeStrings.Get(19140), -1);
+          if (!m_bIsRadio)
+          {
+            for (unsigned int i = 0; i < PVRChannelGroupsTV.size(); i++)
+              pSpin->AddLabel(PVRChannelGroupsTV[i].GroupName(), PVRChannelGroupsTV[i].GroupID());
+          }
+          else
+          {
+            for (unsigned int i = 0; i < PVRChannelGroupsRadio.size(); i++)
+              pSpin->AddLabel(PVRChannelGroupsRadio[i].GroupName(), PVRChannelGroupsTV[i].GroupID());
+          }
+          pSpin->SetValue(m_channelItems->Get(m_iSelected)->GetPropertyInt("GroupId"));
+        }
+
+        return true;
+      }
+      else if (iControl == BUTTON_EDIT_CHANNEL)
+      {
+        CFileItemPtr pItem = m_channelItems->Get(m_iSelected);
+        if (pItem->GetPropertyBOOL("Virtual"))
+        {
+          CStdString strURL = pItem->GetProperty("StreamURL");
+          if (CGUIDialogKeyboard::ShowAndGetInput(strURL, g_localizeStrings.Get(19214), false))
+            pItem->SetProperty("StreamURL", strURL);
+          return true;
+        }
+
+        CGUIDialogOK::ShowAndGetInput(19033,19038,0,0);
+        return true;
+      }
+      else if (iControl == BUTTON_DELETE_CHANNEL)
+      {
+        CFileItemPtr pItem = m_channelItems->Get(m_iSelected);
+
+        // prompt user for confirmation of channel record
+        CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
+        if (!pDialog)
+          return true;
+
+        pDialog->SetHeading(19211);
+        pDialog->SetLine(0, "");
+        pDialog->SetLine(1, 750);
+        pDialog->SetLine(2, "");
+        pDialog->DoModal();
+
+        if (pDialog->IsConfirmed())
+        {
+          if (pItem->GetPropertyBOOL("Virtual"))
+          {
+            CPVRDatabase *database = g_PVRManager.GetTVDatabase();
+            database->Open();
+            database->RemoveChannel(*pItem->GetPVRChannelInfoTag());
+            database->Close();
+
+            m_channelItems->Remove(m_iSelected);
+            m_viewControl.SetItems(*m_channelItems);
+            Renumber();
+            return true;
+          }
+          CGUIDialogOK::ShowAndGetInput(19033,19038,0,0);
+        }
+        return true;
+      }
+      else if (iControl == BUTTON_NEW_CHANNEL)
+      {
+        std::vector<long> clients;
+
+        CGUIDialogSelect* pDlgSelect = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
+        if (!pDlgSelect)
+          return false;
+
+        pDlgSelect->SetHeading(19213); // Select Client
+        pDlgSelect->Add(g_localizeStrings.Get(19209));
+        clients.push_back(999);
+        CLIENTMAPITR itr;
+        for (itr = g_PVRManager.Clients()->begin() ; itr != g_PVRManager.Clients()->end(); itr++)
+        {
+          CStdString strClient = (*itr).second->GetBackendName() + ":" + (*itr).second->GetConnectionString();
+          clients.push_back((*itr).first);
+          pDlgSelect->Add(strClient);
+        }
+
+        pDlgSelect->DoModal();
+
+        int selection = pDlgSelect->GetSelectedLabel();
+        if (selection >= 0 && selection <= (int) clients.size())
+        {
+          int clientID = clients[selection];
+          if (clientID == 999)
+          {
+            CStdString strURL = "";
+            if (CGUIDialogKeyboard::ShowAndGetInput(strURL, g_localizeStrings.Get(19214), false))
+            {
+              if (!strURL.IsEmpty())
+              {
+                CPVRChannel newchannel;
+                newchannel.SetChannelName(g_localizeStrings.Get(19204));
+                newchannel.SetRadio(m_bIsRadio);
+                newchannel.SetEPGEnabled(false);
+                newchannel.SetVirtual(true);
+                newchannel.SetStreamURL(strURL);
+                newchannel.SetClientID(999);
+
+                CPVRDatabase *database = g_PVRManager.GetTVDatabase();
+                database->Open();
+                database->UpdateChannel(newchannel);
+                database->Close();
+                CFileItemPtr channel(new CFileItem(newchannel));
+
+                channel->SetProperty("ActiveChannel", true);
+                channel->SetProperty("Name", g_localizeStrings.Get(19204));
+                channel->SetProperty("UseEPG", false);
+                channel->SetProperty("GroupId", (int)newchannel.GroupID());
+                channel->SetProperty("Icon", newchannel.IconPath());
+                channel->SetProperty("EPGSource", (int)0);
+                channel->SetProperty("ClientName", g_localizeStrings.Get(19209));
+
+                m_channelItems->AddFront(channel, m_iSelected);
+                m_viewControl.SetItems(*m_channelItems);
+                Renumber();
+              }
+            }
+          }
+          else
+          {
+            CGUIDialogOK::ShowAndGetInput(19033,19038,0,0);
+          }
+        }
+        return true;
+      }
+    }
+    break;
+  }
+
+  return CGUIDialog::OnMessage(message);
+}
+
+void CGUIDialogPVRChannelManager::OnWindowLoaded()
+{
+  CGUIDialog::OnWindowLoaded();
+
+  m_viewControl.Reset();
+  m_viewControl.SetParentWindow(GetID());
+  m_viewControl.AddView(GetControl(CONTROL_LIST_CHANNELS));
+}
+
+void CGUIDialogPVRChannelManager::OnWindowUnload()
+{
+  CGUIDialog::OnWindowUnload();
+  m_viewControl.Reset();
+}
+
+CFileItemPtr CGUIDialogPVRChannelManager::GetCurrentListItem(int offset)
+{
+  return m_channelItems->Get(m_iSelected);
+}
+
+bool CGUIDialogPVRChannelManager::OnPopupMenu(int iItem)
+{
+  // popup the context menu
+  // grab our context menu
+  CContextButtons buttons;
+
+  // mark the item
+  if (iItem >= 0 && iItem < m_channelItems->Size())
+    m_channelItems->Get(iItem)->Select(true);
+  else
+    return false;
+
+  CFileItemPtr pItem = m_channelItems->Get(iItem);
+
+  buttons.Add(CONTEXT_BUTTON_MOVE, 116);              /* Move channel up or down */
+  if (pItem->GetPropertyBOOL("Virtual"))
+    buttons.Add(CONTEXT_BUTTON_EDIT_SOURCE, 1027);    /* Edit virtual channel URL */
+
+  int choice = CGUIDialogContextMenu::ShowAndGetChoice(buttons);
+
+  // deselect our item
+  if (iItem >= 0 && iItem < m_channelItems->Size())
+    m_channelItems->Get(iItem)->Select(false);
+
+  if (choice < 0)
+    return false;
+
+  return OnContextButton(iItem, (CONTEXT_BUTTON)choice);
+}
+
+bool CGUIDialogPVRChannelManager::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
+{
+  /* Check file item is in list range and get his pointer */
+  if (itemNumber < 0 || itemNumber >= (int)m_channelItems->Size()) return false;
+
+  CFileItemPtr pItem = m_channelItems->Get(itemNumber);
+
+  if (button == CONTEXT_BUTTON_MOVE)
+  {
+    m_bMovingMode = true;
+    pItem->Select(true);
+  }
+  else if (button == CONTEXT_BUTTON_EDIT_SOURCE)
+  {
+    CStdString strURL = pItem->GetProperty("StreamURL");
+    if (CGUIDialogKeyboard::ShowAndGetInput(strURL, g_localizeStrings.Get(19214), false))
+      pItem->SetProperty("StreamURL", strURL);
+  }
+  return true;
+}
+
+void CGUIDialogPVRChannelManager::SetData(int iItem)
+{
+  CGUISpinControlEx      *pSpin;
+  CGUIEditControl        *pEdit;
+  CGUIRadioButtonControl *pRadioButton;
+
+  /* Check file item is in list range and get his pointer */
+  if (iItem < 0 || iItem >= (int)m_channelItems->Size()) return;
+
+  CFileItemPtr pItem = m_channelItems->Get(iItem);
+//  CPVRChannel *infotag = pItem->GetPVRChannelInfoTag();
+
+  pEdit = (CGUIEditControl *)GetControl(EDIT_NAME);
+  if (pEdit)
+  {
+    pEdit->SetLabel2(pItem->GetProperty("Name"));
+    pEdit->SetInputType(CGUIEditControl::INPUT_TYPE_TEXT, 19208);
+  }
+
+  pRadioButton = (CGUIRadioButtonControl *)GetControl(RADIOBUTTON_ACTIVE);
+  if (pRadioButton) pRadioButton->SetSelected(pItem->GetPropertyBOOL("ActiveChannel"));
+
+  pRadioButton = (CGUIRadioButtonControl *)GetControl(RADIOBUTTON_USEEPG);
+  if (pRadioButton) pRadioButton->SetSelected(pItem->GetPropertyBOOL("UseEPG"));
+
+  pSpin = (CGUISpinControlEx *)GetControl(SPIN_GROUP_SELECTION);
+  if (pSpin) pSpin->SetValue(pItem->GetPropertyInt("GroupId"));
+
+  pSpin = (CGUISpinControlEx *)GetControl(SPIN_GROUP_SELECTION);
+  if (pSpin) pSpin->SetValue(pItem->GetPropertyInt("EPGSource"));
+
+}
+
+void CGUIDialogPVRChannelManager::Update()
+{
+  // lock our display, as this window is rendered from the player thread
+  g_graphicsContext.Lock();
+  m_viewControl.SetCurrentView(CONTROL_LIST_CHANNELS);
+
+  // empty the lists ready for population
+  Clear();
+
+  const CPVRChannels *channels = g_PVRChannels.Get(m_bIsRadio);
+  for (unsigned int i = 0; i < channels->size(); i++)
+  {
+    CPVRChannel *channel = channels->at(i);
+    CFileItemPtr channelFile(new CFileItem(*channel));
+    channelFile->SetProperty("ActiveChannel", !channel->IsHidden());
+    channelFile->SetProperty("Name", channel->ChannelName());
+    channelFile->SetProperty("UseEPG", channel->EPGEnabled());
+    channelFile->SetProperty("GroupId", channel->GroupID());
+    channelFile->SetProperty("Icon", channel->IconPath());
+    channelFile->SetProperty("EPGSource", (int)0);
+    CStdString number; number.Format("%i", channel->ChannelNumber());
+    channelFile->SetProperty("Number", number);
+
+    if (channel->IsVirtual())
+    {
+      channelFile->SetProperty("Virtual", true);
+      channelFile->SetProperty("StreamURL", channel->StreamURL());
+    }
+
+    CStdString clientName;
+    if (channel->ClientID() == 999) /* XBMC internal */
+      clientName = g_localizeStrings.Get(19209);
+    else
+      clientName = g_PVRManager.Clients()->find(channel->ClientID())->second->GetBackendName() + ":" + g_PVRManager.Clients()->find(channel->ClientID())->second->GetConnectionString();
+    channelFile->SetProperty("ClientName", clientName);
+
+    m_channelItems->Add(channelFile);
+  }
+
+  CGUISpinControlEx *pSpin = (CGUISpinControlEx *)GetControl(SPIN_GROUP_SELECTION);
+  if (pSpin)
+  {
+    pSpin->Clear();
+    pSpin->AddLabel(g_localizeStrings.Get(19140), -1);
+
+    if (m_bIsRadio)
+    {
+      for (unsigned int i = 0; i < PVRChannelGroupsRadio.size(); i++)
+        pSpin->AddLabel(PVRChannelGroupsRadio[i].GroupName(), PVRChannelGroupsRadio[i].GroupID());
+    }
+    else
+    {
+      for (unsigned int i = 0; i < PVRChannelGroupsTV.size(); i++)
+        pSpin->AddLabel(PVRChannelGroupsTV[i].GroupName(), PVRChannelGroupsTV[i].GroupID());
+    }
+  }
+
+  pSpin = (CGUISpinControlEx *)GetControl(SPIN_EPGSOURCE_SELECTION);
+  if (pSpin)
+  {
+    pSpin->Clear();
+    pSpin->AddLabel(g_localizeStrings.Get(19210), 0);
+    /// TODO: Add Labels for EPG scrapers here
+  }
+
+  Renumber();
+  m_viewControl.SetItems(*m_channelItems);
+  m_viewControl.SetSelectedItem(m_iSelected);
+
+  g_graphicsContext.Unlock();
+}
+
+void CGUIDialogPVRChannelManager::Clear()
+{
+  m_viewControl.Clear();
+  m_channelItems->Clear();
+}
+
+void CGUIDialogPVRChannelManager::SaveList() // XXX investigate: renumbering doesn't work
+{
+  if (!m_bContainsChanges)
+   return;
+
+  CPVRDatabase *database = g_PVRManager.GetTVDatabase();
+  database->Open();
+
+  CGUIDialogProgress* pDlgProgress = (CGUIDialogProgress*)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
+  pDlgProgress->SetHeading(190);
+  pDlgProgress->SetLine(0, "");
+  pDlgProgress->SetLine(1, 328);
+  pDlgProgress->SetLine(2, "");
+  pDlgProgress->StartModal();
+  pDlgProgress->Progress();
+  pDlgProgress->SetPercentage(0);
+
+  int iActiveChannels = 0;
+  for (int iListPtr = 0; iListPtr < m_channelItems->Size(); iListPtr++)
+  {
+    if (m_channelItems->Get(iListPtr)->GetPropertyBOOL("ActiveChannel"))
+      ++iActiveChannels;
+  }
+
+  int iNextChannelNumber = 1;
+  int iNextHiddenChannelNumber = iActiveChannels + 1;
+  bool bHasChangedItems = false;
+
+  for (int iListPtr = 0; iListPtr < m_channelItems->Size(); iListPtr++)
+  {
+    bool bChanged = false;
+    CFileItemPtr pItem = m_channelItems->Get(iListPtr);
+    CPVRChannel *channel = pItem->GetPVRChannelInfoTag();
+
+    if (!channel)
+    {
+      //TODO add new channel
+      continue;
+    }
+
+    /* get values from the form */
+    bool bHidden              = !pItem->GetPropertyBOOL("ActiveChannel");
+    bool bVirtual             = pItem->GetPropertyBOOL("Virtual");
+    bool bEPGEnabled          = pItem->GetPropertyBOOL("UseEPG");
+    int iGroupId              = pItem->GetPropertyInt("GroupId");
+    int iEPGSource            = pItem->GetPropertyInt("EPGSource");
+    CStdString strChannelName = pItem->GetProperty("Name");
+    CStdString strIconPath    = pItem->GetProperty("Icon");
+    CStdString strStreamURL   = pItem->GetProperty("StreamURL");
+
+    /* set new values in the channel tag */
+    if (bHidden)
+      bChanged = channel->SetChannelNumber(iNextHiddenChannelNumber++) || bChanged;
+    else
+      bChanged = channel->SetChannelNumber(iNextChannelNumber++) || bChanged;
+    bChanged = channel->SetChannelName(strChannelName) || bChanged;
+    bChanged = channel->SetHidden(bHidden) || bChanged;
+    bChanged = channel->SetIconPath(strIconPath) || bChanged;
+    bChanged = channel->SetGroupID(iGroupId) || bChanged;
+    if (bVirtual)
+      bChanged = channel->SetStreamURL(strStreamURL) || bChanged;
+
+    if (iEPGSource == 0)
+      bChanged = channel->SetEPGScraper("client") || bChanged;
+    // TODO add other scrapers
+    bChanged = channel->SetEPGEnabled(bEPGEnabled) || bChanged;
+
+    if (bChanged)
+    {
+      bHasChangedItems = true;
+      channel->Persist(true);
+    }
+
+    pItem->SetProperty("Changed", false);
+    pDlgProgress->SetPercentage(iListPtr * 100 / m_channelItems->Size());
+  }
+
+  if (bHasChangedItems)
+  {
+    database->CommitInsertQueries();
+    g_PVRManager.Restart(); // XXX not a nice way to refresh the channels, but works for now
+  }
+
+  database->Close();
+
+  m_bContainsChanges = false;
+  pDlgProgress->Close();
+}
+
+void CGUIDialogPVRChannelManager::Renumber()
+{
+  int number = 1;
+  CStdString strNumber;
+  CFileItemPtr pItem;
+  for (int i = 0; i < m_channelItems->Size(); i++)
+  {
+    pItem = m_channelItems->Get(i);
+    if (pItem->GetPropertyBOOL("ActiveChannel"))
+    {
+      strNumber.Format("%i", number++);
+      pItem->SetProperty("Number", strNumber);
+    }
+    else
+      pItem->SetProperty("Number", "-");
+  }
+}
diff --git a/xbmc/GUIDialogPVRChannelManager.h b/xbmc/GUIDialogPVRChannelManager.h
new file mode 100644 (file)
index 0000000..87d9474
--- /dev/null
@@ -0,0 +1,56 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "GUIDialog.h"
+#include "GUIViewControl.h"
+#include "GUIDialogContextMenu.h"
+
+class CGUIDialogPVRChannelManager : public CGUIDialog
+{
+public:
+  CGUIDialogPVRChannelManager(void);
+  virtual ~CGUIDialogPVRChannelManager(void);
+  virtual bool OnMessage(CGUIMessage& message);
+  virtual bool OnAction(const CAction& action);
+  virtual void OnWindowLoaded();
+  virtual void OnWindowUnload();
+  virtual bool HasListItems() const { return true; };
+  virtual CFileItemPtr GetCurrentListItem(int offset = 0);
+
+protected:
+  virtual bool OnPopupMenu(int iItem);
+  virtual bool OnContextButton(int itemNumber, CONTEXT_BUTTON button);
+
+private:
+  void Clear();
+  void Update();
+  void SaveList();
+  void Renumber();
+  void SetData(int iItem);
+  bool m_bIsRadio;
+  bool m_bMovingMode;
+  bool m_bContainsChanges;
+
+  int m_iSelected;
+  CFileItemList* m_channelItems;
+  CGUIViewControl m_viewControl;
+};
diff --git a/xbmc/GUIDialogPVRChannelsOSD.cpp b/xbmc/GUIDialogPVRChannelsOSD.cpp
new file mode 100644 (file)
index 0000000..8310f9a
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "GUIDialogPVRChannelsOSD.h"
+#include "Application.h"
+#include "FileItem.h"
+#include "GUIWindowManager.h"
+#include "GUIDialogOK.h"
+#include "GUIDialogPVRGuideInfo.h"
+#include "ViewState.h"
+
+#include "pvr/PVRManager.h"
+#include "pvr/PVRChannelsContainer.h"
+#include "pvr/PVREpg.h"
+#include "pvr/PVRTimerInfoTag.h"
+
+using namespace std;
+
+#define CONTROL_LIST                  11
+
+CGUIDialogPVRChannelsOSD::CGUIDialogPVRChannelsOSD()
+    : CGUIDialog(WINDOW_DIALOG_PVR_OSD_CHANNELS, "DialogPVRChannelsOSD.xml")
+{
+  m_vecItems = new CFileItemList;
+}
+
+CGUIDialogPVRChannelsOSD::~CGUIDialogPVRChannelsOSD()
+{
+  delete m_vecItems;
+}
+
+bool CGUIDialogPVRChannelsOSD::OnMessage(CGUIMessage& message)
+{
+  switch (message.GetMessage())
+  {
+  case GUI_MSG_WINDOW_DEINIT:
+    {
+      Clear();
+    }
+    break;
+
+  case GUI_MSG_WINDOW_INIT:
+    {
+      /* Close dialog immediately if now TV or radio channel is playing */
+      if (!g_PVRManager.IsPlayingTV() && !g_PVRManager.IsPlayingRadio())
+      {
+        Close();
+        return true;
+      }
+      CGUIWindow::OnMessage(message);
+      Update();
+      return true;
+    }
+    break;
+
+  case GUI_MSG_CLICKED:
+    {
+      int iControl = message.GetSenderId();
+
+      if (m_viewControl.HasControl(iControl))   // list/thumb control
+      {
+        int iItem = m_viewControl.GetSelectedItem();
+        int iAction = message.GetParam1();
+
+        if (iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK)
+        {
+          /* Switch to channel */
+          GotoChannel(iItem);
+          return true;
+        }
+        else if (iAction == ACTION_SHOW_INFO || iAction == ACTION_MOUSE_RIGHT_CLICK)
+        {
+          /* Show information Dialog */
+          ShowInfo(iItem);
+          return true;
+        }
+      }
+    }
+    break;
+  }
+
+  return CGUIDialog::OnMessage(message);
+}
+
+void CGUIDialogPVRChannelsOSD::Update()
+{
+  // lock our display, as this window is rendered from the player thread
+  g_graphicsContext.Lock();
+  m_viewControl.SetCurrentView(DEFAULT_VIEW_LIST);
+
+  // empty the list ready for population
+  Clear();
+
+  bool RadioPlaying;
+  int CurrentChannel;
+  g_PVRManager.GetCurrentChannel(&CurrentChannel, &RadioPlaying);
+
+  CPVRChannels *channels = (CPVRChannels *)g_PVRChannels.Get(RadioPlaying);
+  channels->GetChannels(m_vecItems, g_PVRManager.GetPlayingGroup());
+
+  m_viewControl.SetItems(*m_vecItems);
+  m_viewControl.SetSelectedItem(CurrentChannel-1);
+  g_graphicsContext.Unlock();
+}
+
+void CGUIDialogPVRChannelsOSD::Clear()
+{
+  m_viewControl.Clear();
+  m_vecItems->Clear();
+}
+
+void CGUIDialogPVRChannelsOSD::GotoChannel(int item)
+{
+  /* Check file item is in list range and get his pointer */
+  if (item < 0 || item >= (int)m_vecItems->Size()) return;
+
+  CFileItemPtr pItem = m_vecItems->Get(item);
+
+  if (pItem->m_strPath == g_application.CurrentFile())
+  {
+    Close();
+    return;
+  }
+
+  if (!g_application.PlayFile(*pItem))
+  {
+    CGUIDialogOK::ShowAndGetInput(19033,0,19136,0);
+    return;
+  }
+  m_viewControl.SetItems(*m_vecItems);
+  m_viewControl.SetSelectedItem(item);
+}
+
+void CGUIDialogPVRChannelsOSD::ShowInfo(int item)
+{
+  /* Check file item is in list range and get his pointer */
+  if (item < 0 || item >= (int)m_vecItems->Size()) return;
+
+  CFileItemPtr pItem = m_vecItems->Get(item);
+  if (pItem && pItem->IsPVRChannel())
+  {
+    /* Get the current running show on this channel from the EPG storage */
+    const CPVREpgInfoTag *epgnow = pItem->GetPVRChannelInfoTag()->GetEPGNow();
+    if (!epgnow)
+      return;
+    CFileItem *itemNow  = new CFileItem(*epgnow);
+
+    /* Load programme info dialog */
+    CGUIDialogPVRGuideInfo* pDlgInfo = (CGUIDialogPVRGuideInfo*)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_GUIDE_INFO);
+    if (!pDlgInfo)
+      return;
+
+    /* inform dialog about the file item and open dialog window */
+    pDlgInfo->SetProgInfo(itemNow);
+    pDlgInfo->DoModal();
+    delete itemNow; /* delete previuosly created FileItem */
+  }
+
+  return;
+}
+
+void CGUIDialogPVRChannelsOSD::OnWindowLoaded()
+{
+  CGUIDialog::OnWindowLoaded();
+  m_viewControl.Reset();
+  m_viewControl.SetParentWindow(GetID());
+  m_viewControl.AddView(GetControl(CONTROL_LIST));
+}
+
+void CGUIDialogPVRChannelsOSD::OnWindowUnload()
+{
+  CGUIDialog::OnWindowUnload();
+  m_viewControl.Reset();
+}
+
+CGUIControl *CGUIDialogPVRChannelsOSD::GetFirstFocusableControl(int id)
+{
+  if (m_viewControl.HasControl(id))
+    id = m_viewControl.GetCurrentControl();
+
+  return CGUIWindow::GetFirstFocusableControl(id);
+}
diff --git a/xbmc/GUIDialogPVRChannelsOSD.h b/xbmc/GUIDialogPVRChannelsOSD.h
new file mode 100644 (file)
index 0000000..66a43e2
--- /dev/null
@@ -0,0 +1,46 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "GUIDialog.h"
+#include "GUIViewControl.h"
+
+class CFileItemList;
+
+class CGUIDialogPVRChannelsOSD : public CGUIDialog
+{
+public:
+  CGUIDialogPVRChannelsOSD(void);
+  virtual ~CGUIDialogPVRChannelsOSD(void);
+  virtual bool OnMessage(CGUIMessage& message);
+  virtual void OnWindowLoaded();
+  virtual void OnWindowUnload();
+
+protected:
+  void GotoChannel(int iItem);
+  void ShowInfo(int item);
+  void Clear();
+  void Update();
+  CGUIControl *GetFirstFocusableControl(int id);
+
+  CFileItemList    *m_vecItems;
+  CGUIViewControl   m_viewControl;
+};
diff --git a/xbmc/GUIDialogPVRCutterOSD.cpp b/xbmc/GUIDialogPVRCutterOSD.cpp
new file mode 100644 (file)
index 0000000..1b5a733
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "GUIDialogPVRCutterOSD.h"
+#include "utils/log.h"
+#include "Application.h"
+
+using namespace std;
+
+CGUIDialogPVRCutterOSD::CGUIDialogPVRCutterOSD()
+    : CGUIDialog(WINDOW_DIALOG_PVR_OSD_CUTTER, "DialogPVRCutterOSD.xml")
+{
+}
+
+CGUIDialogPVRCutterOSD::~CGUIDialogPVRCutterOSD()
+{
+}
+
+bool CGUIDialogPVRCutterOSD::OnAction(const CAction& action)
+{
+  if (action.GetID() == ACTION_PREVIOUS_MENU || action.GetID() == ACTION_CLOSE_DIALOG)
+  {
+    Close();
+    return true;
+  }
+
+  return CGUIDialog::OnAction(action);
+}
+
+bool CGUIDialogPVRCutterOSD::OnMessage(CGUIMessage& message)
+{
+  return CGUIDialog::OnMessage(message);
+}
+
+void CGUIDialogPVRCutterOSD::OnInitWindow()
+{
+  CGUIDialog::OnInitWindow();
+}
+
+void CGUIDialogPVRCutterOSD::OnDeinitWindow(int nextWindowID)
+{
+  CGUIDialog::OnDeinitWindow(nextWindowID);
+}
diff --git a/xbmc/GUIDialogPVRCutterOSD.h b/xbmc/GUIDialogPVRCutterOSD.h
new file mode 100644 (file)
index 0000000..35376d5
--- /dev/null
@@ -0,0 +1,34 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "GUIDialog.h"
+
+class CGUIDialogPVRCutterOSD : public CGUIDialog
+{
+public:
+  CGUIDialogPVRCutterOSD(void);
+  virtual ~CGUIDialogPVRCutterOSD(void);
+  virtual bool OnMessage(CGUIMessage& message);
+  virtual bool OnAction(const CAction& action);
+  virtual void OnInitWindow();
+  virtual void OnDeinitWindow(int nextWindowID);
+};
diff --git a/xbmc/GUIDialogPVRDirectorOSD.cpp b/xbmc/GUIDialogPVRDirectorOSD.cpp
new file mode 100644 (file)
index 0000000..0fbe6f5
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+/*
+ * DESCRIPTION:
+ *
+ * Used in Fullscreen view to control, multifeed channel groups.
+ *
+ */
+
+#include "GUIDialogPVRDirectorOSD.h"
+#include "utils/log.h"
+#include "Application.h"
+
+#include "pvr/PVREpg.h"
+#include "pvr/PVREpgInfoTag.h"
+
+using namespace std;
+
+CGUIDialogPVRDirectorOSD::CGUIDialogPVRDirectorOSD()
+    : CGUIDialog(WINDOW_DIALOG_PVR_OSD_DIRECTOR, "DialogPVRDirectorOSD.xml")
+{
+}
+
+CGUIDialogPVRDirectorOSD::~CGUIDialogPVRDirectorOSD()
+{
+}
+
+bool CGUIDialogPVRDirectorOSD::OnAction(const CAction& action)
+{
+  if (action.GetID() == ACTION_PREVIOUS_MENU || action.GetID() == ACTION_CLOSE_DIALOG)
+  {
+    Close();
+    return true;
+  }
+
+  return CGUIDialog::OnAction(action);
+}
+
+bool CGUIDialogPVRDirectorOSD::OnMessage(CGUIMessage& message)
+{
+  return CGUIDialog::OnMessage(message);
+}
+
+void CGUIDialogPVRDirectorOSD::OnInitWindow()
+{
+  CGUIDialog::OnInitWindow();
+}
+
+void CGUIDialogPVRDirectorOSD::OnDeinitWindow(int nextWindowID)
+{
+  CGUIDialog::OnDeinitWindow(nextWindowID);
+}
diff --git a/xbmc/GUIDialogPVRDirectorOSD.h b/xbmc/GUIDialogPVRDirectorOSD.h
new file mode 100644 (file)
index 0000000..7cc5274
--- /dev/null
@@ -0,0 +1,34 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "GUIDialog.h"
+
+class CGUIDialogPVRDirectorOSD : public CGUIDialog
+{
+public:
+  CGUIDialogPVRDirectorOSD(void);
+  virtual ~CGUIDialogPVRDirectorOSD(void);
+  virtual bool OnMessage(CGUIMessage& message);
+  virtual bool OnAction(const CAction& action);
+  virtual void OnInitWindow();
+  virtual void OnDeinitWindow(int nextWindowID);
+};
diff --git a/xbmc/GUIDialogPVRGroupManager.cpp b/xbmc/GUIDialogPVRGroupManager.cpp
new file mode 100644 (file)
index 0000000..15a7650
--- /dev/null
@@ -0,0 +1,289 @@
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "GUIDialogPVRGroupManager.h"
+#include "Application.h"
+#include "FileItem.h"
+#include "GUIDialogKeyboard.h"
+#include "GUIDialogOK.h"
+#include "GUIDialogYesNo.h"
+#include "GUIWindowManager.h"
+#include "LocalizeStrings.h"
+
+#include "pvr/PVRChannelGroups.h"
+#include "pvr/PVRChannelsContainer.h"
+
+using namespace std;
+
+#define CONTROL_LIST_CHANNELS_LEFT    11
+#define CONTROL_LIST_CHANNELS_RIGHT   12
+#define CONTROL_LIST_CHANNEL_GROUPS   13
+#define CONTROL_CURRENT_GROUP_LABEL   20
+#define BUTTON_NEWGROUP               26
+#define BUTTON_RENAMEGROUP            27
+#define BUTTON_DELGROUP               28
+#define BUTTON_OK                     29
+
+CGUIDialogPVRGroupManager::CGUIDialogPVRGroupManager()
+  : CGUIDialog(WINDOW_DIALOG_PVR_GROUP_MANAGER, "DialogPVRGroupManager.xml")
+{
+  m_channelLeftItems  = new CFileItemList;
+  m_channelRightItems = new CFileItemList;
+  m_channelGroupItems = new CFileItemList;
+}
+
+CGUIDialogPVRGroupManager::~CGUIDialogPVRGroupManager()
+{
+  delete m_channelLeftItems;
+  delete m_channelRightItems;
+  delete m_channelGroupItems;
+}
+
+bool CGUIDialogPVRGroupManager::OnMessage(CGUIMessage& message)
+{
+  unsigned int iControl = 0;
+  unsigned int iMessage = message.GetMessage();
+
+  switch (iMessage)
+  {
+    case GUI_MSG_WINDOW_DEINIT:
+    {
+      Clear();
+    }
+    break;
+
+    case GUI_MSG_WINDOW_INIT:
+    {
+      CGUIWindow::OnMessage(message);
+      m_iSelectedLeft  = 0;
+      m_iSelectedRight = 0;
+      m_iSelectedGroup = 0;
+      Update();
+      return true;
+    }
+    break;
+
+    case GUI_MSG_CLICKED:
+    {
+      iControl = message.GetSenderId();
+
+      if (iControl == BUTTON_OK)
+      {
+        Close();
+      }
+      else if (iControl == BUTTON_NEWGROUP)
+      {
+        CStdString strDescription = "";
+        if (CGUIDialogKeyboard::ShowAndGetInput(strDescription, g_localizeStrings.Get(19139), false))
+        {
+          if (strDescription != "")
+          {
+            if (!m_bIsRadio)
+              PVRChannelGroupsTV.AddGroup(strDescription);
+            else
+              PVRChannelGroupsRadio.AddGroup(strDescription);
+            Update();
+          }
+        }
+      }
+      else if (iControl == BUTTON_DELGROUP && m_channelGroupItems->GetFileCount() != 0)
+      {
+        m_iSelectedGroup        = m_viewControlGroup.GetSelectedItem();
+        CFileItemPtr pItemGroup = m_channelGroupItems->Get(m_iSelectedGroup);
+
+        // prompt user for confirmation of channel record
+        CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
+        if (pDialog)
+        {
+          CStdString groupName;
+          if (!m_bIsRadio)
+            groupName = PVRChannelGroupsTV.GetGroupName(atoi(pItemGroup->m_strPath.c_str()));
+          else
+            groupName = PVRChannelGroupsRadio.GetGroupName(atoi(pItemGroup->m_strPath.c_str()));
+
+          pDialog->SetHeading(117);
+          pDialog->SetLine(0, "");
+          pDialog->SetLine(1, groupName);
+          pDialog->SetLine(2, "");
+          pDialog->DoModal();
+
+          if (pDialog->IsConfirmed())
+          {
+            if (!m_bIsRadio)
+              PVRChannelGroupsTV.DeleteGroup(atoi(pItemGroup->m_strPath.c_str()));
+            else
+              PVRChannelGroupsRadio.DeleteGroup(atoi(pItemGroup->m_strPath.c_str()));
+            Update();
+          }
+        }
+        return true;
+      }
+      else if (iControl == BUTTON_RENAMEGROUP && m_channelGroupItems->GetFileCount() != 0)
+      {
+        if (CGUIDialogKeyboard::ShowAndGetInput(m_CurrentGroupName, g_localizeStrings.Get(19139), false))
+        {
+          if (m_CurrentGroupName != "")
+          {
+            if (!m_bIsRadio)
+              PVRChannelGroupsTV.RenameGroup(atoi(m_channelGroupItems->Get(m_iSelectedGroup)->m_strPath.c_str()), m_CurrentGroupName);
+            else
+              PVRChannelGroupsRadio.RenameGroup(atoi(m_channelGroupItems->Get(m_iSelectedGroup)->m_strPath.c_str()), m_CurrentGroupName);
+            Update();
+          }
+        }
+      }
+      else if (m_viewControlLeft.HasControl(iControl))   // list/thumb control
+      {
+        m_iSelectedLeft = m_viewControlLeft.GetSelectedItem();
+        int iAction     = message.GetParam1();
+
+        if (iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK)
+        {
+          if (m_channelGroupItems->GetFileCount() == 0)
+          {
+            CGUIDialogOK::ShowAndGetInput(19033,19137,0,19138);
+          }
+          else if (m_channelLeftItems->GetFileCount() > 0)
+          {
+            CFileItemPtr pItemGroup   = m_channelGroupItems->Get(m_iSelectedGroup);
+            CFileItemPtr pItemChannel = m_channelLeftItems->Get(m_iSelectedLeft);
+            if (!m_bIsRadio)
+              PVRChannelGroupsTV.ChannelToGroup(*pItemChannel->GetPVRChannelInfoTag(), atoi(pItemGroup->m_strPath.c_str()));
+            else
+              PVRChannelGroupsRadio.ChannelToGroup(*pItemChannel->GetPVRChannelInfoTag(), atoi(pItemGroup->m_strPath.c_str()));
+            Update();
+          }
+          return true;
+        }
+      }
+      else if (m_viewControlRight.HasControl(iControl))   // list/thumb control
+      {
+        m_iSelectedRight = m_viewControlRight.GetSelectedItem();
+        int iAction      = message.GetParam1();
+
+        if (iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK)
+        {
+          if (m_channelRightItems->GetFileCount() > 0)
+          {
+            CFileItemPtr pItemChannel = m_channelRightItems->Get(m_iSelectedRight);
+            if (!m_bIsRadio)
+              PVRChannelGroupsTV.ChannelToGroup(*pItemChannel->GetPVRChannelInfoTag(), 0);
+            else
+              PVRChannelGroupsRadio.ChannelToGroup(*pItemChannel->GetPVRChannelInfoTag(), 0);
+            Update();
+          }
+          return true;
+        }
+      }
+      else if (m_viewControlGroup.HasControl(iControl))   // list/thumb control
+      {
+        int iAction = message.GetParam1();
+
+        if (iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK)
+        {
+          m_iSelectedGroup = m_viewControlGroup.GetSelectedItem();
+          Update();
+          return true;
+        }
+      }
+    }
+    break;
+  }
+
+  return CGUIDialog::OnMessage(message);
+}
+
+void CGUIDialogPVRGroupManager::OnWindowLoaded()
+{
+  CGUIDialog::OnWindowLoaded();
+
+  m_viewControlLeft.Reset();
+  m_viewControlLeft.SetParentWindow(GetID());
+  m_viewControlLeft.AddView(GetControl(CONTROL_LIST_CHANNELS_LEFT));
+
+  m_viewControlRight.Reset();
+  m_viewControlRight.SetParentWindow(GetID());
+  m_viewControlRight.AddView(GetControl(CONTROL_LIST_CHANNELS_RIGHT));
+
+  m_viewControlGroup.Reset();
+  m_viewControlGroup.SetParentWindow(GetID());
+  m_viewControlGroup.AddView(GetControl(CONTROL_LIST_CHANNEL_GROUPS));
+}
+
+void CGUIDialogPVRGroupManager::OnWindowUnload()
+{
+  CGUIDialog::OnWindowUnload();
+  m_viewControlLeft.Reset();
+  m_viewControlRight.Reset();
+  m_viewControlGroup.Reset();
+}
+
+void CGUIDialogPVRGroupManager::Update()
+{
+  m_CurrentGroupName = "";
+
+  // lock our display, as this window is rendered from the player thread
+  g_graphicsContext.Lock();
+  m_viewControlLeft.SetCurrentView(CONTROL_LIST_CHANNELS_LEFT);
+  m_viewControlRight.SetCurrentView(CONTROL_LIST_CHANNELS_RIGHT);
+  m_viewControlGroup.SetCurrentView(CONTROL_LIST_CHANNEL_GROUPS);
+
+  // empty the lists ready for population
+  Clear();
+
+  int groups;
+  if (!m_bIsRadio)
+    groups = PVRChannelGroupsTV.GetGroupList(m_channelGroupItems);
+  else
+    groups = PVRChannelGroupsRadio.GetGroupList(m_channelGroupItems);
+
+  m_viewControlGroup.SetItems(*m_channelGroupItems);
+  m_viewControlGroup.SetSelectedItem(m_iSelectedGroup);
+
+  CPVRChannels *channels = (CPVRChannels *)g_PVRChannels.Get(m_bIsRadio);
+  channels->GetChannels(m_channelLeftItems, 0);
+  m_viewControlLeft.SetItems(*m_channelLeftItems);
+  m_viewControlLeft.SetSelectedItem(m_iSelectedLeft);
+
+  if (groups > 0)
+  {
+    CFileItemPtr pItem = m_channelGroupItems->Get(m_viewControlGroup.GetSelectedItem());
+    m_CurrentGroupName = pItem->m_strTitle;
+    SET_CONTROL_LABEL(CONTROL_CURRENT_GROUP_LABEL, m_CurrentGroupName);
+
+    channels->GetChannels(m_channelRightItems, atoi(pItem->m_strPath.c_str()));
+    m_viewControlRight.SetItems(*m_channelRightItems);
+    m_viewControlRight.SetSelectedItem(m_iSelectedRight);
+  }
+
+  g_graphicsContext.Unlock();
+}
+
+void CGUIDialogPVRGroupManager::Clear()
+{
+  m_viewControlLeft.Clear();
+  m_viewControlRight.Clear();
+  m_viewControlGroup.Clear();
+
+  m_channelLeftItems->Clear();
+  m_channelRightItems->Clear();
+  m_channelGroupItems->Clear();
+}
diff --git a/xbmc/GUIDialogPVRGroupManager.h b/xbmc/GUIDialogPVRGroupManager.h
new file mode 100644 (file)
index 0000000..32b414b
--- /dev/null
@@ -0,0 +1,56 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "GUIDialog.h"
+#include "GUIViewControl.h"
+
+class CFileItemList;
+
+class CGUIDialogPVRGroupManager : public CGUIDialog
+{
+public:
+  CGUIDialogPVRGroupManager(void);
+  virtual ~CGUIDialogPVRGroupManager(void);
+  virtual bool OnMessage(CGUIMessage& message);
+  virtual void OnWindowLoaded();
+  virtual void OnWindowUnload();
+  void SetRadio(bool IsRadio) { m_bIsRadio = IsRadio; }
+
+protected:
+  void Clear();
+  void Update();
+
+  CStdString      m_CurrentGroupName;
+  bool            m_bIsRadio;
+
+  unsigned int    m_iSelectedLeft;
+  unsigned int    m_iSelectedRight;
+  unsigned int    m_iSelectedGroup;
+
+  CFileItemList  *m_channelLeftItems;
+  CFileItemList  *m_channelRightItems;
+  CFileItemList  *m_channelGroupItems;
+
+  CGUIViewControl m_viewControlLeft;
+  CGUIViewControl m_viewControlRight;
+  CGUIViewControl m_viewControlGroup;
+};
diff --git a/xbmc/GUIDialogPVRGuideInfo.cpp b/xbmc/GUIDialogPVRGuideInfo.cpp
new file mode 100644 (file)
index 0000000..b30c2a4
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "GUIDialogPVRGuideInfo.h"
+#include "Application.h"
+#include "GUIWindowManager.h"
+#include "GUIDialogOK.h"
+#include "GUIDialogYesNo.h"
+
+#include "pvr/PVRChannelsContainer.h"
+#include "pvr/PVREpgInfoTag.h"
+#include "pvr/PVRTimers.h"
+#include "pvr/PVRTimerInfoTag.h"
+
+using namespace std;
+
+#define CONTROL_BTN_SWITCH              5
+#define CONTROL_BTN_RECORD              6
+#define CONTROL_BTN_OK                  7
+
+CGUIDialogPVRGuideInfo::CGUIDialogPVRGuideInfo(void)
+    : CGUIDialog(WINDOW_DIALOG_PVR_GUIDE_INFO, "DialogPVRGuideInfo.xml")
+    , m_progItem(new CFileItem)
+{
+}
+
+CGUIDialogPVRGuideInfo::~CGUIDialogPVRGuideInfo(void)
+{
+}
+
+bool CGUIDialogPVRGuideInfo::OnMessage(CGUIMessage& message)
+{
+  switch (message.GetMessage())
+  {
+  case GUI_MSG_WINDOW_INIT:
+    {
+      CGUIDialog::OnMessage(message);
+      Update();
+      break;
+    }
+  case GUI_MSG_CLICKED:
+    {
+      int iControl = message.GetSenderId();
+      if (iControl == CONTROL_BTN_OK)
+      {
+        Close();
+        return true;
+      }
+      else if (iControl == CONTROL_BTN_RECORD)
+      {
+        if (m_progItem->GetEPGInfoTag()->ChannelTag()->ChannelNumber() > 0)
+        {
+          if (m_progItem->GetEPGInfoTag()->Timer() == NULL)
+          {
+            // prompt user for confirmation of channel record
+            CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
+
+            if (pDialog)
+            {
+              pDialog->SetHeading(264);
+              pDialog->SetLine(0, "");
+              pDialog->SetLine(1, m_progItem->GetEPGInfoTag()->Title());
+              pDialog->SetLine(2, "");
+              pDialog->DoModal();
+
+              if (pDialog->IsConfirmed())
+              {
+                CPVRTimerInfoTag *newtimer = CPVRTimerInfoTag::CreateFromEpg(*m_progItem->GetEPGInfoTag());
+                CFileItem *item = new CFileItem(*newtimer);
+                CPVRTimers::AddTimer(*item);
+              }
+            }
+          }
+          else
+          {
+            CGUIDialogOK::ShowAndGetInput(19033,19067,0,0);
+          }
+        }
+        Close();
+        return true;
+      }
+      else if (iControl == CONTROL_BTN_SWITCH)
+      {
+        Close();
+
+        const CPVRChannels *channels = g_PVRChannels.Get(m_progItem->GetEPGInfoTag()->ChannelTag()->IsRadio());
+        if (!g_application.PlayFile(CFileItem(*channels->at(m_progItem->GetEPGInfoTag()->ChannelTag()->ChannelNumber()-1))))
+        {
+          CGUIDialogOK::ShowAndGetInput(19033,0,19035,0);
+          return false;
+        }
+        return true;
+      }
+    }
+  }
+
+  return CGUIDialog::OnMessage(message);
+}
+
+void CGUIDialogPVRGuideInfo::SetProgInfo(const CFileItem *item)
+{
+  *m_progItem = *item;
+}
+
+CFileItemPtr CGUIDialogPVRGuideInfo::GetCurrentListItem(int offset)
+{
+  return m_progItem;
+}
+
+void CGUIDialogPVRGuideInfo::Update()
+{
+  // set recording button label
+  CPVREpgInfoTag* tag = m_progItem->GetEPGInfoTag();
+  if (tag->End() > CDateTime::GetCurrentDateTime())
+  {
+    if (tag->Timer() == NULL)
+    {
+      if (tag->Start() < CDateTime::GetCurrentDateTime())
+        SET_CONTROL_LABEL(CONTROL_BTN_RECORD, 264);
+      else
+        SET_CONTROL_LABEL(CONTROL_BTN_RECORD, 19061);
+    }
+    else
+    {
+      if (tag->Start() < CDateTime::GetCurrentDateTime())
+        SET_CONTROL_LABEL(CONTROL_BTN_RECORD, 19059);
+      else
+        SET_CONTROL_LABEL(CONTROL_BTN_RECORD, 19060);
+    }
+  }
+  else
+    SET_CONTROL_HIDDEN(CONTROL_BTN_RECORD);
+}
diff --git a/xbmc/GUIDialogPVRGuideInfo.h b/xbmc/GUIDialogPVRGuideInfo.h
new file mode 100644 (file)
index 0000000..e3edd5d
--- /dev/null
@@ -0,0 +1,41 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "GUIDialog.h"
+
+class CGUIDialogPVRGuideInfo : public CGUIDialog
+{
+public:
+  CGUIDialogPVRGuideInfo(void);
+  virtual ~CGUIDialogPVRGuideInfo(void);
+  virtual bool OnMessage(CGUIMessage& message);
+  virtual bool HasListItems() const { return true; };
+  virtual CFileItemPtr GetCurrentListItem(int offset = 0);
+
+  void SetProgInfo(const CFileItem *item);
+
+protected:
+  void Update();
+
+  CFileItemPtr m_progItem;
+};
+
diff --git a/xbmc/GUIDialogPVRGuideOSD.cpp b/xbmc/GUIDialogPVRGuideOSD.cpp
new file mode 100644 (file)
index 0000000..d4541cd
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "GUIDialogPVRGuideOSD.h"
+#include "Application.h"
+#include "FileItem.h"
+#include "GUIDialogPVRGuideInfo.h"
+#include "GUIWindowManager.h"
+#include "ViewState.h"
+
+#include "pvr/PVRManager.h"
+
+using namespace std;
+
+#define CONTROL_LIST  11
+
+CGUIDialogPVRGuideOSD::CGUIDialogPVRGuideOSD()
+    : CGUIDialog(WINDOW_DIALOG_PVR_OSD_GUIDE, "DialogPVRGuideOSD.xml")
+{
+  m_vecItems = new CFileItemList;
+}
+
+CGUIDialogPVRGuideOSD::~CGUIDialogPVRGuideOSD()
+{
+  delete m_vecItems;
+}
+
+bool CGUIDialogPVRGuideOSD::OnMessage(CGUIMessage& message)
+{
+  switch (message.GetMessage())
+  {
+  case GUI_MSG_WINDOW_DEINIT:
+    {
+      Clear();
+    }
+    break;
+
+  case GUI_MSG_WINDOW_INIT:
+    {
+      /* Close dialog immediately if now TV or radio channel is playing */
+      if (!g_PVRManager.IsPlayingTV() && !g_PVRManager.IsPlayingRadio())
+      {
+        Close();
+        return true;
+      }
+      CGUIWindow::OnMessage(message);
+      Update();
+      return true;
+    }
+    break;
+
+  case GUI_MSG_CLICKED:
+    {
+      int iControl = message.GetSenderId();
+
+      if (m_viewControl.HasControl(iControl))   // list/thumb control
+      {
+        int iItem = m_viewControl.GetSelectedItem();
+        int iAction = message.GetParam1();
+
+        if (iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK)
+        {
+          ShowInfo(iItem);
+          return true;
+        }
+      }
+    }
+    break;
+  }
+
+  return CGUIDialog::OnMessage(message);
+}
+
+void CGUIDialogPVRGuideOSD::Update()
+{
+  // lock our display, as this window is rendered from the player thread
+  g_graphicsContext.Lock();
+  m_viewControl.SetCurrentView(DEFAULT_VIEW_LIST);
+
+  // empty the list ready for population
+  Clear();
+
+  CPVRChannel CurrentChannel;
+  if (g_PVRManager.GetCurrentChannel(&CurrentChannel))
+    CurrentChannel.GetEPG(m_vecItems);
+
+  m_viewControl.SetItems(*m_vecItems);
+  g_graphicsContext.Unlock();
+}
+
+void CGUIDialogPVRGuideOSD::Clear()
+{
+  m_viewControl.Clear();
+  m_vecItems->Clear();
+}
+
+void CGUIDialogPVRGuideOSD::ShowInfo(int item)
+{
+  /* Check file item is in list range and get his pointer */
+  if (item < 0 || item >= (int)m_vecItems->Size()) return;
+
+  CFileItemPtr pItem = m_vecItems->Get(item);
+
+  /* Load programme info dialog */
+  CGUIDialogPVRGuideInfo* pDlgInfo = (CGUIDialogPVRGuideInfo*)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_GUIDE_INFO);
+  if (!pDlgInfo)
+    return;
+
+  /* inform dialog about the file item and open dialog window */
+  pDlgInfo->SetProgInfo(pItem.get());
+  pDlgInfo->DoModal();
+}
+
+void CGUIDialogPVRGuideOSD::OnWindowLoaded()
+{
+  CGUIDialog::OnWindowLoaded();
+  m_viewControl.Reset();
+  m_viewControl.SetParentWindow(GetID());
+  m_viewControl.AddView(GetControl(CONTROL_LIST));
+}
+
+void CGUIDialogPVRGuideOSD::OnWindowUnload()
+{
+  CGUIDialog::OnWindowUnload();
+  m_viewControl.Reset();
+}
+
+CGUIControl *CGUIDialogPVRGuideOSD::GetFirstFocusableControl(int id)
+{
+  if (m_viewControl.HasControl(id))
+    id = m_viewControl.GetCurrentControl();
+
+  return CGUIWindow::GetFirstFocusableControl(id);
+}
diff --git a/xbmc/GUIDialogPVRGuideOSD.h b/xbmc/GUIDialogPVRGuideOSD.h
new file mode 100644 (file)
index 0000000..bbfab08
--- /dev/null
@@ -0,0 +1,46 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "GUIDialog.h"
+#include "GUIViewControl.h"
+
+class CFileItemList;
+
+class CGUIDialogPVRGuideOSD : public CGUIDialog
+{
+public:
+  CGUIDialogPVRGuideOSD(void);
+  virtual ~CGUIDialogPVRGuideOSD(void);
+  virtual bool OnMessage(CGUIMessage& message);
+  virtual void OnWindowLoaded();
+  virtual void OnWindowUnload();
+
+protected:
+  void ShowInfo(int iItem);
+  void Clear();
+  void Update();
+
+  CGUIControl *GetFirstFocusableControl(int id);
+
+  CFileItemList    *m_vecItems;
+  CGUIViewControl   m_viewControl;
+};
diff --git a/xbmc/GUIDialogPVRGuideSearch.cpp b/xbmc/GUIDialogPVRGuideSearch.cpp
new file mode 100644 (file)
index 0000000..04a47c5
--- /dev/null
@@ -0,0 +1,374 @@
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "GUIDialogPVRGuideSearch.h"
+#include "Application.h"
+#include "LocalizeStrings.h"
+#include "GUIEditControl.h"
+#include "GUIRadioButtonControl.h"
+#include "GUISpinControlEx.h"
+#include "GUIWindowManager.h"
+
+#include "pvr/PVREpgSearchFilter.h"
+#include "pvr/PVRChannelGroups.h"
+#include "pvr/PVRChannelsContainer.h"
+
+using namespace std;
+
+#define CONTROL_EDIT_SEARCH       9
+#define CONTROL_BTN_INC_DESC      10
+#define CONTROL_BTN_CASE_SENS     11
+#define CONTROL_SPIN_MIN_DURATION 12
+#define CONTROL_SPIN_MAX_DURATION 13
+#define CONTROL_EDIT_START_DATE   14
+#define CONTROL_EDIT_STOP_DATE    15
+#define CONTROL_EDIT_START_TIME   16
+#define CONTROL_EDIT_STOP_TIME    17
+#define CONTROL_SPIN_GENRE        18
+#define CONTROL_SPIN_NO_REPEATS   19
+#define CONTROL_BTN_UNK_GENRE     20
+#define CONTROL_SPIN_GROUPS       21
+#define CONTROL_BTN_FTA_ONLY      22
+#define CONTROL_SPIN_CHANNELS     23
+#define CONTROL_BTN_IGNORE_TMR    24
+#define CONTROL_BTN_CANCEL        25
+#define CONTROL_BTN_SEARCH        26
+#define CONTROL_BTN_IGNORE_REC    27
+#define CONTROL_BTN_DEFAULTS      28
+
+CGUIDialogPVRGuideSearch::CGUIDialogPVRGuideSearch(void)
+  : CGUIDialog(WINDOW_DIALOG_PVR_GUIDE_SEARCH, "DialogPVRGuideSearch.xml")
+{
+  m_bConfirmed   = false;
+  m_searchfilter = NULL;
+}
+
+bool CGUIDialogPVRGuideSearch::OnMessage(CGUIMessage& message)
+{
+  CGUIDialog::OnMessage(message);
+
+  switch (message.GetMessage())
+  {
+    case GUI_MSG_WINDOW_INIT:
+    {
+      m_bConfirmed = false;
+      m_bCanceled = false;
+    }
+    break;
+
+    case GUI_MSG_CLICKED:
+    {
+      int iControl = message.GetSenderId();
+      if (iControl == CONTROL_BTN_SEARCH)
+      {
+        OnSearch();
+        m_bCanceled = false;
+        Close();
+        return true;
+      }
+      else if (iControl == CONTROL_BTN_CANCEL)
+      {
+        Close();
+        m_bCanceled = true;
+        return true;
+      }
+      else if (iControl == CONTROL_BTN_DEFAULTS)
+      {
+        if (m_searchfilter)
+        {
+          m_searchfilter->Reset();
+          Update();
+        }
+
+        return true;
+      }
+      else if (iControl == CONTROL_SPIN_GROUPS)
+      {
+        /* Set Channel list spin */
+        CGUISpinControlEx *pSpin = (CGUISpinControlEx *)GetControl(CONTROL_SPIN_CHANNELS);
+        if (pSpin)
+        {
+          CFileItemList channelslist_tv;
+          ((CPVRChannels *)g_PVRChannels.GetTV())->GetChannels(&channelslist_tv, m_searchfilter->m_iChannelGroup);
+
+          pSpin->Clear();
+          pSpin->AddLabel(g_localizeStrings.Get(19140), -1);
+          pSpin->AddLabel(g_localizeStrings.Get(19023), -2);
+          pSpin->AddLabel(g_localizeStrings.Get(19024), -3);
+
+          for (int i = 0; i < channelslist_tv.Size(); i++)
+          {
+            int chanNumber = channelslist_tv[i]->GetPVRChannelInfoTag()->ChannelNumber();
+            CStdString string;
+            string.Format("%i %s", chanNumber, channelslist_tv[i]->GetPVRChannelInfoTag()->ChannelName().c_str());
+            pSpin->AddLabel(string, chanNumber);
+          }
+        }
+        return true;
+      }
+    }
+    break;
+  }
+
+  return false;
+}
+
+void CGUIDialogPVRGuideSearch::OnWindowLoaded()
+{
+  Update();
+  return CGUIDialog::OnWindowLoaded();
+}
+
+void CGUIDialogPVRGuideSearch::OnSearch()
+{
+  CGUISpinControlEx      *pSpin;
+  CGUIEditControl        *pEdit;
+  CGUIRadioButtonControl *pRadioButton;
+  CDateTime               dateTime;
+
+  if (!m_searchfilter)
+    return;
+
+  pEdit = (CGUIEditControl *)GetControl(CONTROL_EDIT_SEARCH);
+  if (pEdit) m_searchfilter->m_strSearchTerm = pEdit->GetLabel2();
+
+  pRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_BTN_INC_DESC);
+  if (pRadioButton) m_searchfilter->m_bSearchInDescription = pRadioButton->IsSelected();
+
+  pRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_BTN_CASE_SENS);
+  if (pRadioButton) m_searchfilter->m_bIsCaseSensitive = pRadioButton->IsSelected();
+
+  pRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_BTN_FTA_ONLY);
+  if (pRadioButton) m_searchfilter->m_bFTAOnly = pRadioButton->IsSelected();
+
+  pRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_BTN_UNK_GENRE);
+  if (pRadioButton) m_searchfilter->m_bIncludeUnknownGenres = pRadioButton->IsSelected();
+
+  pRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_BTN_IGNORE_REC);
+  if (pRadioButton) m_searchfilter->m_bIgnorePresentRecordings = pRadioButton->IsSelected();
+
+  pRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_BTN_IGNORE_TMR);
+  if (pRadioButton) m_searchfilter->m_bIgnorePresentTimers = pRadioButton->IsSelected();
+
+  pRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_SPIN_NO_REPEATS);
+  if (pRadioButton) m_searchfilter->m_bPreventRepeats = pRadioButton->IsSelected();
+
+  pSpin = (CGUISpinControlEx *)GetControl(CONTROL_SPIN_GENRE);
+  if (pSpin) m_searchfilter->m_iGenreType = pSpin->GetValue();
+
+  pSpin = (CGUISpinControlEx *)GetControl(CONTROL_SPIN_MIN_DURATION);
+  if (pSpin) m_searchfilter->m_iMinimumDuration = pSpin->GetValue();
+
+  pSpin = (CGUISpinControlEx *)GetControl(CONTROL_SPIN_MAX_DURATION);
+  if (pSpin) m_searchfilter->m_iMaximumDuration = pSpin->GetValue();
+
+  pSpin = (CGUISpinControlEx *)GetControl(CONTROL_SPIN_CHANNELS);
+  if (pSpin) m_searchfilter->m_iChannelNumber = pSpin->GetValue();
+
+  pSpin = (CGUISpinControlEx *)GetControl(CONTROL_SPIN_GROUPS);
+  if (pSpin) m_searchfilter->m_iChannelGroup = pSpin->GetValue();
+
+  pEdit = (CGUIEditControl *)GetControl(CONTROL_EDIT_START_TIME);
+  if (pEdit)
+  {
+    dateTime.SetFromDBTime(pEdit->GetLabel2());
+    dateTime.GetAsSystemTime(m_searchfilter->m_startTime);
+  }
+
+  pEdit = (CGUIEditControl *)GetControl(CONTROL_EDIT_STOP_TIME);
+  if (pEdit)
+  {
+    dateTime.SetFromDBTime(pEdit->GetLabel2());
+    dateTime.GetAsSystemTime(m_searchfilter->m_endTime);
+  }
+
+  pEdit = (CGUIEditControl *)GetControl(CONTROL_EDIT_START_DATE);
+  if (pEdit)
+  {
+    dateTime.SetFromDBDate(pEdit->GetLabel2());
+    dateTime.GetAsSystemTime(m_searchfilter->m_startDate);
+  }
+
+  pEdit = (CGUIEditControl *)GetControl(CONTROL_EDIT_STOP_DATE);
+  if (pEdit)
+  {
+    dateTime.SetFromDBDate(pEdit->GetLabel2());
+    dateTime.GetAsSystemTime(m_searchfilter->m_endDate);
+  }
+
+  m_bConfirmed = true;
+}
+
+void CGUIDialogPVRGuideSearch::Update()
+{
+  CGUISpinControlEx      *pSpin;
+  CGUIEditControl        *pEdit;
+  CGUIRadioButtonControl *pRadioButton;
+
+  if (!m_searchfilter)
+    return;
+
+  pEdit = (CGUIEditControl *)GetControl(CONTROL_EDIT_SEARCH);
+  if (pEdit)
+  {
+    pEdit->SetLabel2(m_searchfilter->m_strSearchTerm);
+    pEdit->SetInputType(CGUIEditControl::INPUT_TYPE_TEXT, 16017);
+  }
+
+  pRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_BTN_CASE_SENS);
+  if (pRadioButton) pRadioButton->SetSelected(m_searchfilter->m_bIsCaseSensitive);
+
+  pRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_BTN_INC_DESC);
+  if (pRadioButton) pRadioButton->SetSelected(m_searchfilter->m_bSearchInDescription);
+
+  pRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_BTN_FTA_ONLY);
+  if (pRadioButton) pRadioButton->SetSelected(m_searchfilter->m_bFTAOnly);
+
+  pRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_BTN_UNK_GENRE);
+  if (pRadioButton) pRadioButton->SetSelected(m_searchfilter->m_bIncludeUnknownGenres);
+
+  pRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_BTN_IGNORE_REC);
+  if (pRadioButton) pRadioButton->SetSelected(m_searchfilter->m_bIgnorePresentRecordings);
+
+  pRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_BTN_IGNORE_TMR);
+  if (pRadioButton) pRadioButton->SetSelected(m_searchfilter->m_bIgnorePresentTimers);
+
+  pRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_SPIN_NO_REPEATS);
+  if (pRadioButton) pRadioButton->SetSelected(m_searchfilter->m_bPreventRepeats);
+
+  /* Set duration list spin */
+  pSpin = (CGUISpinControlEx *)GetControl(CONTROL_SPIN_MIN_DURATION);
+  if (pSpin)
+  {
+    pSpin->Clear();
+    pSpin->AddLabel("-", -1);
+    for (int i = 1; i < 12*60/5; i++)
+    {
+      CStdString string;
+      string.Format(g_localizeStrings.Get(14044),i*5);
+      pSpin->AddLabel(string, i*5);
+    }
+    pSpin->SetValue(m_searchfilter->m_iMinimumDuration);
+  }
+
+  pSpin = (CGUISpinControlEx *)GetControl(CONTROL_SPIN_MAX_DURATION);
+  if (pSpin)
+  {
+    pSpin->Clear();
+    pSpin->AddLabel("-", -1);
+    for (int i = 1; i < 12*60/5; i++)
+    {
+      CStdString string;
+      string.Format(g_localizeStrings.Get(14044),i*5);
+      pSpin->AddLabel(string, i*5);
+    }
+    pSpin->SetValue(m_searchfilter->m_iMaximumDuration);
+  }
+
+  /* Set time fields */
+  pEdit = (CGUIEditControl *)GetControl(CONTROL_EDIT_START_TIME);
+  if (pEdit)
+  {
+    CDateTime time = m_searchfilter->m_startTime;
+    pEdit->SetLabel2(time.GetAsLocalizedTime("", false));
+    pEdit->SetInputType(CGUIEditControl::INPUT_TYPE_TIME, 14066);
+  }
+  pEdit = (CGUIEditControl *)GetControl(CONTROL_EDIT_STOP_TIME);
+  if (pEdit)
+  {
+    CDateTime time = m_searchfilter->m_endTime;
+    pEdit->SetLabel2(time.GetAsLocalizedTime("", false));
+    pEdit->SetInputType(CGUIEditControl::INPUT_TYPE_TIME, 14066);
+  }
+  pEdit = (CGUIEditControl *)GetControl(CONTROL_EDIT_START_DATE);
+  if (pEdit)
+  {
+    CDateTime date = m_searchfilter->m_startDate;
+    pEdit->SetLabel2(date.GetAsDBDate());
+    pEdit->SetInputType(CGUIEditControl::INPUT_TYPE_DATE, 14067);
+  }
+  pEdit = (CGUIEditControl *)GetControl(CONTROL_EDIT_STOP_DATE);
+  if (pEdit)
+  {
+    CDateTime date = m_searchfilter->m_endDate;
+    pEdit->SetLabel2(date.GetAsDBDate());
+    pEdit->SetInputType(CGUIEditControl::INPUT_TYPE_DATE, 14067);
+  }
+
+  /* Set Channel list spin */
+  pSpin = (CGUISpinControlEx *)GetControl(CONTROL_SPIN_CHANNELS);
+  if (pSpin)
+  {
+    CFileItemList channelslist_tv;
+    ((CPVRChannels *) g_PVRChannels.GetTV())->GetChannels(&channelslist_tv, m_searchfilter->m_iChannelGroup);
+
+    pSpin->Clear();
+    pSpin->AddLabel(g_localizeStrings.Get(19140), -1);
+    pSpin->AddLabel(g_localizeStrings.Get(19023), -2);
+    pSpin->AddLabel(g_localizeStrings.Get(19024), -3);
+
+    for (int i = 0; i < channelslist_tv.Size(); i++)
+    {
+      int chanNumber = channelslist_tv[i]->GetPVRChannelInfoTag()->ChannelNumber();
+      CStdString string;
+      string.Format("%i %s", chanNumber, channelslist_tv[i]->GetPVRChannelInfoTag()->ChannelName().c_str());
+      pSpin->AddLabel(string, chanNumber);
+    }
+    pSpin->SetValue(m_searchfilter->m_iChannelNumber);
+  }
+
+  /* Set Group list spin */
+  pSpin = (CGUISpinControlEx *)GetControl(CONTROL_SPIN_GROUPS);
+  if (pSpin)
+  {
+    CFileItemList grouplist;
+    PVRChannelGroupsTV.GetGroupList(&grouplist);
+
+    pSpin->Clear();
+    pSpin->AddLabel(g_localizeStrings.Get(593), -1);
+
+    for (int i = 0; i < grouplist.Size(); i++)
+      pSpin->AddLabel(grouplist[i]->GetLabel(), atoi(grouplist[i]->m_strPath));
+
+    pSpin->SetValue(m_searchfilter->m_iChannelGroup);
+  }
+
+  /* Set Genre list spin */
+  pSpin = (CGUISpinControlEx *)GetControl(CONTROL_SPIN_GENRE);
+  if (pSpin)
+  {
+    pSpin->Clear();
+    pSpin->AddLabel(g_localizeStrings.Get(593), -1);
+    pSpin->AddLabel(g_localizeStrings.Get(19500), EVCONTENTMASK_MOVIEDRAMA);
+    pSpin->AddLabel(g_localizeStrings.Get(19516), EVCONTENTMASK_NEWSCURRENTAFFAIRS);
+    pSpin->AddLabel(g_localizeStrings.Get(19532), EVCONTENTMASK_SHOW);
+    pSpin->AddLabel(g_localizeStrings.Get(19548), EVCONTENTMASK_SPORTS);
+    pSpin->AddLabel(g_localizeStrings.Get(19564), EVCONTENTMASK_CHILDRENYOUTH);
+    pSpin->AddLabel(g_localizeStrings.Get(19580), EVCONTENTMASK_MUSICBALLETDANCE);
+    pSpin->AddLabel(g_localizeStrings.Get(19596), EVCONTENTMASK_ARTSCULTURE);
+    pSpin->AddLabel(g_localizeStrings.Get(19612), EVCONTENTMASK_SOCIALPOLITICALECONOMICS);
+    pSpin->AddLabel(g_localizeStrings.Get(19628), EVCONTENTMASK_EDUCATIONALSCIENCE);
+    pSpin->AddLabel(g_localizeStrings.Get(19644), EVCONTENTMASK_LEISUREHOBBIES);
+    pSpin->AddLabel(g_localizeStrings.Get(19660), EVCONTENTMASK_SPECIAL);
+    pSpin->AddLabel(g_localizeStrings.Get(19499), EVCONTENTMASK_USERDEFINED);
+    pSpin->SetValue(m_searchfilter->m_iGenreType);
+  }
+}
diff --git a/xbmc/GUIDialogPVRGuideSearch.h b/xbmc/GUIDialogPVRGuideSearch.h
new file mode 100644 (file)
index 0000000..74e3203
--- /dev/null
@@ -0,0 +1,46 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "GUIDialog.h"
+
+struct PVREpgSearchFilter;
+
+class CGUIDialogPVRGuideSearch : public CGUIDialog
+{
+public:
+  CGUIDialogPVRGuideSearch(void);
+  virtual ~CGUIDialogPVRGuideSearch(void) {}
+  virtual bool OnMessage(CGUIMessage& message);
+  virtual void OnWindowLoaded();
+
+  void SetFilterData(PVREpgSearchFilter *searchfilter) { m_searchfilter = searchfilter; }
+  bool IsConfirmed() const { return m_bConfirmed; }
+  bool IsCanceled() const { return m_bCanceled; }
+  void OnSearch();
+
+protected:
+  void Update();
+
+  bool m_bConfirmed;
+  bool m_bCanceled;
+  PVREpgSearchFilter *m_searchfilter;
+};
diff --git a/xbmc/GUIDialogPVRRecordingInfo.cpp b/xbmc/GUIDialogPVRRecordingInfo.cpp
new file mode 100644 (file)
index 0000000..1b12184
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "GUIDialogPVRRecordingInfo.h"
+#include "GUIWindowManager.h"
+
+#include "pvr/PVRRecordings.h"
+#include "pvr/PVRChannel.h"
+
+using namespace std;
+
+#define CONTROL_BTN_OK  10
+
+CGUIDialogPVRRecordingInfo::CGUIDialogPVRRecordingInfo(void)
+  : CGUIDialog(WINDOW_DIALOG_PVR_RECORDING_INFO, "DialogPVRRecordingInfo.xml")
+  , m_recordItem(new CFileItem)
+{
+}
+
+bool CGUIDialogPVRRecordingInfo::OnMessage(CGUIMessage& message)
+{
+  if (message.GetMessage() == GUI_MSG_CLICKED)
+  {
+    int iControl = message.GetSenderId();
+
+    if (iControl == CONTROL_BTN_OK)
+    {
+      Close();
+      return true;
+    }
+  }
+
+  return CGUIDialog::OnMessage(message);
+}
+
+void CGUIDialogPVRRecordingInfo::SetRecording(const CFileItem *item)
+{
+  *m_recordItem = *item;
+}
+
+CFileItemPtr CGUIDialogPVRRecordingInfo::GetCurrentListItem(int offset)
+{
+  return m_recordItem;
+}
diff --git a/xbmc/GUIDialogPVRRecordingInfo.h b/xbmc/GUIDialogPVRRecordingInfo.h
new file mode 100644 (file)
index 0000000..376508f
--- /dev/null
@@ -0,0 +1,39 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "GUIDialog.h"
+
+class CGUIDialogPVRRecordingInfo : public CGUIDialog
+{
+public:
+  CGUIDialogPVRRecordingInfo(void);
+  virtual ~CGUIDialogPVRRecordingInfo(void) {}
+  virtual bool OnMessage(CGUIMessage& message);
+  virtual bool HasListItems() const { return true; };
+  virtual CFileItemPtr GetCurrentListItem(int offset = 0);
+
+  void SetRecording(const CFileItem *item);
+
+protected:
+  CFileItemPtr m_recordItem;
+};
+
diff --git a/xbmc/GUIDialogPVRTimerSettings.cpp b/xbmc/GUIDialogPVRTimerSettings.cpp
new file mode 100644 (file)
index 0000000..0c9650e
--- /dev/null
@@ -0,0 +1,381 @@
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "GUIDialogPVRTimerSettings.h"
+#include "GUIDialogKeyboard.h"
+#include "GUIDialogNumeric.h"
+#include "GUISettings.h"
+#include "LocalizeStrings.h"
+
+#include "pvr/PVRTimerInfoTag.h"
+#include "pvr/PVRChannel.h"
+#include "pvr/PVRChannelsContainer.h"
+
+using namespace std;
+
+#define CONTROL_TMR_ACTIVE              20
+#define CONTROL_TMR_CHNAME_TV           21
+#define CONTROL_TMR_DAY                 22
+#define CONTROL_TMR_BEGIN               23
+#define CONTROL_TMR_END                 24
+#define CONTROL_TMR_PRIORITY            26
+#define CONTROL_TMR_LIFETIME            27
+#define CONTROL_TMR_FIRST_DAY           28
+#define CONTROL_TMR_NAME                29
+#define CONTROL_TMR_RADIO               50
+#define CONTROL_TMR_CHNAME_RADIO        51
+
+CGUIDialogPVRTimerSettings::CGUIDialogPVRTimerSettings(void)
+  : CGUIDialogSettings(WINDOW_DIALOG_PVR_TIMER_SETTING, "DialogPVRTimerSettings.xml")
+{
+  m_cancelled = true;
+  m_tmp_day   = 11;
+}
+
+void CGUIDialogPVRTimerSettings::CreateSettings()
+{
+  CPVRTimerInfoTag* tag = m_timerItem->GetPVRTimerInfoTag();
+
+  // clear out any old settings
+  m_settings.clear();
+
+  // create our settings controls
+  AddBool(CONTROL_TMR_ACTIVE, 19074, &tag->m_bIsActive);
+  AddButton(CONTROL_TMR_NAME, 19075, &tag->m_strTitle, true);
+  AddBool(CONTROL_TMR_RADIO, 19077, &tag->m_bIsRadio);
+
+  /// Channel names
+  {
+    // For TV
+    CFileItemList channelslist_tv;
+    SETTINGSTRINGS channelstrings_tv;
+    ((CPVRChannels *) g_PVRChannels.GetTV())->GetChannels(&channelslist_tv, -1);
+
+    channelstrings_tv.push_back("0 dummy");
+
+    for (int i = 0; i < channelslist_tv.Size(); i++)
+    {
+      CStdString string;
+      CFileItemPtr item = channelslist_tv[i];
+      string.Format("%i %s", item->GetPVRChannelInfoTag()->ChannelNumber(), item->GetPVRChannelInfoTag()->ChannelName().c_str());
+      channelstrings_tv.push_back(string);
+    }
+
+    AddSpin(CONTROL_TMR_CHNAME_TV, 19078, &tag->m_iChannelNumber, channelstrings_tv.size(), channelstrings_tv);
+    EnableSettings(CONTROL_TMR_CHNAME_TV, !tag->m_bIsRadio);
+
+    // For Radio
+    CFileItemList channelslist_radio;
+    SETTINGSTRINGS channelstrings_radio;
+    ((CPVRChannels *) g_PVRChannels.GetRadio())->GetChannels(&channelslist_radio, -1);
+
+    channelstrings_radio.push_back("0 dummy");
+
+    for (int i = 0; i < channelslist_radio.Size(); i++)
+    {
+      CStdString string;
+      CFileItemPtr item = channelslist_radio[i];
+      string.Format("%i %s", item->GetPVRChannelInfoTag()->ChannelNumber(), item->GetPVRChannelInfoTag()->ChannelName().c_str());
+      channelstrings_radio.push_back(string);
+    }
+
+    AddSpin(CONTROL_TMR_CHNAME_RADIO, 19078, &tag->m_iChannelNumber, channelstrings_radio.size(), channelstrings_radio);
+    EnableSettings(CONTROL_TMR_CHNAME_RADIO, tag->m_bIsRadio);
+  }
+
+  /// Day
+  {
+    SETTINGSTRINGS daystrings;
+    tm time_cur;
+    tm time_tmr;
+
+    daystrings.push_back(g_localizeStrings.Get(19086));
+    daystrings.push_back(g_localizeStrings.Get(19087));
+    daystrings.push_back(g_localizeStrings.Get(19088));
+    daystrings.push_back(g_localizeStrings.Get(19089));
+    daystrings.push_back(g_localizeStrings.Get(19090));
+    daystrings.push_back(g_localizeStrings.Get(19091));
+    daystrings.push_back(g_localizeStrings.Get(19092));
+    daystrings.push_back(g_localizeStrings.Get(19093));
+    daystrings.push_back(g_localizeStrings.Get(19094));
+    daystrings.push_back(g_localizeStrings.Get(19095));
+    daystrings.push_back(g_localizeStrings.Get(19096));
+    CDateTime time = CDateTime::GetCurrentDateTime();
+    CDateTime timestart = tag->m_StartTime;
+
+    /* get diffence of timer in days between today and timer start date */
+    time.GetAsTm(time_cur);
+    timestart.GetAsTm(time_tmr);
+
+    if (time_tmr.tm_yday - time_cur.tm_yday >= 0)
+      m_tmp_day += time_tmr.tm_yday - time_cur.tm_yday;
+    else
+      m_tmp_day += time_tmr.tm_yday - time_cur.tm_yday + 365;
+
+    for (int i = 1; i < 365; ++i)
+    {
+      CStdString string = time.GetAsLocalizedDate();
+      daystrings.push_back(string);
+      time += CDateTimeSpan(1, 0, 0, 0);
+    }
+
+    if (tag->m_bIsRepeating)
+    {
+      if (tag->m_iWeekdays == 0x01)
+        m_tmp_day = 0;
+      else if (tag->m_iWeekdays == 0x02)
+        m_tmp_day = 1;
+      else if (tag->m_iWeekdays == 0x04)
+        m_tmp_day = 2;
+      else if (tag->m_iWeekdays == 0x08)
+        m_tmp_day = 3;
+      else if (tag->m_iWeekdays == 0x10)
+        m_tmp_day = 4;
+      else if (tag->m_iWeekdays == 0x20)
+        m_tmp_day = 5;
+      else if (tag->m_iWeekdays == 0x40)
+        m_tmp_day = 6;
+      else if (tag->m_iWeekdays == 0x1F)
+        m_tmp_day = 7;
+      else if (tag->m_iWeekdays == 0x3F)
+        m_tmp_day = 8;
+      else if (tag->m_iWeekdays == 0x7F)
+        m_tmp_day = 9;
+      else if (tag->m_iWeekdays == 0x60)
+        m_tmp_day = 10;
+    }
+
+    AddSpin(CONTROL_TMR_DAY, 19079, &m_tmp_day, daystrings.size(), daystrings);
+  }
+
+  AddButton(CONTROL_TMR_BEGIN, 19080, &timerStartTimeStr, true);
+  AddButton(CONTROL_TMR_END, 19081, &timerEndTimeStr, true);
+  AddSpin(CONTROL_TMR_PRIORITY, 19082, &tag->m_iPriority, 0, 99);
+  AddSpin(CONTROL_TMR_LIFETIME, 19083, &tag->m_iLifetime, 0, 365);
+
+  /// First day
+  {
+    SETTINGSTRINGS daystrings;
+    tm time_cur;
+    tm time_tmr;
+
+    CDateTime time = CDateTime::GetCurrentDateTime();
+    CDateTime timestart = tag->m_FirstDay;
+
+    /* get diffence of timer in days between today and timer start date */
+    if (time < timestart)
+    {
+      time.GetAsTm(time_cur);
+      timestart.GetAsTm(time_tmr);
+
+      if (time_tmr.tm_yday - time_cur.tm_yday >= 0)
+      {
+        m_tmp_iFirstDay += time_tmr.tm_yday - time_cur.tm_yday + 1;
+      }
+      else
+      {
+        m_tmp_iFirstDay += time_tmr.tm_yday - time_cur.tm_yday + 365 + 1;
+      }
+    }
+
+    daystrings.push_back(g_localizeStrings.Get(19030));
+
+    for (int i = 1; i < 365; ++i)
+    {
+      CStdString string = time.GetAsLocalizedDate();
+      daystrings.push_back(string);
+      time += CDateTimeSpan(1, 0, 0, 0);
+    }
+
+    AddSpin(CONTROL_TMR_FIRST_DAY, 19084, &m_tmp_iFirstDay, daystrings.size(), daystrings);
+
+    if (tag->m_bIsRepeating)
+      EnableSettings(CONTROL_TMR_FIRST_DAY, true);
+    else
+      EnableSettings(CONTROL_TMR_FIRST_DAY, false);
+  }
+}
+
+void CGUIDialogPVRTimerSettings::OnSettingChanged(SettingInfo &setting)
+{
+  CPVRTimerInfoTag* tag = m_timerItem->GetPVRTimerInfoTag();
+
+  if (setting.id == CONTROL_TMR_NAME)
+  {
+    if (CGUIDialogKeyboard::ShowAndGetInput(tag->m_strTitle, g_localizeStrings.Get(19097), false))
+    {
+      UpdateSetting(CONTROL_TMR_NAME);
+    }
+  }
+  else if (setting.id == CONTROL_TMR_RADIO)
+  {
+    CPVRChannel* channeltag = NULL;
+    if (!tag->IsRadio())
+    {
+      EnableSettings(CONTROL_TMR_CHNAME_TV, true);
+      EnableSettings(CONTROL_TMR_CHNAME_RADIO, false);
+      channeltag = ((CPVRChannels *) g_PVRChannels.GetTV())->GetByChannelNumber(tag->Number());
+    }
+    else
+    {
+      EnableSettings(CONTROL_TMR_CHNAME_TV, false);
+      EnableSettings(CONTROL_TMR_CHNAME_RADIO, true);
+      channeltag = ((CPVRChannels *) g_PVRChannels.GetRadio())->GetByChannelNumber(tag->Number());
+    }
+
+    if (channeltag)
+    {
+      tag->SetClientNumber(channeltag->ClientChannelNumber());
+      tag->SetClientID(channeltag->ClientID());
+      tag->SetRadio(channeltag->IsRadio());
+      tag->SetNumber(channeltag->ChannelNumber());
+    }
+  }
+  else if (setting.id == CONTROL_TMR_CHNAME_TV || setting.id == CONTROL_TMR_CHNAME_RADIO)
+  {
+    CPVRChannel* channeltag = ((CPVRChannels *) g_PVRChannels.Get(tag->IsRadio()))->GetByChannelNumber(tag->Number());
+
+    if (channeltag)
+    {
+      tag->SetClientNumber(channeltag->ClientChannelNumber());
+      tag->SetClientID(channeltag->ClientID());
+      tag->SetRadio(channeltag->IsRadio());
+      tag->SetNumber(channeltag->ChannelNumber());
+    }
+  }
+  else if (setting.id == CONTROL_TMR_DAY && m_tmp_day > 10)
+  {
+    CDateTime time = CDateTime::GetCurrentDateTime();
+    CDateTime timestart = timerStartTime;
+    CDateTime timestop = timerEndTime;
+    int m_tmp_diff;
+    tm time_cur;
+    tm time_tmr;
+
+    /* get diffence of timer in days between today and timer start date */
+    time.GetAsTm(time_cur);
+    timestart.GetAsTm(time_tmr);
+
+    if (time_tmr.tm_yday - time_cur.tm_yday >= 0)
+      m_tmp_diff = time_tmr.tm_yday - time_cur.tm_yday;
+    else
+      m_tmp_diff = time_tmr.tm_yday - time_cur.tm_yday + 365;
+
+    tag->m_StartTime = timestart + CDateTimeSpan(m_tmp_day-11-m_tmp_diff, 0, 0, 0);
+    tag->m_StopTime  = timestop  + CDateTimeSpan(m_tmp_day-11-m_tmp_diff, 0, 0, 0);
+
+    EnableSettings(CONTROL_TMR_FIRST_DAY, false);
+
+    tag->m_bIsRepeating = false;
+    tag->m_iWeekdays = 0;
+  }
+  else if (setting.id == CONTROL_TMR_DAY && m_tmp_day <= 10)
+  {
+    EnableSettings(CONTROL_TMR_FIRST_DAY, true);
+    tag->m_bIsRepeating = true;
+
+    if (m_tmp_day == 0)
+      tag->m_iWeekdays = 0x01;
+    else if (m_tmp_day == 1)
+      tag->m_iWeekdays = 0x02;
+    else if (m_tmp_day == 2)
+      tag->m_iWeekdays = 0x04;
+    else if (m_tmp_day == 3)
+      tag->m_iWeekdays = 0x08;
+    else if (m_tmp_day == 4)
+      tag->m_iWeekdays = 0x10;
+    else if (m_tmp_day == 5)
+      tag->m_iWeekdays = 0x20;
+    else if (m_tmp_day == 6)
+      tag->m_iWeekdays = 0x40;
+    else if (m_tmp_day == 7)
+      tag->m_iWeekdays = 0x1F;
+    else if (m_tmp_day == 8)
+      tag->m_iWeekdays = 0x3F;
+    else if (m_tmp_day == 9)
+      tag->m_iWeekdays = 0x7F;
+    else if (m_tmp_day == 10)
+      tag->m_iWeekdays = 0x60;
+    else
+      tag->m_iWeekdays = 0;
+  }
+  else if (setting.id == CONTROL_TMR_BEGIN)
+  {
+    if (CGUIDialogNumeric::ShowAndGetTime(timerStartTime, g_localizeStrings.Get(14066)))
+    {
+      CDateTime timestart = timerStartTime;
+      int start_day       = tag->m_StartTime.GetDay();
+      int start_month     = tag->m_StartTime.GetMonth();
+      int start_year      = tag->m_StartTime.GetYear();
+      int start_hour      = timestart.GetHour();
+      int start_minute    = timestart.GetMinute();
+      tag->m_StartTime.SetDateTime(start_year, start_month, start_day, start_hour, start_minute, 0);
+
+      timerStartTimeStr = tag->m_StartTime.GetAsLocalizedTime("", false);
+      UpdateSetting(CONTROL_TMR_BEGIN);
+    }
+  }
+  else if (setting.id == CONTROL_TMR_END)
+  {
+    if (CGUIDialogNumeric::ShowAndGetTime(timerEndTime, g_localizeStrings.Get(14066)))
+    {
+      CDateTime timestop = timerEndTime;
+      int start_day       = tag->m_StopTime.GetDay();
+      int start_month     = tag->m_StopTime.GetMonth();
+      int start_year      = tag->m_StopTime.GetYear();
+      int start_hour      = timestop.GetHour();
+      int start_minute    = timestop.GetMinute();
+      tag->m_StopTime.SetDateTime(start_year, start_month, start_day, start_hour, start_minute, 0);
+
+      timerEndTimeStr = tag->m_StopTime.GetAsLocalizedTime("", false);
+      UpdateSetting(CONTROL_TMR_END);
+    }
+  }
+  else if (setting.id == CONTROL_TMR_FIRST_DAY && m_tmp_day <= 10)
+  {
+    if (m_tmp_iFirstDay > 0)
+      tag->m_FirstDay = CDateTime::GetCurrentDateTime() + CDateTimeSpan(m_tmp_iFirstDay-1, 0, 0, 0);
+    else
+      tag->m_FirstDay = NULL;
+  }
+}
+
+void CGUIDialogPVRTimerSettings::SetTimer(CFileItem *item)
+{
+  m_timerItem         = item;
+  m_cancelled         = true;
+
+  m_timerItem->GetPVRTimerInfoTag()->m_StartTime.GetAsSystemTime(timerStartTime);
+  m_timerItem->GetPVRTimerInfoTag()->m_StopTime.GetAsSystemTime(timerEndTime);
+  timerStartTimeStr   = m_timerItem->GetPVRTimerInfoTag()->m_StartTime.GetAsLocalizedTime("", false);
+  timerEndTimeStr     = m_timerItem->GetPVRTimerInfoTag()->m_StopTime.GetAsLocalizedTime("", false);
+
+  m_tmp_iFirstDay     = 0;
+  m_tmp_day           = 11;
+}
+
+void CGUIDialogPVRTimerSettings::OnOkay()
+{
+  m_cancelled = false;
+  CPVRTimerInfoTag* tag = m_timerItem->GetPVRTimerInfoTag();
+  if (tag->Title() == g_localizeStrings.Get(19056))
+    tag->SetTitle(CPVRChannels::GetByClientFromAll(tag->ClientNumber(), tag->ClientID())->ChannelName());
+}
diff --git a/xbmc/GUIDialogPVRTimerSettings.h b/xbmc/GUIDialogPVRTimerSettings.h
new file mode 100644 (file)
index 0000000..5ce738e
--- /dev/null
@@ -0,0 +1,53 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "DateTime.h"
+#include "GUIDialogSettings.h"
+#include "GUIListItem.h"
+
+class CFileItem;
+
+class CGUIDialogPVRTimerSettings : public CGUIDialogSettings
+{
+public:
+  CGUIDialogPVRTimerSettings(void);
+  virtual ~CGUIDialogPVRTimerSettings(void) {}
+  void SetTimer(CFileItem *item);
+  bool GetOK() { return !m_cancelled; }
+
+protected:
+  virtual void CreateSettings();
+  virtual void OnSettingChanged(SettingInfo &setting);
+  virtual void OnOkay();
+  virtual void OnCancel() { m_cancelled = true; }
+
+  SYSTEMTIME      timerStartTime;
+  SYSTEMTIME      timerEndTime;
+  CStdString      timerStartTimeStr;
+  CStdString      timerEndTimeStr;
+  int             m_tmp_iFirstDay;;
+  int             m_tmp_day;
+
+  CFileItem      *m_timerItem;
+  bool            m_cancelled;
+};
+
diff --git a/xbmc/GUIDialogPVRUpdateProgressBar.cpp b/xbmc/GUIDialogPVRUpdateProgressBar.cpp
new file mode 100644 (file)
index 0000000..4f5c905
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "GUIDialogPVRUpdateProgressBar.h"
+#include "GUIProgressControl.h"
+#include "GUISliderControl.h"
+#include "utils/SingleLock.h"
+
+#define CONTROL_LABELHEADER       30
+#define CONTROL_LABELTITLE        31
+#define CONTROL_PROGRESS          32
+
+CGUIDialogPVRUpdateProgressBar::CGUIDialogPVRUpdateProgressBar(void)
+  : CGUIDialog(WINDOW_DIALOG_EPG_SCAN, "DialogPVRUpdateProgressBar.xml")
+{
+  m_loadOnDemand = false;
+}
+
+bool CGUIDialogPVRUpdateProgressBar::OnMessage(CGUIMessage& message)
+{
+  switch (message.GetMessage())
+  {
+  case GUI_MSG_WINDOW_INIT:
+    {
+      CGUIDialog::OnMessage(message);
+
+      m_strTitle.Empty();
+      m_strHeader.Empty();
+      m_fPercentDone = -1.0f;
+
+      UpdateState();
+      return true;
+    }
+    break;
+  }
+
+  return CGUIDialog::OnMessage(message);
+}
+
+void CGUIDialogPVRUpdateProgressBar::Render()
+{
+  if (m_bRunning)
+    UpdateState();
+
+  CGUIDialog::Render();
+}
+
+void CGUIDialogPVRUpdateProgressBar::SetHeader(const CStdString& strHeader)
+{
+  CSingleLock lock (m_critical);
+
+  m_strHeader = strHeader;
+}
+
+void CGUIDialogPVRUpdateProgressBar::SetTitle(const CStdString& strTitle)
+{
+  CSingleLock lock (m_critical);
+
+  m_strTitle = strTitle;
+}
+
+void CGUIDialogPVRUpdateProgressBar::SetProgress(int currentItem, int itemCount)
+{
+  CSingleLock lock (m_critical);
+
+  m_fPercentDone = (float)((currentItem*100)/itemCount);
+  if (m_fPercentDone > 100.0F)
+    m_fPercentDone = 100.0F;
+}
+
+void CGUIDialogPVRUpdateProgressBar::UpdateState()
+{
+  CSingleLock lock (m_critical);
+
+  SET_CONTROL_LABEL(CONTROL_LABELHEADER, m_strHeader);
+  SET_CONTROL_LABEL(CONTROL_LABELTITLE, m_strTitle);
+
+  if (m_fPercentDone > -1.0f)
+  {
+    SET_CONTROL_VISIBLE(CONTROL_PROGRESS);
+    CGUIProgressControl* pProgressCtrl=(CGUIProgressControl*)GetControl(CONTROL_PROGRESS);
+    if (pProgressCtrl) pProgressCtrl->SetPercentage(m_fPercentDone);
+  }
+}
+
diff --git a/xbmc/GUIDialogPVRUpdateProgressBar.h b/xbmc/GUIDialogPVRUpdateProgressBar.h
new file mode 100644 (file)
index 0000000..5218b1c
--- /dev/null
@@ -0,0 +1,44 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "GUIDialog.h"
+
+class CGUIDialogPVRUpdateProgressBar : public CGUIDialog
+{
+public:
+  CGUIDialogPVRUpdateProgressBar(void);
+  virtual ~CGUIDialogPVRUpdateProgressBar(void) {}
+  virtual bool OnMessage(CGUIMessage& message);
+  virtual void Render();
+  void SetProgress(int currentItem, int itemCount);
+  void SetHeader(const CStdString& strHeader);
+  void SetTitle(const CStdString& strTitle);
+  void UpdateState();
+
+protected:
+  CStdString        m_strTitle;
+  CStdString        m_strHeader;
+  CCriticalSection  m_critical;
+  float             m_fPercentDone;
+  int               m_currentItem;
+  int               m_itemCount;
+};
index 095ac26..3335907 100644 (file)
@@ -26,6 +26,8 @@
 #include "utils/GUIInfoManager.h"
 #include "utils/TimeUtils.h"
 #include "StringUtils.h"
+#include "FileItem.h"
+#include "GUISettings.h"
 
 #define SEEK_BAR_DISPLAY_TIME 2000L
 #define SEEK_BAR_SEEK_TIME     500L
index 53a8aef..36d8f49 100644 (file)
@@ -23,6 +23,7 @@
 #include "GUISpinControlEx.h"
 #include "GUIRadioButtonControl.h"
 #include "GUISettingsSliderControl.h"
+#include "GUIEditControl.h"
 #include "GUIImage.h"
 #include "GUIControlGroupList.h"
 #include "LocalizeStrings.h"
@@ -37,6 +38,8 @@
 #define CONTROL_DEFAULT_SPIN        9
 #define CONTROL_DEFAULT_SLIDER     10
 #define CONTROL_DEFAULT_SEPARATOR  11
+#define CONTROL_DEFAULT_EDIT       12
+#define CONTROL_DEFAULT_EDIT_NUM   13
 #define CONTROL_OKAY_BUTTON        28
 #define CONTROL_CANCEL_BUTTON      29
 #define CONTROL_START              30
@@ -47,6 +50,8 @@ using namespace std;
 CGUIDialogSettings::CGUIDialogSettings(int id, const char *xmlFile)
     : CGUIDialog(id, xmlFile)
 {
+  m_pOriginalEdit = NULL;
+  m_pOriginalEditNum = NULL;
   m_pOriginalSpin = NULL;
   m_pOriginalRadioButton = NULL;
   m_pOriginalSettingsButton = NULL;
@@ -87,11 +92,15 @@ void CGUIDialogSettings::SetupPage()
 {
   // cleanup first, if necessary
   FreeControls();
+  m_pOriginalEdit = (CGUIEditControl*)GetControl(CONTROL_DEFAULT_EDIT);
+  m_pOriginalEditNum = (CGUIEditControl*)GetControl(CONTROL_DEFAULT_EDIT_NUM);
   m_pOriginalSpin = (CGUISpinControlEx*)GetControl(CONTROL_DEFAULT_SPIN);
   m_pOriginalRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_DEFAULT_RADIOBUTTON);
   m_pOriginalSettingsButton = (CGUIButtonControl *)GetControl(CONTROL_DEFAULT_BUTTON);
   m_pOriginalSlider = (CGUISettingsSliderControl *)GetControl(CONTROL_DEFAULT_SLIDER);
   m_pOriginalSeparator = (CGUIImage *)GetControl(CONTROL_DEFAULT_SEPARATOR);
+  if (m_pOriginalEdit) m_pOriginalEdit->SetVisible(false);
+  if (m_pOriginalEditNum) m_pOriginalEditNum->SetVisible(false);
   if (m_pOriginalSpin) m_pOriginalSpin->SetVisible(false);
   if (m_pOriginalRadioButton) m_pOriginalRadioButton->SetVisible(false);
   if (m_pOriginalSettingsButton) m_pOriginalSettingsButton->SetVisible(false);
@@ -99,7 +108,14 @@ void CGUIDialogSettings::SetupPage()
   if (m_pOriginalSeparator) m_pOriginalSeparator->SetVisible(false);
 
   // update our settings label
+  if (GetID() == WINDOW_DIALOG_PVR_TIMER_SETTING)
+  {
+    SET_CONTROL_LABEL(CONTROL_SETTINGS_LABEL, g_localizeStrings.Get(19057));
+  }
+  else
+  {
   SET_CONTROL_LABEL(CONTROL_SETTINGS_LABEL, g_localizeStrings.Get(13395 + GetID() - WINDOW_DIALOG_VIDEO_OSD_SETTINGS));
+  }
 
   CGUIControlGroupList *group = (CGUIControlGroupList *)GetControl(CONTROL_GROUP_LIST);
   if (!group)
@@ -184,11 +200,25 @@ void CGUIDialogSettings::UpdateSetting(unsigned int id)
       if (setting.formatFunction) pControl->SetTextValue(setting.formatFunction(value, setting.interval));
     }
   }
-  else if (setting.type == SettingInfo::BUTTON)
+  else if (setting.type == SettingInfo::BUTTON_DIALOG)
   {
     SET_CONTROL_LABEL(controlID,setting.name);
-    if (m_usePopupSliders && setting.data && setting.formatFunction)
-      SET_CONTROL_LABEL2(controlID,setting.formatFunction(*(float *)setting.data, setting.interval));
+    CGUIButtonControl *pControl = (CGUIButtonControl *)GetControl(controlID);
+    if (pControl && setting.data) pControl->SetLabel2(*(CStdString *)setting.data);
+  }
+  else if (setting.type == SettingInfo::EDIT)
+  {
+    CGUIEditControl *pControl = (CGUIEditControl *)GetControl(controlID);
+    if (pControl && setting.data) pControl->SetLabel2(*(CStdString *)setting.data);
+  }
+  else if (setting.type == SettingInfo::EDIT_NUM)
+  {
+    CGUIEditControl *pControl = (CGUIEditControl *)GetControl(controlID);
+    if (pControl && setting.data) {
+      CStdString strIndex;
+      strIndex.Format("%i", *(int *)setting.data);
+      pControl->SetLabel2(strIndex);
+    }
   }
 
   if (setting.enabled)
@@ -234,6 +264,24 @@ void CGUIDialogSettings::OnClick(int iID)
     CGUISpinControlEx *pControl = (CGUISpinControlEx *)GetControl(iID);
     if (setting.data) *(int *)setting.data = pControl->GetValue();
   }
+  else if (setting.type == SettingInfo::BUTTON_DIALOG)
+  {
+    CGUIButtonControl *pControl = (CGUIButtonControl *)GetControl(iID);
+    if (setting.data) *(CStdString *)setting.data = pControl->GetLabel2();
+  }
+  else if (setting.type == SettingInfo::EDIT)
+  {
+    CGUIEditControl *pControl = (CGUIEditControl *)GetControl(iID);
+    if (setting.data) *(CStdString *)setting.data = pControl->GetLabel2();
+  }
+  else if (setting.type == SettingInfo::EDIT_NUM)
+  {
+    CGUIEditControl *pControl = (CGUIEditControl *)GetControl(iID);
+    if (setting.data) {
+        CStdString strIndex = pControl->GetLabel2();
+        *(int *)setting.data = atol(strIndex.c_str());
+    }
+  }
   else if (setting.type == SettingInfo::CHECK)
   {
     CGUIRadioButtonControl *pControl = (CGUIRadioButtonControl *)GetControl(iID);
@@ -273,7 +321,15 @@ void CGUIDialogSettings::FreeControls()
 void CGUIDialogSettings::AddSetting(SettingInfo &setting, float width, int iControlID)
 {
   CGUIControl *pControl = NULL;
-  if (setting.type == SettingInfo::BUTTON && m_pOriginalSettingsButton)
+  if (setting.type == SettingInfo::BUTTON_DIALOG && m_pOriginalSettingsButton)
+  {
+    pControl = new CGUIButtonControl(*m_pOriginalSettingsButton);
+    if (!pControl) return ;
+    ((CGUIButtonControl *)pControl)->SetLabel(setting.name);
+    pControl->SetWidth(width);
+       if (setting.data) ((CGUIButtonControl *)pControl)->SetLabel2(*(CStdString *)setting.data);
+  }
+  else if (setting.type == SettingInfo::BUTTON && m_pOriginalSettingsButton)
   {
     pControl = new CGUIButtonControl(*m_pOriginalSettingsButton);
     if (!pControl) return ;
@@ -282,6 +338,27 @@ void CGUIDialogSettings::AddSetting(SettingInfo &setting, float width, int iCont
       ((CGUIButtonControl *)pControl)->SetLabel2(setting.formatFunction(*(float *)setting.data, setting.interval));
     pControl->SetWidth(width);
   }
+  else if (setting.type == SettingInfo::EDIT && m_pOriginalEdit)
+  {
+    pControl = new CGUIEditControl(*m_pOriginalEdit);
+    if (!pControl) return ;
+    ((CGUIEditControl *)pControl)->SetLabel(setting.name);
+    pControl->SetWidth(width);
+    if (setting.data) ((CGUIEditControl *)pControl)->SetLabel2(*(CStdString *)setting.data);
+  }
+  else if (setting.type == SettingInfo::EDIT_NUM && m_pOriginalEditNum)
+  {
+    pControl = new CGUIEditControl(*m_pOriginalEditNum);
+    if (!pControl) return ;
+    ((CGUIEditControl *)pControl)->SetLabel(setting.name);
+    pControl->SetWidth(width);
+    ((CGUIEditControl *)pControl)->SetInputType(CGUIEditControl::INPUT_TYPE_NUMBER, 0);
+    if (setting.data) {
+        CStdString strIndex;
+        strIndex.Format("%i", *(int *)setting.data);
+        ((CGUIEditControl *)pControl)->SetLabel2(strIndex);
+    }
+  }
   else if (setting.type == SettingInfo::SEPARATOR && m_pOriginalSeparator)
   {
     pControl = new CGUIImage(*m_pOriginalSeparator);
@@ -337,6 +414,28 @@ void CGUIDialogSettings::AddSetting(SettingInfo &setting, float width, int iCont
     delete pControl;
 }
 
+void CGUIDialogSettings::AddEdit(unsigned int id, int label, CStdString *str, bool enabled)
+{
+  SettingInfo setting;
+  setting.id = id;
+  setting.name = g_localizeStrings.Get(label);
+  setting.type = SettingInfo::EDIT;
+  setting.enabled  = enabled;
+  setting.data = str;
+  m_settings.push_back(setting);
+}
+
+void CGUIDialogSettings::AddNumEdit(unsigned int id, int label, int *current, bool enabled)
+{
+  SettingInfo setting;
+  setting.id = id;
+  setting.name = g_localizeStrings.Get(label);
+  setting.type = SettingInfo::EDIT_NUM;
+  setting.enabled  = enabled;
+  setting.data = current;
+  m_settings.push_back(setting);
+}
+
 void CGUIDialogSettings::AddButton(unsigned int id, int label, float *current, float min, float interval, float max, FORMATFUNCTION function)
 {
   SettingInfo setting;
@@ -351,6 +450,17 @@ void CGUIDialogSettings::AddButton(unsigned int id, int label, float *current, f
   m_settings.push_back(setting);
 }
 
+void CGUIDialogSettings::AddButton(unsigned int id, int label, CStdString *str, bool bOn)
+{
+  SettingInfo setting;
+  setting.id = id;
+  setting.name = g_localizeStrings.Get(label);
+  setting.type = SettingInfo::BUTTON_DIALOG;
+  setting.enabled  = bOn;
+  setting.data = str;
+  m_settings.push_back(setting);
+}
+
 void CGUIDialogSettings::AddBool(unsigned int id, int label, bool *on, bool enabled)
 {
   SettingInfo setting;
@@ -362,6 +472,18 @@ void CGUIDialogSettings::AddBool(unsigned int id, int label, bool *on, bool enab
   m_settings.push_back(setting);
 }
 
+void CGUIDialogSettings::AddSpin(unsigned int id, int label, int *current, unsigned int max, const SETTINGSTRINGS &entries)
+{
+  SettingInfo setting;
+  setting.id = id;
+  setting.name = g_localizeStrings.Get(label);
+  setting.type = SettingInfo::SPIN;
+  setting.data = current;
+  for (unsigned int i = 0; i < max; i++)
+    setting.entry.push_back(make_pair(i, entries[i]));
+  m_settings.push_back(setting);
+}
+
 void CGUIDialogSettings::AddSpin(unsigned int id, int label, int *current, unsigned int max, const int *entries)
 {
   SettingInfo setting;
index 678a867..7df0cf9 100644 (file)
@@ -28,14 +28,16 @@ class CGUISpinControlEx;
 class CGUIButtonControl;
 class CGUIRadioButtonControl;
 class CGUISettingsSliderControl;
+class CGUIEditControl;
 class CGUIImage;
 
+typedef std::vector<CStdString> SETTINGSTRINGS;
 typedef CStdString (*FORMATFUNCTION) (float value, float min);
 
 class SettingInfo
 {
 public:
-  enum SETTING_TYPE { NONE=0, BUTTON, CHECK, CHECK_UCHAR, SPIN, SLIDER, SEPARATOR };
+  enum SETTING_TYPE { NONE=0, EDIT, EDIT_NUM, BUTTON, BUTTON_DIALOG, CHECK, CHECK_UCHAR, SPIN, SLIDER, SEPARATOR };
   SettingInfo()
   {
     id = 0;
@@ -83,8 +85,12 @@ protected:
 
   void AddSetting(SettingInfo &setting, float width, int iControlID);
 
+  void AddEdit(unsigned int id, int label, CStdString *str, bool enabled = true);
+  void AddNumEdit(unsigned int id, int label, int *current, bool enabled = true);
   void AddButton(unsigned int id, int label, float *current = NULL, float min = 0, float interval = 0, float max = 0, FORMATFUNCTION function = NULL);
+  void AddButton(unsigned int it, int label, CStdString *str, bool bOn=true);
   void AddBool(unsigned int id, int label, bool *on, bool enabled = true);
+  void AddSpin(unsigned int id, int label, int *current, unsigned int max, const SETTINGSTRINGS &entries);
   void AddSpin(unsigned int id, int label, int *current, unsigned int max, const int *entries);
   void AddSpin(unsigned int id, int label, int *current, unsigned int min, unsigned int max, const char* minLabel = NULL);
   void AddSpin(unsigned int id, int label, int *current, std::vector<std::pair<int, CStdString> > &values);
@@ -92,6 +98,8 @@ protected:
   void AddSlider(unsigned int id, int label, float *current, float min, float interval, float max, FORMATFUNCTION formatFunction, bool allowPopup = true);
   void AddSeparator(unsigned int id);
 
+  CGUIEditControl *m_pOriginalEdit;
+  CGUIEditControl *m_pOriginalEditNum;
   CGUISpinControlEx *m_pOriginalSpin;
   CGUIRadioButtonControl *m_pOriginalRadioButton;
   CGUIButtonControl *m_pOriginalSettingsButton;
index bd455d1..7a8843c 100644 (file)
@@ -90,6 +90,7 @@ void CGUIDialogVideoSettings::CreateSettings()
     entries.push_back(make_pair(VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF, 16318));
     entries.push_back(make_pair(VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF        , 16317));
     entries.push_back(make_pair(VS_INTERLACEMETHOD_VDPAU_INVERSE_TELECINE     , 16314));
+    entries.push_back(make_pair(VS_INTERLACEMETHOD_AUTO_ION             , 16320));
 
     /* remove unsupported methods */
     for(vector<pair<int, int> >::iterator it = entries.begin(); it != entries.end();)
index 62102b2..f64aae6 100644 (file)
@@ -646,6 +646,12 @@ bool CGUIMediaWindow::GetDirectory(const CStdString &strDirectory, CFileItemList
       m_history.RemoveParentPath();
   }
 
+  int iWindow = GetID();
+  if (iWindow == WINDOW_TV && (items.m_strPath == "pvr://recordings/" ||
+                               items.m_strPath.Left(15) == "pvr://channels/" ||
+                               items.m_strPath.Left(13) == "pvr://timers/"))
+    return true;
+
   if (m_guiState.get() && !m_guiState->HideParentDirItems() && !items.m_strPath.IsEmpty())
   {
     CFileItemPtr pItem(new CFileItem(".."));
@@ -655,7 +661,6 @@ bool CGUIMediaWindow::GetDirectory(const CStdString &strDirectory, CFileItemList
     items.AddFront(pItem, 0);
   }
 
-  int iWindow = GetID();
   CStdStringArray regexps;
 
   if (iWindow == WINDOW_VIDEO_FILES)
@@ -790,7 +795,8 @@ bool CGUIMediaWindow::Update(const CStdString &strDirectory)
   if (!bSelectedFound)
     m_viewControl.SetSelectedItem(0);
 
-  m_history.AddPath(m_vecItems->m_strPath);
+  if (iWindow != WINDOW_TV || (iWindow == WINDOW_TV && m_vecItems->m_strPath.Left(17) == "pvr://recordings/"))
+    m_history.AddPath(m_vecItems->m_strPath);
 
   //m_history.DumpPathHistory();
 
index 957e75c..41104df 100644 (file)
@@ -47,11 +47,14 @@ using namespace std;
 using namespace ADDON;
 
 // String id's of the masks
+#define MASK_DAYS   17999
+#define MASK_HOURS  17998
 #define MASK_MINS   14044
 #define MASK_SECS   14045
 #define MASK_MS    14046
 #define MASK_PERCENT 14047
 #define MASK_KBPS   14048
+#define MASK_MB    17997
 #define MASK_KB    14049
 #define MASK_DB    14050
 
@@ -751,6 +754,49 @@ void CGUISettings::Initialize()
   AddInt(NULL, "window.height", 0, 480, 10, 1, INT_MAX, SPIN_CONTROL_INT);
 
   AddPath(NULL,"system.playlistspath",20006,"set default",BUTTON_CONTROL_PATH_INPUT,false);
+
+  // tv settings (access over TV menu from home window)
+  AddGroup(8, 19180);
+  CSettingsCategory* pvr = AddCategory(8, "pvrmanager", 128);
+  AddBool(pvr, "pvrmanager.enabled", 449  , false);
+  AddString(pvr, "pvrmanager.channelmanager", 19199, "", BUTTON_CONTROL_STANDARD);
+  AddString(pvr, "pvrmanager.channelscan", 19117, "", BUTTON_CONTROL_STANDARD);
+  AddString(pvr, "pvrmanager.resetdb", 19185, "", BUTTON_CONTROL_STANDARD);
+
+  CSettingsCategory* pvrm = AddCategory(8, "pvrmenu", 19181);
+  AddInt(pvrm, "pvrmenu.daystodisplay", 19182, 2, 1, 1, 14, SPIN_CONTROL_INT_PLUS, MASK_DAYS);
+  AddInt(pvrm, "pvrmenu.lingertime", 19183, 0, 0, 30, 960, SPIN_CONTROL_INT_PLUS, MASK_MINS);
+  AddBool(pvrm, "pvrmenu.infoswitch", 19178, true);
+  AddBool(pvrm, "pvrmenu.infotimeout", 19179, true);
+  AddInt(pvrm, "pvrmenu.infotime", 19184, 5, 1, 1, 10, SPIN_CONTROL_INT_PLUS, MASK_SECS);
+  AddBool(pvrm, "pvrmenu.hidevideolength", 19169, true);
+  AddSeparator(pvrm, "pvrmenu.sep1");
+  AddString(pvrm, "pvrmenu.iconpath", 19018, "", BUTTON_CONTROL_PATH_INPUT, false, 657);
+  AddString(pvrm, "pvrmenu.searchicons", 19167, "", BUTTON_CONTROL_STANDARD);
+  AddSeparator(pvrm, "pvrmenu.sep2");
+  AddInt(pvrm, "pvrmenu.defaultguideview", 19065, GUIDE_VIEW_CHANNEL, GUIDE_VIEW_CHANNEL, 1, GUIDE_VIEW_TIMELINE, SPIN_CONTROL_TEXT);
+
+  CSettingsCategory* pvre = AddCategory(8, "pvrepg", 19069);
+  AddInt(pvre, "pvrepg.epgscan", 19070, 5, 1, 1, 24, SPIN_CONTROL_INT_PLUS, MASK_HOURS);
+  AddInt(pvre, "pvrepg.epgupdate", 19071, 120, 15, 15, 480, SPIN_CONTROL_INT_PLUS, MASK_MINS);
+  AddBool(pvre, "pvrepg.ignoredbforclient", 19072, false);
+  AddString(pvre, "pvrepg.resetepg", 19187, "", BUTTON_CONTROL_STANDARD);
+
+  CSettingsCategory* pvrp = AddCategory(8, "pvrplayback", 19177);
+  AddBool(pvrp, "pvrplayback.switchautoclose", 19168, true);
+  AddBool(pvrp, "pvrplayback.playminimized", 19171, true);
+  AddInt(pvrp, "pvrplayback.scantime", 19170, 15, 1, 1, 60, SPIN_CONTROL_INT_PLUS, MASK_SECS);
+  AddInt(pvrp, "pvrplayback.channelentrytimeout", 19073, 0, 0, 250, 2000, SPIN_CONTROL_INT_PLUS, MASK_MS);
+  AddSeparator(pvrp, "pvrplayback.sep1");
+  AddBool(pvrp, "pvrplayback.signalquality", 19037, true);
+  AddInt(pvrp, "pvrplayback.startlast", 19189, START_LAST_CHANNEL_OFF, START_LAST_CHANNEL_OFF, 1, START_LAST_CHANNEL_ON, SPIN_CONTROL_TEXT);
+
+  CSettingsCategory* pvrr = AddCategory(8, "pvrrecord", 19043);
+  AddInt(pvrr, "pvrrecord.instantrecordtime", 19172, 180, 1, 1, 720, SPIN_CONTROL_INT_PLUS, MASK_MINS);
+  AddInt(pvrr, "pvrrecord.defaultpriority", 19173, 50, 1, 1, 100, SPIN_CONTROL_INT_PLUS);
+  AddInt(pvrr, "pvrrecord.defaultlifetime", 19174, 99, 1, 1, 365, SPIN_CONTROL_INT_PLUS, MASK_DAYS);
+  AddInt(pvrr, "pvrrecord.marginstart", 19175, 2, 1, 1, 60, SPIN_CONTROL_INT_PLUS, MASK_MINS);
+  AddInt(pvrr, "pvrrecord.marginstop", 19176, 10, 1, 1, 60, SPIN_CONTROL_INT_PLUS, MASK_MINS);
 }
 
 CGUISettings::~CGUISettings(void)
@@ -802,6 +848,8 @@ void CGUISettings::AddBool(CSettingsCategory* cat, const char *strSetting, int i
   CSettingBool* pSetting = new CSettingBool(iOrder, CStdString(strSetting).ToLower(), iLabel, bData, iControlType);
   if (!pSetting) return ;
   settingsMap.insert(pair<CStdString, CSetting*>(CStdString(strSetting).ToLower(), pSetting));
+
+  SetChangedAndNotify();
 }
 bool CGUISettings::GetBool(const char *strSetting) const
 {
@@ -828,6 +876,9 @@ void CGUISettings::SetBool(const char *strSetting, bool bSetting)
   if (it != settingsMap.end())
   { // old category
     ((CSettingBool*)(*it).second)->SetData(bSetting);
+
+    SetChangedAndNotify();
+
     return ;
   }
   // Assert here and write debug output
@@ -841,6 +892,9 @@ void CGUISettings::ToggleBool(const char *strSetting)
   if (it != settingsMap.end())
   { // old category
     ((CSettingBool*)(*it).second)->SetData(!((CSettingBool *)(*it).second)->GetData());
+
+    SetChangedAndNotify();
+
     return ;
   }
   // Assert here and write debug output
@@ -853,6 +907,8 @@ void CGUISettings::AddFloat(CSettingsCategory* cat, const char *strSetting, int
   CSettingFloat* pSetting = new CSettingFloat(iOrder, CStdString(strSetting).ToLower(), iLabel, fData, fMin, fStep, fMax, iControlType);
   if (!pSetting) return ;
   settingsMap.insert(pair<CStdString, CSetting*>(CStdString(strSetting).ToLower(), pSetting));
+
+  SetChangedAndNotify();
 }
 
 float CGUISettings::GetFloat(const char *strSetting) const
@@ -876,6 +932,9 @@ void CGUISettings::SetFloat(const char *strSetting, float fSetting)
   if (it != settingsMap.end())
   {
     ((CSettingFloat *)(*it).second)->SetData(fSetting);
+
+    SetChangedAndNotify();
+
     return ;
   }
   // Assert here and write debug output
@@ -900,6 +959,8 @@ void CGUISettings::AddInt(CSettingsCategory* cat, const char *strSetting, int iL
   CSettingInt* pSetting = new CSettingInt(iOrder, CStdString(strSetting).ToLower(), iLabel, iData, iMin, iStep, iMax, iControlType, strFormat);
   if (!pSetting) return ;
   settingsMap.insert(pair<CStdString, CSetting*>(CStdString(strSetting).ToLower(), pSetting));
+
+  SetChangedAndNotify();
 }
 
 void CGUISettings::AddInt(CSettingsCategory* cat, const char *strSetting, int iLabel, int iData, int iMin, int iStep, int iMax, int iControlType, int iFormat, int iLabelMin/*=-1*/)
@@ -908,6 +969,8 @@ void CGUISettings::AddInt(CSettingsCategory* cat, const char *strSetting, int iL
   CSettingInt* pSetting = new CSettingInt(iOrder, CStdString(strSetting).ToLower(), iLabel, iData, iMin, iStep, iMax, iControlType, iFormat, iLabelMin);
   if (!pSetting) return ;
   settingsMap.insert(pair<CStdString, CSetting*>(CStdString(strSetting).ToLower(), pSetting));
+
+  SetChangedAndNotify();
 }
 
 void CGUISettings::AddInt(CSettingsCategory* cat, const char *strSetting,
@@ -918,6 +981,8 @@ void CGUISettings::AddInt(CSettingsCategory* cat, const char *strSetting,
   CSettingInt* pSetting = new CSettingInt(iOrder, CStdString(strSetting).ToLower(), iLabel, iData, entries, iControlType);
   if (!pSetting) return ;
   settingsMap.insert(pair<CStdString, CSetting*>(CStdString(strSetting).ToLower(), pSetting));
+
+  SetChangedAndNotify();
 }
 
 void CGUISettings::AddHex(CSettingsCategory* cat, const char *strSetting, int iLabel, int iData, int iMin, int iStep, int iMax, int iControlType, const char *strFormat)
@@ -926,6 +991,8 @@ void CGUISettings::AddHex(CSettingsCategory* cat, const char *strSetting, int iL
   CSettingHex* pSetting = new CSettingHex(iOrder, CStdString(strSetting).ToLower(), iLabel, iData, iMin, iStep, iMax, iControlType, strFormat);
   if (!pSetting) return ;
   settingsMap.insert(pair<CStdString, CSetting*>(CStdString(strSetting).ToLower(), pSetting));
+
+  SetChangedAndNotify();
 }
 
 int CGUISettings::GetInt(const char *strSetting) const
@@ -949,6 +1016,9 @@ void CGUISettings::SetInt(const char *strSetting, int iSetting)
   if (it != settingsMap.end())
   {
     ((CSettingInt *)(*it).second)->SetData(iSetting);
+
+    SetChangedAndNotify();
+
     return ;
   }
   // Assert here and write debug output
@@ -961,6 +1031,8 @@ void CGUISettings::AddString(CSettingsCategory* cat, const char *strSetting, int
   CSettingString* pSetting = new CSettingString(iOrder, CStdString(strSetting).ToLower(), iLabel, strData, iControlType, bAllowEmpty, iHeadingString);
   if (!pSetting) return ;
   settingsMap.insert(pair<CStdString, CSetting*>(CStdString(strSetting).ToLower(), pSetting));
+
+  SetChangedAndNotify();
 }
 
 void CGUISettings::AddPath(CSettingsCategory* cat, const char *strSetting, int iLabel, const char *strData, int iControlType, bool bAllowEmpty, int iHeadingString)
@@ -969,6 +1041,8 @@ void CGUISettings::AddPath(CSettingsCategory* cat, const char *strSetting, int i
   CSettingPath* pSetting = new CSettingPath(iOrder, CStdString(strSetting).ToLower(), iLabel, strData, iControlType, bAllowEmpty, iHeadingString);
   if (!pSetting) return ;
   settingsMap.insert(pair<CStdString, CSetting*>(CStdString(strSetting).ToLower(), pSetting));
+
+  SetChangedAndNotify();
 }
 
 void CGUISettings::AddDefaultAddon(CSettingsCategory* cat, const char *strSetting, int iLabel, const char *strData, const TYPE type)
@@ -977,6 +1051,8 @@ void CGUISettings::AddDefaultAddon(CSettingsCategory* cat, const char *strSettin
   CSettingAddon* pSetting = new CSettingAddon(iOrder, CStdString(strSetting).ToLower(), iLabel, strData, type);
   if (!pSetting) return ;
   settingsMap.insert(pair<CStdString, CSetting*>(CStdString(strSetting).ToLower(), pSetting));
+
+  SetChangedAndNotify();
 }
 
 const CStdString &CGUISettings::GetString(const char *strSetting, bool bPrompt) const
@@ -1020,6 +1096,9 @@ void CGUISettings::SetString(const char *strSetting, const char *strData)
   if (it != settingsMap.end())
   {
     ((CSettingString *)(*it).second)->SetData(strData);
+
+    SetChangedAndNotify();
+
     return ;
   }
   // Assert here and write debug output
@@ -1150,6 +1229,8 @@ void CGUISettings::LoadFromXML(TiXmlElement *pRootElement, mapIter &it, bool adv
       }
     }
   }
+
+  SetChangedAndNotify();
 }
 
 void CGUISettings::SaveXML(TiXmlNode *pRootNode)
@@ -1186,6 +1267,8 @@ void CGUISettings::SaveXML(TiXmlNode *pRootNode)
       }
     }
   }
+
+  SetChangedAndNotify();
 }
 
 void CGUISettings::Clear()
@@ -1196,6 +1279,15 @@ void CGUISettings::Clear()
   for (unsigned int i = 0; i < settingsGroups.size(); i++)
     delete settingsGroups[i];
   settingsGroups.clear();
+
+  SetChanged();
+  NotifyObservers("settings");
+}
+
+void CGUISettings::SetChangedAndNotify(void)
+{
+  SetChanged();
+  NotifyObservers("settings");
 }
 
 float square_error(float x, float y)
@@ -1262,4 +1354,6 @@ void CGUISettings::SetResolution(RESOLUTION res)
   }
   SetString("videoscreen.screenmode", mode);
   m_LookAndFeelResolution = res;
+
+  SetChangedAndNotify();
 }
index c9c8175..30a95e0 100644 (file)
@@ -25,6 +25,7 @@
 #include <map>
 #include "Resolution.h"
 #include "addons/IAddon.h"
+#include "utils/Observer.h"
 
 class TiXmlNode;
 class TiXmlElement;
@@ -124,6 +125,15 @@ class TiXmlElement;
 #define APM_HIPOWER_STANDBY 2
 #define APM_LOPOWER_STANDBY 3
 
+#define GUIDE_VIEW_CHANNEL          0
+#define GUIDE_VIEW_NOW              1
+#define GUIDE_VIEW_NEXT             2
+#define GUIDE_VIEW_TIMELINE         3
+
+#define START_LAST_CHANNEL_OFF      0
+#define START_LAST_CHANNEL_MIN      1
+#define START_LAST_CHANNEL_ON       2
+
 #define SETTINGS_TYPE_BOOL      1
 #define SETTINGS_TYPE_FLOAT     2
 #define SETTINGS_TYPE_INT       3
@@ -427,7 +437,7 @@ private:
 
 typedef std::vector<CSetting *> vecSettings;
 
-class CGUISettings
+class CGUISettings : public Observable
 {
 public:
   CGUISettings();
@@ -484,6 +494,8 @@ public:
 
   void Clear();
 
+  void SetChangedAndNotify(void);
+
 private:
   typedef std::map<CStdString, CSetting*>::iterator mapIter;
   typedef std::map<CStdString, CSetting*>::const_iterator constMapIter;
index 6365281..755c50a 100644 (file)
@@ -122,8 +122,9 @@ void CGUIViewControl::SetCurrentView(int viewMode)
   // Update it with the contents
   UpdateContents(pNewView, item);
 
-  // Update our view control
-  UpdateViewAsControl(((CGUIBaseContainer *)pNewView)->GetLabel());
+  // Update our view control only if we are not in the TV Window
+  if (m_parentWindow != WINDOW_TV)
+    UpdateViewAsControl(((CGUIBaseContainer *)pNewView)->GetLabel());
 }
 
 void CGUIViewControl::SetItems(CFileItemList &items)
index 063b033..0bf2b06 100644 (file)
@@ -25,6 +25,7 @@
 #include "GUIViewStateVideo.h"
 #include "GUIViewStatePictures.h"
 #include "GUIViewStatePrograms.h"
+#include "GUIViewStateTV.h"
 #include "PlayListPlayer.h"
 #include "Util.h"
 #include "URL.h"
@@ -116,6 +117,9 @@ CGUIViewState* CGUIViewState::GetViewState(int windowId, const CFileItemList& it
   if (windowId==WINDOW_VIDEO_PLAYLIST)
     return new CGUIViewStateWindowVideoPlaylist(items);
 
+  if (windowId==WINDOW_TV)
+    return new CGUIViewStateWindowTV(items);
+
   if (windowId==WINDOW_PICTURES)
     return new CGUIViewStateWindowPictures(items);
 
diff --git a/xbmc/GUIViewStateTV.cpp b/xbmc/GUIViewStateTV.cpp
new file mode 100644 (file)
index 0000000..56781d5
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ *      Copyright (C) 2005-2008 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "GUIViewStateTV.h"
+#include "PlayListPlayer.h"
+#include "GUIBaseContainer.h" // for VIEW_TYPE_*
+#include "VideoDatabase.h"
+#include "GUISettings.h"
+#include "AdvancedSettings.h"
+#include "Settings.h"
+#include "FileItem.h"
+#include "Key.h"
+#include "Util.h"
+#include "LocalizeStrings.h"
+#include "utils/log.h"
+
+CGUIViewStateWindowTV::CGUIViewStateWindowTV(const CFileItemList& items) : CGUIViewState(items)
+{
+  if (items.m_strPath.Left(17) == "pvr://recordings/")
+  {
+    if (g_guiSettings.GetBool("filelists.ignorethewhensorting"))
+      AddSortMethod(SORT_METHOD_LABEL_IGNORE_THE, 551, LABEL_MASKS("%L", "%I", "%L", ""));  // FileName, Size | Foldername, e
+    else
+      AddSortMethod(SORT_METHOD_LABEL, 551, LABEL_MASKS("%L", "%I", "%L", ""));  // FileName, Size | Foldername, empty
+    AddSortMethod(SORT_METHOD_SIZE, 553, LABEL_MASKS("%L", "%I", "%L", "%I"));  // FileName, Size | Foldername, Size
+    AddSortMethod(SORT_METHOD_DATE, 552, LABEL_MASKS("%L", "%J", "%L", "%J"));  // FileName, Date | Foldername, Date
+    AddSortMethod(SORT_METHOD_FILE, 561, LABEL_MASKS("%L", "%I", "%L", ""));  // Filename, Size | FolderName, empty
+
+    SetSortMethod(g_settings.m_viewStateVideoFiles.m_sortMethod);
+    SetViewAsControl(g_settings.m_viewStateVideoFiles.m_viewMode);
+    SetSortOrder(g_settings.m_viewStateVideoFiles.m_sortOrder);
+  }
+  LoadViewState(items.m_strPath, WINDOW_TV);
+}
+
+bool CGUIViewStateWindowTV::AutoPlayNextItem()
+{
+  return false;
+}
+
+//CStdString CGUIViewStateWindowTV::GetExtensions()
+//{
+//  return  ".pvr|.timer"";
+//}
+
+bool CGUIViewStateWindowTV::HideParentDirItems()
+{
+  return false;
+}
+
+void CGUIViewStateWindowTV::SaveViewState()
+{
+
+}
diff --git a/xbmc/GUIViewStateTV.h b/xbmc/GUIViewStateTV.h
new file mode 100644 (file)
index 0000000..c362905
--- /dev/null
@@ -0,0 +1,36 @@
+#pragma once
+
+/*
+ *      Copyright (C) 2005-2008 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "GUIViewState.h"
+
+
+class CGUIViewStateWindowTV : public CGUIViewState
+{
+public:
+  CGUIViewStateWindowTV(const CFileItemList& items);
+protected:
+  virtual bool AutoPlayNextItem();
+  virtual bool HideParentDirItems();
+//  virtual CStdString GetExtensions();
+  virtual void SaveViewState();
+};
index 532016a..155c8ce 100644 (file)
@@ -23,6 +23,7 @@
 #include "PlayListPlayer.h"
 #include "FileSystem/VideoDatabaseDirectory.h"
 #include "FileSystem/PluginDirectory.h"
+#include "FileSystem/PVRDirectory.h"
 #include "GUIBaseContainer.h"
 #include "VideoDatabase.h"
 #include "GUISettings.h"
index 9e80719..058911d 100644 (file)
@@ -35,8 +35,9 @@
 #include "GUITextLayout.h"
 #include "GUIWindowManager.h"
 #include "GUIDialogFullScreenInfo.h"
-#include "GUIDialogAudioSubtitleSettings.h"
 #include "GUIDialogNumeric.h"
+#include "GUIDialogAudioSubtitleSettings.h"
+#include "GUISelectButtonControl.h"
 #include "GUISliderControl.h"
 #include "Settings.h"
 #include "FileItem.h"
 #include "DateTime.h"
 #include "ButtonTranslator.h"
 
+#include "pvr/PVRManager.h"
+#include "pvr/PVRChannelGroups.h"
+#include "pvr/PVRChannelGroup.h"
+
 #include <stdio.h>
 
 
@@ -58,6 +63,7 @@
 #define LABEL_ROW1                       10
 #define LABEL_ROW2                       11
 #define LABEL_ROW3                       12
+#define CONTROL_GROUP_CHOOSER            503
 
 #define BTN_OSD_VIDEO                    13
 #define BTN_OSD_AUDIO                    14
@@ -119,6 +125,7 @@ CGUIWindowFullScreen::CGUIWindowFullScreen(void)
   m_dwShowViewModeTimeout = 0;
   m_bShowCurrentTime = false;
   m_subsLayout = NULL;
+  m_bGroupSelectShow = false;
   m_sliderAction = 0;
   // audio
   //  - language
@@ -200,36 +207,52 @@ bool CGUIWindowFullScreen::OnAction(const CAction &action)
     break;
 
   case ACTION_STEP_BACK:
-    if (m_timeCodePosition > 0)
-      SeekToTimeCodeStamp(SEEK_RELATIVE, SEEK_BACKWARD);
+    if (!g_application.CurrentFileItem().HasPVRChannelInfoTag())
+    {
+      if (m_timeCodePosition > 0)
+        SeekToTimeCodeStamp(SEEK_RELATIVE, SEEK_BACKWARD);
+      else
+        g_application.m_pPlayer->Seek(false, false);
+    }
     else
-      g_application.m_pPlayer->Seek(false, false);
+      SeekTV(false, false);
     return true;
-    break;
 
   case ACTION_STEP_FORWARD:
-    if (m_timeCodePosition > 0)
-      SeekToTimeCodeStamp(SEEK_RELATIVE, SEEK_FORWARD);
+    if (!g_application.CurrentFileItem().HasPVRChannelInfoTag())
+    {
+      if (m_timeCodePosition > 0)
+        SeekToTimeCodeStamp(SEEK_RELATIVE, SEEK_FORWARD);
+      else
+        g_application.m_pPlayer->Seek(true, false);
+    }
     else
-      g_application.m_pPlayer->Seek(true, false);
+      SeekTV(true, false);
     return true;
-    break;
 
   case ACTION_BIG_STEP_BACK:
-    if (m_timeCodePosition > 0)
-      SeekToTimeCodeStamp(SEEK_RELATIVE, SEEK_BACKWARD);
+    if (!g_application.CurrentFileItem().HasPVRChannelInfoTag())
+    {
+      if (m_timeCodePosition > 0)
+        SeekToTimeCodeStamp(SEEK_RELATIVE, SEEK_BACKWARD);
+      else
+        g_application.m_pPlayer->Seek(false, true);
+    }
     else
-      g_application.m_pPlayer->Seek(false, true);
+      SeekTV(false, true);
     return true;
-    break;
 
   case ACTION_BIG_STEP_FORWARD:
-    if (m_timeCodePosition > 0)
-      SeekToTimeCodeStamp(SEEK_RELATIVE, SEEK_FORWARD);
+    if (!g_application.CurrentFileItem().HasPVRChannelInfoTag())
+    {
+      if (m_timeCodePosition > 0)
+        SeekToTimeCodeStamp(SEEK_RELATIVE, SEEK_FORWARD);
+      else
+        g_application.m_pPlayer->Seek(true, true);
+    }
     else
-      g_application.m_pPlayer->Seek(true, true);
+      SeekTV(true, true);
     return true;
-    break;
 
   case ACTION_NEXT_SCENE:
     if (g_application.m_pPlayer->SeekScene(true))
@@ -271,6 +294,7 @@ bool CGUIWindowFullScreen::OnAction(const CAction &action)
       CGUIDialogFullScreenInfo* pDialog = (CGUIDialogFullScreenInfo*)g_windowManager.GetWindow(WINDOW_DIALOG_FULLSCREEN_INFO);
       if (pDialog)
       {
+        CFileItem item(g_application.CurrentFileItem());
         pDialog->DoModal();
         return true;
       }
@@ -401,13 +425,23 @@ bool CGUIWindowFullScreen::OnAction(const CAction &action)
       if (g_application.CurrentFileItem().IsLiveTV())
       {
         int channelNr = -1;
+        int currentChannelNr;
+        g_PVRManager.GetCurrentChannel(&currentChannelNr, NULL);
 
-        CStdString strChannel;
-        strChannel.Format("%i", action.GetID() - REMOTE_0);
-        if (CGUIDialogNumeric::ShowAndGetNumber(strChannel, g_localizeStrings.Get(19000)))
-          channelNr = atoi(strChannel.c_str());
+        if (action.GetID() == REMOTE_0)
+        {
+          channelNr = g_PVRManager.GetPreviousChannel();
+        }
+        else
+        {
+          int autoCloseTime = g_guiSettings.GetBool("pvrplayback.switchautoclose") ? 1500 : 0;
+          CStdString strChannel;
+          strChannel.Format("%i", action.GetID() - REMOTE_0);
+          if (CGUIDialogNumeric::ShowAndGetNumber(strChannel, g_localizeStrings.Get(19000), autoCloseTime) || autoCloseTime)
+            channelNr = atoi(strChannel.c_str());
+        }
 
-        if (channelNr > 0)
+        if (channelNr > 0 && channelNr != currentChannelNr)
           OnAction(CAction(ACTION_CHANNEL_SWITCH, (float)channelNr));
       }
       else
@@ -443,6 +477,18 @@ bool CGUIWindowFullScreen::OnAction(const CAction &action)
     }
     return true;
     break;
+  case ACTION_SHOW_PLAYLIST:
+    {
+      CFileItem item(g_application.CurrentFileItem());
+      if (item.HasPVRChannelInfoTag())
+        g_windowManager.ActivateWindow(WINDOW_DIALOG_PVR_OSD_CHANNELS);
+      else if (item.HasVideoInfoTag())
+        g_windowManager.ActivateWindow(WINDOW_VIDEO_PLAYLIST);
+      else if (item.HasMusicInfoTag())
+        g_windowManager.ActivateWindow(WINDOW_MUSIC_PLAYLIST);
+    }
+    return true;
+    break;
   case ACTION_ZOOM_IN:
     {
       g_settings.m_currentVideoSettings.m_CustomZoomAmount += 0.01f;
@@ -512,6 +558,7 @@ bool CGUIWindowFullScreen::OnAction(const CAction &action)
   default:
       break;
   }
+
   return CGUIWindow::OnAction(action);
 }
 
@@ -546,6 +593,8 @@ void CGUIWindowFullScreen::OnWindowLoaded()
     pLabel->SetVisible(true);
     pLabel->SetLabel("$INFO(VIDEOPLAYER.TIME) / $INFO(VIDEOPLAYER.DURATION)");
   }
+
+  FillInTVGroups();
 }
 
 bool CGUIWindowFullScreen::OnMessage(CGUIMessage& message)
@@ -564,6 +613,7 @@ bool CGUIWindowFullScreen::OnMessage(CGUIMessage& message)
       g_infoManager.SetShowInfo(false);
       g_infoManager.SetShowCodec(false);
       m_bShowCurrentTime = false;
+      m_bGroupSelectShow = false;
       g_infoManager.SetDisplayAfterSeek(0); // Make sure display after seek is off.
 
       // switch resolution
@@ -610,6 +660,14 @@ bool CGUIWindowFullScreen::OnMessage(CGUIMessage& message)
       if (pDialog) pDialog->Close(true);
       pDialog = (CGUIDialog *)g_windowManager.GetWindow(WINDOW_DIALOG_FULLSCREEN_INFO);
       if (pDialog) pDialog->Close(true);
+      pDialog = (CGUIDialog *)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_OSD_CHANNELS);
+      if (pDialog) pDialog->Close(true);
+      pDialog = (CGUIDialog *)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_OSD_GUIDE);
+      if (pDialog) pDialog->Close(true);
+      pDialog = (CGUIDialog *)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_OSD_DIRECTOR);
+      if (pDialog) pDialog->Close(true);
+      pDialog = (CGUIDialog *)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_OSD_CUTTER);
+      if (pDialog) pDialog->Close(true);
 
       FreeResources(true);
 
@@ -633,6 +691,55 @@ bool CGUIWindowFullScreen::OnMessage(CGUIMessage& message)
 
       return true;
     }
+  case GUI_MSG_CLICKED:
+    {
+      unsigned int iControl = message.GetSenderId();
+      if (iControl == CONTROL_GROUP_CHOOSER)
+      {
+        int iNewGroup = -1; // All Channels
+
+        CPVRChannelGroups *groups;
+        if (!g_PVRManager.IsPlayingRadio())
+          groups = &PVRChannelGroupsTV;
+        else
+          groups = &PVRChannelGroupsRadio;
+
+        // Get the currently selected label of the Select button
+        CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), iControl);
+        OnMessage(msg);
+        CStdString strLabel = msg.GetLabel();
+        if (msg.GetParam1() != 0)
+        {
+          // Go with the currently selected Label String from the Select button
+          // thru all Group names, if one of this names match the label load
+          // the ID of this group, if no equal name is found the default group
+          // for all channels is used.
+          for (int i = 0; i < (int) groups->size(); ++i)
+          {
+            if (strLabel == groups->at(i).GroupName())
+            {
+              iNewGroup = groups->at(i).GroupID();
+              break;
+            }
+          }
+        }
+
+        // Switch to the first channel of the new group if the new group ID is
+        // different from the current one.
+        if (iNewGroup != g_PVRManager.GetPlayingGroup())
+        {
+          g_PVRManager.SetPlayingGroup(iNewGroup);
+          OnAction(CAction(ACTION_CHANNEL_SWITCH, (float) groups->GetFirstChannelForGroupID(iNewGroup)));
+        }
+
+        // hide the control and reset focus
+        m_bGroupSelectShow = false;
+        SET_CONTROL_HIDDEN(CONTROL_GROUP_CHOOSER);
+//        SET_CONTROL_FOCUS(0, 0);
+        return true;
+      }
+      break;
+    }
   case GUI_MSG_SETFOCUS:
   case GUI_MSG_LOSTFOCUS:
     if (message.GetSenderId() != WINDOW_FULLSCREEN_VIDEO) return true;
@@ -822,6 +929,7 @@ void CGUIWindowFullScreen::FrameMove()
     SET_CONTROL_VISIBLE(LABEL_ROW2);
     SET_CONTROL_VISIBLE(LABEL_ROW3);
     SET_CONTROL_VISIBLE(BLUE_BAR);
+    SET_CONTROL_HIDDEN(CONTROL_GROUP_CHOOSER);
   }
   else if (m_timeCodeShow)
   {
@@ -829,6 +937,15 @@ void CGUIWindowFullScreen::FrameMove()
     SET_CONTROL_HIDDEN(LABEL_ROW2);
     SET_CONTROL_HIDDEN(LABEL_ROW3);
     SET_CONTROL_VISIBLE(BLUE_BAR);
+    SET_CONTROL_HIDDEN(CONTROL_GROUP_CHOOSER);
+  }
+  else if (m_bGroupSelectShow)
+  {
+    SET_CONTROL_HIDDEN(LABEL_ROW1);
+    SET_CONTROL_HIDDEN(LABEL_ROW2);
+    SET_CONTROL_HIDDEN(LABEL_ROW3);
+    SET_CONTROL_HIDDEN(BLUE_BAR);
+    SET_CONTROL_VISIBLE(CONTROL_GROUP_CHOOSER);
   }
   else
   {
@@ -836,6 +953,7 @@ void CGUIWindowFullScreen::FrameMove()
     SET_CONTROL_HIDDEN(LABEL_ROW2);
     SET_CONTROL_HIDDEN(LABEL_ROW3);
     SET_CONTROL_HIDDEN(BLUE_BAR);
+    SET_CONTROL_HIDDEN(CONTROL_GROUP_CHOOSER);
   }
 }
 
@@ -927,6 +1045,23 @@ void CGUIWindowFullScreen::SeekToTimeCodeStamp(SEEK_TYPE type, SEEK_DIRECTION di
   m_timeCodeShow = false;
 }
 
+void CGUIWindowFullScreen::SeekTV(bool bPlus, bool bLargeStep)
+{
+  if (bLargeStep)
+  {
+    if (bPlus)
+      OnAction(CAction(ACTION_NEXT_ITEM));
+    else
+      OnAction(CAction(ACTION_PREV_ITEM));
+    return;
+  }
+  else if (!bLargeStep)
+  {
+    ChangetheTVGroup(bPlus);
+    return;
+  }
+}
+
 double CGUIWindowFullScreen::GetTimeCodeStamp()
 {
   // Convert the timestamp into an integer
@@ -989,6 +1124,63 @@ void CGUIWindowFullScreen::OnSliderChange(void *data, CGUISliderControl *slider)
   }
 }
 
+void CGUIWindowFullScreen::FillInTVGroups()
+{
+  CGUIMessage msgReset(GUI_MSG_LABEL_RESET, GetID(), CONTROL_GROUP_CHOOSER);
+  g_windowManager.SendMessage(msgReset);
+
+  CPVRChannelGroups *groups;
+  if (!g_PVRManager.IsPlayingRadio())
+    groups = &PVRChannelGroupsTV;
+  else
+    groups = &PVRChannelGroupsRadio;
+
+  int iGroup        = 0;
+  int iCurrentGroup = 0;
+  {
+    // First Group is All channels (ID = -1)
+    CGUIMessage msg(GUI_MSG_LABEL_ADD, GetID(), CONTROL_GROUP_CHOOSER, iGroup++);
+    msg.SetLabel(593);
+    g_windowManager.SendMessage(msg);
+  }
+  for (int i = 0; i < (int) groups->size(); ++i)
+  {
+    if (groups->at(i).GroupID() == g_PVRManager.GetPlayingGroup())
+      iCurrentGroup = iGroup;
+
+    CGUIMessage msg(GUI_MSG_LABEL_ADD, GetID(), CONTROL_GROUP_CHOOSER, iGroup++);
+    msg.SetLabel(groups->at(i).GroupName());
+    g_windowManager.SendMessage(msg);
+  }
+  CGUIMessage msgSel(GUI_MSG_ITEM_SELECT, GetID(), CONTROL_GROUP_CHOOSER, iCurrentGroup);
+  g_windowManager.SendMessage(msgSel);
+}
+
+void CGUIWindowFullScreen::ChangetheTVGroup(bool next)
+{
+  CGUISelectButtonControl* pButton = (CGUISelectButtonControl*)GetControl(CONTROL_GROUP_CHOOSER);
+  if (!pButton)
+    return;
+
+  if (!m_bGroupSelectShow)
+  {
+    SET_CONTROL_VISIBLE(CONTROL_GROUP_CHOOSER);
+    SET_CONTROL_FOCUS(CONTROL_GROUP_CHOOSER, 0);
+
+    // fire off an event that we've pressed this button...
+    OnAction(CAction(ACTION_SELECT_ITEM));
+
+    m_bGroupSelectShow = true;
+  }
+  else
+  {
+    if (next)
+      pButton->OnRight();
+    else
+      pButton->OnLeft();
+  }
+}
+
 void CGUIWindowFullScreen::ToggleOSD()
 {
   CGUIWindowOSD *pOSD = (CGUIWindowOSD *)g_windowManager.GetWindow(WINDOW_OSD);
index da08905..d73fa09 100644 (file)
@@ -41,6 +41,7 @@ public:
   virtual void Render();
   virtual void OnWindowLoaded();
   void ChangetheTimeCode(int remote);
+  void ChangetheTVGroup(bool next);
 
   virtual void OnSliderChange(void *data, CGUISliderControl *slider);
 protected:
@@ -49,7 +50,9 @@ protected:
 
 private:
   void RenderTTFSubtitles();
+  void SeekTV(bool bPlus, bool bLargeStep);
   void SeekChapter(int iChapter);
+  void FillInTVGroups();
   void ToggleOSD();
 
   enum SEEK_TYPE { SEEK_ABSOLUTE, SEEK_RELATIVE };
@@ -81,6 +84,7 @@ private:
 
   bool m_bShowCurrentTime;
 
+  bool m_bGroupSelectShow;
   bool m_timeCodeShow;
   unsigned int m_timeCodeTimeout;
   int m_timeCodeStamp[6];
index 28fcf1b..6fa65ae 100644 (file)
@@ -48,6 +48,10 @@ void CGUIWindowOSD::FrameMove()
     if (g_Mouse.IsActive() || g_windowManager.IsWindowActive(WINDOW_DIALOG_AUDIO_OSD_SETTINGS)
                            || g_windowManager.IsWindowActive(WINDOW_DIALOG_VIDEO_OSD_SETTINGS)
                            || g_windowManager.IsWindowActive(WINDOW_DIALOG_VIDEO_BOOKMARKS)
+                           || g_windowManager.IsWindowActive(WINDOW_DIALOG_PVR_OSD_CHANNELS)
+                           || g_windowManager.IsWindowActive(WINDOW_DIALOG_PVR_OSD_GUIDE)
+                           || g_windowManager.IsWindowActive(WINDOW_DIALOG_PVR_OSD_DIRECTOR)
+                           || g_windowManager.IsWindowActive(WINDOW_DIALOG_PVR_OSD_CUTTER)
                            || g_windowManager.IsWindowActive(WINDOW_DIALOG_OSD_TELETEXT))
       SetAutoClose(100); // enough for 10fps
   }
@@ -100,6 +104,14 @@ bool CGUIWindowOSD::OnMessage(CGUIMessage& message)
       if (pDialog && pDialog->IsDialogRunning()) pDialog->Close(true);
       pDialog = (CGUIDialog *)g_windowManager.GetWindow(WINDOW_DIALOG_VIDEO_BOOKMARKS);
       if (pDialog && pDialog->IsDialogRunning()) pDialog->Close(true);
+      pDialog = (CGUIDialog *)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_OSD_CHANNELS);
+      if (pDialog && pDialog->IsDialogRunning()) pDialog->Close(true);
+      pDialog = (CGUIDialog *)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_OSD_GUIDE);
+      if (pDialog && pDialog->IsDialogRunning()) pDialog->Close(true);
+      pDialog = (CGUIDialog *)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_OSD_DIRECTOR);
+      if (pDialog && pDialog->IsDialogRunning()) pDialog->Close(true);
+      pDialog = (CGUIDialog *)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_OSD_CUTTER);
+      if (pDialog && pDialog->IsDialogRunning()) pDialog->Close(true);
       pDialog = (CGUIDialog *)g_windowManager.GetWindow(WINDOW_DIALOG_OSD_TELETEXT);
       if (pDialog && pDialog->IsDialogRunning()) pDialog->Close(true);
     }
index f38432c..4d1e6e2 100644 (file)
@@ -56,6 +56,7 @@
 #include "GUIDialogKeyboard.h"
 #include "GUIDialogYesNo.h"
 #include "GUIDialogOK.h"
+#include "GUIDialogPVRChannelManager.h"
 #include "GUIWindowPrograms.h"
 #include "addons/Visualisation.h"
 #include "addons/AddonManager.h"
@@ -81,6 +82,8 @@
 #endif
 #include "GUIDialogAccessPoints.h"
 #include "FileSystem/Directory.h"
+#include "pvr/PVRChannels.h"
+#include "pvr/PVRManager.h"
 
 #include "FileItem.h"
 #include "GUIToggleButtonControl.h"
@@ -134,7 +137,7 @@ CGUIWindowSettingsCategory::CGUIWindowSettingsCategory(void)
   m_pOriginalImage = NULL;
   m_pOriginalEdit = NULL;
   // set the correct ID range...
-  m_idRange = 8;
+  m_idRange = 9;
   m_iScreen = 0;
   // set the network settings so that we don't reset them unnecessarily
   m_iNetworkAssignment = -1;
@@ -612,6 +615,25 @@ void CGUIWindowSettingsCategory::CreateSettings()
       pControl->AddLabel(g_localizeStrings.Get(13509), RESAMPLE_REALLYHIGH);
       pControl->SetValue(pSettingInt->GetData());
     }
+    else if (strSetting.Equals("pvrmenu.defaultguideview"))
+    {
+      CSettingInt *pSettingInt = (CSettingInt*)pSetting;
+      CGUISpinControlEx *pControl = (CGUISpinControlEx *)GetControl(GetSetting(strSetting)->GetID());
+      pControl->AddLabel(g_localizeStrings.Get(19029), GUIDE_VIEW_CHANNEL);
+      pControl->AddLabel(g_localizeStrings.Get(19030), GUIDE_VIEW_NOW);
+      pControl->AddLabel(g_localizeStrings.Get(19031), GUIDE_VIEW_NEXT);
+      pControl->AddLabel(g_localizeStrings.Get(19032), GUIDE_VIEW_TIMELINE);
+      pControl->SetValue(pSettingInt->GetData());
+    }
+    else if (strSetting.Equals("pvrplayback.startlast"))
+    {
+      CSettingInt *pSettingInt = (CSettingInt*)pSetting;
+      CGUISpinControlEx *pControl = (CGUISpinControlEx *)GetControl(GetSetting(strSetting)->GetID());
+      pControl->AddLabel(g_localizeStrings.Get(106), START_LAST_CHANNEL_OFF);
+      pControl->AddLabel(g_localizeStrings.Get(19190), START_LAST_CHANNEL_MIN);
+      pControl->AddLabel(g_localizeStrings.Get(107), START_LAST_CHANNEL_ON);
+      pControl->SetValue(pSettingInt->GetData());
+    }
   }
 
   if (m_vecSections[m_iSection]->m_strCategory == "network")
@@ -770,6 +792,11 @@ void CGUIWindowSettingsCategory::UpdateSettings()
       CGUIControl *pControl = (CGUIControl *)GetControl(pSettingControl->GetID());
       if (pControl) pControl->SetEnabled(g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE);
     }
+    else if (!strSetting.Equals("pvr.enabled") && strSetting.Left(4).Equals("pvrmanager."))
+    {
+      CGUIControl *pControl = (CGUIControl *)GetControl(pSettingControl->GetID());
+      if (pControl) pControl->SetEnabled(g_guiSettings.GetBool("pvrmanager.enabled"));
+    }
     else if (!strSetting.Equals("services.esenabled")
              && strSetting.Left(11).Equals("services.es"))
     {
@@ -1020,6 +1047,13 @@ void CGUIWindowSettingsCategory::UpdateSettings()
           pControl->SetEnabled(addon->HasSettings());
       }
     }
+    else if (!strSetting.Equals("pvrmanager.enabled")
+             && !strSetting.Equals("pvrmanager.resetdb")
+             && strSetting.Left(3).Equals("pvr"))
+    {
+      CGUIControl *pControl = (CGUIControl *)GetControl(pSettingControl->GetID());
+      if (pControl) pControl->SetEnabled(g_guiSettings.GetBool("pvrmanager.enabled"));
+    }
 #if defined(_LINUX) && !defined(__APPLE__)
     else if (strSetting.Equals("audiooutput.custompassthrough"))
     {
@@ -1590,7 +1624,7 @@ void CGUIWindowSettingsCategory::OnSettingChanged(CBaseSettingControl *pSettingC
     if (CAddonMgr::Get().GetAddon(g_guiSettings.GetString("screensaver.mode"), addon, ADDON_SCREENSAVER))
       CGUIDialogAddonSettings::ShowAndGetInput(addon);
   }
-  else if (strSetting.Equals("debug.screenshotpath") || strSetting.Equals("audiocds.recordingpath") || strSetting.Equals("subtitles.custompath"))
+  else if (strSetting.Equals("debug.screenshotpath") || strSetting.Equals("audiocds.recordingpath") || strSetting.Equals("subtitles.custompath") || strSetting.Equals("pvrmenu.iconpath"))
   {
     CSettingString *pSettingString = (CSettingString *)pSettingControl->GetSetting();
     CStdString path = g_guiSettings.GetString(strSetting,false);
@@ -1602,7 +1636,11 @@ void CGUIWindowSettingsCategory::OnSettingChanged(CBaseSettingControl *pSettingC
     UpdateSettings();
     bool bWriteOnly = true;
 
-    if (strSetting.Equals("subtitles.custompath"))
+    if (strSetting.Equals("pvrmenu.iconpath"))
+    {
+      bWriteOnly = false;
+    }
+    else if (strSetting.Equals("subtitles.custompath"))
     {
       bWriteOnly = false;
       shares = g_settings.m_videoSources;
@@ -1740,6 +1778,13 @@ void CGUIWindowSettingsCategory::OnSettingChanged(CBaseSettingControl *pSettingC
     }
 #endif
   }
+  else if (strSetting.Equals("pvrmanager.enabled"))
+  {
+    if (g_guiSettings.GetBool("pvrmanager.enabled"))
+      g_application.StartPVRManager();
+    else
+      g_application.StopPVRManager();
+  }
   else if (strSetting.Equals("masterlock.lockcode"))
   {
     // Now Prompt User to enter the old and then the new MasterCode!
@@ -1867,6 +1912,33 @@ void CGUIWindowSettingsCategory::OnSettingChanged(CBaseSettingControl *pSettingC
   {
     CUtil::DeleteVideoDatabaseDirectoryCache();
   }
+  else if (strSetting.Equals("pvrmenu.searchicons"))
+  {
+    CPVRChannels::SearchMissingChannelIcons();
+  }
+  else if (strSetting.Equals("pvrmanager.resetdb"))
+  {
+    if (CGUIDialogYesNo::ShowAndGetInput(19098, 19186, 750, 0))
+      g_PVRManager.ResetDatabase();
+  }
+  else if (strSetting.Equals("pvrepg.resetepg"))
+  {
+    if (CGUIDialogYesNo::ShowAndGetInput(19098, 19188, 750, 0))
+      g_PVRManager.ResetEPG();
+  }
+  else if (strSetting.Equals("pvrmanager.channelscan"))
+  {
+    if (CGUIDialogYesNo::ShowAndGetInput(19098, 19118, 19194, 0))
+      g_PVRManager.StartChannelScan();
+  }
+  else if (strSetting.Equals("pvrmanager.channelmanager"))
+  {
+    CGUIDialogPVRChannelManager *dialog = (CGUIDialogPVRChannelManager *)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_CHANNEL_MANAGER);
+    if (dialog)
+    {
+       dialog->DoModal();
+    }
+  }
 
   UpdateSettings();
 }
index f8c3fd7..308106e 100644 (file)
 #define CONTROL_BT_NETWORK  96
 #define CONTROL_BT_VIDEO    97
 #define CONTROL_BT_HARDWARE 98
+#define CONTROL_BT_PVR      99
 
 #define CONTROL_START       CONTROL_BT_STORAGE
-#define CONTROL_END         CONTROL_BT_HARDWARE
+#define CONTROL_END         CONTROL_BT_PVR
 
 CGUIWindowSystemInfo::CGUIWindowSystemInfo(void)
 :CGUIWindow(WINDOW_SYSTEM_INFORMATION, "SettingsSystemInfo.xml")
@@ -159,6 +160,23 @@ void CGUIWindowSystemInfo::FrameMove()
     SetControlLabel(i++, "%s: %s", 22012, SYSTEM_TOTAL_MEMORY);
     SetControlLabel(i++, "%s: %s", 158, SYSTEM_FREE_MEMORY);
   }
+  else if(m_section == CONTROL_BT_PVR)
+  {
+    SET_CONTROL_LABEL(40,g_localizeStrings.Get(19166));
+    int i = 2;
+
+    SetControlLabel(i++, "%s: %s", 19120, PVR_BACKEND_NUMBER);
+    i++; // empty line
+    SetControlLabel(i++, "%s: %s", 19012, PVR_BACKEND_NAME);
+    SetControlLabel(i++, "%s: %s", 19114, PVR_BACKEND_VERSION);
+    SetControlLabel(i++, "%s: %s", 19115, PVR_BACKEND_HOST);
+    SetControlLabel(i++, "%s: %s", 19116, PVR_BACKEND_DISKSPACE);
+    SetControlLabel(i++, "%s: %s", 19019, PVR_BACKEND_CHANNELS);
+    SetControlLabel(i++, "%s: %s", 19163, PVR_BACKEND_RECORDINGS);
+    SetControlLabel(i++, "%s: %s", 19025, PVR_BACKEND_TIMERS);
+  }
+  SET_CONTROL_LABEL(52, "XBMC "+g_infoManager.GetLabel(SYSTEM_BUILD_VERSION)+" (Compiled : "+g_infoManager.GetLabel(SYSTEM_BUILD_DATE)+")");
+
   CGUIWindow::FrameMove();
 }
 
diff --git a/xbmc/GUIWindowTV.cpp b/xbmc/GUIWindowTV.cpp
new file mode 100644 (file)
index 0000000..6f1e129
--- /dev/null
@@ -0,0 +1,1888 @@
+/*
+ *      Copyright (C) 2005-2008 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+/* Standard includes */
+#include "Application.h"
+#include "FileSystem/File.h"
+#include "FileSystem/StackDirectory.h"
+#include "GUISettings.h"
+#include "GUIWindowManager.h"
+#include "LocalizeStrings.h"
+#include "MediaManager.h"
+#include "Picture.h"
+#include "Settings.h"
+#include "TextureCache.h"
+#include "utils/log.h"
+#include "URL.h"
+
+/* Dialog windows includes */
+#include "GUIDialogFileBrowser.h"
+#include "GUIDialogProgress.h"
+#include "GUIDialogYesNo.h"
+#include "GUIDialogOK.h"
+#include "GUIDialogNumeric.h"
+#include "GUIDialogKeyboard.h"
+#include "GUIDialogPVRGuideInfo.h"
+#include "GUIDialogPVRRecordingInfo.h"
+#include "GUIDialogPVRTimerSettings.h"
+#include "GUIDialogPVRGroupManager.h"
+#include "GUIDialogPVRGuideSearch.h"
+#include "GUIUserMessages.h"
+#include "GUIEPGGridContainer.h"
+
+/* self include */
+#include "GUIWindowTV.h"
+
+/* TV control */
+#include "pvr/PVRManager.h"
+#include "pvr/PVREpgInfoTag.h"
+#include "pvr/PVRChannelGroups.h"
+#include "pvr/PVRChannelGroup.h"
+#include "pvr/PVRTimerInfoTag.h"
+#include "pvr/PVRChannelsContainer.h"
+
+using namespace std;
+
+#define CONTROL_LIST_TIMELINE        10
+#define CONTROL_LIST_CHANNELS_TV     11
+#define CONTROL_LIST_CHANNELS_RADIO  12
+#define CONTROL_LIST_RECORDINGS      13
+#define CONTROL_LIST_TIMERS          14
+#define CONTROL_LIST_GUIDE_CHANNEL   15
+#define CONTROL_LIST_GUIDE_NOW_NEXT  16
+#define CONTROL_LIST_SEARCH          17
+
+#define CONTROL_LABELHEADER          29
+#define CONTROL_LABELGROUP           30
+
+#define CONTROL_BTNGUIDE             31
+#define CONTROL_BTNCHANNELS_TV       32
+#define CONTROL_BTNCHANNELS_RADIO    33
+#define CONTROL_BTNRECORDINGS        34
+#define CONTROL_BTNTIMERS            35
+#define CONTROL_BTNSEARCH            36
+#define CONTROL_BTNGUIDE_CHANNEL     37
+#define CONTROL_BTNGUIDE_NOW         38
+#define CONTROL_BTNGUIDE_NEXT        39
+#define CONTROL_BTNGUIDE_TIMELINE    40
+
+/**
+ * \brief Class constructor
+ */
+CGUIWindowTV::CGUIWindowTV(void) : CGUIMediaWindow(WINDOW_TV, "MyTV.xml")
+{
+  m_iCurrSubTVWindow              = TV_WINDOW_UNKNOWN;
+  m_iSavedSubTVWindow             = TV_WINDOW_UNKNOWN;
+  m_iSelected_GUIDE               = 0;
+  m_iSelected_CHANNELS_TV         = 0;
+  m_iSelected_CHANNELS_RADIO      = 0;
+  m_iSelected_RECORDINGS          = 0;
+  m_iSelected_RECORDINGS_Path     = "pvr://recordings/";
+  m_iSelected_TIMERS              = 0;
+  m_iSelected_SEARCH              = 0;
+  m_iCurrentTVGroup               = -1;
+  m_iCurrentRadioGroup            = -1;
+  m_bShowHiddenChannels           = false;
+  m_bSearchStarted                = false;
+  m_bSearchConfirmed              = false;
+  m_iGuideView                    = g_guiSettings.GetInt("pvrmenu.defaultguideview");
+  m_guideGrid                     = NULL;
+  m_iSortOrder_SEARCH             = SORT_ORDER_ASC;
+  m_iSortMethod_SEARCH            = SORT_METHOD_DATE;
+  m_iSortOrder_TIMERS             = SORT_ORDER_ASC;
+  m_iSortMethod_TIMERS            = SORT_METHOD_DATE;
+}
+
+CGUIWindowTV::~CGUIWindowTV()
+{
+}
+
+bool CGUIWindowTV::OnMessage(CGUIMessage& message)
+{
+  unsigned int iControl = 0;
+  unsigned int iMessage = message.GetMessage();
+
+  if (iMessage == GUI_MSG_FOCUSED)
+  {
+    /* Get the focused control Identifier */
+    iControl = message.GetControlId();
+
+    /* Process Identifier for focused Subwindow select buttons or list item.
+     * If a new conrol becomes highlighted load his subwindow data
+     */
+    if (iControl == CONTROL_BTNGUIDE || m_iSavedSubTVWindow == TV_WINDOW_TV_PROGRAM)
+    {
+      if (m_iCurrSubTVWindow != TV_WINDOW_TV_PROGRAM)
+        UpdateGuide();
+      else
+        m_iSelected_GUIDE = m_viewControl.GetSelectedItem();
+
+      m_iCurrSubTVWindow = TV_WINDOW_TV_PROGRAM;
+    }
+    else if (iControl == CONTROL_BTNCHANNELS_TV || m_iSavedSubTVWindow == TV_WINDOW_CHANNELS_TV)
+    {
+      if (m_iCurrSubTVWindow != TV_WINDOW_CHANNELS_TV)
+        UpdateChannelsTV();
+      else
+        m_iSelected_CHANNELS_TV = m_viewControl.GetSelectedItem();
+
+      m_iCurrSubTVWindow = TV_WINDOW_CHANNELS_TV;
+    }
+    else if (iControl == CONTROL_BTNCHANNELS_RADIO || m_iSavedSubTVWindow == TV_WINDOW_CHANNELS_RADIO)
+    {
+      if (m_iCurrSubTVWindow != TV_WINDOW_CHANNELS_RADIO)
+        UpdateChannelsRadio();
+      else
+        m_iSelected_CHANNELS_RADIO = m_viewControl.GetSelectedItem();
+
+      m_iCurrSubTVWindow = TV_WINDOW_CHANNELS_RADIO;
+    }
+    else if (iControl == CONTROL_BTNRECORDINGS || m_iSavedSubTVWindow == TV_WINDOW_RECORDINGS)
+    {
+      if (m_iCurrSubTVWindow != TV_WINDOW_RECORDINGS)
+        UpdateRecordings();
+      else
+      {
+        m_iSelected_RECORDINGS_Path = m_vecItems->m_strPath;
+        m_iSelected_RECORDINGS = m_viewControl.GetSelectedItem();
+      }
+
+      m_iCurrSubTVWindow = TV_WINDOW_RECORDINGS;
+    }
+    else if (iControl == CONTROL_BTNTIMERS || m_iSavedSubTVWindow == TV_WINDOW_TIMERS)
+    {
+      if (m_iCurrSubTVWindow != TV_WINDOW_TIMERS)
+        UpdateTimers();
+      else
+        m_iSelected_TIMERS =  m_viewControl.GetSelectedItem();
+
+      m_iCurrSubTVWindow = TV_WINDOW_TIMERS;
+    }
+    else if (iControl == CONTROL_BTNSEARCH || m_iSavedSubTVWindow == TV_WINDOW_SEARCH)
+    {
+      if (m_iCurrSubTVWindow != TV_WINDOW_SEARCH)
+        UpdateSearch();
+      else
+        m_iSelected_SEARCH = m_viewControl.GetSelectedItem();
+
+      m_iCurrSubTVWindow = TV_WINDOW_SEARCH;
+    }
+
+    if (m_iSavedSubTVWindow != TV_WINDOW_UNKNOWN)
+      m_iSavedSubTVWindow = TV_WINDOW_UNKNOWN;
+  }
+  else if (iMessage == GUI_MSG_CLICKED)
+  {
+    iControl = message.GetSenderId();
+
+    if (iControl == CONTROL_BTNGUIDE)
+    {
+      m_iGuideView++;
+
+      if (m_iGuideView > GUIDE_VIEW_TIMELINE)
+      {
+        m_iGuideView = 0;
+      }
+
+      UpdateGuide();
+      return true;
+    }
+    else if (iControl == CONTROL_BTNGUIDE_CHANNEL)
+    {
+      m_iGuideView = GUIDE_VIEW_CHANNEL;
+      UpdateGuide();
+      return true;
+    }
+    else if (iControl == CONTROL_BTNGUIDE_NOW)
+    {
+      m_iGuideView = GUIDE_VIEW_NOW;
+      UpdateGuide();
+      return true;
+    }
+    else if (iControl == CONTROL_BTNGUIDE_NEXT)
+    {
+      m_iGuideView = GUIDE_VIEW_NEXT;
+      UpdateGuide();
+      return true;
+    }
+    else if (iControl == CONTROL_BTNGUIDE_TIMELINE)
+    {
+      m_iGuideView = GUIDE_VIEW_TIMELINE;
+      UpdateGuide();
+      return true;
+    }
+    else if (iControl == CONTROL_BTNCHANNELS_TV)
+    {
+      m_iCurrentTVGroup = PVRChannelGroupsTV.GetNextGroupID(m_iCurrentTVGroup);
+      UpdateChannelsTV();
+      return true;
+    }
+    else if (iControl == CONTROL_BTNCHANNELS_RADIO)
+    {
+      m_iCurrentRadioGroup = PVRChannelGroupsRadio.GetNextGroupID(m_iCurrentRadioGroup);
+      UpdateChannelsRadio();
+      return true;
+    }
+    else if (iControl == CONTROL_BTNRECORDINGS)
+    {
+      g_PVRManager.TriggerRecordingsUpdate();
+      UpdateRecordings();
+      return true;
+    }
+    else if (iControl == CONTROL_BTNTIMERS)
+    {
+      PVRTimers.Update();
+      UpdateTimers();
+      return true;
+    }
+    else if (iControl == CONTROL_BTNSEARCH)
+    {
+      ShowSearchResults();
+    }
+    else if (iControl == CONTROL_LIST_TIMELINE ||
+             iControl == CONTROL_LIST_GUIDE_CHANNEL ||
+             iControl == CONTROL_LIST_GUIDE_NOW_NEXT)
+    {
+      int iAction = message.GetParam1();
+      int iItem = m_viewControl.GetSelectedItem();
+
+      /* Check file item is in list range and get his pointer */
+      if (iItem < 0 || iItem >= (int)m_vecItems->Size()) return true;
+
+      CFileItemPtr pItem = m_vecItems->Get(iItem);
+
+      /* Process actions */
+      if ((iAction == ACTION_SELECT_ITEM) || (iAction == ACTION_SHOW_INFO || iAction == ACTION_MOUSE_LEFT_CLICK))
+      {
+        /* Show information Dialog */
+        ShowEPGInfo(pItem.get());
+        return true;
+      }
+      else if (iAction == ACTION_CONTEXT_MENU || iAction == ACTION_MOUSE_RIGHT_CLICK)
+      {
+        /* Show Contextmenu */
+        OnPopupMenu(iItem);
+        return true;
+      }
+      else if (iAction == ACTION_RECORD)
+      {
+        if (pItem->GetEPGInfoTag()->ChannelTag()->ChannelNumber() != -1)
+        {
+          if (pItem->GetEPGInfoTag()->Timer() == NULL)
+          {
+            // prompt user for confirmation of channel record
+            CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
+            if (!pDialog)
+              return false;
+
+            pDialog->SetHeading(264);
+            pDialog->SetLine(0, "");
+            pDialog->SetLine(1, pItem->GetEPGInfoTag()->Title());
+            pDialog->SetLine(2, "");
+            pDialog->DoModal();
+
+            if (!pDialog->IsConfirmed())
+              return true;
+
+            CPVRTimerInfoTag *newtimer = CPVRTimerInfoTag::CreateFromEpg(*pItem->GetEPGInfoTag());
+            CFileItem *item = new CFileItem(*newtimer);
+
+            if (CPVRTimers::AddTimer(*item))
+              PVRTimers.Update();
+          }
+          else
+          {
+            CGUIDialogOK::ShowAndGetInput(19033,19034,0,0);
+          }
+        }
+      }
+      else if (iAction == ACTION_PLAY)
+      {
+        const CPVRChannels *channels = g_PVRChannels.Get(pItem->GetEPGInfoTag()->ChannelTag()->IsRadio());
+        if (!g_application.PlayFile(CFileItem(*channels->at(pItem->GetEPGInfoTag()->ChannelTag()->ChannelNumber()-1))))
+        {
+          CGUIDialogOK::ShowAndGetInput(19033,0,19035,0);
+          return false;
+        }
+        return true;
+      }
+    }
+    else if ((iControl == CONTROL_LIST_CHANNELS_TV) || (iControl == CONTROL_LIST_CHANNELS_RADIO))
+    {
+      /* Get currently performed action */
+      int iAction = message.GetParam1();
+
+      /* Get currently selected item from file list */
+      int iItem = m_viewControl.GetSelectedItem();
+
+      /* Check file item is in list range and get his pointer */
+      if (iItem < 0 || iItem >= (int)m_vecItems->Size()) return true;
+
+      CFileItemPtr pItem = m_vecItems->Get(iItem);
+
+      /* Process actions */
+      if (iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK || iAction == ACTION_PLAY)
+      {
+        /* Check if "Add channel..." entry is pressed by OK, if yes
+           create a new channel and open settings dialog, otherwise
+           open channel with player */
+        if (pItem->m_strPath == "pvr://channels/.add.channel")
+        {
+          CGUIDialogOK::ShowAndGetInput(19033,0,19038,0);
+        }
+        else
+        {
+          if (iControl == CONTROL_LIST_CHANNELS_TV)
+            g_PVRManager.SetPlayingGroup(m_iCurrentTVGroup);
+          if (iControl == CONTROL_LIST_CHANNELS_RADIO)
+            g_PVRManager.SetPlayingGroup(m_iCurrentRadioGroup);
+
+          /* Open tv channel by Player and return */
+          return PlayFile(pItem.get(), g_guiSettings.GetBool("pvrplayback.playminimized"));
+        }
+      }
+      else if (iAction == ACTION_CONTEXT_MENU || iAction == ACTION_MOUSE_RIGHT_CLICK)
+      {
+        //contextmenu
+        OnPopupMenu(iItem);
+        return true;
+      }
+      else if (iAction == ACTION_SHOW_INFO)
+      {
+        /* Show information Dialog */
+        ShowEPGInfo(pItem.get());
+        return true;
+      }
+      else if (iAction == ACTION_DELETE_ITEM)
+      {
+        /* Check if entry is a valid deleteable channel */
+        int iChannel = pItem->GetPVRChannelInfoTag()->ChannelNumber();
+
+        if (iChannel != -1)
+        {
+          CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
+
+          if (pDialog)
+          {
+            pDialog->SetHeading(19039);
+            pDialog->SetLine(0, "");
+            pDialog->SetLine(1, pItem->GetPVRChannelInfoTag()->ChannelName());
+            pDialog->SetLine(2, "");
+            pDialog->DoModal();
+
+            if (!pDialog->IsConfirmed()) return false;
+
+            if (m_iCurrSubTVWindow == TV_WINDOW_CHANNELS_TV)
+            {
+              ((CPVRChannels *) g_PVRChannels.GetTV())->HideChannel(pItem->GetPVRChannelInfoTag(), true);
+              UpdateChannelsTV();
+            }
+            else if (m_iCurrSubTVWindow == TV_WINDOW_CHANNELS_RADIO)
+            {
+              ((CPVRChannels *) g_PVRChannels.GetRadio())->HideChannel(pItem->GetPVRChannelInfoTag(), true);
+              UpdateChannelsRadio();
+            }
+          }
+          return true;
+        }
+      }
+    }
+    else if (iControl == CONTROL_LIST_RECORDINGS)
+    {
+      /* Get currently performed action */
+      int iAction = message.GetParam1();
+
+      /* Get currently selected item from file list */
+      int iItem = m_viewControl.GetSelectedItem();
+
+      /* Check file item is in list range and get his pointer */
+      if (iItem < 0 || iItem >= (int)m_vecItems->Size()) return true;
+
+      CFileItemPtr pItem = m_vecItems->Get(iItem);
+      if (pItem->m_bIsFolder || pItem->IsParentFolder())
+        return OnClick(iItem);
+
+      /* Process actions */
+      if (iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK || iAction == ACTION_PLAY)
+      {
+        /* Open recording with Player and return */
+        return PlayFile(pItem.get(), false);
+      }
+      else if (iAction == ACTION_CONTEXT_MENU || iAction == ACTION_MOUSE_RIGHT_CLICK)
+      {
+        //contextmenu
+        OnPopupMenu(iItem);
+        return true;
+      }
+      else if (iAction == ACTION_SHOW_INFO)
+      {
+        /* Show information Dialog */
+        ShowRecordingInfo(pItem.get());
+        return true;
+      }
+      else if (iAction == ACTION_DELETE_ITEM)
+      {
+        /* Check if entry is a valid deleteable record */
+        if (pItem->GetPVRRecordingInfoTag()->ClientIndex() != -1)
+        {
+          // prompt user for confirmation of record deletion
+          CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
+          if (!pDialog)
+            return false;
+
+          pDialog->SetHeading(122);
+          pDialog->SetLine(0, 19043);
+          pDialog->SetLine(1, "");
+          pDialog->SetLine(2, pItem->GetPVRRecordingInfoTag()->Title());
+          pDialog->DoModal();
+
+          if (pDialog->IsConfirmed())
+          {
+            if (CPVRRecordings::DeleteRecording(*pItem))
+            {
+              PVRRecordings.Update(true);
+              UpdateRecordings();
+            }
+          }
+          return true;
+        }
+      }
+    }
+    else if (iControl == CONTROL_LIST_TIMERS)
+    {
+      /* Get currently performed action */
+      int iAction = message.GetParam1();
+
+      /* Get currently selected item from file list */
+      int iItem = m_viewControl.GetSelectedItem();
+
+      /* Check file item is in list range and get his pointer */
+      if (iItem < 0 || iItem >= (int)m_vecItems->Size()) return true;
+
+      CFileItemPtr pItem = m_vecItems->Get(iItem);
+
+      /* Process actions */
+      if (iAction == ACTION_SHOW_INFO || iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK)
+      {
+        /* Check if "Add timer..." entry is pressed by OK, if yes
+           create a new timer and open settings dialog, otherwise
+           open settings for selected timer entry */
+        if (pItem->m_strPath == "pvr://timers/add.timer")
+        {
+          CPVRTimerInfoTag *newtimer = CPVRTimerInfoTag::InstantTimer();
+          if (!newtimer)
+          {
+            CLog::Log(LOGWARNING,"%s - could not create instant timer",
+                __FUNCTION__);
+            return false;
+          }
+          CFileItem *item = new CFileItem(*newtimer);
+
+          if (ShowTimerSettings(item))
+          {
+            /* Add timer to backend */
+            CPVRTimers::AddTimer(*item);
+            UpdateTimers();
+          }
+        }
+        else
+        {
+          CFileItem fileitem(*pItem);
+
+          if (ShowTimerSettings(&fileitem))
+          {
+            /* Update timer on pvr backend */
+            CPVRTimers::UpdateTimer(fileitem);
+            UpdateTimers();
+          }
+        }
+
+        return true;
+      }
+      else if (iAction == ACTION_CONTEXT_MENU || iAction == ACTION_MOUSE_RIGHT_CLICK)
+      {
+        //contextmenu
+        OnPopupMenu(iItem);
+        return true;
+      }
+      else if (iAction == ACTION_DELETE_ITEM)
+      {
+        /* Check if entry is a valid deleteable timer */
+        if (pItem->GetPVRTimerInfoTag()->ClientIndex() != -1)
+        {
+          // prompt user for confirmation of timer deletion
+          CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
+          if (!pDialog)
+            return false;
+
+          pDialog->SetHeading(122);
+          pDialog->SetLine(0, 19040);
+          pDialog->SetLine(1, "");
+          pDialog->SetLine(2, pItem->GetPVRTimerInfoTag()->Title());
+          pDialog->DoModal();
+
+          if (pDialog->IsConfirmed())
+          {
+            CPVRTimers::DeleteTimer(*pItem);
+            UpdateTimers();
+          }
+          return true;
+        }
+      }
+    }
+    else if (iControl == CONTROL_LIST_SEARCH)
+    {
+      /* Get currently performed action */
+      int iAction = message.GetParam1();
+
+      /* Get currently selected item from file list */
+      int iItem = m_viewControl.GetSelectedItem();
+
+      /* Check file item is in list range and get his pointer */
+      if (iItem < 0 || iItem >= (int)m_vecItems->Size()) return true;
+
+      CFileItemPtr pItem = m_vecItems->Get(iItem);
+
+      /* Process actions */
+      if (iAction == ACTION_SHOW_INFO || iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK)
+      {
+        if (pItem->m_strPath == "pvr://guide/searchresults/empty.epg")
+          ShowSearchResults();
+        else
+          ShowEPGInfo(pItem.get());
+
+        return true;
+      }
+      else if (iAction == ACTION_CONTEXT_MENU || iAction == ACTION_MOUSE_RIGHT_CLICK)
+      {
+        /* Show context menu */
+        OnPopupMenu(iItem);
+        return true;
+      }
+      else if (iAction == ACTION_RECORD)
+      {
+        if (pItem->GetEPGInfoTag()->ChannelTag()->ChannelNumber() != -1)
+        {
+          if (pItem->GetEPGInfoTag()->Timer() == NULL)
+          {
+            // prompt user for confirmation of channel record
+            CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
+            if (!pDialog)
+              return false;
+
+            CPVREpgInfoTag *epgTag = pItem->GetEPGInfoTag();
+
+            pDialog->SetHeading(264);
+            pDialog->SetLine(0, "");
+            pDialog->SetLine(1, epgTag->Title());
+            pDialog->SetLine(2, "");
+            pDialog->DoModal();
+
+            if (!pDialog->IsConfirmed())
+              return true;
+
+
+            CPVRTimerInfoTag *newtimer = CPVRTimerInfoTag::CreateFromEpg(*epgTag);
+            CFileItem *item = new CFileItem(*newtimer);
+
+            if (CPVRTimers::AddTimer(*item))
+              PVRTimers.Update();
+          }
+          else
+            CGUIDialogOK::ShowAndGetInput(19033,19034,0,0);
+        }
+      }
+    }
+  }
+  return CGUIMediaWindow::OnMessage(message);
+}
+
+bool CGUIWindowTV::OnAction(const CAction &action)
+{
+  if (action.GetID() == ACTION_PREVIOUS_MENU)
+  {
+    g_windowManager.PreviousWindow();
+    return true;
+  }
+  else if (action.GetID() == ACTION_PARENT_DIR)
+  {
+    if (m_iCurrSubTVWindow == TV_WINDOW_RECORDINGS && m_vecItems->m_strPath != "pvr://recordings/")
+      GoParentFolder();
+    else
+      g_windowManager.PreviousWindow();
+
+    return true;
+  }
+  return CGUIMediaWindow::OnAction(action);
+}
+
+void CGUIWindowTV::OnWindowLoaded()
+{
+  CGUIMediaWindow::OnWindowLoaded();
+  m_viewControl.Reset();
+  m_viewControl.SetParentWindow(GetID());
+  m_viewControl.AddView(GetControl(CONTROL_LIST_TIMELINE));
+  m_viewControl.AddView(GetControl(CONTROL_LIST_CHANNELS_TV));
+  m_viewControl.AddView(GetControl(CONTROL_LIST_CHANNELS_RADIO));
+  m_viewControl.AddView(GetControl(CONTROL_LIST_RECORDINGS));
+  m_viewControl.AddView(GetControl(CONTROL_LIST_TIMERS));
+  m_viewControl.AddView(GetControl(CONTROL_LIST_GUIDE_CHANNEL));
+  m_viewControl.AddView(GetControl(CONTROL_LIST_GUIDE_NOW_NEXT));
+  m_viewControl.AddView(GetControl(CONTROL_LIST_SEARCH));
+}
+
+void CGUIWindowTV::OnWindowUnload()
+{
+  /* Save current Subwindow selected list position */
+  if (m_iCurrSubTVWindow == TV_WINDOW_TV_PROGRAM)
+    m_iSelected_GUIDE = m_viewControl.GetSelectedItem();
+  else if (m_iCurrSubTVWindow == TV_WINDOW_CHANNELS_TV)
+    m_iSelected_CHANNELS_TV = m_viewControl.GetSelectedItem();
+  else if (m_iCurrSubTVWindow == TV_WINDOW_CHANNELS_RADIO)
+    m_iSelected_CHANNELS_RADIO = m_viewControl.GetSelectedItem();
+  else if (m_iCurrSubTVWindow == TV_WINDOW_RECORDINGS)
+  {
+    m_iSelected_RECORDINGS_Path = m_vecItems->m_strPath;
+    m_iSelected_RECORDINGS = m_viewControl.GetSelectedItem();
+  }
+  else if (m_iCurrSubTVWindow == TV_WINDOW_TIMERS)
+    m_iSelected_TIMERS = m_viewControl.GetSelectedItem();
+  else if (m_iCurrSubTVWindow == TV_WINDOW_SEARCH)
+    m_iSelected_SEARCH = m_viewControl.GetSelectedItem();
+
+  m_iSavedSubTVWindow = m_iCurrSubTVWindow;
+  m_iCurrSubTVWindow  = TV_WINDOW_UNKNOWN;
+
+  m_viewControl.Reset();
+  CGUIMediaWindow::OnWindowUnload();
+  return;
+}
+
+void CGUIWindowTV::OnInitWindow()
+{
+  /* Make sure we have active running clients, otherwise return to
+   * Previous Window.
+   */
+  if (!g_PVRManager.HaveActiveClients())
+  {
+    g_windowManager.PreviousWindow();
+    CGUIDialogOK::ShowAndGetInput(19033,0,19045,19044);
+    return;
+  }
+
+  /* This is a bad way but the SetDefaults function use the first and last
+   * epg date which is not available on construction time, thats why we
+   * but it to Window initialization.
+   */
+  if (!m_bSearchStarted)
+  {
+    m_bSearchStarted = true;
+    m_searchfilter.Reset();
+  }
+
+  if (m_iSavedSubTVWindow == TV_WINDOW_TV_PROGRAM)
+    m_viewControl.SetCurrentView(CONTROL_LIST_GUIDE_CHANNEL);
+  else if (m_iSavedSubTVWindow == TV_WINDOW_CHANNELS_TV)
+    m_viewControl.SetCurrentView(CONTROL_LIST_CHANNELS_TV);
+  else if (m_iSavedSubTVWindow == TV_WINDOW_CHANNELS_RADIO)
+    m_viewControl.SetCurrentView(CONTROL_LIST_CHANNELS_RADIO);
+  else if (m_iSavedSubTVWindow == TV_WINDOW_RECORDINGS)
+    m_viewControl.SetCurrentView(CONTROL_LIST_RECORDINGS);
+  else if (m_iSavedSubTVWindow == TV_WINDOW_TIMERS)
+    m_viewControl.SetCurrentView(CONTROL_LIST_TIMERS);
+  else if (m_iSavedSubTVWindow == TV_WINDOW_SEARCH)
+    m_viewControl.SetCurrentView(CONTROL_LIST_SEARCH);
+
+  CGUIMediaWindow::OnInitWindow();
+  return;
+}
+
+void CGUIWindowTV::GetContextButtons(int itemNumber, CContextButtons &buttons)
+{
+  /* Check file item is in list range and get his pointer */
+  if (itemNumber < 0 || itemNumber >= (int)m_vecItems->Size()) return;
+
+  CFileItemPtr pItem = m_vecItems->Get(itemNumber);
+
+  if (m_iCurrSubTVWindow == TV_WINDOW_CHANNELS_TV || m_iCurrSubTVWindow == TV_WINDOW_CHANNELS_RADIO)
+  {
+    if (itemNumber < 0 || itemNumber >= m_vecItems->Size())
+      return;
+
+    /* check that file item is in list and get his pointer*/
+    if (pItem->m_strPath == "pvr://channels/.add.channel")
+    {
+      /* If yes show only "New Channel" on context menu */
+      buttons.Add(CONTEXT_BUTTON_ADD, 19046);               /* Add new channel */
+    }
+    else
+    {
+      buttons.Add(CONTEXT_BUTTON_INFO, 19047);              /* Channel info button */
+      buttons.Add(CONTEXT_BUTTON_FIND, 19003);              /* Find similar program */
+      buttons.Add(CONTEXT_BUTTON_PLAY_ITEM, 19000);         /* switch to channel */
+      buttons.Add(CONTEXT_BUTTON_SET_THUMB, 20019);         /* Set icon */
+      buttons.Add(CONTEXT_BUTTON_GROUP_MANAGER, 19048);     /* Group managment */
+      buttons.Add(CONTEXT_BUTTON_HIDE, m_bShowHiddenChannels ? 19049 : 19054);        /* HIDE CHANNEL */
+
+      if (m_vecItems->Size() > 1 && !m_bShowHiddenChannels)
+        buttons.Add(CONTEXT_BUTTON_MOVE, 116);              /* Move channel up or down */
+
+      if ((m_iCurrSubTVWindow == TV_WINDOW_CHANNELS_TV && g_PVRChannels.GetTV()->GetNumHiddenChannels() > 0) ||
+          (m_iCurrSubTVWindow == TV_WINDOW_CHANNELS_RADIO && g_PVRChannels.GetRadio()->GetNumHiddenChannels() > 0) ||
+          m_bShowHiddenChannels)
+        buttons.Add(CONTEXT_BUTTON_SHOW_HIDDEN, m_bShowHiddenChannels ? 19050 : 19051);  /* SHOW HIDDEN CHANNELS */
+
+      CGUIMediaWindow::GetContextButtons(itemNumber, buttons);
+
+      if (g_PVRManager.HaveMenuHooks(pItem->GetPVRChannelInfoTag()->ClientID()))
+        buttons.Add(CONTEXT_BUTTON_MENU_HOOKS, 19195);        /* PVR client specific action */
+    }
+  }
+  else if (m_iCurrSubTVWindow == TV_WINDOW_RECORDINGS)           /* Add recordings context buttons */
+  {
+    buttons.Add(CONTEXT_BUTTON_INFO, 19053);              /* Get Information of this recording */
+    buttons.Add(CONTEXT_BUTTON_FIND, 19003);              /* Find similar program */
+    buttons.Add(CONTEXT_BUTTON_PLAY_ITEM, 12021);         /* Play this recording */
+//            buttons.Add(CONTEXT_BUTTON_RESUME_ITEM, 12022);
+    // Update sort by button
+//    if (m_guiState->GetSortMethod()!=SORT_METHOD_NONE)
+//    {
+//      CStdString sortLabel;
+//      sortLabel.Format(g_localizeStrings.Get(550).c_str(), g_localizeStrings.Get(m_guiState->GetSortMethodLabel()).c_str());
+//      buttons.Add(CONTEXT_BUTTON_SORTBY, sortLabel);   /* Sort method */
+//
+//      if (m_guiState->GetDisplaySortOrder()==SORT_ORDER_ASC)
+//        buttons.Add(CONTEXT_BUTTON_SORTASC, 584);        /* Sort up or down */
+//      else
+//        buttons.Add(CONTEXT_BUTTON_SORTASC, 585);        /* Sort up or down */
+//    }
+
+    buttons.Add(CONTEXT_BUTTON_RENAME, 118);              /* Rename this recording */
+    buttons.Add(CONTEXT_BUTTON_DELETE, 117);              /* Delete this recording */
+  }
+  else if (m_iCurrSubTVWindow == TV_WINDOW_TIMERS)           /* Add timer context buttons */
+  {
+    /* Check for a empty file item list, means only a
+        file item with the name "Add timer..." is present */
+    if (pItem->m_strPath == "pvr://timers/add.timer")
+    {
+      /* If yes show only "New Timer" on context menu */
+      buttons.Add(CONTEXT_BUTTON_ADD, 19056);             /* NEW TIMER */
+      if (m_vecItems->Size() > 1)
+      {
+        buttons.Add(CONTEXT_BUTTON_SORTBY_NAME, 103);     /* Sort by Name */
+        buttons.Add(CONTEXT_BUTTON_SORTBY_DATE, 104);     /* Sort by Date */
+      }
+    }
+    else
+    {
+      /* If any timers are present show more */
+      buttons.Add(CONTEXT_BUTTON_EDIT, 19057);            /* Edit Timer */
+      buttons.Add(CONTEXT_BUTTON_ADD, 19056);             /* NEW TIMER */
+      buttons.Add(CONTEXT_BUTTON_ACTIVATE, 19058);        /* ON/OFF */
+      buttons.Add(CONTEXT_BUTTON_RENAME, 118);            /* Rename Timer */
+      buttons.Add(CONTEXT_BUTTON_DELETE, 117);            /* Delete Timer */
+      buttons.Add(CONTEXT_BUTTON_SORTBY_NAME, 103);       /* Sort by Name */
+      buttons.Add(CONTEXT_BUTTON_SORTBY_DATE, 104);       /* Sort by Date */
+      if (g_PVRManager.HaveMenuHooks(pItem->GetPVRTimerInfoTag()->ClientID()))
+        buttons.Add(CONTEXT_BUTTON_MENU_HOOKS, 19195);    /* PVR client specific action */
+    }
+  }
+  else if (m_iCurrSubTVWindow == TV_WINDOW_TV_PROGRAM)
+  {
+    if (pItem->GetEPGInfoTag()->End() > CDateTime::GetCurrentDateTime())
+    {
+      if (pItem->GetEPGInfoTag()->Timer() == NULL)
+      {
+        if (pItem->GetEPGInfoTag()->Start() < CDateTime::GetCurrentDateTime())
+        {
+          buttons.Add(CONTEXT_BUTTON_START_RECORD, 264);             /* RECORD programme */
+        }
+        else
+        {
+          buttons.Add(CONTEXT_BUTTON_START_RECORD, 19061);
+        }
+      }
+      else
+      {
+        if (pItem->GetEPGInfoTag()->Start() < CDateTime::GetCurrentDateTime())
+        {
+          buttons.Add(CONTEXT_BUTTON_STOP_RECORD, 19059);
+        }
+        else
+        {
+          buttons.Add(CONTEXT_BUTTON_STOP_RECORD, 19060);
+        }
+      }
+    }
+
+    buttons.Add(CONTEXT_BUTTON_INFO, 658);               /* Epg info button */
+    buttons.Add(CONTEXT_BUTTON_PLAY_ITEM, 19000);        /* Switch channel */
+    buttons.Add(CONTEXT_BUTTON_FIND, 19003);             /* Find similar program */
+    if (m_iGuideView == GUIDE_VIEW_TIMELINE)
+    {
+      buttons.Add(CONTEXT_BUTTON_BEGIN, 19063);          /* Go to begin */
+      buttons.Add(CONTEXT_BUTTON_END, 19064);            /* Go to end */
+    }
+    if (g_PVRManager.HaveMenuHooks(pItem->GetEPGInfoTag()->ChannelTag()->ClientID()))
+      buttons.Add(CONTEXT_BUTTON_MENU_HOOKS, 19195);        /* PVR client specific action */
+  }
+  else if (m_iCurrSubTVWindow == TV_WINDOW_SEARCH)
+  {
+    if (pItem->GetLabel() != g_localizeStrings.Get(19027))
+    {
+      if (pItem->GetEPGInfoTag()->End() > CDateTime::GetCurrentDateTime())
+      {
+        if (pItem->GetEPGInfoTag()->Timer() == NULL)
+        {
+          if (pItem->GetEPGInfoTag()->Start() < CDateTime::GetCurrentDateTime())
+            buttons.Add(CONTEXT_BUTTON_START_RECORD, 264);             /* RECORD programme */
+          else
+            buttons.Add(CONTEXT_BUTTON_START_RECORD, 19061);    /* Create a Timer */
+        }
+        else
+        {
+          if (pItem->GetEPGInfoTag()->Start() < CDateTime::GetCurrentDateTime())
+            buttons.Add(CONTEXT_BUTTON_STOP_RECORD, 19059);     /* Stop recording */
+          else
+            buttons.Add(CONTEXT_BUTTON_STOP_RECORD, 19060);     /* Delete Timer */
+        }
+      }
+
+      buttons.Add(CONTEXT_BUTTON_INFO, 658);              /* Epg info button */
+      buttons.Add(CONTEXT_BUTTON_SORTBY_CHANNEL, 19062);  /* Sort by channel */
+      buttons.Add(CONTEXT_BUTTON_SORTBY_NAME, 103);       /* Sort by Name */
+      buttons.Add(CONTEXT_BUTTON_SORTBY_DATE, 104);       /* Sort by Date */
+      buttons.Add(CONTEXT_BUTTON_CLEAR, 20375);           /* Clear search results */
+      if (g_PVRManager.HaveMenuHooks(pItem->GetEPGInfoTag()->ChannelTag()->ClientID()))
+        buttons.Add(CONTEXT_BUTTON_MENU_HOOKS, 19195);        /* PVR client specific action */
+    }
+  }
+}
+
+bool CGUIWindowTV::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
+{
+  /* Check file item is in list range and get his pointer */
+  if (itemNumber < 0 || itemNumber >= (int)m_vecItems->Size()) return false;
+
+  CFileItemPtr pItem = m_vecItems->Get(itemNumber);
+
+  if (button == CONTEXT_BUTTON_PLAY_ITEM)
+  {
+    if (m_iCurrSubTVWindow == TV_WINDOW_RECORDINGS)
+    {
+      return PlayFile(pItem.get(), false);
+    }
+
+    if (m_iCurrSubTVWindow == TV_WINDOW_CHANNELS_TV ||
+        m_iCurrSubTVWindow == TV_WINDOW_CHANNELS_RADIO)
+    {
+      if (m_iCurrSubTVWindow == TV_WINDOW_CHANNELS_TV)
+        g_PVRManager.SetPlayingGroup(m_iCurrentTVGroup);
+      if (m_iCurrSubTVWindow == TV_WINDOW_CHANNELS_RADIO)
+        g_PVRManager.SetPlayingGroup(m_iCurrentRadioGroup);
+
+      return PlayFile(pItem.get(), g_guiSettings.GetBool("pvrplayback.playminimized"));
+    }
+    else if (m_iCurrSubTVWindow == TV_WINDOW_TV_PROGRAM)
+    {
+      const CPVRChannels *channels = g_PVRChannels.Get(pItem->GetEPGInfoTag()->ChannelTag()->IsRadio());
+      if (!g_application.PlayFile(CFileItem(*channels->at(pItem->GetEPGInfoTag()->ChannelTag()->ChannelNumber()-1))))
+      {
+        CGUIDialogOK::ShowAndGetInput(19033,0,19035,0);
+        return false;
+      }
+      return true;
+    }
+  }
+  else if (button == CONTEXT_BUTTON_MOVE)
+  {
+    CStdString strIndex;
+    strIndex.Format("%i", pItem->GetPVRChannelInfoTag()->ChannelNumber());
+    CGUIDialogNumeric::ShowAndGetNumber(strIndex, g_localizeStrings.Get(19052));
+    int newIndex = atoi(strIndex.c_str());
+
+    if (newIndex != pItem->GetPVRChannelInfoTag()->ChannelNumber())
+    {
+      if (m_iCurrSubTVWindow == TV_WINDOW_CHANNELS_TV)
+      {
+        ((CPVRChannels *) g_PVRChannels.GetTV())->MoveChannel(pItem->GetPVRChannelInfoTag()->ChannelNumber(), newIndex);
+        UpdateChannelsTV();
+      }
+      else if (m_iCurrSubTVWindow == TV_WINDOW_CHANNELS_RADIO)
+      {
+        ((CPVRChannels *) g_PVRChannels.GetRadio())->MoveChannel(pItem->GetPVRChannelInfoTag()->ChannelNumber(), newIndex);
+        UpdateChannelsRadio();
+      }
+    }
+  }
+  else if (button == CONTEXT_BUTTON_HIDE)
+  {
+    // prompt user for confirmation of channel hide
+    CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
+
+    if (pDialog)
+    {
+      pDialog->SetHeading(19039);
+      pDialog->SetLine(0, "");
+      pDialog->SetLine(1, pItem->GetPVRChannelInfoTag()->ChannelName());
+      pDialog->SetLine(2, "");
+      pDialog->DoModal();
+
+      if (!pDialog->IsConfirmed()) return false;
+
+      if (m_iCurrSubTVWindow == TV_WINDOW_CHANNELS_TV)
+      {
+        ((CPVRChannels *) g_PVRChannels.GetTV())->HideChannel(pItem->GetPVRChannelInfoTag());
+        UpdateChannelsTV();
+      }
+      else if (m_iCurrSubTVWindow == TV_WINDOW_CHANNELS_RADIO)
+      {
+        ((CPVRChannels *) g_PVRChannels.GetRadio())->HideChannel(pItem->GetPVRChannelInfoTag());
+        UpdateChannelsRadio();
+      }
+    }
+  }
+  else if (button == CONTEXT_BUTTON_SHOW_HIDDEN)
+  {
+    if (m_bShowHiddenChannels)
+      m_bShowHiddenChannels = false;
+    else
+      m_bShowHiddenChannels = true;
+
+    if (m_iCurrSubTVWindow == TV_WINDOW_CHANNELS_TV)
+      UpdateChannelsTV();
+    else if (m_iCurrSubTVWindow == TV_WINDOW_CHANNELS_RADIO)
+      UpdateChannelsRadio();
+  }
+  else if (button == CONTEXT_BUTTON_SET_THUMB)
+  {
+    if (g_settings.GetCurrentProfile().canWriteSources() && !g_passwordManager.IsProfileLockUnlocked())
+      return false;
+    else if (!g_passwordManager.IsMasterLockUnlocked(true))
+      return false;
+
+    // setup our thumb list
+    CFileItemList items;
+
+    // add the current thumb, if available
+    if (!pItem->GetPVRChannelInfoTag()->IconPath().IsEmpty())
+    {
+      CFileItemPtr current(new CFileItem("thumb://Current", false));
+      current->SetThumbnailImage(pItem->GetPVRChannelInfoTag()->IconPath());
+      current->SetLabel(g_localizeStrings.Get(20016));
+      items.Add(current);
+    }
+    else if (pItem->HasThumbnail())
+    { // already have a thumb that the share doesn't know about - must be a local one, so we mayaswell reuse it.
+      CFileItemPtr current(new CFileItem("thumb://Current", false));
+      current->SetThumbnailImage(pItem->GetThumbnailImage());
+      current->SetLabel(g_localizeStrings.Get(20016));
+      items.Add(current);
+    }
+
+    // and add a "no thumb" entry as well
+    CFileItemPtr nothumb(new CFileItem("thumb://None", false));
+    nothumb->SetIconImage(pItem->GetIconImage());
+    nothumb->SetLabel(g_localizeStrings.Get(20018));
+    items.Add(nothumb);
+
+    CStdString strThumb;
+    VECSOURCES shares;
+    if (g_guiSettings.GetString("pvrmenu.iconpath") != "")
+    {
+      CMediaSource share1;
+      share1.strPath = g_guiSettings.GetString("pvrmenu.iconpath");
+      share1.strName = g_localizeStrings.Get(19018);
+      shares.push_back(share1);
+    }
+    g_mediaManager.GetLocalDrives(shares);
+    if (!CGUIDialogFileBrowser::ShowAndGetImage(items, shares, g_localizeStrings.Get(1030), strThumb))
+      return false;
+
+    if (strThumb == "thumb://Current")
+      return true;
+
+    if (strThumb == "thumb://None")
+      strThumb = "";
+
+    if (m_iCurrSubTVWindow == TV_WINDOW_CHANNELS_TV)
+    {
+      pItem->GetPVRChannelInfoTag()->SetIconPath(strThumb, true);
+      UpdateChannelsTV();
+    }
+    else if (m_iCurrSubTVWindow == TV_WINDOW_CHANNELS_RADIO)
+    {
+      pItem->GetPVRChannelInfoTag()->SetIconPath(strThumb, true);
+      UpdateChannelsRadio();
+    }
+
+    return true;
+  }
+  else if (button == CONTEXT_BUTTON_EDIT)
+  {
+    if (m_iCurrSubTVWindow == TV_WINDOW_TIMERS)
+    {
+      CFileItem fileitem(*pItem);
+
+      if (ShowTimerSettings(&fileitem))
+      {
+        CPVRTimers::UpdateTimer(fileitem);
+        UpdateTimers();
+      }
+    }
+
+    return true;
+  }
+  else if (button == CONTEXT_BUTTON_ADD)
+  {
+    if (m_iCurrSubTVWindow == TV_WINDOW_CHANNELS_RADIO ||
+        m_iCurrSubTVWindow == TV_WINDOW_CHANNELS_TV)
+    {
+      CGUIDialogOK::ShowAndGetInput(19033,0,19038,0);
+    }
+    else if (m_iCurrSubTVWindow == TV_WINDOW_TIMERS)
+    {
+      CPVRTimerInfoTag *newtimer = CPVRTimerInfoTag::InstantTimer();
+      CFileItem *item = new CFileItem(*newtimer);
+
+      if (ShowTimerSettings(item))
+      {
+        CPVRTimers::AddTimer(*item);
+        UpdateTimers();
+      }
+
+      return true;
+    }
+  }
+  else if (button == CONTEXT_BUTTON_ACTIVATE)
+  {
+    int return_str_id;
+
+    if (pItem->GetPVRTimerInfoTag()->Active() == true)
+    {
+      pItem->GetPVRTimerInfoTag()->SetActive(false);
+      return_str_id = 13106;
+    }
+    else
+    {
+      pItem->GetPVRTimerInfoTag()->SetActive(true);
+      return_str_id = 305;
+    }
+
+    CGUIDialogOK::ShowAndGetInput(19033, 19040, 0, return_str_id);
+
+    CPVRTimers::UpdateTimer(*pItem);
+    UpdateTimers(); /** Force list update **/
+    return true;
+  }
+  else if (button == CONTEXT_BUTTON_RENAME)
+  {
+    if (m_iCurrSubTVWindow == TV_WINDOW_TIMERS)
+    {
+      CStdString strNewName = pItem->GetPVRTimerInfoTag()->Title();
+      if (CGUIDialogKeyboard::ShowAndGetInput(strNewName, g_localizeStrings.Get(19042), false))
+      {
+        CPVRTimers::RenameTimer(*pItem, strNewName);
+        UpdateTimers();
+      }
+
+      return true;
+    }
+    else if (m_iCurrSubTVWindow == TV_WINDOW_RECORDINGS)
+    {
+      CStdString strNewName = pItem->GetPVRRecordingInfoTag()->Title();
+      if (CGUIDialogKeyboard::ShowAndGetInput(strNewName, g_localizeStrings.Get(19041), false))
+      {
+        if (CPVRRecordings::RenameRecording(*pItem, strNewName))
+        {
+          UpdateRecordings();
+        }
+      }
+    }
+  }
+  else if (button == CONTEXT_BUTTON_DELETE)
+  {
+    if (m_iCurrSubTVWindow == TV_WINDOW_TIMERS)
+    {
+      // prompt user for confirmation of timer deletion
+      CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
+
+      if (pDialog)
+      {
+        pDialog->SetHeading(122);
+        pDialog->SetLine(0, 19040);
+        pDialog->SetLine(1, "");
+        pDialog->SetLine(2, pItem->GetPVRTimerInfoTag()->Title());
+        pDialog->DoModal();
+
+        if (!pDialog->IsConfirmed()) return false;
+
+        CPVRTimers::DeleteTimer(*pItem);
+
+        UpdateTimers();
+      }
+
+      return true;
+    }
+    else if (m_iCurrSubTVWindow == TV_WINDOW_RECORDINGS)
+    {
+      CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
+
+      if (pDialog)
+      {
+        pDialog->SetHeading(122);
+        pDialog->SetLine(0, 19043);
+        pDialog->SetLine(1, "");
+        pDialog->SetLine(2, pItem->GetPVRRecordingInfoTag()->Title());
+        pDialog->DoModal();
+
+        if (!pDialog->IsConfirmed()) return false;
+
+        if (CPVRRecordings::DeleteRecording(*pItem))
+        {
+          PVRRecordings.Update(true);
+          UpdateRecordings();
+        }
+      }
+
+      return true;
+    }
+  }
+  else if (button == CONTEXT_BUTTON_INFO)
+  {
+    if (m_iCurrSubTVWindow == TV_WINDOW_CHANNELS_TV ||
+        m_iCurrSubTVWindow == TV_WINDOW_CHANNELS_RADIO ||
+        m_iCurrSubTVWindow == TV_WINDOW_TV_PROGRAM ||
+        m_iCurrSubTVWindow == TV_WINDOW_SEARCH)
+    {
+      ShowEPGInfo(pItem.get());
+    }
+    else if (m_iCurrSubTVWindow == TV_WINDOW_RECORDINGS)
+    {
+      ShowRecordingInfo(pItem.get());
+    }
+  }
+  else if (button == CONTEXT_BUTTON_START_RECORD)
+  {
+    if (m_iCurrSubTVWindow == TV_WINDOW_TV_PROGRAM || m_iCurrSubTVWindow == TV_WINDOW_SEARCH)
+    {
+      int iChannel = pItem->GetEPGInfoTag()->ChannelTag()->ChannelNumber();
+
+      if (iChannel != -1)
+      {
+        if (pItem->GetEPGInfoTag()->Timer() == NULL)
+        {
+          // prompt user for confirmation of channel record
+          CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
+
+          if (pDialog)
+          {
+            pDialog->SetHeading(264);
+            pDialog->SetLine(0, pItem->GetEPGInfoTag()->ChannelTag()->ChannelName());
+            pDialog->SetLine(1, "");
+            pDialog->SetLine(2, pItem->GetEPGInfoTag()->Title());
+            pDialog->DoModal();
+
+            if (pDialog->IsConfirmed())
+            {
+              CPVRTimerInfoTag *newtimer = CPVRTimerInfoTag::CreateFromEpg(*pItem->GetEPGInfoTag());
+              CFileItem *item = new CFileItem(*newtimer);
+
+              if (CPVRTimers::AddTimer(*item))
+                PVRTimers.Update();
+            }
+          }
+        }
+        else
+        {
+          CGUIDialogOK::ShowAndGetInput(19033,19034,0,0);
+        }
+      }
+    }
+  }
+  else if (button == CONTEXT_BUTTON_STOP_RECORD)
+  {
+    if (m_iCurrSubTVWindow == TV_WINDOW_TV_PROGRAM || m_iCurrSubTVWindow == TV_WINDOW_SEARCH)
+    {
+      int iChannel = pItem->GetEPGInfoTag()->ChannelTag()->ChannelNumber();
+
+      if (iChannel != -1)
+      {
+        if (pItem->GetEPGInfoTag()->Timer() != NULL)
+        {
+          CFileItemList timerlist;
+
+          if (PVRTimers.GetTimers(&timerlist) > 0)
+          {
+            for (int i = 0; i < timerlist.Size(); ++i)
+            {
+              if ((timerlist[i]->GetPVRTimerInfoTag()->Number() == pItem->GetEPGInfoTag()->ChannelTag()->ChannelNumber()) &&
+                  (timerlist[i]->GetPVRTimerInfoTag()->Start() <= pItem->GetEPGInfoTag()->Start()) &&
+                  (timerlist[i]->GetPVRTimerInfoTag()->Stop() >= pItem->GetEPGInfoTag()->End()) &&
+                  (timerlist[i]->GetPVRTimerInfoTag()->IsRepeating() != true))
+              {
+                if (CPVRTimers::DeleteTimer(*timerlist[i]))
+                  PVRTimers.Update();
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+  else if (button == CONTEXT_BUTTON_GROUP_MANAGER)
+  {
+    if (m_iCurrSubTVWindow == TV_WINDOW_CHANNELS_TV)
+      ShowGroupManager(false);
+    else if (m_iCurrSubTVWindow == TV_WINDOW_CHANNELS_RADIO)
+      ShowGroupManager(true);
+  }
+  else if (button == CONTEXT_BUTTON_RESUME_ITEM)
+  {
+
+  }
+  else if (button == CONTEXT_BUTTON_CLEAR)
+  {
+    if (m_iCurrSubTVWindow == TV_WINDOW_SEARCH)
+    {
+      m_bSearchStarted = false;
+      m_bSearchConfirmed = false;
+      m_searchfilter.Reset();
+      UpdateSearch();
+    }
+  }
+  else if (button == CONTEXT_BUTTON_SORTASC) // sort asc
+  {
+    if (m_guiState.get())
+      m_guiState->SetNextSortOrder();
+    UpdateFileList();
+    return true;
+  }
+  else if (button == CONTEXT_BUTTON_SORTBY) // sort by
+  {
+    if (m_guiState.get())
+      m_guiState->SetNextSortMethod();
+    UpdateFileList();
+    return true;
+  }
+  else if (button == CONTEXT_BUTTON_SORTBY_CHANNEL)
+  {
+    if (m_iCurrSubTVWindow == TV_WINDOW_SEARCH)
+    {
+      if (m_iSortMethod_SEARCH != SORT_METHOD_CHANNEL)
+      {
+        m_iSortMethod_SEARCH = SORT_METHOD_CHANNEL;
+        m_iSortOrder_SEARCH  = SORT_ORDER_ASC;
+      }
+      else
+      {
+        m_iSortOrder_SEARCH = m_iSortOrder_SEARCH == SORT_ORDER_ASC ? SORT_ORDER_DESC : SORT_ORDER_ASC;
+      }
+      UpdateSearch();
+    }
+  }
+  else if (button == CONTEXT_BUTTON_SORTBY_NAME)
+  {
+    if (m_iCurrSubTVWindow == TV_WINDOW_TIMERS)
+    {
+      if (m_iSortMethod_TIMERS != SORT_METHOD_LABEL)
+      {
+        m_iSortMethod_TIMERS = SORT_METHOD_LABEL;
+        m_iSortOrder_TIMERS  = SORT_ORDER_ASC;
+      }
+      else
+      {
+        m_iSortOrder_TIMERS = m_iSortOrder_TIMERS == SORT_ORDER_ASC ? SORT_ORDER_DESC : SORT_ORDER_ASC;
+      }
+      UpdateTimers();
+    }
+    else if (m_iCurrSubTVWindow == TV_WINDOW_SEARCH)
+    {
+      if (m_iSortMethod_SEARCH != SORT_METHOD_LABEL)
+      {
+        m_iSortMethod_SEARCH = SORT_METHOD_LABEL;
+        m_iSortOrder_SEARCH  = SORT_ORDER_ASC;
+      }
+      else
+      {
+        m_iSortOrder_SEARCH = m_iSortOrder_SEARCH == SORT_ORDER_ASC ? SORT_ORDER_DESC : SORT_ORDER_ASC;
+      }
+      UpdateSearch();
+    }
+  }
+  else if (button == CONTEXT_BUTTON_SORTBY_DATE)
+  {
+    if (m_iCurrSubTVWindow == TV_WINDOW_TIMERS)
+    {
+      if (m_iSortMethod_TIMERS != SORT_METHOD_DATE)
+      {
+        m_iSortMethod_TIMERS = SORT_METHOD_DATE;
+        m_iSortOrder_TIMERS  = SORT_ORDER_ASC;
+      }
+      else
+      {
+        m_iSortOrder_TIMERS = m_iSortOrder_TIMERS == SORT_ORDER_ASC ? SORT_ORDER_DESC : SORT_ORDER_ASC;
+      }
+      UpdateTimers();
+    }
+    else if (m_iCurrSubTVWindow == TV_WINDOW_SEARCH)
+    {
+      if (m_iSortMethod_SEARCH != SORT_METHOD_DATE)
+      {
+        m_iSortMethod_SEARCH = SORT_METHOD_DATE;
+        m_iSortOrder_SEARCH  = SORT_ORDER_ASC;
+      }
+      else
+      {
+        m_iSortOrder_SEARCH = m_iSortOrder_SEARCH == SORT_ORDER_ASC ? SORT_ORDER_DESC : SORT_ORDER_ASC;
+      }
+      UpdateSearch();
+    }
+  }
+  else if (button == CONTEXT_BUTTON_BEGIN)
+  {
+    m_guideGrid->GoToBegin();
+  }
+  else if (button == CONTEXT_BUTTON_END)
+  {
+    m_guideGrid->GoToEnd();
+  }
+  else if (button == CONTEXT_BUTTON_FIND)
+  {
+    m_searchfilter.Reset();
+    if (pItem->IsEPG())
+    {
+      m_searchfilter.m_strSearchTerm = "\"" + pItem->GetEPGInfoTag()->Title() + "\"";
+    }
+    else if (pItem->IsPVRChannel())
+    {
+      m_searchfilter.m_strSearchTerm = "\"" + pItem->GetPVRChannelInfoTag()->GetEPGNow()->Title() + "\"";
+    }
+    else if (pItem->IsPVRRecording())
+    {
+      m_searchfilter.m_strSearchTerm = "\"" + pItem->GetPVRRecordingInfoTag()->Title() + "\"";
+    }
+    m_bSearchConfirmed = true;
+    SET_CONTROL_FOCUS(CONTROL_BTNSEARCH, 0);
+    UpdateSearch();
+    SET_CONTROL_FOCUS(CONTROL_LIST_SEARCH, 0);
+  }
+  else if (button == CONTEXT_BUTTON_MENU_HOOKS)
+  {
+    if (pItem->IsEPG())
+    {
+      g_PVRManager.ProcessMenuHooks(pItem->GetEPGInfoTag()->ChannelTag()->ClientID());
+    }
+    else if (pItem->IsPVRChannel())
+    {
+      g_PVRManager.ProcessMenuHooks(pItem->GetPVRChannelInfoTag()->ClientID());
+    }
+    else if (pItem->IsPVRRecording())
+    {
+      g_PVRManager.ProcessMenuHooks(pItem->GetPVRRecordingInfoTag()->ClientID());
+    }
+    else if (pItem->IsPVRTimer())
+    {
+      g_PVRManager.ProcessMenuHooks(pItem->GetPVRTimerInfoTag()->ClientID());
+    }
+  }
+
+  return CGUIMediaWindow::OnContextButton(itemNumber, button);
+}
+
+void CGUIWindowTV::ShowEPGInfo(CFileItem *item)
+{
+  /* Check item is TV epg or channel information tag */
+  if (item->IsEPG())
+  {
+    /* Load programme info dialog */
+    CGUIDialogPVRGuideInfo* pDlgInfo = (CGUIDialogPVRGuideInfo*)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_GUIDE_INFO);
+    if (!pDlgInfo)
+      return;
+
+    /* inform dialog about the file item */
+    pDlgInfo->SetProgInfo(item);
+//    PVREpgs.SetVariableData(m_vecItems);
+
+    /* Open dialog window */
+    pDlgInfo->DoModal();
+  }
+  else if (item->IsPVRChannel())
+  {
+    const CPVREpgInfoTag *epgnow = item->GetPVRChannelInfoTag()->GetEPGNow();
+    if (!epgnow)
+    {
+      CGUIDialogOK::ShowAndGetInput(19033,0,19055,0);
+      return;
+    }
+
+    CFileItem *itemNow  = new CFileItem(*epgnow);
+
+    /* Load programme info dialog */
+    CGUIDialogPVRGuideInfo* pDlgInfo = (CGUIDialogPVRGuideInfo*)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_GUIDE_INFO);
+    if (!pDlgInfo)
+      return;
+
+    /* inform dialog about the file item */
+    pDlgInfo->SetProgInfo(itemNow);
+
+    /* Open dialog window */
+    pDlgInfo->DoModal();
+  }
+  else
+    CLog::Log(LOGERROR, "CGUIWindowTV: Can't open programme info dialog, no epg or channel info tag!");
+}
+
+void CGUIWindowTV::ShowRecordingInfo(CFileItem *item)
+{
+  /* Check item is TV record information tag */
+  if (!item->IsPVRRecording())
+  {
+    CLog::Log(LOGERROR, "CGUIWindowTV: Can't open recording info dialog, no record info tag!");
+    return;
+  }
+
+  /* Load record info dialog */
+  CGUIDialogPVRRecordingInfo* pDlgInfo = (CGUIDialogPVRRecordingInfo*)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_RECORDING_INFO);
+
+  if (!pDlgInfo)
+    return;
+
+  /* inform dialog about the file item */
+  pDlgInfo->SetRecording(item);
+
+  /* Open dialog window */
+  pDlgInfo->DoModal();
+
+  /* Return to caller */
+  return;
+}
+
+bool CGUIWindowTV::ShowTimerSettings(CFileItem *item)
+{
+  /* Check item is TV timer information tag */
+  if (!item->IsPVRTimer())
+  {
+    CLog::Log(LOGERROR, "CGUIWindowTV: Can't open timer settings dialog, no timer info tag!");
+    return false;
+  }
+
+  /* Load timer settings dialog */
+  CGUIDialogPVRTimerSettings* pDlgInfo = (CGUIDialogPVRTimerSettings*)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_TIMER_SETTING);
+
+  if (!pDlgInfo)
+    return false;
+
+  /* inform dialog about the file item */
+  pDlgInfo->SetTimer(item);
+
+  /* Open dialog window */
+  pDlgInfo->DoModal();
+
+  /* Get modify flag from window and return it to caller */
+  return pDlgInfo->GetOK();
+}
+
+void CGUIWindowTV::ShowGroupManager(bool IsRadio)
+{
+  /* Load group manager dialog */
+  CGUIDialogPVRGroupManager* pDlgInfo = (CGUIDialogPVRGroupManager*)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_GROUP_MANAGER);
+
+  if (!pDlgInfo)
+    return;
+
+  pDlgInfo->SetRadio(IsRadio);
+
+  /* Open dialog window */
+  pDlgInfo->DoModal();
+
+  return;
+}
+
+void CGUIWindowTV::ShowSearchResults()
+{
+  /* Load timer settings dialog */
+  CGUIDialogPVRGuideSearch* pDlgInfo = (CGUIDialogPVRGuideSearch*)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_GUIDE_SEARCH);
+
+  if (!pDlgInfo)
+    return;
+
+  pDlgInfo->SetFilterData(&m_searchfilter);
+
+  /* Open dialog window */
+  pDlgInfo->DoModal();
+
+  if (pDlgInfo->IsConfirmed())
+  {
+    m_bSearchConfirmed = true;
+    UpdateSearch();
+  }
+
+  return;
+}
+
+void CGUIWindowTV::UpdateGuide()
+{
+  CPVRChannel CurrentChannel;
+  g_PVRManager.GetCurrentChannel(&CurrentChannel);
+
+  m_vecItems->Clear();
+
+  if (m_iGuideView == GUIDE_VIEW_CHANNEL)
+  {
+    m_guideGrid = NULL;
+    m_viewControl.SetCurrentView(CONTROL_LIST_GUIDE_CHANNEL);
+
+    SET_CONTROL_LABEL(CONTROL_BTNGUIDE, g_localizeStrings.Get(19029));
+    SET_CONTROL_LABEL(CONTROL_LABELGROUP, CurrentChannel.ChannelName().c_str());
+
+    if (CurrentChannel.GetEPG(m_vecItems) == 0)
+    {
+      CFileItemPtr item;
+      item.reset(new CFileItem("pvr://guide/" + CurrentChannel.ChannelName() + "/empty.epg", false));
+      item->SetLabel(g_localizeStrings.Get(19028));
+      item->SetLabelPreformated(true);
+      m_vecItems->Add(item);
+    }
+    m_viewControl.SetItems(*m_vecItems);
+  }
+  else if (m_iGuideView == GUIDE_VIEW_NOW)
+  {
+    m_guideGrid = NULL;
+
+    m_viewControl.SetCurrentView(CONTROL_LIST_GUIDE_NOW_NEXT);
+
+    SET_CONTROL_LABEL(CONTROL_BTNGUIDE, g_localizeStrings.Get(19029) + ": " + g_localizeStrings.Get(19030));
+    SET_CONTROL_LABEL(CONTROL_LABELGROUP, g_localizeStrings.Get(19030));
+
+    if (PVREpgs.GetEPGNow(m_vecItems, CurrentChannel.IsRadio()) == 0)
+    {
+      CFileItemPtr item;
+      item.reset(new CFileItem("pvr://guide/now/empty.epg", false));
+      item->SetLabel(g_localizeStrings.Get(19028));
+      item->SetLabelPreformated(true);
+      m_vecItems->Add(item);
+    }
+    m_viewControl.SetItems(*m_vecItems);
+  }
+  else if (m_iGuideView == GUIDE_VIEW_NEXT)
+  {
+    m_guideGrid = NULL;
+
+    m_viewControl.SetCurrentView(CONTROL_LIST_GUIDE_NOW_NEXT);
+
+    SET_CONTROL_LABEL(CONTROL_BTNGUIDE, g_localizeStrings.Get(19029) + ": " + g_localizeStrings.Get(19031));
+    SET_CONTROL_LABEL(CONTROL_LABELGROUP, g_localizeStrings.Get(19031));
+
+    if (PVREpgs.GetEPGNext(m_vecItems, CurrentChannel.IsRadio()) == 0)
+    {
+      CFileItemPtr item;
+      item.reset(new CFileItem("pvr://guide/next/empty.epg", false));
+      item->SetLabel(g_localizeStrings.Get(19028));
+      item->SetLabelPreformated(true);
+      m_vecItems->Add(item);
+    }
+    m_viewControl.SetItems(*m_vecItems);
+  }
+  else if (m_iGuideView == GUIDE_VIEW_TIMELINE)
+  {
+    SET_CONTROL_LABEL(CONTROL_BTNGUIDE, g_localizeStrings.Get(19029) + ": " + g_localizeStrings.Get(19032));
+    SET_CONTROL_LABEL(CONTROL_LABELGROUP, g_localizeStrings.Get(19032));
+
+    if (PVREpgs.GetEPGAll(m_vecItems, CurrentChannel.IsRadio()) > 0)
+    {
+      CDateTime now = CDateTime::GetCurrentDateTime();
+      CDateTime m_gridStart = now - CDateTimeSpan(0, 0, 0, (now.GetMinute() % 30) * 60 + now.GetSecond()) - CDateTimeSpan(0, g_guiSettings.GetInt("pvrmenu.lingertime") / 60, g_guiSettings.GetInt("pvrmenu.lingertime") % 60, 0);
+      CDateTime m_gridEnd = m_gridStart + CDateTimeSpan(g_guiSettings.GetInt("pvrmenu.daystodisplay"), 0, 0, 0);
+      m_guideGrid = (CGUIEPGGridContainer*)GetControl(CONTROL_LIST_TIMELINE);
+      m_guideGrid->SetStartEnd(m_gridStart, m_gridEnd);
+      m_viewControl.SetCurrentView(CONTROL_LIST_TIMELINE);
+
+//      m_viewControl.SetSelectedItem(m_iSelected_GUIDE);
+    }
+  }
+
+  SET_CONTROL_LABEL(CONTROL_LABELHEADER, g_localizeStrings.Get(19029));
+}
+
+void CGUIWindowTV::UpdateChannelsTV()
+{
+  m_vecItems->Clear();
+  m_viewControl.SetCurrentView(CONTROL_LIST_CHANNELS_TV);
+  if (!m_bShowHiddenChannels)
+    m_vecItems->m_strPath = "pvr://channels/tv/" + PVRChannelGroupsTV.GetGroupName(m_iCurrentTVGroup) + "/";
+  else
+    m_vecItems->m_strPath = "pvr://channels/tv/.hidden/";
+  Update(m_vecItems->m_strPath);
+
+  if (m_vecItems->Size() == 0)
+  {
+    if (m_bShowHiddenChannels)
+    {
+      m_bShowHiddenChannels = false;
+      UpdateChannelsTV();
+      return;
+    }
+    else if (m_iCurrentTVGroup != -1)
+    {
+      m_iCurrentTVGroup = PVRChannelGroupsTV.GetNextGroupID(m_iCurrentTVGroup);
+      UpdateChannelsTV();
+      return;
+    }
+  }
+
+  m_viewControl.SetSelectedItem(m_iSelected_CHANNELS_TV);
+
+  SET_CONTROL_LABEL(CONTROL_LABELHEADER, g_localizeStrings.Get(19023));
+  if (m_bShowHiddenChannels)
+    SET_CONTROL_LABEL(CONTROL_LABELGROUP, g_localizeStrings.Get(19022));
+  else
+    SET_CONTROL_LABEL(CONTROL_LABELGROUP, PVRChannelGroupsTV.GetGroupName(m_iCurrentTVGroup));
+}
+
+void CGUIWindowTV::UpdateChannelsRadio()
+{
+  m_vecItems->Clear();
+  m_viewControl.SetCurrentView(CONTROL_LIST_CHANNELS_RADIO);
+  if (!m_bShowHiddenChannels)
+    m_vecItems->m_strPath = "pvr://channels/radio/" + PVRChannelGroupsRadio.GetGroupName(m_iCurrentRadioGroup) + "/";
+  else
+    m_vecItems->m_strPath = "pvr://channels/radio/.hidden/";
+  Update(m_vecItems->m_strPath);
+
+  if (m_vecItems->Size() == 0)
+  {
+    if (m_bShowHiddenChannels)
+    {
+      m_bShowHiddenChannels = false;
+      UpdateChannelsRadio();
+      return;
+    }
+    else if (m_iCurrentRadioGroup != -1)
+    {
+      m_iCurrentRadioGroup = PVRChannelGroupsRadio.GetNextGroupID(m_iCurrentRadioGroup);
+      UpdateChannelsRadio();
+      return;
+    }
+  }
+
+  m_viewControl.SetSelectedItem(m_iSelected_CHANNELS_RADIO);
+
+  SET_CONTROL_LABEL(CONTROL_LABELHEADER, g_localizeStrings.Get(19024));
+  if (m_bShowHiddenChannels)
+    SET_CONTROL_LABEL(CONTROL_LABELGROUP, g_localizeStrings.Get(19022));
+  else
+    SET_CONTROL_LABEL(CONTROL_LABELGROUP, PVRChannelGroupsRadio.GetGroupName(m_iCurrentRadioGroup));
+}
+
+void CGUIWindowTV::UpdateRecordings()
+{
+  m_vecItems->Clear();
+  m_viewControl.SetCurrentView(CONTROL_LIST_RECORDINGS);
+  m_vecItems->m_strPath = "pvr://recordings/";
+  Update(m_iSelected_RECORDINGS_Path);
+  m_viewControl.SetSelectedItem(m_iSelected_RECORDINGS);
+
+  SET_CONTROL_LABEL(CONTROL_LABELHEADER, g_localizeStrings.Get(19017));
+  SET_CONTROL_LABEL(CONTROL_LABELGROUP, "");
+}
+
+void CGUIWindowTV::UpdateTimers()
+{
+  m_vecItems->Clear();
+  m_viewControl.SetCurrentView(CONTROL_LIST_TIMERS);
+  m_vecItems->m_strPath = "pvr://timers/";
+  Update(m_vecItems->m_strPath);
+  m_vecItems->Sort(m_iSortMethod_TIMERS, m_iSortOrder_TIMERS);
+  m_viewControl.SetItems(*m_vecItems);
+  m_viewControl.SetSelectedItem(m_iSelected_TIMERS);
+
+  SET_CONTROL_LABEL(CONTROL_LABELHEADER, g_localizeStrings.Get(19025));
+  SET_CONTROL_LABEL(CONTROL_LABELGROUP, "");
+}
+
+void CGUIWindowTV::UpdateSearch()
+{
+  m_vecItems->Clear();
+  m_viewControl.SetCurrentView(CONTROL_LIST_SEARCH);
+
+  if (m_bSearchConfirmed)
+  {
+    CGUIDialogProgress* dlgProgress = (CGUIDialogProgress*)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
+    if (dlgProgress)
+    {
+      dlgProgress->SetHeading(194);
+      dlgProgress->SetLine(0, m_searchfilter.m_strSearchTerm);
+      dlgProgress->SetLine(1, "");
+      dlgProgress->SetLine(2, "");
+      dlgProgress->StartModal();
+      dlgProgress->Progress();
+    }
+
+    PVREpgs.GetEPGSearch(m_vecItems, m_searchfilter);
+    if (dlgProgress)
+      dlgProgress->Close();
+
+    if (m_vecItems->Size() == 0)
+    {
+      CGUIDialogOK::ShowAndGetInput(194, 284, 0, 0);
+      m_bSearchConfirmed = false;
+    }
+  }
+
+  if (m_vecItems->Size() == 0)
+  {
+    CFileItemPtr item;
+    item.reset(new CFileItem("pvr://guide/searchresults/empty.epg", false));
+    item->SetLabel(g_localizeStrings.Get(19027));
+    item->SetLabelPreformated(true);
+    m_vecItems->Add(item);
+  }
+  else
+  {
+    m_vecItems->Sort(m_iSortMethod_SEARCH, m_iSortOrder_SEARCH);
+  }
+
+  m_viewControl.SetItems(*m_vecItems);
+  m_viewControl.SetSelectedItem(m_iSelected_SEARCH);
+
+  SET_CONTROL_LABEL(CONTROL_LABELHEADER, g_localizeStrings.Get(283));
+  SET_CONTROL_LABEL(CONTROL_LABELGROUP, "");
+}
+
+void CGUIWindowTV::UpdateButtons()
+{
+  if (m_iGuideView == GUIDE_VIEW_CHANNEL)
+    SET_CONTROL_LABEL(CONTROL_BTNGUIDE, g_localizeStrings.Get(19029));
+  else if (m_iGuideView == GUIDE_VIEW_NOW)
+    SET_CONTROL_LABEL(CONTROL_BTNGUIDE, g_localizeStrings.Get(19029) + ": " + g_localizeStrings.Get(19030));
+  else if (m_iGuideView == GUIDE_VIEW_NEXT)
+    SET_CONTROL_LABEL(CONTROL_BTNGUIDE, g_localizeStrings.Get(19029) + ": " + g_localizeStrings.Get(19031));
+  else if (m_iGuideView == GUIDE_VIEW_TIMELINE)
+    SET_CONTROL_LABEL(CONTROL_BTNGUIDE, g_localizeStrings.Get(19029) + ": " + g_localizeStrings.Get(19032));
+}
+
+void CGUIWindowTV::UpdateData(TVWindow update)
+{
+  if (m_iCurrSubTVWindow == TV_WINDOW_TV_PROGRAM && update == TV_WINDOW_TV_PROGRAM)
+  {
+
+  }
+  else if (m_iCurrSubTVWindow == TV_WINDOW_CHANNELS_TV && update == TV_WINDOW_CHANNELS_TV)
+    UpdateChannelsTV();
+  else if (m_iCurrSubTVWindow == TV_WINDOW_CHANNELS_RADIO && update == TV_WINDOW_CHANNELS_RADIO)
+    UpdateChannelsRadio();
+  else if (m_iCurrSubTVWindow == TV_WINDOW_RECORDINGS && update == TV_WINDOW_RECORDINGS)
+    UpdateRecordings();
+  else if (m_iCurrSubTVWindow == TV_WINDOW_TIMERS && update == TV_WINDOW_TIMERS)
+    UpdateTimers();
+
+  UpdateButtons();
+}
+
+bool CGUIWindowTV::PlayFile(CFileItem *item, bool playMinimized)
+{
+  if (playMinimized)
+  {
+    if (item->m_strPath == g_application.CurrentFile())
+    {
+      CGUIMessage msg(GUI_MSG_FULLSCREEN, 0, GetID());
+      g_windowManager.SendMessage(msg);
+      return true;
+    }
+    else
+    {
+      g_settings.m_bStartVideoWindowed = true;
+    }
+  }
+
+  if (item->m_strPath.Left(17) == "pvr://recordings/")
+  {
+    /* For recordings we check here for a available stream URL */
+    CStdString stream = item->GetPVRRecordingInfoTag()->StreamURL();
+    if (stream != "")
+    {
+      /* Isolate the folder from the filename */
+      size_t found = stream.find_last_of("/");
+      if (found == CStdString::npos)
+        found = stream.find_last_of("\\");
+
+      if (found != CStdString::npos)
+      {
+        /* Check here for asterix at the begin of the filename */
+        if (stream[found+1] == '*')
+        {
+          /* Create a "stack://" url with all files matching the extension */
+          CStdString ext = CUtil::GetExtension(stream);
+          CStdString dir = stream.substr(0, found).c_str();
+
+          CFileItemList items;
+          CDirectory::GetDirectory(dir, items);
+          items.Sort(SORT_METHOD_FILE ,SORT_ORDER_ASC);
+
+          vector<int> stack;
+          for (int i = 0; i < items.Size(); ++i)
+          {
+            if (CUtil::GetExtension(items[i]->m_strPath) == ext)
+              stack.push_back(i);
+          }
+
+          if (stack.size() > 0)
+          {
+            /* If we have a stack change the path of the item to it */
+            CStackDirectory dir;
+            CStdString stackPath = dir.ConstructStackPath(items, stack);
+            item->m_strPath = stackPath;
+          }
+        }
+        else
+        {
+          /* If no asterix is present play only the given stream URL */
+          item->m_strPath = stream;
+        }
+      }
+      else
+      {
+        CLog::Log(LOGERROR, "CGUIWindowTV: Can't open recording, no valid filename!");
+        CGUIDialogOK::ShowAndGetInput(19033,0,19036,0);
+        return false;
+      }
+    }
+
+    if (!g_application.PlayFile(*item, false))
+    {
+      CGUIDialogOK::ShowAndGetInput(19033,0,19036,0);
+      return false;
+    }
+  }
+  else
+  {
+    /* Play Live TV */
+    if (!g_application.PlayFile(*item, false))
+    {
+      CGUIDialogOK::ShowAndGetInput(19033,0,19035,0);
+      return false;
+    }
+  }
+
+  return true;
+}
diff --git a/xbmc/GUIWindowTV.h b/xbmc/GUIWindowTV.h
new file mode 100644 (file)
index 0000000..95f6a56
--- /dev/null
@@ -0,0 +1,101 @@
+#pragma once
+
+/*
+ *      Copyright (C) 2005-2008 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "GUIMediaWindow.h"
+#include "pvr/PVREpgSearchFilter.h"
+#include "pvr/PVREpgs.h"
+
+class CGUIEPGGridContainer;
+
+enum TVWindow
+{
+  TV_WINDOW_UNKNOWN         = 0,
+  TV_WINDOW_TV_PROGRAM      = 1,
+  TV_WINDOW_CHANNELS_TV     = 2,
+  TV_WINDOW_CHANNELS_RADIO  = 3,
+  TV_WINDOW_RECORDINGS      = 4,
+  TV_WINDOW_TIMERS          = 5,
+  TV_WINDOW_SEARCH          = 6
+};
+
+class CGUIWindowTV : public CGUIMediaWindow
+{
+public:
+  CGUIWindowTV(void);
+  virtual ~CGUIWindowTV(void);
+  virtual bool OnMessage(CGUIMessage& message);
+  virtual bool OnAction(const CAction &action);
+  virtual void OnWindowLoaded();
+  virtual void OnWindowUnload();
+  virtual void OnInitWindow();
+
+  void UpdateData(TVWindow update);
+
+protected:
+  virtual void GetContextButtons(int itemNumber, CContextButtons &buttons);
+  virtual bool OnContextButton(int itemNumber, CONTEXT_BUTTON button);
+  virtual void UpdateButtons();
+
+private:
+  TVWindow m_iCurrSubTVWindow;    /* Active subwindow */
+  TVWindow m_iSavedSubTVWindow;   /* Last subwindow, required if main window is shown again */
+  bool m_bShowHiddenChannels;
+  bool m_bSearchStarted;
+  bool m_bSearchConfirmed;
+  PVREpgSearchFilter m_searchfilter;
+
+  /* Selected item in associated list, required for subwindow change */
+  int m_iSelected_GUIDE;
+  int m_iSelected_CHANNELS_TV;
+  int m_iSelected_CHANNELS_RADIO;
+  int m_iSelected_RECORDINGS;
+  CStdString m_iSelected_RECORDINGS_Path;
+  int m_iSelected_TIMERS;
+  int m_iSelected_SEARCH;
+
+  SORT_ORDER m_iSortOrder_TIMERS;
+  SORT_METHOD m_iSortMethod_TIMERS;
+  SORT_ORDER m_iSortOrder_SEARCH;
+  SORT_METHOD m_iSortMethod_SEARCH;
+
+  int m_iGuideView;
+  int m_iCurrentTVGroup;
+  int m_iCurrentRadioGroup;
+
+  void ShowEPGInfo(CFileItem *item);
+  void ShowRecordingInfo(CFileItem *item);
+  bool ShowTimerSettings(CFileItem *item);
+  void ShowGroupManager(bool IsRadio);
+  void ShowSearchResults();
+
+  void UpdateGuide();
+  void UpdateChannelsTV();
+  void UpdateChannelsRadio();
+  void UpdateRecordings();
+  void UpdateTimers();
+  void UpdateSearch();
+
+  bool PlayFile(CFileItem *item, bool playMinimized = false);
+
+  CGUIEPGGridContainer   *m_guideGrid;
+};
index eb2240a..8d615fc 100644 (file)
@@ -63,6 +63,7 @@
 #include "utils/log.h"
 #include "utils/FileUtils.h"
 #include "utils/AnnouncementManager.h"
+#include "pvr/PVRRecordings.h"
 
 #include "addons/Skin.h"
 #include "MediaManager.h"
@@ -1326,6 +1327,62 @@ bool CGUIWindowVideoBase::OnPlayMedia(int iItem)
     item.SetProperty("original_listitem_url", pItem->m_strPath);
   }
 
+  if (item.m_strPath.Left(17) == "pvr://recordings/")
+  {
+    /* For recordings we check here for a available stream URL */
+    CPVRRecordingInfoTag *tag = PVRRecordings.GetByPath(item.m_strPath);
+    if (tag && !tag->StreamURL().IsEmpty())
+    {
+      CStdString stream = tag->StreamURL();
+
+      /* Isolate the folder from the filename */
+      size_t found = stream.find_last_of("/");
+      if (found == CStdString::npos)
+        found = stream.find_last_of("\\");
+
+      if (found != CStdString::npos)
+      {
+        /* Check here for asterix at the begin of the filename */
+        if (stream[found+1] == '*')
+        {
+          /* Create a "stack://" url with all files matching the extension */
+          CStdString ext = CUtil::GetExtension(stream);
+          CStdString dir = stream.substr(0, found).c_str();
+
+          CFileItemList items;
+          CDirectory::GetDirectory(dir, items);
+          items.Sort(SORT_METHOD_FILE ,SORT_ORDER_ASC);
+
+          vector<int> stack;
+          for (int i = 0; i < items.Size(); ++i)
+          {
+            if (CUtil::GetExtension(items[i]->m_strPath) == ext)
+              stack.push_back(i);
+          }
+
+          if (stack.size() > 0)
+          {
+            /* If we have a stack change the path of the item to it */
+            CStackDirectory dir;
+            CStdString stackPath = dir.ConstructStackPath(items, stack);
+            item.m_strPath = stackPath;
+          }
+        }
+        else
+        {
+          /* If no asterix is present play only the given stream URL */
+          item.m_strPath = stream;
+        }
+      }
+      else
+      {
+        CLog::Log(LOGERROR, "CGUIWindowTV: Can't open recording, no valid filename!");
+        CGUIDialogOK::ShowAndGetInput(19033,0,19036,0);
+        return false;
+      }
+    }
+  }
+
   PlayMovie(&item);
 
   return true;
index 0ec44db..8d94882 100644 (file)
@@ -142,6 +142,17 @@ SRCS=Application.cpp \
      GUIDialogSelect.cpp \
      GUIDialogSettings.cpp \
      GUIDialogSubMenu.cpp \
+     GUIDialogPVRChannelManager.cpp \
+     GUIDialogPVRChannelsOSD.cpp \
+     GUIDialogPVRCutterOSD.cpp \
+     GUIDialogPVRDirectorOSD.cpp \
+     GUIDialogPVRGroupManager.cpp \
+     GUIDialogPVRGuideInfo.cpp \
+     GUIDialogPVRGuideOSD.cpp \
+     GUIDialogPVRGuideSearch.cpp \
+     GUIDialogPVRRecordingInfo.cpp \
+     GUIDialogPVRTimerSettings.cpp \
+     GUIDialogPVRUpdateProgressBar.cpp \
      GUIDialogTeletext.cpp \
      GUIDialogTextViewer.cpp \
      GUIDialogVideoBookmarks.cpp \
@@ -161,6 +172,8 @@ SRCS=Application.cpp \
      GUIWindowPrograms.cpp \
      GUIWindowScreensaver.cpp \
      GUIWindowSystemInfo.cpp \
+     GUIViewStateTV.cpp \
+     GUIWindowTV.cpp \
      GUIWindowVisualisation.cpp \
      GUIWindowWeather.cpp \
      BackgroundInfoLoader.cpp \
index 40b4707..b8469f5 100644 (file)
@@ -107,7 +107,7 @@ void CSettings::Initialize()
 
   m_pictureExtensions = ".png|.jpg|.jpeg|.bmp|.gif|.ico|.tif|.tiff|.tga|.pcx|.cbz|.zip|.cbr|.rar|.m3u|.dng|.nef|.cr2|.crw|.orf|.arw|.erf|.3fr|.dcr|.x3f|.mef|.raf|.mrw|.pef|.sr2|.rss";
   m_musicExtensions = ".nsv|.m4a|.flac|.aac|.strm|.pls|.rm|.rma|.mpa|.wav|.wma|.ogg|.mp3|.mp2|.m3u|.mod|.amf|.669|.dmf|.dsm|.far|.gdm|.imf|.it|.m15|.med|.okt|.s3m|.stm|.sfx|.ult|.uni|.xm|.sid|.ac3|.dts|.cue|.aif|.aiff|.wpl|.ape|.mac|.mpc|.mp+|.mpp|.shn|.zip|.rar|.wv|.nsf|.spc|.gym|.adx|.dsp|.adp|.ymf|.ast|.afc|.hps|.xsp|.xwav|.waa|.wvs|.wam|.gcm|.idsp|.mpdsp|.mss|.spt|.rsd|.mid|.kar|.sap|.cmc|.cmr|.dmc|.mpt|.mpd|.rmt|.tmc|.tm8|.tm2|.oga|.url|.pxml|.tta|.rss|.cm3|.cms|.dlt|.brstm";
-  m_videoExtensions = ".m4v|.3g2|.3gp|.nsv|.tp|.ts|.ty|.strm|.pls|.rm|.rmvb|.m3u|.ifo|.mov|.qt|.divx|.xvid|.bivx|.vob|.nrg|.img|.iso|.pva|.wmv|.asf|.asx|.ogm|.m2v|.avi|.bin|.dat|.mpg|.mpeg|.mp4|.mkv|.avc|.vp3|.svq3|.nuv|.viv|.dv|.fli|.flv|.rar|.001|.wpl|.zip|.vdr|.dvr-ms|.xsp|.mts|.m2t|.m2ts|.evo|.ogv|.sdp|.avs|.rec|.url|.pxml|.vc1|.h264|.rcv|.rss|.mpls|.webm|.bdmv";
+  m_videoExtensions = ".m4v|.3g2|.3gp|.nsv|.tp|.ts|.ty|.strm|.pls|.rm|.rmvb|.m3u|.ifo|.mov|.qt|.divx|.xvid|.bivx|.vob|.nrg|.img|.iso|.pva|.wmv|.asf|.asx|.ogm|.m2v|.avi|.bin|.dat|.mpg|.mpeg|.mp4|.mkv|.avc|.vp3|.svq3|.nuv|.viv|.dv|.fli|.flv|.rar|.001|.wpl|.zip|.vdr|.dvr-ms|.xsp|.mts|.m2t|.m2ts|.evo|.ogv|.sdp|.avs|.rec|.url|.pxml|.vc1|.h264|.rcv|.rss|.mpls|.webm|.bdmv|.pvr";
   // internal music extensions
   m_musicExtensions += "|.sidstream|.oggstream|.nsfstream|.asapstream|.cdda";
 
@@ -691,7 +691,7 @@ bool CSettings::LoadSettings(const CStdString& strSettingsFile)
   if (pElement)
   {
     int interlaceMethod;
-    GetInteger(pElement, "interlacemethod", interlaceMethod, VS_INTERLACEMETHOD_NONE, VS_INTERLACEMETHOD_NONE, VS_INTERLACEMETHOD_INVERSE_TELECINE);
+    GetInteger(pElement, "interlacemethod", interlaceMethod, VS_INTERLACEMETHOD_NONE, VS_INTERLACEMETHOD_NONE, VS_INTERLACEMETHOD_AUTO_ION);
     m_defaultVideoSettings.m_InterlaceMethod = (EINTERLACEMETHOD)interlaceMethod;
     int scalingMethod;
     GetInteger(pElement, "scalingmethod", scalingMethod, VS_SCALINGMETHOD_LINEAR, VS_SCALINGMETHOD_NEAREST, VS_SCALINGMETHOD_AUTO);
index 3e71171..6c7fb29 100644 (file)
@@ -24,6 +24,9 @@
 #include "StringUtils.h"
 #include "VideoInfoTag.h"
 #include "MusicInfoTag.h"
+#include "pvr/PVREpg.h"
+#include "pvr/PVREpgInfoTag.h"
+#include "pvr/PVRTimerInfoTag.h"
 #include "FileItem.h"
 #include "URL.h"
 #include "utils/log.h"
@@ -153,7 +156,13 @@ void SSortFileItem::ByDate(CFileItemPtr &item)
   if (!item) return;
 
   CStdString label;
-  label.Format("%s %s", item->m_dateTime.GetAsDBDateTime().c_str(), item->GetLabel().c_str());
+  if (item->IsEPG())
+    label.Format("%s %s", item->GetEPGInfoTag()->Start().GetAsDBDateTime().c_str(), item->GetLabel().c_str());
+  else if (item->IsPVRTimer())
+    label.Format("%s %s", item->GetPVRTimerInfoTag()->Start().GetAsDBDateTime().c_str(), item->GetLabel().c_str());
+  else
+    label.Format("%s %s", item->m_dateTime.GetAsDBDateTime().c_str(), item->GetLabel().c_str());
+
   item->SetSortLabel(label);
 }
 
@@ -470,6 +479,16 @@ void SSortFileItem::ByProductionCode(CFileItemPtr &item)
   item->SetSortLabel(item->GetVideoInfoTag()->m_strProductionCode);
 }
 
+void SSortFileItem::ByChannel(CFileItemPtr &item)
+{
+  if (!item) return;
+
+  if (item->IsEPG())
+    item->SetSortLabel(item->GetEPGInfoTag()->ChannelTag()->ChannelName());
+  else if (item->IsPVRChannel())
+    item->SetSortLabel(item->GetPVRChannelInfoTag()->ChannelName());
+}
+
 void SSortFileItem::ByBitrate(CFileItemPtr &item)
 {
   if (!item) return;
index 4b4be34..0d68931 100644 (file)
@@ -57,6 +57,7 @@ struct SSortFileItem
   static void BySongTrackNum(CFileItemPtr &item);
   static void BySongDuration(CFileItemPtr &item);
   static void BySongRating(CFileItemPtr &item);
+  static void ByChannel(CFileItemPtr &item);
 
   static void ByProgramCount(CFileItemPtr &item);
 
@@ -118,6 +119,7 @@ typedef enum {
   SORT_METHOD_LASTPLAYED,
   SORT_METHOD_LISTENERS,
   SORT_METHOD_UNSORTED,
+  SORT_METHOD_CHANNEL,
   SORT_METHOD_BITRATE,
   SORT_METHOD_MAX
 } SORT_METHOD;
index 7d03d23..c855230 100644 (file)
@@ -156,6 +156,7 @@ void CURL::Parse(const CStdString& strURL1)
   int iEnd = strURL.length();
   const char* sep = NULL;
 
+  //TODO fix all Addon paths
   CStdString strProtocol2 = GetTranslatedProtocol();
   if(m_strProtocol.Equals("rss") ||
      m_strProtocol.Equals("addons"))
@@ -164,6 +165,7 @@ void CURL::Parse(const CStdString& strURL1)
   if(strProtocol2.Equals("http")
     || strProtocol2.Equals("https")
     || strProtocol2.Equals("plugin")
+    || m_strProtocol.Equals("addon")
     || strProtocol2.Equals("hdhomerun")
     || strProtocol2.Equals("rtsp")
     || strProtocol2.Equals("zip"))
@@ -287,6 +289,7 @@ void CURL::Parse(const CStdString& strURL1)
     || m_strProtocol.CompareNoCase("musicdb") == 0
     || m_strProtocol.CompareNoCase("videodb") == 0
     || m_strProtocol.CompareNoCase("lastfm") == 0
+    || m_strProtocol.CompareNoCase("pvr") == 0
     || m_strProtocol.Left(3).CompareNoCase("mem") == 0)
   {
     if (m_strHostName != "" && m_strFileName != "")
index f5a1d8c..6085544 100644 (file)
@@ -45,6 +45,7 @@
 #include "FileSystem/MultiPathDirectory.h"
 #include "FileSystem/DirectoryCache.h"
 #include "FileSystem/SpecialProtocol.h"
+#include "FileSystem/PVRDirectory.h"
 #include "FileSystem/RSSDirectory.h"
 #include "ThumbnailCache.h"
 #ifdef HAS_FILESYSTEM_RAR
@@ -1158,6 +1159,11 @@ bool CUtil::IsVTP(const CStdString& strFile)
   return strFile.Left(4).Equals("vtp:");
 }
 
+bool CUtil::IsPVR(const CStdString& strFile)
+{
+  return strFile.Left(4).Equals("pvr:");
+}
+
 bool CUtil::IsHTSP(const CStdString& strFile)
 {
   return strFile.Left(5).Equals("htsp:");
@@ -1165,6 +1171,9 @@ bool CUtil::IsHTSP(const CStdString& strFile)
 
 bool CUtil::IsLiveTV(const CStdString& strFile)
 {
+  if (strFile.Left(14).Equals("pvr://channels"))
+    return true;
+
   if(IsTuxBox(strFile)
   || IsVTP(strFile)
   || IsHDHomeRun(strFile)
@@ -1178,6 +1187,11 @@ bool CUtil::IsLiveTV(const CStdString& strFile)
   return false;
 }
 
+bool CUtil::IsTVRecording(const CStdString& strFile)
+{
+  return strFile.Left(15).Equals("pvr://recording");
+}
+
 bool CUtil::IsMusicDb(const CStdString& strFile)
 {
   return strFile.Left(8).Equals("musicdb:");
@@ -2772,6 +2786,10 @@ bool CUtil::SupportsFileOperations(const CStdString& strPath)
     return true;
   if (IsSmb(strPath))
     return true;
+  if (IsTVRecording(strPath))
+  {
+    return CPVRDirectory::SupportsFileOperations(strPath);
+  }
   if (IsMythTV(strPath))
   {
     /*
index 530182f..b533f4a 100644 (file)
@@ -105,8 +105,10 @@ public:
   static bool IsMythTV(const CStdString& strFile);
   static bool IsHDHomeRun(const CStdString& strFile);
   static bool IsVTP(const CStdString& strFile);
+  static bool IsPVR(const CStdString& strFile);
   static bool IsHTSP(const CStdString& strFile);
   static bool IsLiveTV(const CStdString& strFile);
+  static bool IsTVRecording(const CStdString& strFile);
   static bool IsMusicDb(const CStdString& strFile);
   static bool IsVideoDb(const CStdString& strFile);
   static bool IsLastFM(const CStdString& strFile);
index bc74373..0109f6c 100644 (file)
@@ -88,6 +88,8 @@
 #define XINPUT_IR_REMOTE_GREEN          252
 #define XINPUT_IR_REMOTE_YELLOW         253
 #define XINPUT_IR_REMOTE_BLUE           254
+#define XINPUT_IR_REMOTE_PLAYLIST       255
+#define XINPUT_IR_REMOTE_GUIDE          256
 
 typedef struct _XINPUT_IR_REMOTE
 {
index 182c1df..cb417d7 100644 (file)
@@ -149,7 +149,7 @@ static const TypeMapping types[] =
    {"xbmc.gui.skin",                     ADDON_SKIN,                  166, "DefaultAddonSkin.png" },
    {"xbmc.gui.webinterface",             ADDON_WEB_INTERFACE,         199, "DefaultAddonWebSkin.png" },
    {"xbmc.addon.repository",             ADDON_REPOSITORY,          24011, "DefaultAddonRepository.png" },
-   {"pvrclient",                         ADDON_PVRDLL,                  0, "" },
+   {"xbmc.pvrclient",                    ADDON_PVRDLL,              24019, "" },
    {"xbmc.addon.video",                  ADDON_VIDEO,                1037, "DefaultAddonVideo.png" },
    {"xbmc.addon.audio",                  ADDON_AUDIO,                1038, "DefaultAddonMusic.png" },
    {"xbmc.addon.image",                  ADDON_IMAGE,                1039, "DefaultAddonPicture.png" },
@@ -360,6 +360,9 @@ void CAddon::BuildLibName(const cp_extension_t *extension)
     case ADDON_VIZ:
       ext = ADDON_VIS_EXT;
       break;
+    case ADDON_PVRDLL:
+      ext = ADDON_PVRDLL_EXT;
+      break;
     case ADDON_SCRIPT:
     case ADDON_SCRIPT_LIBRARY:
     case ADDON_SCRIPT_LYRICS:
@@ -395,6 +398,7 @@ void CAddon::BuildLibName(const cp_extension_t *extension)
       case ADDON_SCRAPER_MUSICVIDEOS:
       case ADDON_SCRAPER_TVSHOWS:
       case ADDON_SCRAPER_LIBRARY:
+      case ADDON_PVRDLL:
       case ADDON_PLUGIN:
       case ADDON_SERVICE:
         {
index 9767253..c761417 100644 (file)
@@ -29,6 +29,7 @@
 
 class CURL;
 class TiXmlElement;
+class CAddonHelpers_Addon;
 
 typedef struct cp_plugin_info_t cp_plugin_info_t;
 typedef struct cp_extension_t cp_extension_t;
@@ -143,6 +144,12 @@ public:
    */
   virtual CStdString GetSetting(const CStdString& key);
 
+  /*! \brief Load the default settings and override these with any previously configured user settings
+   \return true if settings exist, false otherwise
+   \sa LoadUserSettings, SaveSettings, HasSettings, HasUserSettings, GetSetting, UpdateSetting
+   */
+  virtual bool LoadSettings();
+
   TiXmlElement* GetSettingsXML();
   virtual CStdString GetString(uint32_t id);
 
@@ -171,17 +178,13 @@ public:
   ADDONDEPS GetDeps();
 
 protected:
+  friend class CAddonHelpers_Addon;
+
   CAddon(const CAddon&); // protected as all copying is handled by Clone()
   CAddon(const CAddon&, const AddonPtr&);
   const AddonPtr Parent() const { return m_parent; }
   virtual void BuildLibName(const cp_extension_t *ext = NULL);
 
-  /*! \brief Load the default settings and override these with any previously configured user settings
-   \return true if settings exist, false otherwise
-   \sa LoadUserSettings, SaveSettings, HasSettings, HasUserSettings, GetSetting, UpdateSetting
-   */
-  virtual bool LoadSettings();
-
   /*! \brief Load the user settings
    \return true if user settings exist, false otherwise
    \sa LoadSettings, SaveSettings, HasSettings, HasUserSettings, GetSetting, UpdateSetting
@@ -208,7 +211,7 @@ protected:
   bool              m_userSettingsLoaded;
 
 private:
-  friend class AddonMgr;
+  friend class CAddonMgr;
   AddonProps m_props;
   const AddonPtr    m_parent;
   CStdString        m_userSettingsPath;
index 99f3ac1..ce55f67 100644 (file)
@@ -28,6 +28,7 @@
 #include "FileSystem/File.h"
 #include "FileSystem/SpecialProtocol.h"
 #include "FileSystem/Directory.h"
+#include "AddonHelpers_local.h"
 #include "log.h"
 
 using namespace XFILE;
@@ -59,12 +60,12 @@ namespace ADDON
     virtual bool LoadSettings();
     TheStruct* m_pStruct;
     TheProps*     m_pInfo;
+    CAddonHelpers* m_pHelpers;
 
   private:
     TheDll* m_pDll;
     bool m_initialized;
     bool LoadDll();
-    bool m_needsavedsettings;
 
     virtual ADDON_STATUS TransferSettings();
     TiXmlElement MakeSetting(DllSetting& setting) const;
@@ -100,7 +101,6 @@ CAddonDll<TheDll, TheStruct, TheProps>::CAddonDll(const cp_extension_t *ext)
   m_initialized = false;
   m_pDll        = NULL;
   m_pInfo       = NULL;
-  m_needsavedsettings = false;
 }
 
 template<class TheDll, typename TheStruct, typename TheProps>
@@ -111,7 +111,7 @@ CAddonDll<TheDll, TheStruct, TheProps>::CAddonDll(const AddonProps &props)
   m_initialized = false;
   m_pDll        = NULL;
   m_pInfo       = NULL;
-  m_needsavedsettings = false;
+  m_pHelpers    = NULL;
 }
 
 template<class TheDll, typename TheStruct, typename TheProps>
@@ -188,14 +188,19 @@ bool CAddonDll<TheDll, TheStruct, TheProps>::Create()
   if (!LoadDll())
     return false;
 
+  /* Allocate the helper function class to allow crosstalk over
+     helper libraries */
+  m_pHelpers = new CAddonHelpers(this);
+
+  /* Call Create to make connections, initializing data or whatever is
+     needed to become the AddOn running */
   try
   {
-    ADDON_STATUS status = m_pDll->Create(NULL, m_pInfo);
+    ADDON_STATUS status = m_pDll->Create(m_pHelpers->GetCallbacks(), m_pInfo);
     if (status == STATUS_OK)
       m_initialized = true;
-    else if ((status == STATUS_NEED_SETTINGS) || (status == STATUS_NEED_SAVEDSETTINGS))
+    else if (status == STATUS_NEED_SETTINGS)
     {
-      m_needsavedsettings = (status == STATUS_NEED_SAVEDSETTINGS);
       if (TransferSettings() == STATUS_OK)
         m_initialized = true;
       else
@@ -221,20 +226,6 @@ void CAddonDll<TheDll, TheStruct, TheProps>::Stop()
   /* Inform dll to stop all activities */
   try
   {
-    if (m_needsavedsettings)  // If the addon supports it we save some settings to settings.xml before stop
-    {
-      char   str_id[64];
-      char   str_value[1024];
-      CAddon::LoadUserSettings();
-      for (unsigned int i=0; (strcmp(str_id,"###End") != 0); i++)
-      {
-        strcpy(str_id, "###GetSavedSettings");
-        sprintf (str_value, "%i", i);
-        m_pDll->SetSetting((const char*)&str_id, (void*)&str_value);
-        if (strcmp(str_id,"###End") != 0) UpdateSetting(str_id, str_value);
-      }
-      CAddon::SaveSettings();
-    }
     if (m_pDll) m_pDll->Stop();
   }
   catch (std::exception &e)
@@ -260,6 +251,8 @@ void CAddonDll<TheDll, TheStruct, TheProps>::Destroy()
   {
     HandleException(e, "m_pDll->Unload");
   }
+  delete m_pHelpers;
+  m_pHelpers = NULL;
   delete m_pStruct;
   m_pStruct = NULL;
   delete m_pDll;
@@ -412,7 +405,7 @@ ADDON_STATUS CAddonDll<TheDll, TheStruct, TheProps>::TransferSettings()
           status = m_pDll->SetSetting(id, (const char*) GetSetting(id).c_str());
         }
         else if (strcmpi(type, "integer") == 0 || strcmpi(type, "enum") == 0 ||
-          strcmpi(type, "labelenum") == 0 || strcmpi(type, "rangeofnum") == 0)
+          strcmpi(type, "labelenum") == 0)
         {
           int tmp = atoi(GetSetting(id));
           status = m_pDll->SetSetting(id, (int*) &tmp);
diff --git a/xbmc/addons/AddonHelpers_Addon.cpp b/xbmc/addons/AddonHelpers_Addon.cpp
new file mode 100644 (file)
index 0000000..ff66af2
--- /dev/null
@@ -0,0 +1,236 @@
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "Application.h"
+#include "Addon.h"
+#include "AddonHelpers_Addon.h"
+#include "log.h"
+#include "LangInfo.h"
+
+namespace ADDON
+{
+
+CAddonHelpers_Addon::CAddonHelpers_Addon(CAddon* addon)
+{
+  m_addon     = addon;
+  m_callbacks = new CB_AddOnLib;
+
+  /* AddOn Helper functions */
+  m_callbacks->Log                = AddOnLog;
+  m_callbacks->QueueNotification  = QueueNotification;
+  m_callbacks->GetSetting         = GetAddonSetting;
+  m_callbacks->UnknownToUTF8      = UnknownToUTF8;
+  m_callbacks->GetLocalizedString = GetLocalizedString;
+  m_callbacks->GetDVDMenuLanguage = GetDVDMenuLanguage;
+}
+
+CAddonHelpers_Addon::~CAddonHelpers_Addon()
+{
+  delete m_callbacks;
+}
+
+void CAddonHelpers_Addon::AddOnLog(void *addonData, const addon_log_t loglevel, const char *msg)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper)
+    return;
+
+  CAddonHelpers_Addon* addonHelper = helper->GetHelperAddon();
+
+  try
+  {
+    CStdString xbmcMsg;
+    xbmcMsg.Format("AddOnLog: %s/%s: %s", TranslateType(addonHelper->m_addon->Type()).c_str(), addonHelper->m_addon->Name().c_str(), msg);
+
+    int xbmclog;
+    switch (loglevel)
+    {
+      case LOG_ERROR:
+        xbmclog = LOGERROR;
+        break;
+      case LOG_INFO:
+        xbmclog = LOGINFO;
+        break;
+      case LOG_NOTICE:
+        xbmclog = LOGNOTICE;
+        break;
+      case LOG_DEBUG:
+      default:
+        xbmclog = LOGDEBUG;
+        break;
+    }
+
+    /* finally write the logmessage */
+    CLog::Log(xbmclog, "%s", xbmcMsg.c_str());
+  }
+  catch (std::exception &e)
+  {
+    CLog::Log(LOGERROR, "AddOnLog: %s/%s - exception '%s' during AddOnLogCallback occurred, contact Developer '%s' of this AddOn", TranslateType(addonHelper->m_addon->Type()).c_str(), addonHelper->m_addon->Name().c_str(), e.what(), addonHelper->m_addon->Author().c_str());
+    return;
+  }
+}
+
+void CAddonHelpers_Addon::QueueNotification(void *addonData, const queue_msg_t type, const char *msg)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper)
+    return;
+
+  CAddonHelpers_Addon* addonHelper = helper->GetHelperAddon();
+
+  try
+  {
+    switch (type)
+    {
+      case QUEUE_WARNING:
+        g_application.m_guiDialogKaiToast.QueueNotification(CGUIDialogKaiToast::Warning, addonHelper->m_addon->Name(), msg, 3000, true);
+        CLog::Log(LOGDEBUG, "%s: %s-%s - Warning Message : %s", __FUNCTION__, TranslateType(addonHelper->m_addon->Type()).c_str(), addonHelper->m_addon->Name().c_str(), msg);
+        break;
+
+      case QUEUE_ERROR:
+        g_application.m_guiDialogKaiToast.QueueNotification(CGUIDialogKaiToast::Error, addonHelper->m_addon->Name(), msg, 3000, true);
+        CLog::Log(LOGDEBUG, "%s: %s-%s - Error Message : %s", __FUNCTION__, TranslateType(addonHelper->m_addon->Type()).c_str(), addonHelper->m_addon->Name().c_str(), msg);
+        break;
+
+      case QUEUE_INFO:
+      default:
+        g_application.m_guiDialogKaiToast.QueueNotification(CGUIDialogKaiToast::Info, addonHelper->m_addon->Name(), msg, 3000, false);
+        CLog::Log(LOGDEBUG, "%s: %s-%s - Info Message : %s", __FUNCTION__, TranslateType(addonHelper->m_addon->Type()).c_str(), addonHelper->m_addon->Name().c_str(), msg);
+        break;
+    }
+  }
+  catch (std::exception &e)
+  {
+    CLog::Log(LOGERROR, "QueueNotification: %s/%s - exception '%s' during AddOnLogCallback occurred, contact Developer '%s' of this AddOn", TranslateType(addonHelper->m_addon->Type()).c_str(), addonHelper->m_addon->Name().c_str(), e.what(), addonHelper->m_addon->Author().c_str());
+    return;
+  }
+}
+
+bool CAddonHelpers_Addon::GetAddonSetting(void *addonData, const char* settingName, void *settingValue)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper || settingName == NULL || settingValue == NULL)
+    return false;
+
+  CAddonHelpers_Addon* addonHelper = helper->GetHelperAddon();
+
+  try
+  {
+    CLog::Log(LOGDEBUG, "CAddonHelpers_Addon: AddOn %s request Setting %s", addonHelper->m_addon->Name().c_str(), settingName);
+
+    if (!addonHelper->m_addon->LoadSettings())
+    {
+      CLog::Log(LOGERROR, "Could't get Settings for AddOn: %s", addonHelper->m_addon->Name().c_str());
+      return false;
+    }
+
+    TiXmlElement *setting = addonHelper->m_addon->GetSettingsXML()->FirstChildElement("setting");
+    while (setting)
+    {
+      const char *id = setting->Attribute("id");
+      const char *type = setting->Attribute("type");
+
+      if (strcmpi(id, settingName) == 0 && type)
+      {
+        if (strcmpi(type, "text") == 0 || strcmpi(type, "ipaddress") == 0 ||
+            strcmpi(type, "folder") == 0 || strcmpi(type, "action") == 0 ||
+            strcmpi(type, "music") == 0 || strcmpi(type, "pictures") == 0 ||
+            strcmpi(type, "folder") == 0 || strcmpi(type, "programs") == 0 ||
+            strcmpi(type, "files") == 0 || strcmpi(type, "fileenum") == 0)
+        {
+          strcpy((char*) settingValue, addonHelper->m_addon->GetSetting(id).c_str());
+          return true;
+        }
+        else if (strcmpi(type, "integer") == 0 || strcmpi(type, "enum") == 0 ||
+                 strcmpi(type, "labelenum") == 0)
+        {
+          *(int*) settingValue = (int) atoi(addonHelper->m_addon->GetSetting(id));
+          return true;
+        }
+        else if (strcmpi(type, "bool") == 0)
+        {
+          *(bool*) settingValue = (bool) (addonHelper->m_addon->GetSetting(id) == "true" ? true : false);
+          return true;
+        }
+        else
+        {
+          CLog::Log(LOGERROR, "Unknown setting type '%s' for id %s in %s", type, id, addonHelper->m_addon->Name().c_str());
+        }
+      }
+      setting = setting->NextSiblingElement("setting");
+    }
+  }
+  catch (std::exception &e)
+  {
+    CLog::Log(LOGERROR, "PVR: %s - exception '%s' during GetAddonSetting occurred, contact Developer '%s' of this AddOn", addonHelper->m_addon->Name().c_str(), e.what(), addonHelper->m_addon->Author().c_str());
+  }
+  return false;
+}
+
+char* CAddonHelpers_Addon::UnknownToUTF8(const char *sourceDest)
+{
+  CStdString string;
+  if (sourceDest != NULL)
+    g_charsetConverter.unknownToUTF8(sourceDest, string);
+  else
+    string = "";
+  char *buffer = (char*) malloc (string.length()+1);
+  strcpy(buffer, string.c_str());
+  return buffer;
+}
+
+const char* CAddonHelpers_Addon::GetLocalizedString(const void* addonData, long dwCode)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper)
+    return NULL;
+
+  CAddonHelpers_Addon* addonHelper = helper->GetHelperAddon();
+
+  CStdString string;
+  if (dwCode >= 30000 && dwCode <= 30999)
+    string = addonHelper->m_addon->GetString(dwCode).c_str();
+  else if (dwCode >= 32000 && dwCode <= 32999)
+    string = addonHelper->m_addon->GetString(dwCode).c_str();
+  else
+    string = g_localizeStrings.Get(dwCode).c_str();
+
+  char *buffer = (char*) malloc (string.length()+1);
+  strcpy(buffer, string.c_str());
+  return buffer;
+}
+
+const char* CAddonHelpers_Addon::GetDVDMenuLanguage(const void* addonData)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper)
+    return NULL;
+
+  CStdString string = g_langInfo.GetDVDMenuLanguage();
+
+  char *buffer = (char*) malloc (string.length()+1);
+  strcpy(buffer, string.c_str());
+  return buffer;
+}
+
+
+
+}; /* namespace ADDON */
diff --git a/xbmc/addons/AddonHelpers_Addon.h b/xbmc/addons/AddonHelpers_Addon.h
new file mode 100644 (file)
index 0000000..0280aa2
--- /dev/null
@@ -0,0 +1,50 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "AddonHelpers_local.h"
+
+namespace ADDON
+{
+
+class CAddonHelpers_Addon
+{
+public:
+  CAddonHelpers_Addon(CAddon* addon);
+  ~CAddonHelpers_Addon();
+
+  /**! \name General Functions */
+  CB_AddOnLib *GetCallbacks() { return m_callbacks; }
+
+  /**! \name Callback functions */
+  static void AddOnLog(void *addonData, const addon_log_t loglevel, const char *msg);
+  static void QueueNotification(void *addonData, const queue_msg_t type, const char *msg);
+  static bool GetAddonSetting(void *addonData, const char* settingName, void *settingValue);
+  static char* UnknownToUTF8(const char *sourceDest);
+  static const char* GetLocalizedString(const void* addonData, long dwCode);
+  static const char* GetDVDMenuLanguage(const void* addonData);
+
+private:
+  CB_AddOnLib  *m_callbacks;
+  CAddon       *m_addon;
+};
+
+}; /* namespace ADDON */
diff --git a/xbmc/addons/AddonHelpers_GUI.cpp b/xbmc/addons/AddonHelpers_GUI.cpp
new file mode 100644 (file)
index 0000000..3520e5d
--- /dev/null
@@ -0,0 +1,1539 @@
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "Application.h"
+#include "Addon.h"
+#include "AddonHelpers_GUI.h"
+#include "log.h"
+#include "Skin.h"
+#include "FileItem.h"
+#include "FileSystem/File.h"
+#include "GUIWindowManager.h"
+#include "TextureManager.h"
+#include "GUISettings.h"
+#include "GUISpinControlEx.h"
+#include "GUIRadioButtonControl.h"
+#include "GUISettingsSliderControl.h"
+#include "GUIEditControl.h"
+#include "GUIProgressControl.h"
+
+#define CONTROL_BTNVIEWASICONS  2
+#define CONTROL_BTNSORTBY       3
+#define CONTROL_BTNSORTASC      4
+#define CONTROL_LABELFILES      12
+
+using namespace std;
+
+namespace ADDON
+{
+
+static int iXBMCGUILockRef = 0;
+
+CAddonHelpers_GUI::CAddonHelpers_GUI(CAddon* addon)
+{
+  m_addon     = addon;
+  m_callbacks = new CB_GUILib;
+
+  /* GUI Helper functions */
+  m_callbacks->Lock                           = CAddonHelpers_GUI::Lock;
+  m_callbacks->Unlock                         = CAddonHelpers_GUI::Unlock;
+  m_callbacks->GetScreenHeight                = CAddonHelpers_GUI::GetScreenHeight;
+  m_callbacks->GetScreenWidth                 = CAddonHelpers_GUI::GetScreenWidth;
+  m_callbacks->GetVideoResolution             = CAddonHelpers_GUI::GetVideoResolution;
+  m_callbacks->Window_New                     = CAddonHelpers_GUI::Window_New;
+  m_callbacks->Window_Delete                  = CAddonHelpers_GUI::Window_Delete;
+  m_callbacks->Window_SetCallbacks            = CAddonHelpers_GUI::Window_SetCallbacks;
+  m_callbacks->Window_Show                    = CAddonHelpers_GUI::Window_Show;
+  m_callbacks->Window_Close                   = CAddonHelpers_GUI::Window_Close;
+  m_callbacks->Window_DoModal                 = CAddonHelpers_GUI::Window_DoModal;
+  m_callbacks->Window_SetFocusId              = CAddonHelpers_GUI::Window_SetFocusId;
+  m_callbacks->Window_GetFocusId              = CAddonHelpers_GUI::Window_GetFocusId;
+  m_callbacks->Window_SetCoordinateResolution = CAddonHelpers_GUI::Window_SetCoordinateResolution;
+  m_callbacks->Window_SetProperty             = CAddonHelpers_GUI::Window_SetProperty;
+  m_callbacks->Window_SetPropertyInt          = CAddonHelpers_GUI::Window_SetPropertyInt;
+  m_callbacks->Window_SetPropertyBool         = CAddonHelpers_GUI::Window_SetPropertyBool;
+  m_callbacks->Window_SetPropertyDouble       = CAddonHelpers_GUI::Window_SetPropertyDouble;
+  m_callbacks->Window_GetProperty             = CAddonHelpers_GUI::Window_GetProperty;
+  m_callbacks->Window_GetPropertyInt          = CAddonHelpers_GUI::Window_GetPropertyInt;
+  m_callbacks->Window_GetPropertyBool         = CAddonHelpers_GUI::Window_GetPropertyBool;
+  m_callbacks->Window_GetPropertyDouble       = CAddonHelpers_GUI::Window_GetPropertyDouble;
+  m_callbacks->Window_ClearProperties         = CAddonHelpers_GUI::Window_ClearProperties;
+
+  m_callbacks->Window_GetListSize             = CAddonHelpers_GUI::Window_GetListSize;
+  m_callbacks->Window_ClearList               = CAddonHelpers_GUI::Window_ClearList;
+  m_callbacks->Window_AddItem                 = CAddonHelpers_GUI::Window_AddItem;
+  m_callbacks->Window_AddStringItem           = CAddonHelpers_GUI::Window_AddStringItem;
+  m_callbacks->Window_RemoveItem              = CAddonHelpers_GUI::Window_RemoveItem;
+  m_callbacks->Window_GetListItem             = CAddonHelpers_GUI::Window_GetListItem;
+  m_callbacks->Window_SetCurrentListPosition  = CAddonHelpers_GUI::Window_SetCurrentListPosition;
+  m_callbacks->Window_GetCurrentListPosition  = CAddonHelpers_GUI::Window_GetCurrentListPosition;
+
+  m_callbacks->Window_GetControl_Spin         = CAddonHelpers_GUI::Window_GetControl_Spin;
+  m_callbacks->Window_GetControl_Button       = CAddonHelpers_GUI::Window_GetControl_Button;
+  m_callbacks->Window_GetControl_RadioButton  = CAddonHelpers_GUI::Window_GetControl_RadioButton;
+  m_callbacks->Window_GetControl_Edit         = CAddonHelpers_GUI::Window_GetControl_Edit;
+  m_callbacks->Window_GetControl_Progress     = CAddonHelpers_GUI::Window_GetControl_Progress;
+
+  m_callbacks->Window_SetControlLabel         = CAddonHelpers_GUI::Window_SetControlLabel;
+
+  m_callbacks->Control_Spin_SetVisible        = CAddonHelpers_GUI::Control_Spin_SetVisible;
+  m_callbacks->Control_Spin_SetText           = CAddonHelpers_GUI::Control_Spin_SetText;
+  m_callbacks->Control_Spin_Clear             = CAddonHelpers_GUI::Control_Spin_Clear;
+  m_callbacks->Control_Spin_AddLabel          = CAddonHelpers_GUI::Control_Spin_AddLabel;
+  m_callbacks->Control_Spin_GetValue          = CAddonHelpers_GUI::Control_Spin_GetValue;
+  m_callbacks->Control_Spin_SetValue          = CAddonHelpers_GUI::Control_Spin_SetValue;
+
+  m_callbacks->Control_RadioButton_SetVisible = CAddonHelpers_GUI::Control_RadioButton_SetVisible;
+  m_callbacks->Control_RadioButton_SetText    = CAddonHelpers_GUI::Control_RadioButton_SetText;
+  m_callbacks->Control_RadioButton_SetSelected= CAddonHelpers_GUI::Control_RadioButton_SetSelected;
+  m_callbacks->Control_RadioButton_IsSelected = CAddonHelpers_GUI::Control_RadioButton_IsSelected;
+
+  m_callbacks->Control_Progress_SetPercentage = CAddonHelpers_GUI::Control_Progress_SetPercentage;
+  m_callbacks->Control_Progress_GetPercentage = CAddonHelpers_GUI::Control_Progress_GetPercentage;
+  m_callbacks->Control_Progress_SetInfo       = CAddonHelpers_GUI::Control_Progress_SetInfo;
+  m_callbacks->Control_Progress_GetInfo       = CAddonHelpers_GUI::Control_Progress_GetInfo;
+  m_callbacks->Control_Progress_GetDescription= CAddonHelpers_GUI::Control_Progress_GetDescription;
+
+  m_callbacks->ListItem_Create                = CAddonHelpers_GUI::ListItem_Create;
+  m_callbacks->ListItem_GetLabel              = CAddonHelpers_GUI::ListItem_GetLabel;
+  m_callbacks->ListItem_SetLabel              = CAddonHelpers_GUI::ListItem_SetLabel;
+  m_callbacks->ListItem_GetLabel2             = CAddonHelpers_GUI::ListItem_GetLabel2;
+  m_callbacks->ListItem_SetLabel2             = CAddonHelpers_GUI::ListItem_SetLabel2;
+  m_callbacks->ListItem_SetIconImage          = CAddonHelpers_GUI::ListItem_SetIconImage;
+  m_callbacks->ListItem_SetThumbnailImage     = CAddonHelpers_GUI::ListItem_SetThumbnailImage;
+  m_callbacks->ListItem_SetInfo               = CAddonHelpers_GUI::ListItem_SetInfo;
+  m_callbacks->ListItem_SetProperty           = CAddonHelpers_GUI::ListItem_SetProperty;
+  m_callbacks->ListItem_GetProperty           = CAddonHelpers_GUI::ListItem_GetProperty;
+  m_callbacks->ListItem_SetPath               = CAddonHelpers_GUI::ListItem_SetPath;
+}
+
+CAddonHelpers_GUI::~CAddonHelpers_GUI()
+{
+  delete m_callbacks;
+}
+
+void CAddonHelpers_GUI::Lock()
+{
+  if (iXBMCGUILockRef == 0) g_graphicsContext.Lock();
+  iXBMCGUILockRef++;
+}
+
+void CAddonHelpers_GUI::Unlock()
+{
+  if (iXBMCGUILockRef > 0)
+  {
+    iXBMCGUILockRef--;
+    if (iXBMCGUILockRef == 0) g_graphicsContext.Unlock();
+  }
+}
+
+int CAddonHelpers_GUI::GetScreenHeight()
+{
+  return g_graphicsContext.GetHeight();
+}
+
+int CAddonHelpers_GUI::GetScreenWidth()
+{
+  return g_graphicsContext.GetWidth();
+}
+
+int CAddonHelpers_GUI::GetVideoResolution()
+{
+  return (int)g_graphicsContext.GetVideoResolution();
+}
+
+GUIHANDLE CAddonHelpers_GUI::Window_New(void *addonData, const char *xmlFilename, const char *defaultSkin, bool forceFallback, bool asDialog)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper)
+    return NULL;
+
+  CAddonHelpers_GUI* guiHelper = helper->GetHelperGUI();
+
+  RESOLUTION res;
+  CStdString strSkinPath;
+  if (!forceFallback)
+  {
+    /* Check to see if the XML file exists in current skin. If not use
+       fallback path to find a skin for the addon */
+    strSkinPath = g_SkinInfo->GetSkinPath(xmlFilename, &res);
+
+    if (!XFILE::CFile::Exists(strSkinPath))
+    {
+      /* Check for the matching folder for the skin in the fallback skins folder */
+      CStdString basePath;
+      CUtil::AddFileToFolder(guiHelper->m_addon->Path(), "resources", basePath);
+      CUtil::AddFileToFolder(basePath, "skins", basePath);
+      CUtil::AddFileToFolder(basePath, CUtil::GetFileName(g_SkinInfo->Path()), basePath);
+      strSkinPath = g_SkinInfo->GetSkinPath(xmlFilename, &res, basePath);
+      if (!XFILE::CFile::Exists(strSkinPath))
+      {
+        /* Finally fallback to the DefaultSkin as it didn't exist in either the
+           XBMC Skin folder or the fallback skin folder */
+        forceFallback = true;
+      }
+    }
+  }
+
+  if (forceFallback)
+  {
+    //FIXME make this static method of current skin?
+    CStdString str("none");
+    AddonProps props(str, ADDON_SKIN, str);
+    CSkinInfo skinInfo(props);
+    CStdString basePath;
+    CUtil::AddFileToFolder(guiHelper->m_addon->Path(), "resources", basePath);
+    CUtil::AddFileToFolder(basePath, "skins", basePath);
+    CUtil::AddFileToFolder(basePath, defaultSkin, basePath);
+
+    skinInfo.Start(basePath);
+    strSkinPath = skinInfo.GetSkinPath(xmlFilename, &res, basePath);
+
+    if (!XFILE::CFile::Exists(strSkinPath))
+    {
+      CLog::Log(LOGERROR, "Window_New: %s/%s - XML File '%s' for Window is missing, contact Developer '%s' of this AddOn", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str(), strSkinPath.c_str(), guiHelper->m_addon->Author().c_str());
+      return NULL;
+    }
+  }
+  // window id's 14000 - 14100 are reserved for addons
+  // get first window id that is not in use
+  int id = WINDOW_ADDON_START;
+  // if window 14099 is in use it means addon can't create more windows
+  Lock();
+  if (g_windowManager.GetWindow(WINDOW_ADDON_END))
+  {
+    Unlock();
+    CLog::Log(LOGERROR, "Window_New: %s/%s - maximum number of windows reached", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str());
+    return NULL;
+  }
+  while(id < WINDOW_ADDON_END && g_windowManager.GetWindow(id) != NULL) id++;
+  Unlock();
+
+  CGUIWindow *window;
+  if (!asDialog)
+    window = new CGUIAddonWindow(id, strSkinPath, guiHelper->m_addon);
+  else
+    window = new CGUIAddonWindowDialog(id, strSkinPath, guiHelper->m_addon);
+
+  Lock();
+  g_windowManager.Add(window);
+  Unlock();
+
+  window->SetCoordsRes(res);
+
+  return window;
+}
+
+void CAddonHelpers_GUI::Window_Delete(void *addonData, GUIHANDLE handle)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper)
+    return;
+
+  CAddonHelpers_GUI* guiHelper = helper->GetHelperGUI();
+
+  if (!handle)
+  {
+    CLog::Log(LOGERROR, "Window_Show: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str());
+    return;
+  }
+
+  CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
+  CGUIWindow      *pWindow      = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId);
+  if (!pWindow)
+    return;
+
+  Lock();
+  // first change to an existing window
+  if (g_windowManager.GetActiveWindow() == pAddonWindow->m_iWindowId && !g_application.m_bStop)
+  {
+    if(g_windowManager.GetWindow(pAddonWindow->m_iOldWindowId))
+      g_windowManager.ActivateWindow(pAddonWindow->m_iOldWindowId);
+    else // old window does not exist anymore, switch to home
+      g_windowManager.ActivateWindow(WINDOW_HOME);
+  }
+  // Free any window properties
+  pAddonWindow->ClearProperties();
+  // free the window's resources and unload it (free all guicontrols)
+  pAddonWindow->FreeResources(true);
+
+  g_windowManager.Remove(pAddonWindow->GetID());
+  delete pAddonWindow;
+  Unlock();
+}
+
+void CAddonHelpers_GUI::Window_SetCallbacks(void *addonData, GUIHANDLE handle, GUIHANDLE clienthandle, bool (*initCB)(GUIHANDLE), bool (*clickCB)(GUIHANDLE, int), bool (*focusCB)(GUIHANDLE, int), bool (*onActionCB)(GUIHANDLE handle, int))
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper || !handle)
+    return;
+
+  CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
+
+  Lock();
+  pAddonWindow->m_clientHandle  = clienthandle;
+  pAddonWindow->CBOnInit        = initCB;
+  pAddonWindow->CBOnClick       = clickCB;
+  pAddonWindow->CBOnFocus       = focusCB;
+  pAddonWindow->CBOnAction      = onActionCB;
+  Unlock();
+}
+
+bool CAddonHelpers_GUI::Window_Show(void *addonData, GUIHANDLE handle)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper)
+    return false;
+
+  CAddonHelpers_GUI* guiHelper = helper->GetHelperGUI();
+
+  if (!handle)
+  {
+    CLog::Log(LOGERROR, "Window_Show: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str());
+    return false;
+  }
+
+  CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
+  CGUIWindow      *pWindow      = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId);
+  if (!pWindow)
+    return false;
+
+  if (pAddonWindow->m_iOldWindowId != pAddonWindow->m_iWindowId && pAddonWindow->m_iWindowId != g_windowManager.GetActiveWindow())
+    pAddonWindow->m_iOldWindowId = g_windowManager.GetActiveWindow();
+
+  Lock();
+  if (pAddonWindow->IsDialog())
+    ((CGUIAddonWindowDialog*)pAddonWindow)->Show();
+  else
+    g_windowManager.ActivateWindow(pAddonWindow->m_iWindowId);
+  Unlock();
+
+  return true;
+}
+
+bool CAddonHelpers_GUI::Window_Close(void *addonData, GUIHANDLE handle)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper)
+    return false;
+
+  CAddonHelpers_GUI* guiHelper = helper->GetHelperGUI();
+
+  if (!handle)
+  {
+    CLog::Log(LOGERROR, "Window_Close: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str());
+    return false;
+  }
+
+  CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
+  CGUIWindow      *pWindow      = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId);
+  if (!pWindow)
+    return false;
+
+  pAddonWindow->m_bModal = false;
+  if (pAddonWindow->IsDialog())
+    ((CGUIAddonWindowDialog*)pAddonWindow)->PulseActionEvent();
+  else
+    ((CGUIAddonWindow*)pAddonWindow)->PulseActionEvent();
+
+  Lock();
+  // if it's a dialog, we have to close it a bit different
+  if (pAddonWindow->IsDialog())
+    ((CGUIAddonWindowDialog*)pAddonWindow)->Show(false);
+  else
+    g_windowManager.ActivateWindow(pAddonWindow->m_iOldWindowId);
+  pAddonWindow->m_iOldWindowId = 0;
+
+  Unlock();
+
+  return true;
+}
+
+bool CAddonHelpers_GUI::Window_DoModal(void *addonData, GUIHANDLE handle)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper)
+    return false;
+
+  CAddonHelpers_GUI* guiHelper = helper->GetHelperGUI();
+
+  if (!handle)
+  {
+    CLog::Log(LOGERROR, "Window_DoModal: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str());
+    return false;
+  }
+
+  CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
+  CGUIWindow      *pWindow      = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId);
+  if (!pWindow)
+    return false;
+
+  pAddonWindow->m_bModal = true;
+
+  if (pAddonWindow->m_iWindowId != g_windowManager.GetActiveWindow())
+    Window_Show(addonData, handle);
+
+  return true;
+}
+
+bool CAddonHelpers_GUI::Window_SetFocusId(void *addonData, GUIHANDLE handle, int iControlId)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper)
+    return false;
+
+  CAddonHelpers_GUI* guiHelper = helper->GetHelperGUI();
+
+  if (!handle)
+  {
+    CLog::Log(LOGERROR, "Window_SetFocusId: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str());
+    return false;
+  }
+
+  CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
+  CGUIWindow      *pWindow      = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId);
+  if (!pWindow)
+    return false;
+
+  if(!pWindow->GetControl(iControlId))
+  {
+    CLog::Log(LOGERROR, "Window_SetFocusId: %s/%s - Control does not exist in window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str());
+    return false;
+  }
+
+  Lock();
+  CGUIMessage msg = CGUIMessage(GUI_MSG_SETFOCUS, pAddonWindow->m_iWindowId, iControlId);
+  pWindow->OnMessage(msg);
+  Unlock();
+
+  return true;
+}
+
+int CAddonHelpers_GUI::Window_GetFocusId(void *addonData, GUIHANDLE handle)
+{
+  int iControlId = -1;
+
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper)
+    return iControlId;
+
+  CAddonHelpers_GUI* guiHelper = helper->GetHelperGUI();
+
+  if (!handle)
+  {
+    CLog::Log(LOGERROR, "Window_GetFocusId: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str());
+    return iControlId;
+  }
+
+  CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
+  CGUIWindow      *pWindow      = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId);
+  if (!pWindow)
+    return iControlId;
+
+  Lock();
+  iControlId = pWindow->GetFocusedControlID();
+  Unlock();
+
+  if (iControlId == -1)
+  {
+    CLog::Log(LOGERROR, "Window_GetFocusId: %s/%s - No control in this window has focus", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str());
+    return iControlId;
+  }
+
+  return iControlId;
+}
+
+bool CAddonHelpers_GUI::Window_SetCoordinateResolution(void *addonData, GUIHANDLE handle, int res)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper)
+    return false;
+
+  CAddonHelpers_GUI* guiHelper = helper->GetHelperGUI();
+
+  if (!handle)
+  {
+    CLog::Log(LOGERROR, "SetCoordinateResolution: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str());
+    return false;
+  }
+
+  if (res < RES_HDTV_1080i || res > RES_AUTORES)
+  {
+    CLog::Log(LOGERROR, "SetCoordinateResolution: %s/%s - Invalid resolution", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str());
+    return false;
+  }
+
+  CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
+  CGUIWindow      *pWindow      = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId);
+  if (!pWindow)
+    return false;
+
+  pWindow->SetCoordsRes((RESOLUTION)res);
+
+  return true;
+}
+
+void CAddonHelpers_GUI::Window_SetProperty(void *addonData, GUIHANDLE handle, const char *key, const char *value)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper)
+    return;
+
+  CAddonHelpers_GUI* guiHelper = helper->GetHelperGUI();
+
+  if (!handle)
+  {
+    CLog::Log(LOGERROR, "Window_SetProperty: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str());
+    return;
+  }
+
+  CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
+  CGUIWindow      *pWindow      = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId);
+  if (!pWindow)
+    return;
+
+  CStdString lowerKey = key;
+
+  Lock();
+  pWindow->SetProperty(lowerKey.ToLower(), value);
+  Unlock();
+}
+
+void CAddonHelpers_GUI::Window_SetPropertyInt(void *addonData, GUIHANDLE handle, const char *key, int value)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper)
+    return;
+
+  CAddonHelpers_GUI* guiHelper = helper->GetHelperGUI();
+
+  if (!handle)
+  {
+    CLog::Log(LOGERROR, "Window_SetPropertyInt: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str());
+    return;
+  }
+
+  CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
+  CGUIWindow      *pWindow      = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId);
+  if (!pWindow)
+    return;
+
+  CStdString lowerKey = key;
+
+  Lock();
+  pWindow->SetProperty(lowerKey.ToLower(), value);
+  Unlock();
+}
+
+void CAddonHelpers_GUI::Window_SetPropertyBool(void *addonData, GUIHANDLE handle, const char *key, bool value)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper)
+    return;
+
+  CAddonHelpers_GUI* guiHelper = helper->GetHelperGUI();
+
+  if (!handle)
+  {
+    CLog::Log(LOGERROR, "Window_SetPropertyBool: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str());
+    return;
+  }
+
+  CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
+  CGUIWindow      *pWindow      = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId);
+  if (!pWindow)
+    return;
+
+  CStdString lowerKey = key;
+
+  Lock();
+  pWindow->SetProperty(lowerKey.ToLower(), value);
+  Unlock();
+}
+
+void CAddonHelpers_GUI::Window_SetPropertyDouble(void *addonData, GUIHANDLE handle, const char *key, double value)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper)
+    return;
+
+  CAddonHelpers_GUI* guiHelper = helper->GetHelperGUI();
+
+  if (!handle)
+  {
+    CLog::Log(LOGERROR, "Window_SetPropertyDouble: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str());
+    return;
+  }
+
+  CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
+  CGUIWindow      *pWindow      = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId);
+  if (!pWindow)
+    return;
+
+  CStdString lowerKey = key;
+
+  Lock();
+  pWindow->SetProperty(lowerKey.ToLower(), value);
+  Unlock();
+}
+
+const char* CAddonHelpers_GUI::Window_GetProperty(void *addonData, GUIHANDLE handle, const char *key)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper)
+    return NULL;
+
+  CAddonHelpers_GUI* guiHelper = helper->GetHelperGUI();
+
+  if (!handle)
+  {
+    CLog::Log(LOGERROR, "Window_GetProperty: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str());
+    return NULL;
+  }
+
+  CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
+  CGUIWindow      *pWindow      = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId);
+  if (!pWindow)
+    return NULL;
+
+  Lock();
+  CStdString lowerKey = key;
+  string value = pWindow->GetProperty(lowerKey.ToLower());
+  Unlock();
+
+  return value.c_str();
+}
+
+int CAddonHelpers_GUI::Window_GetPropertyInt(void *addonData, GUIHANDLE handle, const char *key)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper)
+    return -1;
+
+  CAddonHelpers_GUI* guiHelper = helper->GetHelperGUI();
+
+  if (!handle)
+  {
+    CLog::Log(LOGERROR, "Window_GetPropertyInt: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str());
+    return -1;
+  }
+
+  CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
+  CGUIWindow      *pWindow      = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId);
+  if (!pWindow)
+    return -1;
+
+  Lock();
+  CStdString lowerKey = key;
+  int value = pWindow->GetPropertyInt(lowerKey.ToLower());
+  Unlock();
+
+  return value;
+}
+
+bool CAddonHelpers_GUI::Window_GetPropertyBool(void *addonData, GUIHANDLE handle, const char *key)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper)
+    return false;
+
+  CAddonHelpers_GUI* guiHelper = helper->GetHelperGUI();
+
+  if (!handle)
+  {
+    CLog::Log(LOGERROR, "Window_GetPropertyBool: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str());
+    return false;
+  }
+
+  CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
+  CGUIWindow      *pWindow      = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId);
+  if (!pWindow)
+    return false;
+
+  Lock();
+  CStdString lowerKey = key;
+  bool value = pWindow->GetPropertyBool(lowerKey.ToLower());
+  Unlock();
+
+  return value;
+}
+
+double CAddonHelpers_GUI::Window_GetPropertyDouble(void *addonData, GUIHANDLE handle, const char *key)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper)
+    return 0.0;
+
+  CAddonHelpers_GUI* guiHelper = helper->GetHelperGUI();
+
+  if (!handle)
+  {
+    CLog::Log(LOGERROR, "Window_GetPropertyDouble: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str());
+    return 0.0;
+  }
+
+  CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
+  CGUIWindow      *pWindow      = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId);
+  if (!pWindow)
+    return 0.0;
+
+  Lock();
+  CStdString lowerKey = key;
+  double value = pWindow->GetPropertyDouble(lowerKey.ToLower());
+  Unlock();
+
+  return value;
+}
+
+void CAddonHelpers_GUI::Window_ClearProperties(void *addonData, GUIHANDLE handle)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper)
+    return;
+
+  CAddonHelpers_GUI* guiHelper = helper->GetHelperGUI();
+
+  if (!handle)
+  {
+    CLog::Log(LOGERROR, "Window_ClearProperties: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str());
+    return;
+  }
+
+  CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
+  CGUIWindow      *pWindow      = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId);
+  if (!pWindow)
+    return;
+
+  Lock();
+  pWindow->ClearProperties();
+  Unlock();
+}
+
+int CAddonHelpers_GUI::Window_GetListSize(void *addonData, GUIHANDLE handle)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper || !handle)
+    return -1;
+
+  CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
+
+  Lock();
+  int listSize = pAddonWindow->GetListSize();
+  Unlock();
+
+  return listSize;
+}
+
+void CAddonHelpers_GUI::Window_ClearList(void *addonData, GUIHANDLE handle)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper || !handle)
+    return;
+
+  CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
+
+  Lock();
+  pAddonWindow->ClearList();
+  Unlock();
+
+  return;
+}
+
+GUIHANDLE CAddonHelpers_GUI::Window_AddItem(void *addonData, GUIHANDLE handle, GUIHANDLE item, int itemPosition)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper || !handle || !item)
+    return NULL;
+
+  CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
+  CFileItemPtr pItem((CFileItem*)item);
+  Lock();
+  pAddonWindow->AddItem(pItem, itemPosition);
+  Unlock();
+
+  return item;
+}
+
+GUIHANDLE CAddonHelpers_GUI::Window_AddStringItem(void *addonData, GUIHANDLE handle, const char *itemName, int itemPosition)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper || !handle || !itemName)
+    return NULL;
+
+  CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
+  CFileItemPtr item(new CFileItem(itemName));
+  Lock();
+  pAddonWindow->AddItem(item, itemPosition);
+  Unlock();
+
+  return item.get();
+}
+
+void CAddonHelpers_GUI::Window_RemoveItem(void *addonData, GUIHANDLE handle, int itemPosition)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper || !handle)
+    return;
+
+  CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
+
+  Lock();
+  pAddonWindow->RemoveItem(itemPosition);
+  Unlock();
+
+  return;
+}
+
+GUIHANDLE CAddonHelpers_GUI::Window_GetListItem(void *addonData, GUIHANDLE handle, int listPos)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper || !handle)
+    return NULL;
+
+  CAddonHelpers_GUI* guiHelper = helper->GetHelperGUI();
+  CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
+
+  Lock();
+  CFileItemPtr fi = pAddonWindow->GetListItem(listPos);
+  if (fi == NULL)
+  {
+    Unlock();
+    CLog::Log(LOGERROR, "Window_GetListItem: %s/%s - Index out of range", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str());
+    return NULL;
+  }
+  Unlock();
+
+  return fi.get();
+}
+
+void CAddonHelpers_GUI::Window_SetCurrentListPosition(void *addonData, GUIHANDLE handle, int listPos)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper || !handle)
+    return;
+
+  CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
+
+  Lock();
+  pAddonWindow->SetCurrentListPosition(listPos);
+  Unlock();
+
+  return;
+}
+
+int CAddonHelpers_GUI::Window_GetCurrentListPosition(void *addonData, GUIHANDLE handle)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper || !handle)
+    return -1;
+
+  CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
+
+  Lock();
+  int listPos = pAddonWindow->GetCurrentListPosition();
+  Unlock();
+
+  return listPos;
+}
+
+GUIHANDLE CAddonHelpers_GUI::Window_GetControl_Spin(void *addonData, GUIHANDLE handle, int controlId)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper || !handle)
+    return NULL;
+
+  CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
+  CGUIControl* pGUIControl = (CGUIControl*)pAddonWindow->GetControl(controlId);
+  if (pGUIControl && pGUIControl->GetControlType() != CGUIControl::GUICONTROL_SPINEX)
+    return NULL;
+
+  return pGUIControl;
+}
+
+GUIHANDLE CAddonHelpers_GUI::Window_GetControl_Button(void *addonData, GUIHANDLE handle, int controlId)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper || !handle)
+    return NULL;
+
+  CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
+  CGUIControl* pGUIControl = (CGUIControl*)pAddonWindow->GetControl(controlId);
+  if (pGUIControl && pGUIControl->GetControlType() != CGUIControl::GUICONTROL_BUTTON)
+    return NULL;
+
+  return pGUIControl;
+}
+
+GUIHANDLE CAddonHelpers_GUI::Window_GetControl_RadioButton(void *addonData, GUIHANDLE handle, int controlId)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper || !handle)
+    return NULL;
+
+  CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
+  CGUIControl* pGUIControl = (CGUIControl*)pAddonWindow->GetControl(controlId);
+  if (pGUIControl && pGUIControl->GetControlType() != CGUIControl::GUICONTROL_RADIO)
+    return NULL;
+
+  return pGUIControl;
+}
+
+GUIHANDLE CAddonHelpers_GUI::Window_GetControl_Edit(void *addonData, GUIHANDLE handle, int controlId)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper || !handle)
+    return NULL;
+
+  CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
+  CGUIControl* pGUIControl = (CGUIControl*)pAddonWindow->GetControl(controlId);
+  if (pGUIControl && pGUIControl->GetControlType() != CGUIControl::GUICONTROL_EDIT)
+    return NULL;
+
+  return pGUIControl;
+}
+
+GUIHANDLE CAddonHelpers_GUI::Window_GetControl_Progress(void *addonData, GUIHANDLE handle, int controlId)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper || !handle)
+    return NULL;
+
+  CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
+  CGUIControl* pGUIControl = (CGUIControl*)pAddonWindow->GetControl(controlId);
+  if (pGUIControl && pGUIControl->GetControlType() != CGUIControl::GUICONTROL_PROGRESS)
+    return NULL;
+
+  return pGUIControl;
+}
+
+void CAddonHelpers_GUI::Window_SetControlLabel(void *addonData, GUIHANDLE handle, int controlId, const char *label)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper || !handle)
+    return;
+
+  CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle;
+
+  CGUIMessage msg(GUI_MSG_LABEL_SET, pAddonWindow->m_iWindowId, controlId);
+  msg.SetLabel(label);
+  pAddonWindow->OnMessage(msg);
+}
+
+void CAddonHelpers_GUI::Control_Spin_SetVisible(void *addonData, GUIHANDLE spinhandle, bool yesNo)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper || !spinhandle)
+    return;
+
+  CGUISpinControlEx *pSpin = (CGUISpinControlEx*)spinhandle;
+  pSpin->SetVisible(yesNo);
+}
+
+void CAddonHelpers_GUI::Control_Spin_SetText(void *addonData, GUIHANDLE spinhandle, const char *label)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper || !spinhandle)
+    return;
+
+  CGUISpinControlEx *pSpin = (CGUISpinControlEx*)spinhandle;
+  pSpin->SetText(label);
+}
+
+void CAddonHelpers_GUI::Control_Spin_Clear(void *addonData, GUIHANDLE spinhandle)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper || !spinhandle)
+    return;
+
+  CGUISpinControlEx *pSpin = (CGUISpinControlEx*)spinhandle;
+  pSpin->Clear();
+}
+
+void CAddonHelpers_GUI::Control_Spin_AddLabel(void *addonData, GUIHANDLE spinhandle, const char *label, int iValue)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper || !spinhandle)
+    return;
+
+  CGUISpinControlEx *pSpin = (CGUISpinControlEx*)spinhandle;
+  pSpin->AddLabel(label, iValue);
+}
+
+int CAddonHelpers_GUI::Control_Spin_GetValue(void *addonData, GUIHANDLE spinhandle)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper || !spinhandle)
+    return -1;
+
+  CGUISpinControlEx *pSpin = (CGUISpinControlEx*)spinhandle;
+  return pSpin->GetValue();
+}
+
+void CAddonHelpers_GUI::Control_Spin_SetValue(void *addonData, GUIHANDLE spinhandle, int iValue)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper || !spinhandle)
+    return;
+
+  CGUISpinControlEx *pSpin = (CGUISpinControlEx*)spinhandle;
+  pSpin->SetValue(iValue);
+}
+
+void CAddonHelpers_GUI::Control_RadioButton_SetVisible(void *addonData, GUIHANDLE handle, bool yesNo)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper || !handle)
+    return;
+
+  CGUIRadioButtonControl *pRadioButton = (CGUIRadioButtonControl*)handle;
+  pRadioButton->SetVisible(yesNo);
+}
+
+void CAddonHelpers_GUI::Control_RadioButton_SetText(void *addonData, GUIHANDLE handle, const char *label)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper || !handle)
+    return;
+
+  CGUIRadioButtonControl *pRadioButton = (CGUIRadioButtonControl*)handle;
+  pRadioButton->SetLabel(label);
+}
+
+void CAddonHelpers_GUI::Control_RadioButton_SetSelected(void *addonData, GUIHANDLE handle, bool yesNo)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper || !handle)
+    return;
+
+  CGUIRadioButtonControl *pRadioButton = (CGUIRadioButtonControl*)handle;
+  pRadioButton->SetSelected(yesNo);
+}
+
+bool CAddonHelpers_GUI::Control_RadioButton_IsSelected(void *addonData, GUIHANDLE handle)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper || !handle)
+    return false;
+
+  CGUIRadioButtonControl *pRadioButton = (CGUIRadioButtonControl*)handle;
+  return pRadioButton->IsSelected();
+}
+
+void CAddonHelpers_GUI::Control_Progress_SetPercentage(void *addonData, GUIHANDLE handle, float fPercent)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper || !handle)
+    return;
+
+  CGUIProgressControl *pControl = (CGUIProgressControl*)handle;
+  pControl->SetPercentage(fPercent);
+}
+
+float CAddonHelpers_GUI::Control_Progress_GetPercentage(void *addonData, GUIHANDLE handle)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper || !handle)
+    return 0.0;
+
+  CGUIProgressControl *pControl = (CGUIProgressControl*)handle;
+  return pControl->GetPercentage();
+}
+
+void CAddonHelpers_GUI::Control_Progress_SetInfo(void *addonData, GUIHANDLE handle, int iInfo)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper || !handle)
+    return;
+
+  CGUIProgressControl *pControl = (CGUIProgressControl*)handle;
+  pControl->SetInfo(iInfo);
+}
+
+int CAddonHelpers_GUI::Control_Progress_GetInfo(void *addonData, GUIHANDLE handle)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper || !handle)
+    return -1;
+
+  CGUIProgressControl *pControl = (CGUIProgressControl*)handle;
+  return pControl->GetInfo();
+}
+
+const char* CAddonHelpers_GUI::Control_Progress_GetDescription(void *addonData, GUIHANDLE handle)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper || !handle)
+    return NULL;
+
+  CGUIProgressControl *pControl = (CGUIProgressControl*)handle;
+  CStdString string = pControl->GetDescription();
+
+  char *buffer = (char*) malloc (string.length()+1);
+  strcpy(buffer, string.c_str());
+  return buffer;
+}
+
+GUIHANDLE CAddonHelpers_GUI::ListItem_Create(void *addonData, const char *label, const char *label2, const char *iconImage, const char *thumbnailImage, const char *path)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper)
+    return NULL;
+
+  // create CFileItem
+  CFileItem *pItem = new CFileItem();
+  if (!pItem)
+    return NULL;
+
+  if (label)
+    pItem->SetLabel(label);
+  if (label2)
+    pItem->SetLabel2(label2);
+  if (iconImage)
+    pItem->SetIconImage(iconImage);
+  if (thumbnailImage)
+    pItem->SetThumbnailImage(thumbnailImage);
+  if (path)
+    pItem->m_strPath = path;
+
+  return pItem;
+}
+
+const char* CAddonHelpers_GUI::ListItem_GetLabel(void *addonData, GUIHANDLE handle)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper || !handle)
+    return NULL;
+
+  CStdString string = ((CFileItem*)handle)->GetLabel();
+  char *buffer = (char*) malloc (string.length()+1);
+  strcpy(buffer, string.c_str());
+  return buffer;
+}
+
+void CAddonHelpers_GUI::ListItem_SetLabel(void *addonData, GUIHANDLE handle, const char *label)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper || !handle)
+    return;
+
+  ((CFileItem*)handle)->SetLabel(label);
+}
+
+const char* CAddonHelpers_GUI::ListItem_GetLabel2(void *addonData, GUIHANDLE handle)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper || !handle)
+    return NULL;
+
+  CStdString string = ((CFileItem*)handle)->GetLabel2();
+
+  char *buffer = (char*) malloc (string.length()+1);
+  strcpy(buffer, string.c_str());
+  return buffer;
+}
+
+void CAddonHelpers_GUI::ListItem_SetLabel2(void *addonData, GUIHANDLE handle, const char *label)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper || !handle)
+    return;
+
+  ((CFileItem*)handle)->SetLabel2(label);
+}
+
+void CAddonHelpers_GUI::ListItem_SetIconImage(void *addonData, GUIHANDLE handle, const char *image)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper || !handle)
+    return;
+
+  ((CFileItem*)handle)->SetIconImage(image);
+}
+
+void CAddonHelpers_GUI::ListItem_SetThumbnailImage(void *addonData, GUIHANDLE handle, const char *image)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper || !handle)
+    return;
+
+  ((CFileItem*)handle)->SetThumbnailImage(image);
+}
+
+void CAddonHelpers_GUI::ListItem_SetInfo(void *addonData, GUIHANDLE handle, const char *info)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper || !handle)
+    return;
+
+}
+
+void CAddonHelpers_GUI::ListItem_SetProperty(void *addonData, GUIHANDLE handle, const char *key, const char *value)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper || !handle)
+    return;
+
+  ((CFileItem*)handle)->SetProperty(key, value);
+}
+
+const char* CAddonHelpers_GUI::ListItem_GetProperty(void *addonData, GUIHANDLE handle, const char *key)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper || !handle)
+    return NULL;
+
+  CStdString string = ((CFileItem*)handle)->GetProperty(key);
+  char *buffer = (char*) malloc (string.length()+1);
+  strcpy(buffer, string.c_str());
+  return buffer;
+}
+
+void CAddonHelpers_GUI::ListItem_SetPath(void *addonData, GUIHANDLE handle, const char *path)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (!helper || !handle)
+    return;
+
+  ((CFileItem*)handle)->m_strPath = path;
+}
+
+
+
+
+
+
+
+CGUIAddonWindow::CGUIAddonWindow(int id, CStdString strXML, CAddon* addon)
+ : CGUIMediaWindow(id, strXML)
+ , m_iWindowId(id)
+ , m_iOldWindowId(0)
+ , m_bModal(false)
+ , m_bIsDialog(false)
+ , m_addon(addon)
+{
+  m_actionEvent   = CreateEvent(NULL, true, false, NULL);
+  m_loadOnDemand  = false;
+  CBOnInit        = NULL;
+  CBOnFocus       = NULL;
+  CBOnClick       = NULL;
+  CBOnAction      = NULL;
+}
+
+CGUIAddonWindow::~CGUIAddonWindow(void)
+{
+  CloseHandle(m_actionEvent);
+}
+
+bool CGUIAddonWindow::OnAction(const CAction &action)
+{
+  // do the base class window first, and the call to python after this
+  bool ret = CGUIWindow::OnAction(action);  // we don't currently want the mediawindow actions here
+  if (CBOnAction)
+  {
+    CBOnAction(m_clientHandle, action.GetID());
+  }
+  return ret;
+}
+
+bool CGUIAddonWindow::OnMessage(CGUIMessage& message)
+{
+  // TODO: We shouldn't be dropping down to CGUIWindow in any of this ideally.
+  //       We have to make up our minds about what python should be doing and
+  //       what this side of things should be doing
+  switch (message.GetMessage())
+  {
+    case GUI_MSG_WINDOW_DEINIT:
+    {
+      return CGUIMediaWindow::OnMessage(message);
+    }
+    break;
+
+    case GUI_MSG_WINDOW_INIT:
+    {
+      CGUIMediaWindow::OnMessage(message);
+      if (CBOnInit)
+        CBOnInit(m_clientHandle);
+
+      return true;
+    }
+    break;
+
+    case GUI_MSG_SETFOCUS:
+    {
+      if (m_viewControl.HasControl(message.GetControlId()) && m_viewControl.GetCurrentControl() != (int)message.GetControlId())
+      {
+        m_viewControl.SetFocused();
+        return true;
+      }
+      // check if our focused control is one of our category buttons
+      int iControl = message.GetControlId();
+      if (CBOnFocus)
+      {
+        CBOnFocus(m_clientHandle, iControl);
+      }
+    }
+    break;
+    case GUI_MSG_CLICKED:
+    {
+      int iControl=message.GetSenderId();
+      // Handle Sort/View internally. Scripters shouldn't use ID 2, 3 or 4.
+      if (iControl == CONTROL_BTNSORTASC) // sort asc
+      {
+        CLog::Log(LOGINFO, "WindowXML: Internal asc/dsc button not implemented");
+        /*if (m_guiState.get())
+          m_guiState->SetNextSortOrder();
+        UpdateFileList();*/
+        return true;
+      }
+      else if (iControl == CONTROL_BTNSORTBY) // sort by
+      {
+        CLog::Log(LOGINFO, "WindowXML: Internal sort button not implemented");
+        /*if (m_guiState.get())
+          m_guiState->SetNextSortMethod();
+        UpdateFileList();*/
+        return true;
+      }
+
+      if (CBOnClick && iControl && iControl != (int)this->GetID())
+      {
+        CGUIControl* controlClicked = (CGUIControl*)this->GetControl(iControl);
+
+        // The old python way used to check list AND SELECITEM method or if its a button, checkmark.
+        // Its done this way for now to allow other controls without a python version like togglebutton to still raise a onAction event
+        if (controlClicked) // Will get problems if we the id is not on the window and we try to do GetControlType on it. So check to make sure it exists
+        {
+          if ((controlClicked->IsContainer() && (message.GetParam1() == ACTION_SELECT_ITEM ||
+                                                 message.GetParam1() == ACTION_MOUSE_LEFT_CLICK)) ||
+                                                 !controlClicked->IsContainer())
+          {
+            CBOnClick(m_clientHandle, iControl);
+          }
+          else if (controlClicked->IsContainer() && message.GetParam1() == ACTION_MOUSE_RIGHT_CLICK)
+          {
+//            PyXBMCAction* inf = new PyXBMCAction;
+//            inf->pObject = Action_FromAction(CAction(ACTION_CONTEXT_MENU));
+//            inf->pCallbackWindow = pCallbackWindow;
+//
+//            // aquire lock?
+//            PyXBMC_AddPendingCall(Py_XBMC_Event_OnAction, inf);
+//            PulseActionEvent();
+          }
+          return true;
+        }
+      }
+    }
+    break;
+  }
+
+  return CGUIMediaWindow::OnMessage(message);
+}
+
+void CGUIAddonWindow::AllocResources(bool forceLoad /*= FALSE */)
+{
+  CStdString tmpDir;
+  CUtil::GetDirectory(GetProperty("xmlfile"), tmpDir);
+  CStdString fallbackMediaPath;
+  CUtil::GetParentPath(tmpDir, fallbackMediaPath);
+  CUtil::RemoveSlashAtEnd(fallbackMediaPath);
+  m_mediaDir = fallbackMediaPath;
+
+  //CLog::Log(LOGDEBUG, "CGUIPythonWindowXML::AllocResources called: %s", fallbackMediaPath.c_str());
+  g_TextureManager.AddTexturePath(m_mediaDir);
+  CGUIMediaWindow::AllocResources(forceLoad);
+  g_TextureManager.RemoveTexturePath(m_mediaDir);
+}
+
+void CGUIAddonWindow::FreeResources(bool forceUnLoad /*= FALSE */)
+{
+  // Unload temporary language strings
+  ClearAddonStrings();
+
+  CGUIMediaWindow::FreeResources(forceUnLoad);
+}
+
+void CGUIAddonWindow::Render()
+{
+  g_TextureManager.AddTexturePath(m_mediaDir);
+  CGUIMediaWindow::Render();
+  g_TextureManager.RemoveTexturePath(m_mediaDir);
+}
+
+void CGUIAddonWindow::Update()
+{
+}
+
+void CGUIAddonWindow::AddItem(CFileItemPtr fileItem, int itemPosition)
+{
+  if (itemPosition == -1 || itemPosition > m_vecItems->Size())
+  {
+    m_vecItems->Add(fileItem);
+  }
+  else if (itemPosition <  -1 &&  !(itemPosition-1 < m_vecItems->Size()))
+  {
+    m_vecItems->AddFront(fileItem,0);
+  }
+  else
+  {
+    m_vecItems->AddFront(fileItem,itemPosition);
+  }
+  m_viewControl.SetItems(*m_vecItems);
+  UpdateButtons();
+}
+
+void CGUIAddonWindow::RemoveItem(int itemPosition)
+{
+  m_vecItems->Remove(itemPosition);
+  m_viewControl.SetItems(*m_vecItems);
+  UpdateButtons();
+}
+
+int CGUIAddonWindow::GetCurrentListPosition()
+{
+  return m_viewControl.GetSelectedItem();
+}
+
+void CGUIAddonWindow::SetCurrentListPosition(int item)
+{
+  m_viewControl.SetSelectedItem(item);
+}
+
+int CGUIAddonWindow::GetListSize()
+{
+  return m_vecItems->Size();
+}
+
+CFileItemPtr CGUIAddonWindow::GetListItem(int position)
+{
+  if (position < 0 || position >= m_vecItems->Size()) return CFileItemPtr();
+  return m_vecItems->Get(position);
+}
+
+void CGUIAddonWindow::ClearList()
+{
+  ClearFileItems();
+
+  m_viewControl.SetItems(*m_vecItems);
+  UpdateButtons();
+}
+
+void CGUIAddonWindow::GetContextButtons(int itemNumber, CContextButtons &buttons)
+{
+  // maybe on day we can make an easy way to do this context menu
+  // with out this method overriding the MediaWindow version, it will display 'Add to Favorites'
+}
+
+void CGUIAddonWindow::WaitForActionEvent(unsigned int timeout)
+{
+  WaitForSingleObject(m_actionEvent, timeout);
+  ResetEvent(m_actionEvent);
+}
+
+void CGUIAddonWindow::PulseActionEvent()
+{
+  SetEvent(m_actionEvent);
+}
+
+void CGUIAddonWindow::ClearAddonStrings()
+{
+  // Unload temporary language strings
+  g_localizeStrings.ClearBlock(m_addon->Path());
+}
+
+bool CGUIAddonWindow::OnClick(int iItem)
+{
+  // Hook Over calling  CGUIMediaWindow::OnClick(iItem) results in it trying to PLAY the file item
+  // which if its not media is BAD and 99 out of 100 times undesireable.
+  return false;
+}
+
+// SetupShares();
+/*
+ CGUIMediaWindow::OnWindowLoaded() calls SetupShares() so override it
+and just call UpdateButtons();
+*/
+void CGUIAddonWindow::SetupShares()
+{
+  UpdateButtons();
+}
+
+
+CGUIAddonWindowDialog::CGUIAddonWindowDialog(int id, CStdString strXML, CAddon* addon)
+: CGUIAddonWindow(id,strXML,addon)
+{
+  m_bRunning = false;
+  m_loadOnDemand = false;
+  m_bIsDialog = true;
+}
+
+CGUIAddonWindowDialog::~CGUIAddonWindowDialog(void)
+{
+}
+
+bool CGUIAddonWindowDialog::OnMessage(CGUIMessage &message)
+{
+  if (message.GetMessage() == GUI_MSG_WINDOW_DEINIT)
+  {
+    CGUIWindow *pWindow = g_windowManager.GetWindow(g_windowManager.GetActiveWindow());
+    if (pWindow)
+      g_windowManager.ShowOverlay(pWindow->GetOverlayState());
+    return CGUIWindow::OnMessage(message);
+  }
+  return CGUIAddonWindow::OnMessage(message);
+}
+
+void CGUIAddonWindowDialog::Show(bool show /* = true */)
+{
+  int count = ExitCriticalSection(g_graphicsContext);
+  ThreadMessage tMsg = {TMSG_GUI_ADDON_DIALOG, 1, show ? 1 : 0};
+  tMsg.lpVoid = this;
+  g_application.getApplicationMessenger().SendMessage(tMsg, true);
+  RestoreCriticalSection(g_graphicsContext, count);
+}
+
+void CGUIAddonWindowDialog::Show_Internal(bool show /* = true */)
+{
+  if (show)
+  {
+    m_bModal = true;
+    m_bRunning = true;
+    g_windowManager.RouteToWindow(this);
+
+    // active this window...
+    CGUIMessage msg(GUI_MSG_WINDOW_INIT, 0, 0, WINDOW_INVALID, m_iWindowId);
+    OnMessage(msg);
+
+    while (m_bRunning && !g_application.m_bStop)
+    {
+      g_windowManager.Process();
+    }
+  }
+  else // hide
+  {
+    m_bRunning = false;
+
+    CGUIMessage msg(GUI_MSG_WINDOW_DEINIT,0,0);
+    OnMessage(msg);
+
+    g_windowManager.RemoveDialog(GetID());
+  }
+}
+
+}; /* namespace ADDON */
diff --git a/xbmc/addons/AddonHelpers_GUI.h b/xbmc/addons/AddonHelpers_GUI.h
new file mode 100644 (file)
index 0000000..fe8643e
--- /dev/null
@@ -0,0 +1,180 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "AddonHelpers_local.h"
+#include "GUIMediaWindow.h"
+
+class CGUISpinControlEx;
+class CGUIButtonControl;
+class CGUIRadioButtonControl;
+class CGUISettingsSliderControl;
+class CGUIEditControl;
+
+namespace ADDON
+{
+
+class CAddonHelpers_GUI
+{
+public:
+  CAddonHelpers_GUI(CAddon* addon);
+  ~CAddonHelpers_GUI();
+
+  /**! \name General Functions */
+  CB_GUILib *GetCallbacks() { return m_callbacks; }
+
+  static void         Lock();
+  static void         Unlock();
+  static int          GetScreenHeight();
+  static int          GetScreenWidth();
+  static int          GetVideoResolution();
+
+  static GUIHANDLE    Window_New(void *addonData, const char *xmlFilename, const char *defaultSkin, bool forceFallback, bool asDialog);
+  static void         Window_Delete(void *addonData, GUIHANDLE handle);
+  static void         Window_SetCallbacks(void *addonData, GUIHANDLE handle, GUIHANDLE clienthandle, bool (*initCB)(GUIHANDLE), bool (*clickCB)(GUIHANDLE, int), bool (*focusCB)(GUIHANDLE, int), bool (*onActionCB)(GUIHANDLE handle, int));
+  static bool         Window_Show(void *addonData, GUIHANDLE handle);
+  static bool         Window_Close(void *addonData, GUIHANDLE handle);
+  static bool         Window_DoModal(void *addonData, GUIHANDLE handle);
+  static bool         Window_SetFocusId(void *addonData, GUIHANDLE handle, int iControlId);
+  static int          Window_GetFocusId(void *addonData, GUIHANDLE handle);
+  static bool         Window_SetCoordinateResolution(void *addonData, GUIHANDLE handle, int res);
+  static void         Window_SetProperty(void *addonData, GUIHANDLE handle, const char *key, const char *value);
+  static void         Window_SetPropertyInt(void *addonData, GUIHANDLE handle, const char *key, int value);
+  static void         Window_SetPropertyBool(void *addonData, GUIHANDLE handle, const char *key, bool value);
+  static void         Window_SetPropertyDouble(void *addonData, GUIHANDLE handle, const char *key, double value);
+  static const char * Window_GetProperty(void *addonData, GUIHANDLE handle, const char *key);
+  static int          Window_GetPropertyInt(void *addonData, GUIHANDLE handle, const char *key);
+  static bool         Window_GetPropertyBool(void *addonData, GUIHANDLE handle, const char *key);
+  static double       Window_GetPropertyDouble(void *addonData, GUIHANDLE handle, const char *key);
+  static void         Window_ClearProperties(void *addonData, GUIHANDLE handle);
+  static int          Window_GetListSize(void *addonData, GUIHANDLE handle);
+  static void         Window_ClearList(void *addonData, GUIHANDLE handle);
+  static GUIHANDLE    Window_AddItem(void *addonData, GUIHANDLE handle, GUIHANDLE item, int itemPosition);
+  static GUIHANDLE    Window_AddStringItem(void *addonData, GUIHANDLE handle, const char *itemName, int itemPosition);
+  static void         Window_RemoveItem(void *addonData, GUIHANDLE handle, int itemPosition);
+  static GUIHANDLE    Window_GetListItem(void *addonData, GUIHANDLE handle, int listPos);
+  static void         Window_SetCurrentListPosition(void *addonData, GUIHANDLE handle, int listPos);
+  static int          Window_GetCurrentListPosition(void *addonData, GUIHANDLE handle);
+  static GUIHANDLE    Window_GetControl_Spin(void *addonData, GUIHANDLE handle, int controlId);
+  static GUIHANDLE    Window_GetControl_Button(void *addonData, GUIHANDLE handle, int controlId);
+  static GUIHANDLE    Window_GetControl_RadioButton(void *addonData, GUIHANDLE handle, int controlId);
+  static GUIHANDLE    Window_GetControl_Edit(void *addonData, GUIHANDLE handle, int controlId);
+  static GUIHANDLE    Window_GetControl_Progress(void *addonData, GUIHANDLE handle, int controlId);
+  static void         Window_SetControlLabel(void *addonData, GUIHANDLE handle, int controlId, const char *label);
+  static void         Control_Spin_SetVisible(void *addonData, GUIHANDLE spinhandle, bool yesNo);
+  static void         Control_Spin_SetText(void *addonData, GUIHANDLE spinhandle, const char *label);
+  static void         Control_Spin_Clear(void *addonData, GUIHANDLE spinhandle);
+  static void         Control_Spin_AddLabel(void *addonData, GUIHANDLE spinhandle, const char *label, int iValue);
+  static int          Control_Spin_GetValue(void *addonData, GUIHANDLE spinhandle);
+  static void         Control_Spin_SetValue(void *addonData, GUIHANDLE spinhandle, int iValue);
+  static void         Control_RadioButton_SetVisible(void *addonData, GUIHANDLE handle, bool yesNo);
+  static void         Control_RadioButton_SetText(void *addonData, GUIHANDLE handle, const char *label);
+  static void         Control_RadioButton_SetSelected(void *addonData, GUIHANDLE handle, bool yesNo);
+  static bool         Control_RadioButton_IsSelected(void *addonData, GUIHANDLE handle);
+  static void         Control_Progress_SetPercentage(void *addonData, GUIHANDLE handle, float fPercent);
+  static float        Control_Progress_GetPercentage(void *addonData, GUIHANDLE handle);
+  static void         Control_Progress_SetInfo(void *addonData, GUIHANDLE handle, int iInfo);
+  static int          Control_Progress_GetInfo(void *addonData, GUIHANDLE handle);
+  static const char * Control_Progress_GetDescription(void *addonData, GUIHANDLE handle);
+  static GUIHANDLE    ListItem_Create(void *addonData, const char *label, const char *label2, const char *iconImage, const char *thumbnailImage, const char *path);
+  static const char * ListItem_GetLabel(void *addonData, GUIHANDLE handle);
+  static void         ListItem_SetLabel(void *addonData, GUIHANDLE handle, const char *label);
+  static const char * ListItem_GetLabel2(void *addonData, GUIHANDLE handle);
+  static void         ListItem_SetLabel2(void *addonData, GUIHANDLE handle, const char *label);
+  static void         ListItem_SetIconImage(void *addonData, GUIHANDLE handle, const char *image);
+  static void         ListItem_SetThumbnailImage(void *addonData, GUIHANDLE handle, const char *image);
+  static void         ListItem_SetInfo(void *addonData, GUIHANDLE handle, const char *info);
+  static void         ListItem_SetProperty(void *addonData, GUIHANDLE handle, const char *key, const char *value);
+  static const char * ListItem_GetProperty(void *addonData, GUIHANDLE handle, const char *key);
+  static void         ListItem_SetPath(void *addonData, GUIHANDLE handle, const char *path);
+
+private:
+  CB_GUILib    *m_callbacks;
+  CAddon       *m_addon;
+};
+
+class CGUIAddonWindow : public CGUIMediaWindow
+{
+friend class CAddonHelpers_GUI;
+
+public:
+  CGUIAddonWindow(int id, CStdString strXML, CAddon* addon);
+  virtual ~CGUIAddonWindow(void);
+
+  virtual bool      OnMessage(CGUIMessage& message);
+  virtual bool      OnAction(const CAction &action);
+  virtual void      AllocResources(bool forceLoad = false);
+  virtual void      FreeResources(bool forceUnLoad = false);
+  virtual void      Render();
+  void              WaitForActionEvent(unsigned int timeout);
+  void              PulseActionEvent();
+  void              AddItem(CFileItemPtr fileItem, int itemPosition);
+  void              RemoveItem(int itemPosition);
+  void              ClearList();
+  CFileItemPtr      GetListItem(int position);
+  int               GetListSize();
+  int               GetCurrentListPosition();
+  void              SetCurrentListPosition(int item);
+  virtual bool      OnClick(int iItem);
+
+protected:
+  virtual void     Update();
+  virtual void     GetContextButtons(int itemNumber, CContextButtons &buttons);
+  void             ClearAddonStrings();
+  void             SetupShares();
+
+  bool (*CBOnInit)(GUIHANDLE cbhdl);
+  bool (*CBOnFocus)(GUIHANDLE cbhdl, int controlId);
+  bool (*CBOnClick)(GUIHANDLE cbhdl, int controlId);
+  bool (*CBOnAction)(GUIHANDLE cbhdl, int);
+
+  GUIHANDLE        m_clientHandle;
+  const int m_iWindowId;
+  int m_iOldWindowId;
+  bool m_bModal;
+  bool m_bIsDialog;
+
+private:
+  HANDLE           m_actionEvent;
+  CAddon          *m_addon;
+  CStdString       m_mediaDir;
+};
+
+class CGUIAddonWindowDialog : public CGUIAddonWindow
+{
+public:
+  CGUIAddonWindowDialog(int id, CStdString strXML, CAddon* addon);
+  virtual ~CGUIAddonWindowDialog(void);
+
+  void            Show(bool show = true);
+  virtual bool    OnMessage(CGUIMessage &message);
+  virtual bool    IsDialogRunning() const { return m_bRunning; }
+  virtual bool    IsDialog() const { return true;};
+  virtual bool    IsModalDialog() const { return true; };
+  virtual bool    IsMediaWindow() const { return false; };
+
+  void Show_Internal(bool show = true);
+
+private:
+  bool             m_bRunning;
+};
+
+}; /* namespace ADDON */
diff --git a/xbmc/addons/AddonHelpers_PVR.cpp b/xbmc/addons/AddonHelpers_PVR.cpp
new file mode 100644 (file)
index 0000000..5eb48b1
--- /dev/null
@@ -0,0 +1,271 @@
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "Application.h"
+#include "AddonHelpers_PVR.h"
+#include "log.h"
+
+#include "pvr/PVREpg.h"
+#include "pvr/PVRManager.h"
+#include "addons/PVRClient.h"
+
+namespace ADDON
+{
+
+CAddonHelpers_PVR::CAddonHelpers_PVR(CAddon* addon)
+{
+  m_addon     = addon;
+  m_callbacks = new CB_PVRLib;
+
+  /* Write XBMC PVR specific Add-on function addresses to callback table */
+  m_callbacks->TransferEpgEntry       = PVRTransferEpgEntry;
+  m_callbacks->TransferChannelEntry   = PVRTransferChannelEntry;
+  m_callbacks->TransferTimerEntry     = PVRTransferTimerEntry;
+  m_callbacks->TransferRecordingEntry = PVRTransferRecordingEntry;
+  m_callbacks->AddMenuHook            = PVRAddMenuHook;
+  m_callbacks->Recording              = PVRRecording;
+  m_callbacks->TriggerTimerUpdate     = PVRTriggerTimerUpdate;
+  m_callbacks->TriggerRecordingUpdate = PVRTriggerRecordingUpdate;
+  m_callbacks->FreeDemuxPacket        = PVRFreeDemuxPacket;
+  m_callbacks->AllocateDemuxPacket    = PVRAllocateDemuxPacket;
+};
+
+CAddonHelpers_PVR::~CAddonHelpers_PVR()
+{
+  delete m_callbacks;
+};
+
+void CAddonHelpers_PVR::PVRTransferEpgEntry(void *addonData, const PVRHANDLE handle, const PVR_PROGINFO *epgentry)
+{
+  CAddonHelpers* addon = (CAddonHelpers*) addonData;
+  if (addon == NULL || handle == NULL || epgentry == NULL)
+  {
+    CLog::Log(LOGERROR, "PVR: PVRTransferEpgEntry is called with NULL-Pointer!!!");
+    return;
+  }
+
+  CPVREpg *xbmcEpg        = (CPVREpg*) handle->DATA_ADDRESS;
+  PVR_PROGINFO *epgentry2 = (PVR_PROGINFO*) epgentry;
+  CPVRClient* client      = (CPVRClient*) handle->CALLER_ADDRESS;
+  epgentry2->starttime   += client->GetTimeCorrection();
+  epgentry2->endtime     += client->GetTimeCorrection();
+  xbmcEpg->UpdateEntry(epgentry2, handle->DATA_IDENTIFIER == 1);
+
+  return;
+}
+
+void CAddonHelpers_PVR::PVRTransferChannelEntry(void *addonData, const PVRHANDLE handle, const PVR_CHANNEL *channel)
+{
+  CAddonHelpers* addon = (CAddonHelpers*) addonData;
+  if (addon == NULL || handle == NULL || channel == NULL)
+  {
+    CLog::Log(LOGERROR, "PVR: PVRTransferChannelEntry is called with NULL-Pointer!!!");
+    return;
+  }
+
+  CPVRClient* client         = (CPVRClient*) handle->CALLER_ADDRESS;
+  CPVRChannels *xbmcChannels = (CPVRChannels*) handle->DATA_ADDRESS;
+  CPVRChannel *tag           = new CPVRChannel();
+
+  tag->SetChannelID(-1);
+  tag->SetChannelNumber(-1);
+  tag->SetClientChannelNumber(channel->number);
+  tag->SetGroupID(0);
+  tag->SetClientID(client->GetClientID());
+  tag->SetUniqueID(channel->uid);
+  tag->SetChannelName(channel->name);
+  tag->SetClientChannelName(channel->callsign);
+  tag->SetIconPath(channel->iconpath);
+  tag->SetEncryptionSystem(channel->encryption);
+  tag->SetRadio(channel->radio);
+  tag->SetHidden(channel->hide);
+  tag->SetRecording(channel->recording);
+  tag->SetInputFormat(channel->input_format);
+  tag->SetStreamURL(channel->stream_url);
+
+  xbmcChannels->Update(tag);
+  return;
+}
+
+void CAddonHelpers_PVR::PVRTransferRecordingEntry(void *addonData, const PVRHANDLE handle, const PVR_RECORDINGINFO *recording)
+{
+  CAddonHelpers* addon = (CAddonHelpers*) addonData;
+  if (addon == NULL || handle == NULL || recording == NULL)
+  {
+    CLog::Log(LOGERROR, "PVR: PVRTransferRecordingEntry is called with NULL-Pointer!!!");
+    return;
+  }
+
+  CPVRClient* client = (CPVRClient*) handle->CALLER_ADDRESS;
+  CPVRRecordings *xbmcRecordings = (CPVRRecordings*) handle->DATA_ADDRESS;
+
+  CPVRRecordingInfoTag tag;
+
+  tag.SetClientIndex(recording->index);
+  tag.SetClientID(client->GetClientID());
+  tag.SetTitle(recording->title);
+  tag.SetRecordingTime(recording->recording_time);
+  tag.SetDuration(CDateTimeSpan(0, 0, recording->duration / 60, recording->duration % 60));
+  tag.SetPriority(recording->priority);
+  tag.SetLifetime(recording->lifetime);
+  tag.SetDirectory(recording->directory);
+  tag.SetPlot(recording->description);
+  tag.SetPlotOutline(recording->subtitle);
+  tag.SetStreamURL(recording->stream_url);
+  tag.SetChannelName(recording->channel_name);
+
+  xbmcRecordings->push_back(tag);
+  return;
+}
+
+void CAddonHelpers_PVR::PVRTransferTimerEntry(void *addonData, const PVRHANDLE handle, const PVR_TIMERINFO *timer)
+{
+  CAddonHelpers* addon = (CAddonHelpers*) addonData;
+  if (addon == NULL || handle == NULL || timer == NULL)
+  {
+    CLog::Log(LOGERROR, "PVR: PVRTransferTimerEntry is called with NULL-Pointer!!!");
+    return;
+  }
+
+  CPVRTimers *xbmcTimers = (CPVRTimers*) handle->DATA_ADDRESS;
+  CPVRClient* client     = (CPVRClient*) handle->CALLER_ADDRESS;
+  CPVRChannel *channel   = CPVRChannels::GetByClientFromAll(timer->channelNum, client->GetClientID());
+
+  if (channel == NULL)
+  {
+    CLog::Log(LOGERROR, "PVR: PVRTransferTimerEntry is called with not present channel");
+    return;
+  }
+
+  CPVRTimerInfoTag tag;
+  tag.SetClientID(client->GetClientID());
+  tag.SetClientIndex(timer->index);
+  tag.SetActive(timer->active);
+  tag.SetTitle(timer->title);
+  tag.SetDir(timer->directory);
+  tag.SetClientNumber(timer->channelNum);
+  tag.SetStart((time_t) (timer->starttime+client->GetTimeCorrection()));
+  tag.SetStop((time_t) (timer->endtime+client->GetTimeCorrection()));
+  tag.SetFirstDay((time_t) (timer->firstday+client->GetTimeCorrection()));
+  tag.SetPriority(timer->priority);
+  tag.SetLifetime(timer->lifetime);
+  tag.SetRecording(timer->recording);
+  tag.SetRepeating(timer->repeat);
+  tag.SetWeekdays(timer->repeatflags);
+  tag.SetNumber(channel->ChannelNumber());
+  tag.SetRadio(channel->IsRadio());
+  CStdString path;
+  path.Format("pvr://client%i/timers/%i", tag.ClientID(), tag.ClientIndex());
+  tag.SetPath(path);
+
+  xbmcTimers->Update(tag);
+  return;
+}
+
+void CAddonHelpers_PVR::PVRAddMenuHook(void *addonData, PVR_MENUHOOK *hook)
+{
+  CAddonHelpers* addon = (CAddonHelpers*) addonData;
+  if (addon == NULL || hook == NULL)
+  {
+    CLog::Log(LOGERROR, "PVR: PVRAddMenuHook is called with NULL-Pointer!!!");
+    return;
+  }
+
+  CAddonHelpers_PVR* addonHelper = addon->GetHelperPVR();
+  CPVRClient* client  = (CPVRClient*) addonHelper->m_addon;
+  PVR_MENUHOOKS *hooks = client->GetMenuHooks();
+
+  PVR_MENUHOOK hookInt;
+  hookInt.hook_id   = hook->hook_id;
+  hookInt.string_id = hook->string_id;
+  hooks->push_back(hookInt);
+}
+
+void CAddonHelpers_PVR::PVRRecording(void *addonData, const char *Name, const char *FileName, bool On)
+{
+  CAddonHelpers* addon = (CAddonHelpers*) addonData;
+  if (addon == NULL)
+  {
+    CLog::Log(LOGERROR, "PVR: PVRRecording is called with NULL-Pointer!!!");
+    return;
+  }
+
+  CAddonHelpers_PVR* addonHelper = addon->GetHelperPVR();
+
+  CStdString line1;
+  CStdString line2;
+  if (On)
+    line1.Format(g_localizeStrings.Get(19197), addonHelper->m_addon->Name());
+  else
+    line1.Format(g_localizeStrings.Get(19198), addonHelper->m_addon->Name());
+
+  if (Name)
+    line2 = Name;
+  else if (FileName)
+    line2 = FileName;
+  else
+    line2 = "";
+
+  g_application.m_guiDialogKaiToast.QueueNotification(CGUIDialogKaiToast::Info, line1, line2, 5000, false);
+  CLog::Log(LOGDEBUG, "%s: %s-%s - Recording %s : %s %s", __FUNCTION__, TranslateType(addonHelper->m_addon->Type()).c_str(), addonHelper->m_addon->Name().c_str(), On ? "started" : "finished", Name, FileName);
+}
+
+void CAddonHelpers_PVR::PVRTriggerTimerUpdate(void *addonData)
+{
+  CAddonHelpers* addon = (CAddonHelpers*) addonData;
+  if (addon == NULL)
+  {
+    CLog::Log(LOGERROR, "PVR: PVRTriggerTimerUpdate is called with NULL-Pointer!!!");
+    return;
+  }
+
+  CAddonHelpers_PVR* addonHelper = addon->GetHelperPVR();
+
+  g_PVRManager.TriggerTimersUpdate(false);
+  CLog::Log(LOGDEBUG, "%s: %s-%s - Triggered Timer Update", __FUNCTION__, TranslateType(addonHelper->m_addon->Type()).c_str(), addonHelper->m_addon->Name().c_str());
+}
+
+void CAddonHelpers_PVR::PVRTriggerRecordingUpdate(void *addonData)
+{
+  CAddonHelpers* addon = (CAddonHelpers*) addonData;
+  if (addon == NULL)
+  {
+    CLog::Log(LOGERROR, "PVR: PVRTriggerRecordingUpdate is called with NULL-Pointer!!!");
+    return;
+  }
+
+  CAddonHelpers_PVR* addonHelper = addon->GetHelperPVR();
+
+  g_PVRManager.TriggerRecordingsUpdate(false);
+  CLog::Log(LOGDEBUG, "%s: %s-%s - Triggered Recording Update", __FUNCTION__, TranslateType(addonHelper->m_addon->Type()).c_str(), addonHelper->m_addon->Name().c_str());
+}
+
+void CAddonHelpers_PVR::PVRFreeDemuxPacket(void *addonData, DemuxPacket* pPacket)
+{
+  CDVDDemuxUtils::FreeDemuxPacket(pPacket);
+}
+
+DemuxPacket* CAddonHelpers_PVR::PVRAllocateDemuxPacket(void *addonData, int iDataSize)
+{
+  return CDVDDemuxUtils::AllocateDemuxPacket(iDataSize);
+}
+
+}; /* namespace ADDON */
diff --git a/xbmc/addons/AddonHelpers_PVR.h b/xbmc/addons/AddonHelpers_PVR.h
new file mode 100644 (file)
index 0000000..b5eccb6
--- /dev/null
@@ -0,0 +1,55 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "AddonHelpers_local.h"
+#include "include/xbmc_pvr_types.h"
+
+namespace ADDON
+{
+
+class CAddonHelpers_PVR
+{
+public:
+  CAddonHelpers_PVR(CAddon* addon);
+  ~CAddonHelpers_PVR();
+
+  /**! \name General Functions */
+  CB_PVRLib *GetCallbacks() { return m_callbacks; }
+
+  /**! \name Callback functions */
+  static void PVRTransferEpgEntry(void *addonData, const PVRHANDLE handle, const PVR_PROGINFO *epgentry);
+  static void PVRTransferChannelEntry(void *addonData, const PVRHANDLE handle, const PVR_CHANNEL *channel);
+  static void PVRTransferTimerEntry(void *addonData, const PVRHANDLE handle, const PVR_TIMERINFO *timer);
+  static void PVRTransferRecordingEntry(void *addonData, const PVRHANDLE handle, const PVR_RECORDINGINFO *recording);
+  static void PVRAddMenuHook(void *addonData, PVR_MENUHOOK *hook);
+  static void PVRRecording(void *addonData, const char *Name, const char *FileName, bool On);
+  static void PVRTriggerTimerUpdate(void *addonData);
+  static void PVRTriggerRecordingUpdate(void *addonData);
+  static void PVRFreeDemuxPacket(void *addonData, DemuxPacket* pPacket);
+  static DemuxPacket* PVRAllocateDemuxPacket(void *addonData, int iDataSize = 0);
+
+private:
+  CB_PVRLib    *m_callbacks;
+  CAddon       *m_addon;
+};
+
+}; /* namespace ADDON */
diff --git a/xbmc/addons/AddonHelpers_local.cpp b/xbmc/addons/AddonHelpers_local.cpp
new file mode 100644 (file)
index 0000000..e0f1097
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "Addon.h"
+#include "AddonHelpers_local.h"
+#include "AddonHelpers_Addon.h"
+#include "AddonHelpers_GUI.h"
+#include "AddonHelpers_PVR.h"
+#include "FileSystem/SpecialProtocol.h"
+#include "log.h"
+
+namespace ADDON
+{
+
+CAddonHelpers::CAddonHelpers(CAddon* addon)
+{
+  m_addon       = addon;
+  m_callbacks   = new AddonCB;
+  m_helperAddon = NULL;
+  m_helperGUI   = NULL;
+  m_helperPVR   = NULL;
+
+  m_callbacks->libBasePath           = strdup(_P("special://xbmcbin/addons"));
+  m_callbacks->addonData             = this;
+  m_callbacks->AddOnLib_RegisterMe   = CAddonHelpers::AddOnLib_RegisterMe;
+  m_callbacks->AddOnLib_UnRegisterMe = CAddonHelpers::AddOnLib_UnRegisterMe;
+  m_callbacks->GUILib_RegisterMe     = CAddonHelpers::GUILib_RegisterMe;
+  m_callbacks->GUILib_UnRegisterMe   = CAddonHelpers::GUILib_UnRegisterMe;
+  m_callbacks->PVRLib_RegisterMe     = CAddonHelpers::PVRLib_RegisterMe;
+  m_callbacks->PVRLib_UnRegisterMe   = CAddonHelpers::PVRLib_UnRegisterMe;
+}
+
+CAddonHelpers::~CAddonHelpers()
+{
+  delete m_helperAddon;
+  m_helperAddon = NULL;
+  delete m_helperGUI;
+  m_helperGUI = NULL;
+  delete m_helperPVR;
+  m_helperPVR = NULL;
+  delete m_callbacks;
+  m_callbacks = NULL;
+}
+
+CB_AddOnLib* CAddonHelpers::AddOnLib_RegisterMe(void *addonData)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (helper == NULL)
+  {
+    CLog::Log(LOGERROR, "Addon-Helper: AddOnLib_RegisterMe is called with NULL-Pointer!!!");
+    return NULL;
+  }
+
+  helper->m_helperAddon = new CAddonHelpers_Addon(helper->m_addon);
+  return helper->m_helperAddon->GetCallbacks();
+}
+
+void CAddonHelpers::AddOnLib_UnRegisterMe(void *addonData, CB_AddOnLib *cbTable)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (helper == NULL)
+  {
+    CLog::Log(LOGERROR, "Addon-Helper: AddOnLib_UnRegisterMe is called with NULL-Pointer!!!");
+    return;
+  }
+
+  delete helper->m_helperAddon;
+  helper->m_helperAddon = NULL;
+}
+
+CB_GUILib* CAddonHelpers::GUILib_RegisterMe(void *addonData)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (helper == NULL)
+  {
+    CLog::Log(LOGERROR, "Addon-Helper: GUILib_RegisterMe is called with NULL-Pointer!!!");
+    return NULL;
+  }
+
+  helper->m_helperGUI = new CAddonHelpers_GUI(helper->m_addon);
+  return helper->m_helperGUI->GetCallbacks();
+}
+
+void CAddonHelpers::GUILib_UnRegisterMe(void *addonData, CB_GUILib *cbTable)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (helper == NULL)
+  {
+    CLog::Log(LOGERROR, "Addon-Helper: GUILib_UnRegisterMe is called with NULL-Pointer!!!");
+    return;
+  }
+
+  delete helper->m_helperGUI;
+  helper->m_helperGUI = NULL;
+}
+
+CB_PVRLib* CAddonHelpers::PVRLib_RegisterMe(void *addonData)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (helper == NULL)
+  {
+    CLog::Log(LOGERROR, "Addon-Helper: PVRLib_RegisterMe is called with NULL-Pointer!!!");
+    return NULL;
+  }
+
+  helper->m_helperPVR = new CAddonHelpers_PVR(helper->m_addon);
+  return helper->m_helperPVR->GetCallbacks();
+}
+
+void CAddonHelpers::PVRLib_UnRegisterMe(void *addonData, CB_PVRLib *cbTable)
+{
+  CAddonHelpers* helper = (CAddonHelpers*) addonData;
+  if (helper == NULL)
+  {
+    CLog::Log(LOGERROR, "Addon-Helper: PVRLib_UnRegisterMe is called with NULL-Pointer!!!");
+    return;
+  }
+
+  delete helper->m_helperPVR;
+  helper->m_helperPVR = NULL;
+}
+
+}; /* namespace ADDON */
diff --git a/xbmc/addons/AddonHelpers_local.h b/xbmc/addons/AddonHelpers_local.h
new file mode 100644 (file)
index 0000000..a37850c
--- /dev/null
@@ -0,0 +1,258 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "cores/dvdplayer/DVDDemuxers/DVDDemuxUtils.h"
+#include "addons/include/xbmc_pvr_types.h"
+#include "../../addons/library.xbmc.addon/libXBMC_addon.h"
+#include "../../addons/library.xbmc.gui/libXBMC_gui.h"
+
+typedef void (*AddOnLogCallback)(void *addonData, const addon_log_t loglevel, const char *msg);
+typedef void (*AddOnQueueNotification)(void *addonData, const queue_msg_t type, const char *msg);
+typedef bool (*AddOnGetSetting)(void *addonData, const char *settingName, void *settingValue);
+typedef char* (*AddOnUnknownToUTF8)(const char *sourceDest);
+typedef const char* (*AddOnGetLocalizedString)(const void* addonData, long dwCode);
+typedef const char* (*AddOnGetDVDMenuLanguage)(const void* addonData);
+
+typedef struct CB_AddOn
+{
+  AddOnLogCallback        Log;
+  AddOnQueueNotification  QueueNotification;
+  AddOnGetSetting         GetSetting;
+  AddOnUnknownToUTF8      UnknownToUTF8;
+  AddOnGetLocalizedString GetLocalizedString;
+  AddOnGetDVDMenuLanguage GetDVDMenuLanguage;
+} CB_AddOnLib;
+
+typedef void (*GUILock)();
+typedef void (*GUIUnlock)();
+typedef int (*GUIGetScreenHeight)();
+typedef int (*GUIGetScreenWidth)();
+typedef int (*GUIGetVideoResolution)();
+typedef GUIHANDLE   (*GUIWindow_New)(void *addonData, const char *xmlFilename, const char *defaultSkin, bool forceFallback, bool asDialog);
+typedef void        (*GUIWindow_Delete)(void *addonData, GUIHANDLE handle);
+typedef void        (*GUIWindow_SetCallbacks)(void *addonData, GUIHANDLE handle, GUIHANDLE clienthandle, bool (*)(GUIHANDLE handle), bool (*)(GUIHANDLE handle, int), bool (*)(GUIHANDLE handle, int), bool (*)(GUIHANDLE handle, int));
+typedef bool        (*GUIWindow_Show)(void *addonData, GUIHANDLE handle);
+typedef bool        (*GUIWindow_Close)(void *addonData, GUIHANDLE handle);
+typedef bool        (*GUIWindow_DoModal)(void *addonData, GUIHANDLE handle);
+typedef bool        (*GUIWindow_SetFocusId)(void *addonData, GUIHANDLE handle, int iControlId);
+typedef int         (*GUIWindow_GetFocusId)(void *addonData, GUIHANDLE handle);
+typedef bool        (*GUIWindow_SetCoordinateResolution)(void *addonData, GUIHANDLE handle, int res);
+typedef void        (*GUIWindow_SetProperty)(void *addonData, GUIHANDLE handle, const char *key, const char *value);
+typedef void        (*GUIWindow_SetPropertyInt)(void *addonData, GUIHANDLE handle, const char *key, int value);
+typedef void        (*GUIWindow_SetPropertyBool)(void *addonData, GUIHANDLE handle, const char *key, bool value);
+typedef void        (*GUIWindow_SetPropertyDouble)(void *addonData, GUIHANDLE handle, const char *key, double value);
+typedef const char* (*GUIWindow_GetProperty)(void *addonData, GUIHANDLE handle, const char *key);
+typedef int         (*GUIWindow_GetPropertyInt)(void *addonData, GUIHANDLE handle, const char *key);
+typedef bool        (*GUIWindow_GetPropertyBool)(void *addonData, GUIHANDLE handle, const char *key);
+typedef double      (*GUIWindow_GetPropertyDouble)(void *addonData, GUIHANDLE handle, const char *key);
+typedef void        (*GUIWindow_ClearProperties)(void *addonData, GUIHANDLE handle);
+typedef int         (*GUIWindow_GetListSize)(void *addonData, GUIHANDLE handle);
+typedef void        (*GUIWindow_ClearList)(void *addonData, GUIHANDLE handle);
+typedef GUIHANDLE   (*GUIWindow_AddItem)(void *addonData, GUIHANDLE handle, GUIHANDLE item, int itemPosition);
+typedef GUIHANDLE   (*GUIWindow_AddStringItem)(void *addonData, GUIHANDLE handle, const char *itemName, int itemPosition);
+typedef void        (*GUIWindow_RemoveItem)(void *addonData, GUIHANDLE handle, int itemPosition);
+typedef GUIHANDLE   (*GUIWindow_GetListItem)(void *addonData, GUIHANDLE handle, int listPos);
+typedef void        (*GUIWindow_SetCurrentListPosition)(void *addonData, GUIHANDLE handle, int listPos);
+typedef int         (*GUIWindow_GetCurrentListPosition)(void *addonData, GUIHANDLE handle);
+typedef GUIHANDLE   (*GUIWindow_GetControl_Spin)(void *addonData, GUIHANDLE handle, int controlId);
+typedef GUIHANDLE   (*GUIWindow_GetControl_Button)(void *addonData, GUIHANDLE handle, int controlId);
+typedef GUIHANDLE   (*GUIWindow_GetControl_RadioButton)(void *addonData, GUIHANDLE handle, int controlId);
+typedef GUIHANDLE   (*GUIWindow_GetControl_Edit)(void *addonData, GUIHANDLE handle, int controlId);
+typedef GUIHANDLE   (*GUIWindow_GetControl_Progress)(void *addonData, GUIHANDLE handle, int controlId);
+typedef void        (*GUIWindow_SetControlLabel)(void *addonData, GUIHANDLE handle, int controlId, const char *label);
+typedef void        (*GUIControl_Spin_SetVisible)(void *addonData, GUIHANDLE spinhandle, bool yesNo);
+typedef void        (*GUIControl_Spin_SetText)(void *addonData, GUIHANDLE spinhandle, const char *label);
+typedef void        (*GUIControl_Spin_Clear)(void *addonData, GUIHANDLE spinhandle);
+typedef void        (*GUIControl_Spin_AddLabel)(void *addonData, GUIHANDLE spinhandle, const char *label, int iValue);
+typedef int         (*GUIControl_Spin_GetValue)(void *addonData, GUIHANDLE spinhandle);
+typedef void        (*GUIControl_Spin_SetValue)(void *addonData, GUIHANDLE spinhandle, int iValue);
+typedef void        (*GUIControl_RadioButton_SetVisible)(void *addonData, GUIHANDLE handle, bool yesNo);
+typedef void        (*GUIControl_RadioButton_SetText)(void *addonData, GUIHANDLE handle, const char *label);
+typedef void        (*GUIControl_RadioButton_SetSelected)(void *addonData, GUIHANDLE handle, bool yesNo);
+typedef bool        (*GUIControl_RadioButton_IsSelected)(void *addonData, GUIHANDLE handle);
+typedef void        (*GUIControl_Progress_SetPercentage)(void *addonData, GUIHANDLE handle, float fPercent);
+typedef float       (*GUIControl_Progress_GetPercentage)(void *addonData, GUIHANDLE handle);
+typedef void        (*GUIControl_Progress_SetInfo)(void *addonData, GUIHANDLE handle, int iInfo);
+typedef int         (*GUIControl_Progress_GetInfo)(void *addonData, GUIHANDLE handle);
+typedef const char* (*GUIControl_Progress_GetDescription)(void *addonData, GUIHANDLE handle);
+typedef GUIHANDLE   (*GUIListItem_Create)(void *addonData, const char *label, const char *label2, const char *iconImage, const char *thumbnailImage, const char *path);
+typedef const char* (*GUIListItem_GetLabel)(void *addonData, GUIHANDLE handle);
+typedef void        (*GUIListItem_SetLabel)(void *addonData, GUIHANDLE handle, const char *label);
+typedef const char* (*GUIListItem_GetLabel2)(void *addonData, GUIHANDLE handle);
+typedef void        (*GUIListItem_SetLabel2)(void *addonData, GUIHANDLE handle, const char *label);
+typedef void        (*GUIListItem_SetIconImage)(void *addonData, GUIHANDLE handle, const char *image);
+typedef void        (*GUIListItem_SetThumbnailImage)(void *addonData, GUIHANDLE handle, const char *image);
+typedef void        (*GUIListItem_SetInfo)(void *addonData, GUIHANDLE handle, const char *info);
+typedef void        (*GUIListItem_SetProperty)(void *addonData, GUIHANDLE handle, const char *key, const char *value);
+typedef const char* (*GUIListItem_GetProperty)(void *addonData, GUIHANDLE handle, const char *key);
+typedef void        (*GUIListItem_SetPath)(void *addonData, GUIHANDLE handle, const char *path);
+
+typedef struct CB_GUILib
+{
+  GUILock                             Lock;
+  GUIUnlock                           Unlock;
+  GUIGetScreenHeight                  GetScreenHeight;
+  GUIGetScreenWidth                   GetScreenWidth;
+  GUIGetVideoResolution               GetVideoResolution;
+  GUIWindow_New                       Window_New;
+  GUIWindow_Delete                    Window_Delete;
+  GUIWindow_SetCallbacks              Window_SetCallbacks;
+  GUIWindow_Show                      Window_Show;
+  GUIWindow_Close                     Window_Close;
+  GUIWindow_DoModal                   Window_DoModal;
+  GUIWindow_SetFocusId                Window_SetFocusId;
+  GUIWindow_GetFocusId                Window_GetFocusId;
+  GUIWindow_SetCoordinateResolution   Window_SetCoordinateResolution;
+  GUIWindow_SetProperty               Window_SetProperty;
+  GUIWindow_SetPropertyInt            Window_SetPropertyInt;
+  GUIWindow_SetPropertyBool           Window_SetPropertyBool;
+  GUIWindow_SetPropertyDouble         Window_SetPropertyDouble;
+  GUIWindow_GetProperty               Window_GetProperty;
+  GUIWindow_GetPropertyInt            Window_GetPropertyInt;
+  GUIWindow_GetPropertyBool           Window_GetPropertyBool;
+  GUIWindow_GetPropertyDouble         Window_GetPropertyDouble;
+  GUIWindow_ClearProperties           Window_ClearProperties;
+  GUIWindow_GetListSize               Window_GetListSize;
+  GUIWindow_ClearList                 Window_ClearList;
+  GUIWindow_AddItem                   Window_AddItem;
+  GUIWindow_AddStringItem             Window_AddStringItem;
+  GUIWindow_RemoveItem                Window_RemoveItem;
+  GUIWindow_GetListItem               Window_GetListItem;
+  GUIWindow_SetCurrentListPosition    Window_SetCurrentListPosition;
+  GUIWindow_GetCurrentListPosition    Window_GetCurrentListPosition;
+  GUIWindow_GetControl_Spin           Window_GetControl_Spin;
+  GUIWindow_GetControl_Button         Window_GetControl_Button;
+  GUIWindow_GetControl_RadioButton    Window_GetControl_RadioButton;
+  GUIWindow_GetControl_Edit           Window_GetControl_Edit;
+  GUIWindow_GetControl_Progress       Window_GetControl_Progress;
+  GUIWindow_SetControlLabel           Window_SetControlLabel;
+  GUIControl_Spin_SetVisible          Control_Spin_SetVisible;
+  GUIControl_Spin_SetText             Control_Spin_SetText;
+  GUIControl_Spin_Clear               Control_Spin_Clear;
+  GUIControl_Spin_AddLabel            Control_Spin_AddLabel;
+  GUIControl_Spin_GetValue            Control_Spin_GetValue;
+  GUIControl_Spin_SetValue            Control_Spin_SetValue;
+  GUIControl_RadioButton_SetVisible   Control_RadioButton_SetVisible;
+  GUIControl_RadioButton_SetText      Control_RadioButton_SetText;
+  GUIControl_RadioButton_SetSelected  Control_RadioButton_SetSelected;
+  GUIControl_RadioButton_IsSelected   Control_RadioButton_IsSelected;
+  GUIControl_Progress_SetPercentage   Control_Progress_SetPercentage;
+  GUIControl_Progress_GetPercentage   Control_Progress_GetPercentage;
+  GUIControl_Progress_SetInfo         Control_Progress_SetInfo;
+  GUIControl_Progress_GetInfo         Control_Progress_GetInfo;
+  GUIControl_Progress_GetDescription  Control_Progress_GetDescription;
+  GUIListItem_Create                  ListItem_Create;
+  GUIListItem_GetLabel                ListItem_GetLabel;
+  GUIListItem_SetLabel                ListItem_SetLabel;
+  GUIListItem_GetLabel2               ListItem_GetLabel2;
+  GUIListItem_SetLabel2               ListItem_SetLabel2;
+  GUIListItem_SetIconImage            ListItem_SetIconImage;
+  GUIListItem_SetThumbnailImage       ListItem_SetThumbnailImage;
+  GUIListItem_SetInfo                 ListItem_SetInfo;
+  GUIListItem_SetProperty             ListItem_SetProperty;
+  GUIListItem_GetProperty             ListItem_GetProperty;
+  GUIListItem_SetPath                 ListItem_SetPath;
+
+} CB_GUILib;
+
+typedef void (*PVRTransferEpgEntry)(void *userData, const PVRHANDLE handle, const PVR_PROGINFO *epgentry);
+typedef void (*PVRTransferChannelEntry)(void *userData, const PVRHANDLE handle, const PVR_CHANNEL *chan);
+typedef void (*PVRTransferTimerEntry)(void *userData, const PVRHANDLE handle, const PVR_TIMERINFO *timer);
+typedef void (*PVRTransferRecordingEntry)(void *userData, const PVRHANDLE handle, const PVR_RECORDINGINFO *recording);
+typedef void (*PVRAddMenuHook)(void *addonData, PVR_MENUHOOK *hook);
+typedef void (*PVRRecording)(void *addonData, const char *Name, const char *FileName, bool On);
+typedef void (*PVRTriggerTimerUpdate)(void *addonData);
+typedef void (*PVRTriggerRecordingUpdate)(void *addonData);
+typedef void (*PVRFreeDemuxPacket)(void *addonData, DemuxPacket* pPacket);
+typedef DemuxPacket* (*PVRAllocateDemuxPacket)(void *addonData, int iDataSize);
+
+typedef struct CB_PVRLib
+{
+  PVRTransferEpgEntry       TransferEpgEntry;
+  PVRTransferChannelEntry   TransferChannelEntry;
+  PVRTransferTimerEntry     TransferTimerEntry;
+  PVRTransferRecordingEntry TransferRecordingEntry;
+  PVRAddMenuHook            AddMenuHook;
+  PVRRecording              Recording;
+  PVRTriggerTimerUpdate     TriggerTimerUpdate;
+  PVRTriggerRecordingUpdate TriggerRecordingUpdate;
+  PVRFreeDemuxPacket        FreeDemuxPacket;
+  PVRAllocateDemuxPacket    AllocateDemuxPacket;
+
+} CB_PVRLib;
+
+
+typedef CB_AddOnLib* (*XBMCAddOnLib_RegisterMe)(void *addonData);
+typedef void (*XBMCAddOnLib_UnRegisterMe)(void *addonData, CB_AddOnLib *cbTable);
+typedef CB_GUILib* (*XBMCGUILib_RegisterMe)(void *addonData);
+typedef void (*XBMCGUILib_UnRegisterMe)(void *addonData, CB_GUILib *cbTable);
+typedef CB_PVRLib* (*XBMCPVRLib_RegisterMe)(void *addonData);
+typedef void (*XBMCPVRLib_UnRegisterMe)(void *addonData, CB_PVRLib *cbTable);
+
+typedef struct AddonCB
+{
+  const char                *libBasePath;                  ///> Never, never change this!!!
+  void                      *addonData;
+  XBMCAddOnLib_RegisterMe    AddOnLib_RegisterMe;
+  XBMCAddOnLib_UnRegisterMe  AddOnLib_UnRegisterMe;
+  XBMCGUILib_RegisterMe      GUILib_RegisterMe;
+  XBMCGUILib_UnRegisterMe    GUILib_UnRegisterMe;
+  XBMCPVRLib_RegisterMe      PVRLib_RegisterMe;
+  XBMCPVRLib_UnRegisterMe    PVRLib_UnRegisterMe;
+} AddonCB;
+
+
+namespace ADDON
+{
+
+class CAddon;
+class CAddonHelpers_Addon;
+class CAddonHelpers_GUI;
+class CAddonHelpers_PVR;
+
+class CAddonHelpers
+{
+public:
+  CAddonHelpers(CAddon* addon);
+  ~CAddonHelpers();
+  AddonCB *GetCallbacks() { return m_callbacks; }
+
+  static CB_AddOnLib* AddOnLib_RegisterMe(void *addonData);
+  static void AddOnLib_UnRegisterMe(void *addonData, CB_AddOnLib *cbTable);
+  static CB_GUILib* GUILib_RegisterMe(void *addonData);
+  static void GUILib_UnRegisterMe(void *addonData, CB_GUILib *cbTable);
+  static CB_PVRLib* PVRLib_RegisterMe(void *addonData);
+  static void PVRLib_UnRegisterMe(void *addonData, CB_PVRLib *cbTable);
+
+  CAddonHelpers_Addon *GetHelperAddon() { return m_helperAddon; }
+  CAddonHelpers_GUI *GetHelperGUI() { return m_helperGUI; }
+  CAddonHelpers_PVR *GetHelperPVR() { return m_helperPVR; }
+
+private:
+  AddonCB             *m_callbacks;
+  CAddon              *m_addon;
+  CAddonHelpers_Addon *m_helperAddon;
+  CAddonHelpers_GUI   *m_helperGUI;
+  CAddonHelpers_PVR   *m_helperPVR;
+};
+
+}; /* namespace ADDON */
index d678082..94a0bb8 100644 (file)
 #include "DllScreenSaver.h"
 #include "ScreenSaver.h"
 #endif
+#ifdef HAS_PVRCLIENTS
+#include "DllPVRClient.h"
+#include "PVRClient.h"
+#endif
 //#ifdef HAS_SCRAPERS
 #include "Scraper.h"
 //#endif
@@ -101,6 +105,7 @@ AddonPtr CAddonMgr::Factory(const cp_extension_t *props)
       return AddonPtr(new CScraper(props));
     case ADDON_VIZ:
     case ADDON_SCREENSAVER:
+    case ADDON_PVRDLL:
       { // begin temporary platform handling for Dlls
         // ideally platforms issues will be handled by C-Pluff
         // this is not an attempt at a solution
@@ -131,6 +136,12 @@ AddonPtr CAddonMgr::Factory(const cp_extension_t *props)
           return AddonPtr(new CVisualisation(props));
 #endif
         }
+        else if (type == ADDON_PVRDLL)
+        {
+#ifdef HAS_PVRCLIENTS
+          return AddonPtr(new CPVRClient(props));
+#endif
+        }
         else
           return AddonPtr(new CScreenSaver(props));
       }
@@ -324,6 +335,7 @@ bool CAddonMgr::HasOutdatedAddons(bool enabled /*= true*/)
 
 bool CAddonMgr::GetAddons(const TYPE &type, VECADDONS &addons, bool enabled /* = true */)
 {
+  CStdString xbmcPath = _P("special://xbmc/addons");
   CSingleLock lock(m_critSection);
   addons.clear();
   cp_status_t status;
@@ -333,6 +345,11 @@ bool CAddonMgr::GetAddons(const TYPE &type, VECADDONS &addons, bool enabled /* =
   for(int i=0; i <num; i++)
   {
     AddonPtr addon(Factory(exts[i]));
+    if (addon && addon->Type() == ADDON_PVRDLL && addon->Path().Left(xbmcPath.size()).Equals(xbmcPath))
+    {
+      if (m_database.IsSystemPVRAddonEnabled(addon->ID()) != enabled)
+        addon->Disable();
+    }
     if (addon && m_database.IsAddonDisabled(addon->ID()) != enabled)
       addons.push_back(addon);
   }
@@ -344,14 +361,24 @@ bool CAddonMgr::GetAddon(const CStdString &str, AddonPtr &addon, const TYPE &typ
 {
   CSingleLock lock(m_critSection);
 
+  CStdString xbmcPath = _P("special://xbmc/addons");
   cp_status_t status;
   cp_plugin_info_t *cpaddon = m_cpluff->get_plugin_info(m_cp_context, str.c_str(), &status);
   if (status == CP_OK && cpaddon)
   {
     addon = GetAddonFromDescriptor(cpaddon);
     m_cpluff->release_info(m_cp_context, cpaddon);
-    if (addon.get() && enabledOnly && m_database.IsAddonDisabled(addon->ID()))
-      return false;
+
+    if (addon && addon.get() && enabledOnly)
+    {
+      if (addon->Type() == ADDON_PVRDLL && addon->Path().Left(xbmcPath.size()).Equals(xbmcPath))
+      {
+        if (!m_database.IsSystemPVRAddonEnabled(addon->ID()))
+          return false;
+      }
+      else if (m_database.IsAddonDisabled(addon->ID()))
+        return false;
+    }
     return NULL != addon.get();
   }
   if (cpaddon)
@@ -504,6 +531,8 @@ AddonPtr CAddonMgr::AddonFromProps(AddonProps& addonProps)
       return AddonPtr(new CScreenSaver(addonProps));
     case ADDON_VIZ_LIBRARY:
       return AddonPtr(new CAddonLibrary(addonProps));
+    case ADDON_PVRDLL:
+      return AddonPtr(new CPVRClient(addonProps));
     case ADDON_REPOSITORY:
       return AddonPtr(new CRepository(addonProps));
     default:
index fa9fdc6..bf4fb74 100644 (file)
@@ -49,6 +49,7 @@ namespace ADDON
   const CStdString ADDON_PYTHON_EXT           = "*.py";
   const CStdString ADDON_SCRAPER_EXT          = "*.xml";
   const CStdString ADDON_SCREENSAVER_EXT      = "*.xbs";
+  const CStdString ADDON_PVRDLL_EXT           = "*.pvr"; 
   const CStdString ADDON_DSP_AUDIO_EXT        = "*.adsp";
   const CStdString ADDON_VERSION_RE = "(?<Major>\\d*)\\.?(?<Minor>\\d*)?\\.?(?<Build>\\d*)?\\.?(?<Revision>\\d*)?";
 
diff --git a/xbmc/addons/DllPVRClient.h b/xbmc/addons/DllPVRClient.h
new file mode 100644 (file)
index 0000000..26e8868
--- /dev/null
@@ -0,0 +1,30 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "DllAddon.h"
+#include "include/xbmc_pvr_types.h"
+
+class DllPVRClient : public DllAddon<PVRClient, PVR_PROPS>
+{
+  // this is populated via Macro calls in DllAddon.h
+};
+
index 58bed9d..b479c27 100644 (file)
@@ -9,7 +9,13 @@ SRCS=Addon.cpp \
                 Service.cpp \
                 Skin.cpp \
                 Visualisation.cpp \
+                PVRClient.cpp \
                 fft.cpp \
+                Skin.cpp \
+                AddonHelpers_Addon.cpp \
+                AddonHelpers_GUI.cpp \
+                AddonHelpers_PVR.cpp \
+                AddonHelpers_local.cpp \
                 Repository.cpp \
 
 LIB=addons.a
diff --git a/xbmc/addons/PVRClient.cpp b/xbmc/addons/PVRClient.cpp
new file mode 100644 (file)
index 0000000..788fb44
--- /dev/null
@@ -0,0 +1,1007 @@
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+/*
+ * Description:
+ *
+ * Class CPVRClient is used as a specific interface between the PVR-Client
+ * library and the PVRManager. Every loaded Client have his own CPVRClient
+ * Class, it handle default data for the Manager in the case the Client
+ * can't provide the data and it act as exception handler for all function
+ * called inside client. Further it translate the "C" compatible data
+ * strucures to classes that can easily used by the PVRManager.
+ *
+ * It generate also a callback table with pointers to useful helper
+ * functions, that can be used inside the client to access XBMC
+ * internals.
+ */
+
+#include <vector>
+#include "Application.h"
+#include "LocalizeStrings.h"
+#include "StringUtils.h"
+#include "FileItem.h"
+#include "PVRClient.h"
+#include "pvr/PVRManager.h"
+#include "URL.h"
+#include "AdvancedSettings.h"
+#include "../utils/log.h"
+#include "../utils/SingleLock.h"
+
+using namespace std;
+using namespace ADDON;
+
+CPVRClient::CPVRClient(const ADDON::AddonProps& props) : CAddonDll<DllPVRClient, PVRClient, PVR_PROPS>(props)
+                              , m_ReadyToUse(false)
+                              , m_hostName("unknown")
+                              , m_iTimeCorrection(0)
+{
+}
+
+CPVRClient::CPVRClient(const cp_extension_t *ext) : CAddonDll<DllPVRClient, PVRClient, PVR_PROPS>(ext)
+                              , m_ReadyToUse(false)
+                              , m_hostName("unknown")
+                              , m_iTimeCorrection(0)
+{
+}
+
+CPVRClient::~CPVRClient()
+{
+}
+
+bool CPVRClient::Create(long clientID, IPVRClientCallback *pvrCB)
+{
+  CLog::Log(LOGDEBUG, "PVR: %s - Creating PVR-Client AddOn", Name().c_str());
+
+  m_manager = pvrCB;
+
+  m_pInfo           = new PVR_PROPS;
+  m_pInfo->clientID = clientID;
+  CStdString userpath = _P(Profile());
+  m_pInfo->userpath = userpath.c_str();
+  CStdString clientpath = _P(Path());
+  m_pInfo->clientpath = clientpath.c_str();
+
+  /* Call Create to make connections, initializing data or whatever is
+     needed to become the AddOn running */
+  if (CAddonDll<DllPVRClient, PVRClient, PVR_PROPS>::Create())
+  {
+    m_ReadyToUse = true;
+    m_hostName   = m_pStruct->GetConnectionString();
+    if (!g_advancedSettings.m_bDisableEPGTimeCorrection)
+    {
+      time_t localTime;
+      time_t backendTime = 0;
+      int    gmtOffset   = 0;
+      CDateTime::GetCurrentDateTime().GetAsTime(localTime);
+      PVR_ERROR err = GetBackendTime(&backendTime, &gmtOffset);
+      if (err == PVR_ERROR_NO_ERROR && gmtOffset != 0)
+      {
+        /* Is really a big time difference between PVR Backend and XBMC or only a bad GMT Offset? */
+        if (backendTime-localTime >= 30 || backendTime-localTime <= -30)
+        {
+          m_iTimeCorrection = gmtOffset;
+          CLog::Log(LOGDEBUG, "PVR: %s/%s - Using a timezone difference of '%i' minutes to correct EPG times", Name().c_str(), m_hostName.c_str(), m_iTimeCorrection/60);
+        }
+        else
+        {
+          m_iTimeCorrection = 0;
+          CLog::Log(LOGDEBUG, "PVR: %s/%s - Ignoring the timezone difference of '%i' minutes (No difference betweem XBMC and Backend Clock found)", Name().c_str(), m_hostName.c_str(), m_iTimeCorrection/60);
+        }
+      }
+    }
+    else if (g_advancedSettings.m_iUserDefinedEPGTimeCorrection > 0)
+    {
+      m_iTimeCorrection = g_advancedSettings.m_iUserDefinedEPGTimeCorrection*60;
+      CLog::Log(LOGDEBUG, "PVR: %s/%s - Using a userdefined timezone difference of '%i' minutes (taken from advancedsettings.xml)", Name().c_str(), m_hostName.c_str(), g_advancedSettings.m_iUserDefinedEPGTimeCorrection);
+    }
+    else
+    {
+      m_iTimeCorrection = 0;
+      CLog::Log(LOGDEBUG, "PVR: %s/%s - Timezone difference correction is disabled in advancedsettings.xml", Name().c_str(), m_hostName.c_str());
+    }
+  }
+
+  return m_ReadyToUse;
+}
+
+void CPVRClient::Destroy()
+{
+  /* tell the AddOn to disconnect and prepare for destruction */
+  try
+  {
+    CLog::Log(LOGDEBUG, "PVR: %s/%s - Destroying PVR-Client AddOn", Name().c_str(), m_hostName.c_str());
+    m_ReadyToUse = false;
+
+    /* Tell the client to destroy */
+    CAddonDll<DllPVRClient, PVRClient, PVR_PROPS>::Destroy();
+    m_menuhooks.clear();
+  }
+  catch (std::exception &e)
+  {
+    CLog::Log(LOGERROR, "PVR: %s/%s - exception '%s' during destruction of AddOn occurred, contact Developer '%s' of this AddOn", Name().c_str(), m_hostName.c_str(), e.what(), Author().c_str());
+  }
+}
+
+bool CPVRClient::ReCreate()
+{
+  long clientID = m_pInfo->clientID;
+  IPVRClientCallback *pvrCB = m_manager;
+  Destroy();
+  return Create(clientID, pvrCB);
+}
+
+long CPVRClient::GetID()
+{
+  return m_pInfo->clientID;
+}
+
+PVR_ERROR CPVRClient::GetProperties(PVR_SERVERPROPS *props)
+{
+  CSingleLock lock(m_critSection);
+
+  try
+  {
+    return m_pStruct->GetProperties(props);
+  }
+  catch (std::exception &e)
+  {
+    CLog::Log(LOGERROR, "PVR: %s/%s - exception '%s' during GetProperties occurred, contact Developer '%s' of this AddOn", Name().c_str(), m_hostName.c_str(), e.what(), Author().c_str());
+
+    /* Set all properties in a case of exception to not supported */
+    props->SupportChannelLogo        = false;
+    props->SupportTimeShift          = false;
+    props->SupportEPG                = false;
+    props->SupportRecordings         = false;
+    props->SupportTimers             = false;
+    props->SupportRadio              = false;
+    props->SupportChannelSettings    = false;
+    props->SupportDirector           = false;
+    props->SupportBouquets           = false;
+    props->SupportChannelScan        = false;
+  }
+  return PVR_ERROR_UNKOWN;
+}
+
+
+/**********************************************************
+ * General PVR Functions
+ */
+
+const std::string CPVRClient::GetBackendName()
+{
+  CSingleLock lock(m_critSection);
+
+  if (m_ReadyToUse)
+  {
+    try
+    {
+      return m_pStruct->GetBackendName();
+    }
+    catch (std::exception &e)
+    {
+      CLog::Log(LOGERROR, "PVR: %s/%s - exception '%s' during GetBackendName occurred, contact Developer '%s' of this AddOn", Name().c_str(), m_hostName.c_str(), e.what(), Author().c_str());
+    }
+  }
+  /* return string "Unavailable" as fallback */
+  return g_localizeStrings.Get(161);
+}
+
+const std::string CPVRClient::GetBackendVersion()
+{
+  CSingleLock lock(m_critSection);
+
+  if (m_ReadyToUse)
+  {
+    try
+    {
+      return m_pStruct->GetBackendVersion();
+    }
+    catch (std::exception &e)
+    {
+      CLog::Log(LOGERROR, "PVR: %s/%s - exception '%s' during GetBackendVersion occurred, contact Developer '%s' of this AddOn", Name().c_str(), m_hostName.c_str(), e.what(), Author().c_str());
+    }
+  }
+  /* return string "Unavailable" as fallback */
+  return g_localizeStrings.Get(161);
+}
+
+const std::string CPVRClient::GetConnectionString()
+{
+  CSingleLock lock(m_critSection);
+
+  if (m_ReadyToUse)
+  {
+    try
+    {
+      return m_pStruct->GetConnectionString();
+    }
+    catch (std::exception &e)
+    {
+      CLog::Log(LOGERROR, "PVR: %s/%s - exception '%s' during GetConnectionString occurred, contact Developer '%s' of this AddOn", Name().c_str(), m_hostName.c_str(), e.what(), Author().c_str());
+    }
+  }
+  /* return string "Unavailable" as fallback */
+  return g_localizeStrings.Get(161);
+}
+
+PVR_ERROR CPVRClient::GetDriveSpace(long long *total, long long *used)
+{
+  CSingleLock lock(m_critSection);
+
+  if (m_ReadyToUse)
+  {
+    try
+    {
+      return m_pStruct->GetDriveSpace(total, used);
+    }
+    catch (std::exception &e)
+    {
+      CLog::Log(LOGERROR, "PVR: %s/%s - exception '%s' during GetDriveSpace occurred, contact Developer '%s' of this AddOn", Name().c_str(), m_hostName.c_str(), e.what(), Author().c_str());
+    }
+  }
+  *total = 0;
+  *used  = 0;
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+PVR_ERROR CPVRClient::GetBackendTime(time_t *localTime, int *gmtOffset)
+{
+  CSingleLock lock(m_critSection);
+
+  if (m_ReadyToUse)
+  {
+    try
+    {
+      return m_pStruct->GetBackendTime(localTime, gmtOffset);
+    }
+    catch (std::exception &e)
+    {
+      CLog::Log(LOGERROR, "PVR: %s/%s - exception '%s' during GetBackendTime occurred, contact Developer '%s' of this AddOn", Name().c_str(), m_hostName.c_str(), e.what(), Author().c_str());
+    }
+  }
+  *localTime = 0;
+  *gmtOffset = 0;
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+PVR_ERROR CPVRClient::StartChannelScan()
+{
+  CSingleLock lock(m_critSection);
+
+  if (m_ReadyToUse)
+  {
+    try
+    {
+      return m_pStruct->DialogChannelScan();
+    }
+    catch (std::exception &e)
+    {
+      CLog::Log(LOGERROR, "PVR: %s/%s - exception '%s' during StartChannelScan occurred, contact Developer '%s' of this AddOn", Name().c_str(), m_hostName.c_str(), e.what(), Author().c_str());
+    }
+  }
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+void CPVRClient::CallMenuHook(const PVR_MENUHOOK &hook)
+{
+  CSingleLock lock(m_critSection);
+
+  if (m_ReadyToUse)
+  {
+    try
+    {
+      m_pStruct->MenuHook(hook);
+    }
+    catch (std::exception &e)
+    {
+      CLog::Log(LOGERROR, "PVR: %s/%s - exception '%s' during CallMenuHook occurred, contact Developer '%s' of this AddOn", Name().c_str(), m_hostName.c_str(), e.what(), Author().c_str());
+    }
+  }
+}
+
+/**********************************************************
+ * EPG PVR Functions
+ */
+
+PVR_ERROR CPVRClient::GetEPGForChannel(const CPVRChannel &channelinfo, CPVREpg *epg, time_t start, time_t end, bool toDB/* = false*/)
+{
+  CSingleLock lock(m_critSection);
+
+  PVR_ERROR ret = PVR_ERROR_UNKOWN;
+
+  if (m_ReadyToUse)
+  {
+    try
+    {
+      if (start)
+        start -= m_iTimeCorrection;
+      if (end)
+        end -= m_iTimeCorrection;
+      PVR_CHANNEL tag;
+      PVRHANDLE_STRUCT handle;
+      handle.CALLER_ADDRESS = this;
+      handle.DATA_ADDRESS = (CPVREpg*) epg;
+      handle.DATA_IDENTIFIER = toDB ? 1 : 0;
+      WriteClientChannelInfo(channelinfo, tag);
+      ret = m_pStruct->RequestEPGForChannel(&handle, tag, start, end);
+      if (ret != PVR_ERROR_NO_ERROR)
+        throw ret;
+
+      return PVR_ERROR_NO_ERROR;
+    }
+    catch (std::exception &e)
+    {
+      CLog::Log(LOGERROR, "PVR: %s/%s - exception '%s' during GetEPGForChannel occurred, contact Developer '%s' of this AddOn", Name().c_str(), m_hostName.c_str(), e.what(), Author().c_str());
+    }
+    catch (PVR_ERROR ret)
+    {
+      CLog::Log(LOGERROR, "PVR: %s/%s - Client returns bad error (%i) after GetEPGForChannel", Name().c_str(), m_hostName.c_str(), ret);
+    }
+  }
+  return ret;
+}
+
+
+/**********************************************************
+ * Channels PVR Functions
+ */
+
+int CPVRClient::GetNumChannels()
+{
+  CSingleLock lock(m_critSection);
+
+  if (m_ReadyToUse)
+  {
+    try
+    {
+      return m_pStruct->GetNumChannels();
+    }
+    catch (std::exception &e)
+    {
+      CLog::Log(LOGERROR, "PVR: %s/%s - exception '%s' during GetNumChannels occurred, contact Developer '%s' of this AddOn", Name().c_str(), m_hostName.c_str(), e.what(), Author().c_str());
+    }
+  }
+  return -1;
+}
+
+PVR_ERROR CPVRClient::GetChannelList(CPVRChannels &channels, bool radio)
+{
+  CSingleLock lock(m_critSection);
+
+  PVR_ERROR ret = PVR_ERROR_UNKOWN;
+
+  if (m_ReadyToUse)
+  {
+    try
+    {
+      PVRHANDLE_STRUCT handle;
+      handle.CALLER_ADDRESS = this;
+      handle.DATA_ADDRESS = (CPVRChannels*) &channels;
+      ret = m_pStruct->RequestChannelList(&handle, radio);
+      if (ret != PVR_ERROR_NO_ERROR)
+        throw ret;
+
+      return PVR_ERROR_NO_ERROR;
+    }
+    catch (std::exception &e)
+    {
+      CLog::Log(LOGERROR, "PVR: %s/%s - exception '%s' during GetChannelList occurred, contact Developer '%s' of this AddOn", Name().c_str(), m_hostName.c_str(), e.what(), Author().c_str());
+    }
+    catch (PVR_ERROR ret)
+    {
+      CLog::Log(LOGERROR, "PVR: %s/%s - Client returns bad error (%i) after GetChannelList", Name().c_str(), m_hostName.c_str(), ret);
+    }
+  }
+  return ret;
+}
+
+/**********************************************************
+ * Recordings PVR Functions
+ */
+
+int CPVRClient::GetNumRecordings(void)
+{
+  CSingleLock lock(m_critSection);
+
+  if (m_ReadyToUse)
+  {
+    try
+    {
+      return m_pStruct->GetNumRecordings();
+    }
+    catch (std::exception &e)
+    {
+      CLog::Log(LOGERROR, "PVR: %s/%s - exception '%s' during GetNumRecordings occurred, contact Developer '%s' of this AddOn", Name().c_str(), m_hostName.c_str(), e.what(), Author().c_str());
+    }
+  }
+  return -1;
+}
+
+PVR_ERROR CPVRClient::GetAllRecordings(CPVRRecordings *results)
+{
+  CSingleLock lock(m_critSection);
+
+  PVR_ERROR ret = PVR_ERROR_UNKOWN;
+
+  if (m_ReadyToUse)
+  {
+    try
+    {
+      PVRHANDLE_STRUCT handle;
+      handle.CALLER_ADDRESS = this;
+      handle.DATA_ADDRESS = (CPVRRecordings*) results;
+      ret = m_pStruct->RequestRecordingsList(&handle);
+      if (ret != PVR_ERROR_NO_ERROR)
+        throw ret;
+
+      return PVR_ERROR_NO_ERROR;
+    }
+    catch (std::exception &e)
+    {
+      CLog::Log(LOGERROR, "PVR: %s/%s - exception '%s' during GetAllRecordings occurred, contact Developer '%s' of this AddOn", Name().c_str(), m_hostName.c_str(), e.what(), Author().c_str());
+    }
+    catch (PVR_ERROR ret)
+    {
+      CLog::Log(LOGERROR, "PVR: %s/%s - Client returns bad error (%i) after GetAllRecordings", Name().c_str(), m_hostName.c_str(), ret);
+    }
+  }
+  return ret;
+}
+
+PVR_ERROR CPVRClient::DeleteRecording(const CPVRRecordingInfoTag &recinfo)
+{
+  CSingleLock lock(m_critSection);
+
+  PVR_ERROR ret = PVR_ERROR_UNKOWN;
+
+  if (m_ReadyToUse)
+  {
+    try
+    {
+      PVR_RECORDINGINFO tag;
+      WriteClientRecordingInfo(recinfo, tag);
+
+      ret = m_pStruct->DeleteRecording(tag);
+      if (ret != PVR_ERROR_NO_ERROR)
+        throw ret;
+
+      return PVR_ERROR_NO_ERROR;
+    }
+    catch (std::exception &e)
+    {
+      CLog::Log(LOGERROR, "PVR: %s/%s - exception '%s' during DeleteRecording occurred, contact Developer '%s' of this AddOn", Name().c_str(), m_hostName.c_str(), e.what(), Author().c_str());
+    }
+    catch (PVR_ERROR ret)
+    {
+      CLog::Log(LOGERROR, "PVR: %s/%s - Client returns bad error (%i) after DeleteRecording", Name().c_str(), m_hostName.c_str(), ret);
+    }
+  }
+  return ret;
+}
+
+PVR_ERROR CPVRClient::RenameRecording(const CPVRRecordingInfoTag &recinfo, CStdString &newname)
+{
+  CSingleLock lock(m_critSection);
+
+  PVR_ERROR ret = PVR_ERROR_UNKOWN;
+
+  if (m_ReadyToUse)
+  {
+    try
+    {
+      PVR_RECORDINGINFO tag;
+      WriteClientRecordingInfo(recinfo, tag);
+
+      ret = m_pStruct->RenameRecording(tag, newname);
+      if (ret != PVR_ERROR_NO_ERROR)
+        throw ret;
+
+      return PVR_ERROR_NO_ERROR;
+    }
+    catch (std::exception &e)
+    {
+      CLog::Log(LOGERROR, "PVR: %s/%s - exception '%s' during RenameRecording occurred, contact Developer '%s' of this AddOn", Name().c_str(), m_hostName.c_str(), e.what(), Author().c_str());
+    }
+    catch (PVR_ERROR ret)
+    {
+      CLog::Log(LOGERROR, "PVR: %s/%s - Client returns bad error (%i) after RenameRecording", Name().c_str(), m_hostName.c_str(), ret);
+    }
+  }
+  return ret;
+}
+
+void CPVRClient::WriteClientRecordingInfo(const CPVRRecordingInfoTag &recordinginfo, PVR_RECORDINGINFO &tag)
+{
+  time_t recTime;
+  recordinginfo.RecordingTime().GetAsTime(recTime);
+  tag.recording_time= recTime+m_iTimeCorrection;
+  tag.index         = recordinginfo.ClientIndex();
+  tag.title         = recordinginfo.Title();
+  tag.subtitle      = recordinginfo.PlotOutline();
+  tag.description   = recordinginfo.Plot();
+  tag.channel_name  = recordinginfo.ChannelName();
+  tag.duration      = recordinginfo.GetDuration();
+  tag.priority      = recordinginfo.Priority();
+  tag.lifetime      = recordinginfo.Lifetime();
+  tag.directory     = recordinginfo.Directory();
+  tag.stream_url    = recordinginfo.StreamURL();
+  return;
+}
+
+
+/**********************************************************
+ * Timers PVR Functions
+ */
+
+int CPVRClient::GetNumTimers(void)
+{
+  CSingleLock lock(m_critSection);
+
+  if (m_ReadyToUse)
+  {
+    try
+    {
+      return m_pStruct->GetNumTimers();
+    }
+    catch (std::exception &e)
+    {
+      CLog::Log(LOGERROR, "PVR: %s/%s - exception '%s' during GetNumTimers occurred, contact Developer '%s' of this AddOn", Name().c_str(), m_hostName.c_str(), e.what(), Author().c_str());
+    }
+  }
+  return -1;
+}
+
+PVR_ERROR CPVRClient::GetAllTimers(CPVRTimers *results)
+{
+  CSingleLock lock(m_critSection);
+
+  PVR_ERROR ret = PVR_ERROR_UNKOWN;
+
+  if (m_ReadyToUse)
+  {
+    try
+    {
+      PVRHANDLE_STRUCT handle;
+      handle.CALLER_ADDRESS = this;
+      handle.DATA_ADDRESS = (CPVRTimers*) results;
+      ret = m_pStruct->RequestTimerList(&handle);
+      if (ret != PVR_ERROR_NO_ERROR)
+        throw ret;
+
+      return PVR_ERROR_NO_ERROR;
+    }
+    catch (std::exception &e)
+    {
+      CLog::Log(LOGERROR, "PVR: %s/%s - exception '%s' during GetAllTimers occurred, contact Developer '%s' of this AddOn", Name().c_str(), m_hostName.c_str(), e.what(), Author().c_str());
+    }
+    catch (PVR_ERROR ret)
+    {
+      CLog::Log(LOGERROR, "PVR: %s/%s - Client returns bad error (%i) after GetAllTimers", Name().c_str(), m_hostName.c_str(), ret);
+    }
+  }
+  return ret;
+}
+
+PVR_ERROR CPVRClient::AddTimer(const CPVRTimerInfoTag &timerinfo)
+{
+  CSingleLock lock(m_critSection);
+
+  PVR_ERROR ret = PVR_ERROR_UNKOWN;
+
+  if (m_ReadyToUse)
+  {
+    try
+    {
+      PVR_TIMERINFO tag;
+      WriteClientTimerInfo(timerinfo, tag);
+
+      //Workaround for string transfer to PVRclient
+      CStdString myTitle = timerinfo.Title();
+      CStdString myDirectory = timerinfo.Dir();
+      tag.title = myTitle.c_str();
+      tag.directory = myDirectory.c_str();
+
+      ret = m_pStruct->AddTimer(tag);
+      if (ret != PVR_ERROR_NO_ERROR)
+        throw ret;
+
+      return PVR_ERROR_NO_ERROR;
+    }
+    catch (std::exception &e)
+    {
+      CLog::Log(LOGERROR, "PVR: %s/%s - exception '%s' during AddTimer occurred, contact Developer '%s' of this AddOn", Name().c_str(), m_hostName.c_str(), e.what(), Author().c_str());
+    }
+    catch (PVR_ERROR ret)
+    {
+      CLog::Log(LOGERROR, "PVR: %s/%s - Client returns bad error (%i) after AddTimer", Name().c_str(), m_hostName.c_str(), ret);
+    }
+  }
+  return ret;
+}
+
+PVR_ERROR CPVRClient::DeleteTimer(const CPVRTimerInfoTag &timerinfo, bool force)
+{
+  CSingleLock lock(m_critSection);
+
+  PVR_ERROR ret = PVR_ERROR_UNKOWN;
+
+  if (m_ReadyToUse)
+  {
+    try
+    {
+      PVR_TIMERINFO tag;
+      WriteClientTimerInfo(timerinfo, tag);
+
+      //Workaround for string transfer to PVRclient
+      CStdString myTitle = timerinfo.Title();
+      tag.title = myTitle.c_str();
+
+      ret = m_pStruct->DeleteTimer(tag, force);
+      if (ret != PVR_ERROR_NO_ERROR)
+        throw ret;
+
+      return PVR_ERROR_NO_ERROR;
+    }
+    catch (std::exception &e)
+    {
+      CLog::Log(LOGERROR, "PVR: %s/%s - exception '%s' during DeleteTimer occurred, contact Developer '%s' of this AddOn", Name().c_str(), m_hostName.c_str(), e.what(), Author().c_str());
+    }
+    catch (PVR_ERROR ret)
+    {
+      CLog::Log(LOGERROR, "PVR: %s/%s - Client returns bad error (%i) after DeleteTimer", Name().c_str(), m_hostName.c_str(), ret);
+    }
+  }
+  return ret;
+}
+
+PVR_ERROR CPVRClient::RenameTimer(const CPVRTimerInfoTag &timerinfo, const CStdString &newname)
+{
+  CSingleLock lock(m_critSection);
+
+  PVR_ERROR ret = PVR_ERROR_UNKOWN;
+
+  if (m_ReadyToUse)
+  {
+    try
+    {
+      PVR_TIMERINFO tag;
+      WriteClientTimerInfo(timerinfo, tag);
+
+      //Workaround for string transfer to PVRclient
+      CStdString myTitle = timerinfo.Title();
+      tag.title = myTitle.c_str();
+
+      ret = m_pStruct->RenameTimer(tag, newname.c_str());
+      if (ret != PVR_ERROR_NO_ERROR)
+        throw ret;
+
+      return PVR_ERROR_NO_ERROR;
+    }
+    catch (std::exception &e)
+    {
+      CLog::Log(LOGERROR, "PVR: %s/%s - exception '%s' during RenameTimer occurred, contact Developer '%s' of this AddOn", Name().c_str(), m_hostName.c_str(), e.what(), Author().c_str());
+    }
+    catch (PVR_ERROR ret)
+    {
+      CLog::Log(LOGERROR, "PVR: %s/%s - Client returns bad error (%i) after RenameTimer", Name().c_str(), m_hostName.c_str(), ret);
+    }
+  }
+  return ret;
+}
+
+PVR_ERROR CPVRClient::UpdateTimer(const CPVRTimerInfoTag &timerinfo)
+{
+  CSingleLock lock(m_critSection);
+
+  PVR_ERROR ret = PVR_ERROR_UNKOWN;
+//
+  if (m_ReadyToUse)
+  {
+    try
+    {
+      PVR_TIMERINFO tag;
+      WriteClientTimerInfo(timerinfo, tag);
+
+      //Workaround for string transfer to PVRclient
+      CStdString myTitle = timerinfo.Title();
+      CStdString myDirectory = timerinfo.Dir();
+      tag.title = myTitle.c_str();
+      tag.directory = myDirectory.c_str();
+
+      ret = m_pStruct->UpdateTimer(tag);
+      if (ret != PVR_ERROR_NO_ERROR)
+        throw ret;
+
+      return PVR_ERROR_NO_ERROR;
+    }
+    catch (std::exception &e)
+    {
+      CLog::Log(LOGERROR, "PVR: %s/%s - exception '%s' during UpdateTimer occurred, contact Developer '%s' of this AddOn", Name().c_str(), m_hostName.c_str(), e.what(), Author().c_str());
+    }
+    catch (PVR_ERROR ret)
+    {
+      CLog::Log(LOGERROR, "PVR: %s/%s - Client returns bad error (%i) after UpdateTimer", Name().c_str(), m_hostName.c_str(), ret);
+    }
+  }
+  return ret;
+}
+
+void CPVRClient::WriteClientTimerInfo(const CPVRTimerInfoTag &timerinfo, PVR_TIMERINFO &tag)
+{
+  tag.index         = timerinfo.ClientIndex();
+  tag.active        = timerinfo.Active();
+  tag.channelNum    = timerinfo.ClientNumber();
+  tag.recording     = timerinfo.IsRecording();
+  tag.title         = timerinfo.Title();
+  tag.directory     = timerinfo.Dir();
+  tag.priority      = timerinfo.Priority();
+  tag.lifetime      = timerinfo.Lifetime();
+  tag.repeat        = timerinfo.IsRepeating();
+  tag.repeatflags   = timerinfo.Weekdays();
+  tag.starttime     = timerinfo.StartTime();
+  tag.starttime    -= m_iTimeCorrection;
+  tag.endtime       = timerinfo.StopTime();
+  tag.endtime      -= m_iTimeCorrection;
+  tag.firstday      = timerinfo.FirstDayTime();
+  tag.firstday     -= m_iTimeCorrection;
+  return;
+}
+
+/**********************************************************
+ * Stream PVR Functions
+ */
+
+bool CPVRClient::OpenLiveStream(const CPVRChannel &channelinfo)
+{
+  CSingleLock lock(m_critSection);
+
+  if (m_ReadyToUse)
+  {
+    try
+    {
+      PVR_CHANNEL tag;
+      WriteClientChannelInfo(channelinfo, tag);
+      return m_pStruct->OpenLiveStream(tag);
+    }
+    catch (std::exception &e)
+    {
+      CLog::Log(LOGERROR, "PVR: %s/%s - exception '%s' during OpenLiveStream occurred, contact Developer '%s' of this AddOn", Name().c_str(), m_hostName.c_str(), e.what(), Author().c_str());
+    }
+  }
+  return false;
+}
+
+void CPVRClient::CloseLiveStream()
+{
+  CSingleLock lock(m_critSection);
+
+  if (m_ReadyToUse)
+  {
+    try
+    {
+      m_pStruct->CloseLiveStream();
+      return;
+    }
+    catch (std::exception &e)
+    {
+      CLog::Log(LOGERROR, "PVR: %s/%s - exception '%s' during CloseLiveStream occurred, contact Developer '%s' of this AddOn", Name().c_str(), m_hostName.c_str(), e.what(), Author().c_str());
+    }
+  }
+  return;
+}
+
+int CPVRClient::ReadLiveStream(void* lpBuf, int64_t uiBufSize)
+{
+  return m_pStruct->ReadLiveStream((unsigned char *)lpBuf, uiBufSize);
+}
+
+int64_t CPVRClient::SeekLiveStream(int64_t iFilePosition, int iWhence/* = SEEK_SET*/)
+{
+  return m_pStruct->SeekLiveStream(iFilePosition, iWhence);
+}
+
+int64_t CPVRClient::PositionLiveStream()
+{
+  return m_pStruct->PositionLiveStream();
+}
+
+int64_t CPVRClient::LengthLiveStream(void)
+{
+  return m_pStruct->LengthLiveStream();
+}
+
+int CPVRClient::GetCurrentClientChannel()
+{
+  CSingleLock lock(m_critSection);
+
+  return m_pStruct->GetCurrentClientChannel();
+}
+
+bool CPVRClient::SwitchChannel(const CPVRChannel &channelinfo)
+{
+  CSingleLock lock(m_critSection);
+
+  PVR_CHANNEL tag;
+  WriteClientChannelInfo(channelinfo, tag);
+  return m_pStruct->SwitchChannel(tag);
+}
+
+bool CPVRClient::SignalQuality(PVR_SIGNALQUALITY &qualityinfo)
+{
+  CSingleLock lock(m_critSection);
+
+  if (m_ReadyToUse)
+  {
+    PVR_ERROR ret = PVR_ERROR_UNKOWN;
+    try
+    {
+      ret = m_pStruct->SignalQuality(qualityinfo);
+      if (ret != PVR_ERROR_NO_ERROR)
+        throw ret;
+
+      return true;
+    }
+    catch (std::exception &e)
+    {
+      CLog::Log(LOGERROR, "PVR: %s/%s - exception '%s' during SignalQuality occurred, contact Developer '%s' of this AddOn", Name().c_str(), m_hostName.c_str(), e.what(), Author().c_str());
+    }
+    catch (PVR_ERROR ret)
+    {
+      CLog::Log(LOGERROR, "PVR: %s/%s - Client returns bad error (%i) after SignalQuality", Name().c_str(), m_hostName.c_str(), ret);
+    }
+  }
+  return false;
+}
+
+const std::string CPVRClient::GetLiveStreamURL(const CPVRChannel &channelinfo)
+{
+  CSingleLock lock(m_critSection);
+
+  if (m_ReadyToUse)
+  {
+    try
+    {
+      PVR_CHANNEL tag;
+      WriteClientChannelInfo(channelinfo, tag);
+      return m_pStruct->GetLiveStreamURL(tag);
+    }
+    catch (std::exception &e)
+    {
+      CLog::Log(LOGERROR, "PVR: %s/%s - exception '%s' during GetLiveStreamURL occurred, contact Developer '%s' of this AddOn", Name().c_str(), m_hostName.c_str(), e.what(), Author().c_str());
+    }
+  }
+  /* return string "Unavailable" as fallback */
+  return g_localizeStrings.Get(161);
+}
+
+void CPVRClient::WriteClientChannelInfo(const CPVRChannel &channelinfo, PVR_CHANNEL &tag)
+{
+  tag.uid               = channelinfo.UniqueID();
+  tag.number            = channelinfo.ClientChannelNumber();
+  tag.name              = channelinfo.ChannelName().c_str();
+  tag.callsign          = channelinfo.ClientChannelName().c_str();
+  tag.iconpath          = channelinfo.IconPath().c_str();
+  tag.encryption        = channelinfo.EncryptionSystem();
+  tag.radio             = channelinfo.IsRadio();
+  tag.hide              = channelinfo.IsHidden();
+  tag.recording         = channelinfo.IsRecording();
+  tag.bouquet           = 0;
+  tag.multifeed         = false;
+  tag.multifeed_master  = 0;
+  tag.multifeed_number  = 0;
+  tag.input_format      = channelinfo.InputFormat();
+  tag.stream_url        = channelinfo.StreamURL();
+  return;
+}
+
+bool CPVRClient::OpenRecordedStream(const CPVRRecordingInfoTag &recinfo)
+{
+  CSingleLock lock(m_critSection);
+
+  PVR_RECORDINGINFO tag;
+  WriteClientRecordingInfo(recinfo, tag);
+  return m_pStruct->OpenRecordedStream(tag);
+}
+
+void CPVRClient::CloseRecordedStream(void)
+{
+  CSingleLock lock(m_critSection);
+
+  return m_pStruct->CloseRecordedStream();
+}
+
+int CPVRClient::ReadRecordedStream(void* lpBuf, int64_t uiBufSize)
+{
+  return m_pStruct->ReadRecordedStream((unsigned char *)lpBuf, uiBufSize);
+}
+
+int64_t CPVRClient::SeekRecordedStream(int64_t iFilePosition, int iWhence/* = SEEK_SET*/)
+{
+  return m_pStruct->SeekRecordedStream(iFilePosition, iWhence);
+}
+
+int64_t CPVRClient::PositionRecordedStream()
+{
+  return m_pStruct->PositionRecordedStream();
+}
+
+int64_t CPVRClient::LengthRecordedStream(void)
+{
+  return m_pStruct->LengthRecordedStream();
+}
+
+PVR_ERROR CPVRClient::GetStreamProperties(PVR_STREAMPROPS *props)
+{
+  CSingleLock lock(m_critSection);
+
+  try
+  {
+    return m_pStruct->GetStreamProperties(props);
+  }
+  catch (std::exception &e)
+  {
+    CLog::Log(LOGERROR, "PVR: %s/%s - exception '%s' during GetStreamProperties occurred, contact Developer '%s' of this AddOn", Name().c_str(), m_hostName.c_str(), e.what(), Author().c_str());
+
+    /* Set all properties in a case of exception to not supported */
+  }
+  return PVR_ERROR_UNKOWN;
+}
+
+void CPVRClient::DemuxReset()
+{
+  m_pStruct->DemuxReset();
+}
+
+void CPVRClient::DemuxAbort()
+{
+  m_pStruct->DemuxAbort();
+}
+
+void CPVRClient::DemuxFlush()
+{
+  m_pStruct->DemuxFlush();
+}
+
+DemuxPacket* CPVRClient::DemuxRead()
+{
+  return m_pStruct->DemuxRead();
+}
+
+/**********************************************************
+ * Addon specific functions
+ * Are used for every type of AddOn
+ */
+
+ADDON_STATUS CPVRClient::SetSetting(const char *settingName, const void *settingValue)
+{
+//  CSingleLock lock(m_critSection);
+//
+//  try
+//  {
+//    return m_pDll->SetSetting(settingName, settingValue);
+//  }
+//  catch (std::exception &e)
+//  {
+//    CLog::Log(LOGERROR, "PVR: %s/%s - exception '%s' during SetSetting occurred, contact Developer '%s' of this AddOn", Name().c_str(), m_hostName.c_str(), e.what(), Author().c_str());
+    return STATUS_UNKNOWN;
+//  }
+}
diff --git a/xbmc/addons/PVRClient.h b/xbmc/addons/PVRClient.h
new file mode 100644 (file)
index 0000000..8a50990
--- /dev/null
@@ -0,0 +1,131 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "Addon.h"
+#include "AddonDll.h"
+#include "DllPVRClient.h"
+#include "../pvr/PVREpgs.h"
+#include "../pvr/PVRChannels.h"
+#include "../pvr/PVRTimers.h"
+#include "../pvr/PVRTimerInfoTag.h"
+#include "../pvr/PVRRecordings.h"
+
+typedef std::vector<PVR_MENUHOOK> PVR_MENUHOOKS;
+
+class IPVRClientCallback
+{
+public:
+  virtual void OnClientMessage(const long clientID, const PVR_EVENT clientEvent, const char* msg)=0;
+};
+
+class CPVRClient : public ADDON::CAddonDll<DllPVRClient, PVRClient, PVR_PROPS>
+{
+public:
+  CPVRClient(const ADDON::AddonProps& props);
+  CPVRClient(const cp_extension_t *ext);
+  ~CPVRClient();
+
+  bool Create(long clientID, IPVRClientCallback *pvrCB);
+  void Destroy();
+  bool ReCreate();
+
+  /* DLL related */
+  bool ReadyToUse() { return m_ReadyToUse; }
+  virtual ADDON_STATUS SetSetting(const char *settingName, const void *settingValue);
+
+  /* Server */
+  long GetID();
+  PVR_ERROR GetProperties(PVR_SERVERPROPS *props);
+
+  /* General */
+  const std::string GetBackendName();
+  const std::string GetBackendVersion();
+  const std::string GetConnectionString();
+  PVR_ERROR GetDriveSpace(long long *total, long long *used);
+  PVR_ERROR GetBackendTime(time_t *localTime, int *gmtOffset);
+  PVR_ERROR StartChannelScan();
+  int GetTimeCorrection() { return m_iTimeCorrection; }
+  int GetClientID() { return m_pInfo->clientID; }
+  bool HaveMenuHooks() { return m_menuhooks.size() > 0; }
+  PVR_MENUHOOKS *GetMenuHooks() { return &m_menuhooks; }
+  void CallMenuHook(const PVR_MENUHOOK &hook);
+
+  /* TV Guide */
+  PVR_ERROR GetEPGForChannel(const CPVRChannel &channelinfo, CPVREpg *epg, time_t start, time_t end, bool toDB = false);
+
+  /* Channels */
+  int GetNumChannels();
+  PVR_ERROR GetChannelList(CPVRChannels &channels, bool radio);
+
+  /* Recordings */
+  int GetNumRecordings(void);
+  PVR_ERROR GetAllRecordings(CPVRRecordings *results);
+  PVR_ERROR DeleteRecording(const CPVRRecordingInfoTag &recinfo);
+  PVR_ERROR RenameRecording(const CPVRRecordingInfoTag &recinfo, CStdString &newname);
+
+  /* Timers */
+  int GetNumTimers(void);
+  PVR_ERROR GetAllTimers(CPVRTimers *results);
+  PVR_ERROR AddTimer(const CPVRTimerInfoTag &timerinfo);
+  PVR_ERROR DeleteTimer(const CPVRTimerInfoTag &timerinfo, bool force = false);
+  PVR_ERROR RenameTimer(const CPVRTimerInfoTag &timerinfo, const CStdString &newname);
+  PVR_ERROR UpdateTimer(const CPVRTimerInfoTag &timerinfo);
+
+  bool OpenLiveStream(const CPVRChannel &channelinfo);
+  void CloseLiveStream();
+  int ReadLiveStream(void* lpBuf, int64_t uiBufSize);
+  int64_t SeekLiveStream(int64_t iFilePosition, int iWhence = SEEK_SET);
+  int64_t PositionLiveStream(void);
+  int64_t LengthLiveStream(void);
+  int GetCurrentClientChannel();
+  bool SwitchChannel(const CPVRChannel &channelinfo);
+  bool SignalQuality(PVR_SIGNALQUALITY &qualityinfo);
+  const std::string GetLiveStreamURL(const CPVRChannel &channelinfo);
+
+  bool OpenRecordedStream(const CPVRRecordingInfoTag &recinfo);
+  void CloseRecordedStream(void);
+  int ReadRecordedStream(void* lpBuf, int64_t uiBufSize);
+  int64_t SeekRecordedStream(int64_t iFilePosition, int iWhence = SEEK_SET);
+  int64_t PositionRecordedStream(void);
+  int64_t LengthRecordedStream(void);
+
+  PVR_ERROR GetStreamProperties(PVR_STREAMPROPS *props);
+  void DemuxReset();
+  void DemuxAbort();
+  void DemuxFlush();
+  DemuxPacket* DemuxRead();
+
+protected:
+  bool                  m_ReadyToUse;
+  IPVRClientCallback   *m_manager;
+  CStdString            m_hostName;
+  CCriticalSection      m_critSection;
+  int                   m_iTimeCorrection;
+  PVR_MENUHOOKS         m_menuhooks;
+
+private:
+  void WriteClientChannelInfo(const CPVRChannel &channelinfo, PVR_CHANNEL &tag);
+  void WriteClientTimerInfo(const CPVRTimerInfoTag &timerinfo, PVR_TIMERINFO &tag);
+  void WriteClientRecordingInfo(const CPVRRecordingInfoTag &recordinginfo, PVR_RECORDINGINFO &tag);
+};
+
+typedef std::vector<CPVRClient*> VECCLIENTS;
index a33e891..dc91f6e 100644 (file)
@@ -228,6 +228,7 @@ bool CSkinInfo::LoadStartupWindows(const cp_extension_t *ext)
   if (!m_startupWindows.size())
   { // nope - add the default ones
     m_startupWindows.push_back(CStartupWindow(WINDOW_HOME, "513"));
+    m_startupWindows.push_back(CStartupWindow(WINDOW_TV, "19180"));
     m_startupWindows.push_back(CStartupWindow(WINDOW_PROGRAMS, "0"));
     m_startupWindows.push_back(CStartupWindow(WINDOW_PICTURES, "1"));
     m_startupWindows.push_back(CStartupWindow(WINDOW_MUSIC, "2"));
diff --git a/xbmc/addons/include/xbmc_pvr_dll.h b/xbmc/addons/include/xbmc_pvr_dll.h
new file mode 100644 (file)
index 0000000..ad76148
--- /dev/null
@@ -0,0 +1,178 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef __XBMC_PVR_H__
+#define __XBMC_PVR_H__
+
+#include "xbmc_addon_dll.h"               /* Dll related functions available to all AddOn's */
+#include "xbmc_pvr_types.h"
+
+extern "C"
+{
+  // Functions that your PVR client must implement, also you must implement the functions from
+  // xbmc_addon_dll.h
+
+  /** PVR General Functions **/
+  PVR_ERROR GetProperties(PVR_SERVERPROPS* pProps);
+  PVR_ERROR GetStreamProperties(PVR_STREAMPROPS* pProps);
+  const char* GetBackendName();
+  const char* GetBackendVersion();
+  const char* GetConnectionString();
+  PVR_ERROR GetDriveSpace(long long *total, long long *used);
+  PVR_ERROR GetBackendTime(time_t *localTime, int *gmtOffset);
+  PVR_ERROR DialogChannelScan();
+  PVR_ERROR MenuHook(const PVR_MENUHOOK &menuhook);
+
+  /** PVR EPG Functions **/
+  PVR_ERROR RequestEPGForChannel(PVRHANDLE handle, const PVR_CHANNEL &channel, time_t start, time_t end);
+
+  /** PVR Bouquets Functions **/
+  int GetNumBouquets();
+  PVR_ERROR RequestBouquetsList(PVRHANDLE handle, int radio);
+
+  /** PVR Channel Functions **/
+  int GetNumChannels();
+  PVR_ERROR RequestChannelList(PVRHANDLE handle, int radio);
+  PVR_ERROR DeleteChannel(unsigned int number);
+  PVR_ERROR RenameChannel(unsigned int number, const char *newname);
+  PVR_ERROR MoveChannel(unsigned int number, unsigned int newnumber);
+  PVR_ERROR DialogChannelSettings(const PVR_CHANNEL &channelinfo);
+  PVR_ERROR DialogAddChannel(const PVR_CHANNEL &channelinfo);
+
+  /** PVR Recording Functions **/
+  int GetNumRecordings();
+  PVR_ERROR RequestRecordingsList(PVRHANDLE handle);
+  PVR_ERROR DeleteRecording(const PVR_RECORDINGINFO &recinfo);
+  PVR_ERROR RenameRecording(const PVR_RECORDINGINFO &recinfo, const char *newname);
+
+  /** PVR Recording cut marks Functions **/
+  bool HaveCutmarks();
+  PVR_ERROR RequestCutMarksList(PVRHANDLE handle);
+  PVR_ERROR AddCutMark(const PVR_CUT_MARK &cutmark);
+  PVR_ERROR DeleteCutMark(const PVR_CUT_MARK &cutmark);
+  PVR_ERROR StartCut();
+
+  /** PVR Timer Functions **/
+  int GetNumTimers();
+  PVR_ERROR RequestTimerList(PVRHANDLE handle);
+  PVR_ERROR AddTimer(const PVR_TIMERINFO &timerinfo);
+  PVR_ERROR DeleteTimer(const PVR_TIMERINFO &timerinfo, bool force);
+  PVR_ERROR RenameTimer(const PVR_TIMERINFO &timerinfo, const char *newname);
+  PVR_ERROR UpdateTimer(const PVR_TIMERINFO &timerinfo);
+
+  /** PVR Live Stream Functions **/
+  bool OpenLiveStream(const PVR_CHANNEL &channelinfo);
+  void CloseLiveStream();
+  int ReadLiveStream(unsigned char* buf, int buf_size);
+  long long SeekLiveStream(long long pos, int whence=SEEK_SET);
+  long long PositionLiveStream(void);
+  long long LengthLiveStream(void);
+  int GetCurrentClientChannel();
+  bool SwitchChannel(const PVR_CHANNEL &channelinfo);
+  PVR_ERROR SignalQuality(PVR_SIGNALQUALITY &qualityinfo);
+
+  /** PVR Secondary Stream Functions **/
+  bool SwapLiveTVSecondaryStream();
+  bool OpenSecondaryStream(const PVR_CHANNEL &channelinfo);
+  void CloseSecondaryStream();
+  int ReadSecondaryStream(unsigned char* buf, int buf_size);
+
+  /** PVR Recording Stream Functions **/
+  bool OpenRecordedStream(const PVR_RECORDINGINFO &recinfo);
+  void CloseRecordedStream(void);
+  int ReadRecordedStream(unsigned char* buf, int buf_size);
+  long long SeekRecordedStream(long long pos, int whence=SEEK_SET);
+  long long PositionRecordedStream(void);
+  long long LengthRecordedStream(void);
+  const char* GetLiveStreamURL(const PVR_CHANNEL &channelinfo);
+
+  /** \name Demuxer Interface */
+  void DemuxReset();
+  void DemuxAbort();
+  void DemuxFlush();
+  DemuxPacket* DemuxRead();
+
+  // function to export the above structure to XBMC
+  void __declspec(dllexport) get_addon(struct PVRClient* pClient)
+  {
+    pClient->GetProperties          = GetProperties;
+    pClient->GetStreamProperties    = GetStreamProperties;
+    pClient->GetConnectionString    = GetConnectionString;
+    pClient->GetBackendName         = GetBackendName;
+    pClient->GetBackendVersion      = GetBackendVersion;
+    pClient->GetDriveSpace          = GetDriveSpace;
+    pClient->GetBackendTime         = GetBackendTime;
+    pClient->DialogChannelScan      = DialogChannelScan;
+    pClient->MenuHook               = MenuHook;
+    pClient->GetNumBouquets         = GetNumBouquets;
+    pClient->RequestBouquetsList    = RequestBouquetsList;
+    pClient->GetNumChannels         = GetNumChannels;
+    pClient->GetNumRecordings       = GetNumRecordings;
+    pClient->GetNumTimers           = GetNumTimers;
+    pClient->RequestEPGForChannel   = RequestEPGForChannel;
+    pClient->RequestChannelList     = RequestChannelList;
+    pClient->DeleteChannel          = DeleteChannel;
+    pClient->RenameChannel          = RenameChannel;
+    pClient->MoveChannel            = MoveChannel;
+    pClient->DialogChannelSettings  = DialogChannelSettings;
+    pClient->DialogAddChannel       = DialogAddChannel;
+    pClient->RequestRecordingsList  = RequestRecordingsList;
+    pClient->DeleteRecording        = DeleteRecording;
+    pClient->RenameRecording        = RenameRecording;
+    pClient->HaveCutmarks           = HaveCutmarks;
+    pClient->RequestCutMarksList    = RequestCutMarksList;
+    pClient->AddCutMark             = AddCutMark;
+    pClient->DeleteCutMark          = DeleteCutMark;
+    pClient->StartCut               = StartCut;
+    pClient->RequestTimerList       = RequestTimerList;
+    pClient->AddTimer               = AddTimer;
+    pClient->DeleteTimer            = DeleteTimer;
+    pClient->RenameTimer            = RenameTimer;
+    pClient->UpdateTimer            = UpdateTimer;
+    pClient->OpenLiveStream         = OpenLiveStream;
+    pClient->CloseLiveStream        = CloseLiveStream;
+    pClient->ReadLiveStream         = ReadLiveStream;
+    pClient->SeekLiveStream         = SeekLiveStream;
+    pClient->PositionLiveStream     = PositionLiveStream;
+    pClient->LengthLiveStream       = LengthLiveStream;
+    pClient->GetCurrentClientChannel= GetCurrentClientChannel;
+    pClient->SwitchChannel          = SwitchChannel;
+    pClient->SignalQuality          = SignalQuality;
+    pClient->SwapLiveTVSecondaryStream  = SwapLiveTVSecondaryStream;
+    pClient->OpenSecondaryStream    = OpenSecondaryStream;
+    pClient->CloseSecondaryStream   = CloseSecondaryStream;
+    pClient->ReadSecondaryStream    = ReadSecondaryStream;
+    pClient->OpenRecordedStream     = OpenRecordedStream;
+    pClient->CloseRecordedStream    = CloseRecordedStream;
+    pClient->ReadRecordedStream     = ReadRecordedStream;
+    pClient->SeekRecordedStream     = SeekRecordedStream;
+    pClient->PositionRecordedStream = PositionRecordedStream;
+    pClient->LengthRecordedStream   = LengthRecordedStream;
+    pClient->GetLiveStreamURL       = GetLiveStreamURL;
+    pClient->DemuxReset             = DemuxReset;
+    pClient->DemuxAbort             = DemuxAbort;
+    pClient->DemuxFlush             = DemuxFlush;
+    pClient->DemuxRead              = DemuxRead;
+  };
+};
+
+#endif
diff --git a/xbmc/addons/include/xbmc_pvr_types.h b/xbmc/addons/include/xbmc_pvr_types.h
new file mode 100644 (file)
index 0000000..7078c4d
--- /dev/null
@@ -0,0 +1,426 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+/**
+ * \file xbmc_pvr_types.h
+ * \brief Implementation of a PVR Client API Interface.
+ * It at common data structures which a shared between XBMC and PVR clients
+ *
+ * \author Team XBMC
+ */
+
+#ifndef __PVRCLIENT_TYPES_H__
+#define __PVRCLIENT_TYPES_H__
+
+#ifdef _WIN32
+#include <windows.h>
+#else
+#ifndef __cdecl
+#define __cdecl
+#endif
+#ifndef __declspec
+#define __declspec(X)
+#endif
+#endif
+#include <string.h>
+
+/*! \note Define "USE_DEMUX" on compile time if demuxing inside pvr
+ * addon is used. Also XBMC's "DVDDemuxPacket.h" file must be inside
+ * the include path of the pvr addon.
+ */
+#ifdef USE_DEMUX
+#include "DVDDemuxPacket.h"
+#else
+struct DemuxPacket;
+#endif
+
+#undef ATTRIBUTE_PACKED
+#undef PRAGMA_PACK_BEGIN
+#undef PRAGMA_PACK_END
+
+#if defined(__GNUC__)
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
+#define ATTRIBUTE_PACKED __attribute__ ((packed))
+#define PRAGMA_PACK 0
+#endif
+#endif
+
+#if !defined(ATTRIBUTE_PACKED)
+#define ATTRIBUTE_PACKED
+#define PRAGMA_PACK 1
+#endif
+
+  /*! \brief PVR Event contents, used to identify the genre of a epg entry
+   */
+#define EVCONTENTMASK_MOVIEDRAMA               0x10
+#define EVCONTENTMASK_NEWSCURRENTAFFAIRS       0x20
+#define EVCONTENTMASK_SHOW                     0x30
+#define EVCONTENTMASK_SPORTS                   0x40
+#define EVCONTENTMASK_CHILDRENYOUTH            0x50
+#define EVCONTENTMASK_MUSICBALLETDANCE         0x60
+#define EVCONTENTMASK_ARTSCULTURE              0x70
+#define EVCONTENTMASK_SOCIALPOLITICALECONOMICS 0x80
+#define EVCONTENTMASK_EDUCATIONALSCIENCE       0x90
+#define EVCONTENTMASK_LEISUREHOBBIES           0xA0
+#define EVCONTENTMASK_SPECIAL                  0xB0
+#define EVCONTENTMASK_USERDEFINED              0xF0
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+  /*! \brief PVR Client Return Data handle
+   */
+  struct PVRHANDLE_STRUCT
+  {
+    void* CALLER_ADDRESS;
+    void* DATA_ADDRESS;
+    int   DATA_IDENTIFIER;
+  };
+  typedef PVRHANDLE_STRUCT* PVRHANDLE;
+
+  /*! \brief PVR Client startup properties
+   * Passed to the Create function
+   */
+  struct PVR_PROPS
+  {
+    int clientID;
+    const char *userpath;
+    const char *clientpath;
+  };
+
+  /*! \brief PVR Client Error Codes
+   */
+  typedef enum {
+    PVR_ERROR_NO_ERROR             = 0,
+    PVR_ERROR_UNKOWN               = -1,
+    PVR_ERROR_NOT_IMPLEMENTED      = -2,
+    PVR_ERROR_SERVER_ERROR         = -3,
+    PVR_ERROR_SERVER_TIMEOUT       = -4,
+    PVR_ERROR_NOT_SYNC             = -5,
+    PVR_ERROR_NOT_DELETED          = -6,
+    PVR_ERROR_NOT_SAVED            = -7,
+    PVR_ERROR_RECORDING_RUNNING    = -8,
+    PVR_ERROR_ALREADY_PRESENT      = -9,
+    PVR_ERROR_NOT_POSSIBLE         = -10,
+  } PVR_ERROR;
+
+  /*! \brief PVR Client Event Codes
+   * Sent via PVRManager callback
+   */
+  typedef enum {
+    PVR_EVENT_UNKNOWN              = 0,
+    PVR_EVENT_CLOSE                = 1,
+    PVR_EVENT_RECORDINGS_CHANGE    = 2,
+    PVR_EVENT_CHANNELS_CHANGE      = 3,
+    PVR_EVENT_TIMERS_CHANGE        = 4,
+  } PVR_EVENT;
+
+#if PRAGMA_PACK
+#pragma pack(1)
+#endif
+
+  /*! \brief PVR Client Properties
+   * Returned on client initialization
+   */
+  typedef struct PVR_SERVERPROPS {
+    bool SupportChannelLogo;            /**< \brief Client support transfer of channel logos */
+    bool SupportChannelSettings;        /**< \brief Client support changing channels on backend */
+    bool SupportTimeShift;              /**< \brief Client handle Live TV Timeshift, otherwise it is handled by XBMC */
+    bool SupportEPG;                    /**< \brief Client provide EPG information */
+    bool SupportTV;                     /**< \brief Client provide TV Channels, is false for Radio only clients */
+    bool SupportRadio;                  /**< \brief Client provide also Radio Channels */
+    bool SupportRecordings;             /**< \brief Client support playback of recordings stored on the backend */
+    bool SupportTimers;                 /**< \brief Client support creation and editing of timers */
+    bool SupportDirector;               /**< \brief Client provide information about multifeed channels, like Sky Select */
+    bool SupportBouquets;               /**< \brief Client support Bouqets */
+    bool SupportChannelScan;            /**< \brief Client support Channelscan */
+    bool HandleInputStream;             /**< \brief Input stream is handled by the client if set, can be false for http */
+    bool HandleDemuxing;                /**< \brief Demux of stream is handled by the client, as example TVFrontend (htsp protocol) */
+  } ATTRIBUTE_PACKED PVR_SERVERPROPS;
+
+  /*! \brief  PVR Stream Properties
+   * Returned on request
+   */
+  typedef struct PVR_STREAMPROPS {
+#define PVR_STREAM_MAX_STREAMS 16
+    int nstreams;
+    struct PVR_STREAM {
+      int id;
+      int physid;
+      unsigned int codec_type;
+      unsigned int codec_id;
+      char language[4];
+      int identifier;
+
+      int fpsscale; // scale of 1000 and a rate of 29970 will result in 29.97 fps
+      int fpsrate;
+      int height; // height of the stream reported by the demuxer
+      int width; // width of the stream reported by the demuxer
+      float aspect; // display aspect of stream
+
+      int channels;
+      int samplerate;
+      int blockalign;
+      int bitrate;
+      int bits_per_sample;
+    } stream[PVR_STREAM_MAX_STREAMS];
+  } ATTRIBUTE_PACKED PVR_STREAMPROPS;
+
+
+  /*! \brief PVR channel defination
+   *
+   * Is used by the TransferChannelEntry function to inform XBMC that this
+   * channel is present, also if a channel is opened this structure is passed in.
+   */
+  typedef struct PVR_CHANNEL {
+    int             uid;                /**< \brief Unique identifier for this channel */
+    int             number;             /**< \brief The backend channel number */
+
+    const char     *name;               /**< \brief Channel name provided by the Broadcast */
+    const char     *callsign;           /**< \brief Channel name provided by the user (if present) */
+    const char     *iconpath;           /**< \brief Path to the channel icon (if present) */
+
+    int             encryption;         /**< \brief This is a encrypted channel and have a CA Id */
+    bool            radio;              /**< \brief This is a radio channel */
+    bool            hide;               /**< \brief This channel is hidden by the user */
+    bool            recording;          /**< \brief This channel is currently recording */
+
+    int             bouquet;            /**< \brief Bouquet ID this channel have (if supported) */
+
+    bool            multifeed;          /**< \brief This is a multifeed channel */
+    int             multifeed_master;   /**< \brief The Master multifeed channel, multifeed_master==number for master itself */
+    int             multifeed_number;   /**< \brief The own number inside multifeed channel list */
+
+    const char     *input_format;       /**< \brief Input format type based upon ffmpeg/libavformat/allformats.c
+                                             if it is unknown leave it empty */
+
+    const char     *stream_url;         /**< \brief The Stream URL to access this channel, it can be all types of protocol
+                                             and types are supported by XBMC or in case the client read the stream leave
+                                             it empty as URL. */
+  } ATTRIBUTE_PACKED PVR_CHANNEL;
+
+  /*! \brief EPG Bouquet Definition
+   */
+  typedef struct PVR_BOUQUET {
+    char*  Name;
+    char*  Category;
+    int    Number;
+  } ATTRIBUTE_PACKED PVR_BOUQUET;
+
+  /*! \brief EPG Programme Definition
+   *
+   * Used to signify an individual broadcast, whether it is also a recording, timer etc.
+   */
+  typedef struct PVR_PROGINFO {
+    unsigned int  uid;
+    int           channum;
+    const char   *title;
+    const char   *subtitle;
+    const char   *description;
+    time_t        starttime;
+    time_t        endtime;
+    int           genre_type;
+    int           genre_sub_type;
+    int           parental_rating;
+  } ATTRIBUTE_PACKED PVR_PROGINFO;
+
+  /*! \brief TV Timer Definition
+   */
+  typedef struct PVR_TIMERINFO {
+    int           index;
+    int           active;
+    const char   *title;
+    const char   *directory;
+    int           channelNum;
+    time_t        starttime;
+    time_t        endtime;
+    time_t        firstday;
+    int           recording;
+    int           priority;
+    int           lifetime;
+    int           repeat;
+    int           repeatflags;
+  } ATTRIBUTE_PACKED PVR_TIMERINFO;
+
+  /*! \brief PVR recording defination
+   *
+   * Is used by the TransferRecordinglEntry function to inform XBMC about this
+   * recording, also if a recording is opened this structure is passed in.
+   */
+  typedef struct PVR_RECORDINGINFO {
+    int             index;              /**< \brief The index number of this recording, it must always set and
+                                             is used to identify this recording later */
+    const char     *directory;          /**< \brief The directory of this recording (used to create a organized structure).
+                                             It is not required, if it is not supported on the backend
+                                             leave it open */
+    const char     *title;              /**< \brief The name of this recording */
+    const char     *subtitle;           /**< \brief Optional subtitle */
+    const char     *description;        /**< \brief Optional description of the recording content */
+    const char     *channel_name;       /**< \brief Optional channel name */
+    time_t          recording_time;     /**< \brief Optional time where this recording was taken */
+    int             duration;           /**< \brief The duration in seconds of this recording */
+    int             priority;           /**< \brief Optional priority of this recording (from 0 - 100) */
+    int             lifetime;           /**< \brief Optional life time in days of this recording */
+    const char     *stream_url;         /**< \brief The Stream URL to access this recording, it can be all types of protocol
+                                             and types are supported by XBMC or in case the client read the stream leave
+                                             it empty as URL. You can also define to play all files inside a folder if you
+                                             use a asterix, as example: "/media/disk/recordings/Alien/ *.ts", in this example
+                                             all files in the directory "/media/disk/recordings/Alien" with the "ts" extensions
+                                             are played by XBMC. */
+  } ATTRIBUTE_PACKED PVR_RECORDINGINFO;
+
+  /*! \brief TV Stream Signal Quality Information
+   */
+  typedef struct PVR_SIGNALQUALITY {
+    char          frontend_name[1024];
+    char          frontend_status[1024];
+    int           snr;
+    int           signal;
+    long          ber;
+    long          unc;
+    double        video_bitrate;
+    double        audio_bitrate;
+    double        dolby_bitrate;
+  } ATTRIBUTE_PACKED PVR_SIGNALQUALITY;
+
+  /*! \brief PVR Addon menu hook element
+   *
+   * Are available in the context menus of the TV window, to perfrom a addon related action.
+   */
+  typedef struct PVR_MENUHOOK {
+    int           hook_id;              /**< \brief An identifier to know what hook is called back to the addon */
+    int           string_id;            /**< \brief The id to a name for this item inside the language files */
+  } ATTRIBUTE_PACKED PVR_MENUHOOK;
+
+  /*! \brief PVR Recordings cut mark action types.
+   */
+  typedef enum {
+    CUT         = 0,
+    MUTE        = 1,
+    SCENE       = 2,
+    COMM_BREAK  = 3
+  } CUT_MARK_ACTION;
+
+  /*! \brief PVR Recordings cut mark element.
+   */
+  typedef struct PVR_CUT_MARK {
+    long long       start;              /**< \brief Start position in milliseconds */
+    long long       stop;               /**< \brief Stop position in milliseconds */
+    CUT_MARK_ACTION action;             /**< \brief the action to be performed */
+  } ATTRIBUTE_PACKED PVR_CUT_MARK;
+
+#if PRAGMA_PACK
+#pragma pack()
+#endif
+
+  /*! \brief Structure to transfer the PVR functions to XBMC
+   */
+  typedef struct PVRClient
+  {
+    /** \name PVR General Functions */
+    PVR_ERROR (__cdecl* GetProperties)(PVR_SERVERPROPS *props);
+    PVR_ERROR (__cdecl* GetStreamProperties)(PVR_STREAMPROPS *props);
+    const char* (__cdecl* GetBackendName)();
+    const char* (__cdecl* GetBackendVersion)();
+    const char* (__cdecl* GetConnectionString)();
+    PVR_ERROR (__cdecl* GetDriveSpace)(long long *total, long long *used);
+    PVR_ERROR (__cdecl* GetBackendTime)(time_t *localTime, int *gmtOffset);
+    PVR_ERROR (__cdecl* DialogChannelScan)();
+    PVR_ERROR (__cdecl* MenuHook)(const PVR_MENUHOOK &menuhook);
+
+    /** \name PVR EPG Functions */
+    PVR_ERROR (__cdecl* RequestEPGForChannel)(PVRHANDLE handle, const PVR_CHANNEL &channel, time_t start, time_t end);
+
+    /** \name PVR Bouquets Functions */
+    int (__cdecl* GetNumBouquets)();
+    PVR_ERROR (__cdecl* RequestBouquetsList)(PVRHANDLE handle, int radio);
+
+    /** \name PVR Channel Functions */
+    int (__cdecl* GetNumChannels)();
+    PVR_ERROR (__cdecl* RequestChannelList)(PVRHANDLE handle, int radio);
+    PVR_ERROR (__cdecl* DeleteChannel)(unsigned int number);
+    PVR_ERROR (__cdecl* RenameChannel)(unsigned int number, const char *newname);
+    PVR_ERROR (__cdecl* MoveChannel)(unsigned int number, unsigned int newnumber);
+    PVR_ERROR (__cdecl* DialogChannelSettings)(const PVR_CHANNEL &channelinfo);
+    PVR_ERROR (__cdecl* DialogAddChannel)(const PVR_CHANNEL &channelinfo);
+
+    /** \name PVR Recording Functions */
+    int (__cdecl* GetNumRecordings)();
+    PVR_ERROR (__cdecl* RequestRecordingsList)(PVRHANDLE handle);
+    PVR_ERROR (__cdecl* DeleteRecording)(const PVR_RECORDINGINFO &recinfo);
+    PVR_ERROR (__cdecl* RenameRecording)(const PVR_RECORDINGINFO &recinfo, const char *newname);
+
+    /** \name PVR Recording cut marks Functions */
+    bool (__cdecl* HaveCutmarks)();
+    PVR_ERROR (__cdecl* RequestCutMarksList)(PVRHANDLE handle);
+    PVR_ERROR (__cdecl* AddCutMark)(const PVR_CUT_MARK &cutmark);
+    PVR_ERROR (__cdecl* DeleteCutMark)(const PVR_CUT_MARK &cutmark);
+    PVR_ERROR (__cdecl* StartCut)();
+
+    /** \name PVR Timer Functions */
+    int (__cdecl* GetNumTimers)();
+    PVR_ERROR (__cdecl* RequestTimerList)(PVRHANDLE handle);
+    PVR_ERROR (__cdecl* AddTimer)(const PVR_TIMERINFO &timerinfo);
+    PVR_ERROR (__cdecl* DeleteTimer)(const PVR_TIMERINFO &timerinfo, bool force);
+    PVR_ERROR (__cdecl* RenameTimer)(const PVR_TIMERINFO &timerinfo, const char *newname);
+    PVR_ERROR (__cdecl* UpdateTimer)(const PVR_TIMERINFO &timerinfo);
+
+    /** \name PVR Live Stream Functions */
+    bool (__cdecl* OpenLiveStream)(const PVR_CHANNEL &channelinfo);
+    void (__cdecl* CloseLiveStream)();
+    int (__cdecl* ReadLiveStream)(unsigned char* buf, int buf_size);
+    long long (__cdecl* SeekLiveStream)(long long pos, int whence);
+    long long (__cdecl* PositionLiveStream)(void);
+    long long (__cdecl* LengthLiveStream)(void);
+    int (__cdecl* GetCurrentClientChannel)();
+    bool (__cdecl* SwitchChannel)(const PVR_CHANNEL &channelinfo);
+    PVR_ERROR (__cdecl* SignalQuality)(PVR_SIGNALQUALITY &qualityinfo);
+    const char* (__cdecl* GetLiveStreamURL)(const PVR_CHANNEL &channelinfo);
+
+    /** \name PVR Secondary Stream Functions */
+    bool (__cdecl* SwapLiveTVSecondaryStream)();
+    bool (__cdecl* OpenSecondaryStream)(const PVR_CHANNEL &channelinfo);
+    void (__cdecl* CloseSecondaryStream)();
+    int (__cdecl* ReadSecondaryStream)(unsigned char* buf, int buf_size);
+
+    /** \name PVR Recording Stream Functions */
+    bool (__cdecl* OpenRecordedStream)(const PVR_RECORDINGINFO &recinfo);
+    void (__cdecl* CloseRecordedStream)(void);
+    int (__cdecl* ReadRecordedStream)(unsigned char* buf, int buf_size);
+    long long (__cdecl* SeekRecordedStream)(long long pos, int whence);
+    long long (__cdecl* PositionRecordedStream)(void);
+    long long (__cdecl* LengthRecordedStream)(void);
+
+    /** \name Demuxer Interface */
+    void (__cdecl* DemuxReset)();
+    void (__cdecl* DemuxAbort)();
+    void (__cdecl* DemuxFlush)();
+    DemuxPacket* (__cdecl* DemuxRead)();
+
+  } PVRClient;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //__PVRCLIENT_TYPES_H__
index 5f2a054..c8f97f1 100644 (file)
@@ -224,7 +224,7 @@ LibraryLoader* DllLoaderContainer::LoadDll(const char* sName, bool bLoadSymbols)
   LibraryLoader* pLoader;
 #ifdef _LINUX
   if (strstr(sName, ".so") != NULL || strstr(sName, ".vis") != NULL || strstr(sName, ".xbs") != NULL
-      || strstr(sName, ".mvis") != NULL || strstr(sName, ".dylib") != NULL || strstr(sName, ".framework") != NULL)
+      || strstr(sName, ".mvis") != NULL || strstr(sName, ".dylib") != NULL || strstr(sName, ".framework") != NULL || strstr(sName, ".pvr") != NULL)
     pLoader = new SoLoader(sName, bLoadSymbols);
   else
 #elif defined(_WIN32)
index 6bcfb21..253c703 100644 (file)
@@ -365,6 +365,9 @@ bool Win32DllLoader::NeedsHooking(const char *dllName)
   CStdStringW strdllNameW;
   g_charsetConverter.utf8ToW(_P(dllName), strdllNameW, false);
   HMODULE hModule = GetModuleHandleW(strdllNameW.c_str());
+  if (hModule == NULL)
+    return false;
+
   wchar_t filepathW[MAX_PATH];
   GetModuleFileNameW(hModule, filepathW, MAX_PATH);
   CStdString dllPath;
index 887c8f3..8ab52a7 100644 (file)
@@ -493,7 +493,8 @@ bool CVDPAU::Supports(VdpVideoMixerFeature feature)
 bool CVDPAU::Supports(EINTERLACEMETHOD method)
 {
   if(method == VS_INTERLACEMETHOD_VDPAU_BOB
-  || method == VS_INTERLACEMETHOD_AUTO)
+  || method == VS_INTERLACEMETHOD_AUTO
+  || method == VS_INTERLACEMETHOD_AUTO_ION)
     return true;
 
   for(SInterlaceMapping* p = g_interlace_mapping; p->method != VS_INTERLACEMETHOD_NONE; p++)
@@ -610,7 +611,17 @@ void CVDPAU::SetDeinterlacing()
     VdpBool enabled[]={1,1,1};
     vdp_st = vdp_video_mixer_set_feature_enables(videoMixer, ARSIZE(feature), feature, enabled);
   }
-  else if (method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL
+  else if (method == VS_INTERLACEMETHOD_AUTO_ION)
+  {
+    if (vid_height <= 576){
+      VdpBool enabled[]={1,1,0};
+      vdp_st = vdp_video_mixer_set_feature_enables(videoMixer, ARSIZE(feature), feature, enabled);
+    }
+    else if (vid_height > 576){
+      VdpBool enabled[]={1,0,0};
+      vdp_st = vdp_video_mixer_set_feature_enables(videoMixer, ARSIZE(feature), feature, enabled);
+    }
+  }  else if (method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL
        ||  method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF)
   {
     VdpBool enabled[]={1,0,0};
@@ -1165,6 +1176,8 @@ int CVDPAU::Decode(AVCodecContext *avctx, AVFrame *pFrame)
 
     if((method == VS_INTERLACEMETHOD_AUTO &&
                   m_DVDVideoPics.front().iFlags & DVP_FLAG_INTERLACED)
+    || (method == VS_INTERLACEMETHOD_AUTO_ION &&
+                     m_DVDVideoPics.front().iFlags & DVP_FLAG_INTERLACED)
     ||  method == VS_INTERLACEMETHOD_VDPAU_BOB
     ||  method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL
     ||  method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF
@@ -1174,6 +1187,7 @@ int CVDPAU::Decode(AVCodecContext *avctx, AVFrame *pFrame)
     {
       if(method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF
       || method == VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF
+      || (method == VS_INTERLACEMETHOD_AUTO_ION && vid_height > 576)
       || avctx->hurry_up)
         m_mixerstep = 0;
       else
index ce3aa3a..fe9fa59 100644 (file)
@@ -23,6 +23,7 @@
 
 #include "StdString.h"
 #include "system.h"
+#include "DVDDemuxPacket.h"
 
 class CDVDInputStream;
 
@@ -205,19 +206,6 @@ public:
   virtual void GetStreamInfo(std::string& strInfo);
 };
 
-typedef struct DemuxPacket
-{
-  BYTE* pData;   // data
-  int iSize;     // data size
-  int iStreamId; // integer representing the stream index
-  int iGroupId;  // the group this data belongs to, used to group data from different streams together
-
-  double pts; // pts in DVD_TIME_BASE
-  double dts; // dts in DVD_TIME_BASE
-  double duration; // duration in DVD_TIME_BASE if available
-} DemuxPacket;
-
-
 class CDVDDemux
 {
 public:
index f39d51e..1ab70ea 100644 (file)
@@ -33,6 +33,7 @@
 #include "DVDInputStreams/DVDInputStream.h"
 #include "DVDInputStreams/DVDInputStreamNavigator.h"
 #include "DVDInputStreams/DVDInputStreamBluray.h"
+#include "DVDInputStreams/DVDInputStreamPVRManager.h"
 #include "DVDDemuxUtils.h"
 #include "DVDClock.h" // for DVD_TIME_BASE
 #include "utils/Win32Exception.h"
diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPVRClient.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPVRClient.cpp
new file mode 100644 (file)
index 0000000..6b9f771
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "DVDInputStreams/DVDInputStream.h"
+#include "DVDDemuxPVRClient.h"
+#include "pvr/PVRManager.h"
+#ifdef _WIN32
+#include <libavcodec/avcodec.h>
+#endif
+
+void CDemuxStreamVideoPVRClient::GetStreamInfo(std::string& strInfo)
+{
+}
+
+void CDemuxStreamAudioPVRClient::GetStreamInfo(std::string& strInfo)
+{
+  switch (codec)
+  {
+    case CODEC_ID_AC3:
+      strInfo = "AC3";
+      break;
+    default:
+      break;
+  }
+}
+
+void CDemuxStreamSubtitlePVRClient::GetStreamInfo(std::string& strInfo)
+{
+}
+
+CDVDDemuxPVRClient::CDVDDemuxPVRClient() : CDVDDemux()
+{
+  m_pInput = NULL;
+  for (int i = 0; i < MAX_PVR_STREAMS; i++) m_streams[i] = NULL;
+}
+
+CDVDDemuxPVRClient::~CDVDDemuxPVRClient()
+{
+  Dispose();
+}
+
+bool CDVDDemuxPVRClient::Open(CDVDInputStream* pInput)
+{
+  Abort();
+  m_pInput = pInput;
+  RequestStreams();
+  return true;
+}
+
+void CDVDDemuxPVRClient::Dispose()
+{
+  for (int i = 0; i < MAX_PVR_STREAMS; i++)
+  {
+    if (m_streams[i])
+    {
+      if (m_streams[i]->ExtraData)
+        delete[] (BYTE*)(m_streams[i]->ExtraData);
+      delete m_streams[i];
+    }
+    m_streams[i] = NULL;
+  }
+  m_pInput = NULL;
+}
+
+void CDVDDemuxPVRClient::Reset()
+{
+  if(m_pInput)
+    g_PVRManager.DemuxReset();
+
+  CDVDInputStream* pInputStream = m_pInput;
+  Dispose();
+  Open(pInputStream);
+}
+
+void CDVDDemuxPVRClient::Abort()
+{
+  if(m_pInput)
+    g_PVRManager.DemuxAbort();
+}
+
+void CDVDDemuxPVRClient::Flush()
+{
+  if(m_pInput)
+    g_PVRManager.DemuxFlush();
+}
+
+DemuxPacket* CDVDDemuxPVRClient::Read()
+{
+  DemuxPacket* pPacket = g_PVRManager.ReadDemuxStream();
+  if (!pPacket)
+    return CDVDDemuxUtils::AllocateDemuxPacket(0);
+
+  if (pPacket->iStreamId == DMX_SPECIALID_STREAMINFO)
+  {
+    UpdateStreams((PVR_STREAMPROPS*)pPacket->pData);
+    CDVDDemuxUtils::FreeDemuxPacket(pPacket);
+    return CDVDDemuxUtils::AllocateDemuxPacket(0);
+  }
+  else if (pPacket->iStreamId == DMX_SPECIALID_STREAMCHANGE)
+  {
+    Reset();
+    CDVDDemuxUtils::FreeDemuxPacket(pPacket);
+    return CDVDDemuxUtils::AllocateDemuxPacket(0);
+  }
+
+  return pPacket;
+}
+
+CDemuxStream* CDVDDemuxPVRClient::GetStream(int iStreamId)
+{
+  if (iStreamId < 0 || iStreamId >= MAX_PVR_STREAMS) return NULL;
+    return m_streams[iStreamId];
+}
+
+void CDVDDemuxPVRClient::RequestStreams()
+{
+  PVR_STREAMPROPS *props = g_PVRManager.GetCurrentStreamProps();
+
+  for (int i = 0; i < props->nstreams; ++i)
+  {
+    if (props->stream[i].codec_type == CODEC_TYPE_AUDIO)
+    {
+      CDemuxStreamAudioPVRClient* st = new CDemuxStreamAudioPVRClient(this);
+      st->iChannels       = props->stream[i].channels;
+      st->iSampleRate     = props->stream[i].samplerate;
+      st->iBlockAlign     = props->stream[i].blockalign;
+      st->iBitRate        = props->stream[i].bitrate;
+      st->iBitsPerSample  = props->stream[i].bits_per_sample;
+      m_streams[props->stream[i].id] = st;
+    }
+    else if (props->stream[i].codec_type == CODEC_TYPE_VIDEO)
+    {
+      CDemuxStreamVideoPVRClient* st = new CDemuxStreamVideoPVRClient(this);
+      st->iFpsScale       = props->stream[i].fpsscale;
+      st->iFpsRate        = props->stream[i].fpsrate;
+      st->iHeight         = props->stream[i].height;
+      st->iWidth          = props->stream[i].width;
+      st->fAspect         = props->stream[i].aspect;
+      m_streams[props->stream[i].id] = st;
+    }
+    else if (props->stream[i].codec_id == CODEC_ID_DVB_TELETEXT)
+    {
+      m_streams[props->stream[i].id] = new CDemuxStreamTeletext();
+    }
+    else if (props->stream[i].codec_type == CODEC_TYPE_SUBTITLE)
+    {
+      CDemuxStreamSubtitlePVRClient* st = new CDemuxStreamSubtitlePVRClient(this);
+      st->identifier      = props->stream[i].identifier;
+      m_streams[props->stream[i].id] = st;
+    }
+    else
+      m_streams[props->stream[i].id] = new CDemuxStream();
+
+    m_streams[props->stream[i].id]->codec       = (CodecID)props->stream[i].codec_id;
+    m_streams[props->stream[i].id]->iId         = props->stream[i].id;
+    m_streams[props->stream[i].id]->iPhysicalId = props->stream[i].physid;
+    m_streams[props->stream[i].id]->language[0] = props->stream[i].language[0];
+    m_streams[props->stream[i].id]->language[1] = props->stream[i].language[1];
+    m_streams[props->stream[i].id]->language[2] = props->stream[i].language[2];
+    m_streams[props->stream[i].id]->language[3] = props->stream[i].language[3];
+
+    CLog::Log(LOGDEBUG,"CDVDDemuxPVRClient::RequestStreams(): added stream %d:%d with codec_id %d", m_streams[props->stream[i].id]->iId, m_streams[props->stream[i].id]->iPhysicalId, m_streams[props->stream[i].id]->codec);
+  }
+}
+
+void CDVDDemuxPVRClient::UpdateStreams(PVR_STREAMPROPS *props)
+{
+  for (int i = 0; i < props->nstreams; ++i)
+  {
+    if (m_streams[props->stream[i].id] == NULL ||
+        m_streams[props->stream[i].id]->codec != (CodecID)props->stream[i].codec_id)
+    {
+      CLog::Log(LOGERROR,"Invalid stream inside UpdateStreams");
+      continue;
+    }
+
+    if (m_streams[props->stream[i].id]->type == STREAM_AUDIO)
+    {
+      CDemuxStreamAudioPVRClient* st = (CDemuxStreamAudioPVRClient*) m_streams[props->stream[i].id];
+      st->iChannels       = props->stream[i].channels;
+      st->iSampleRate     = props->stream[i].samplerate;
+      st->iBlockAlign     = props->stream[i].blockalign;
+      st->iBitRate        = props->stream[i].bitrate;
+      st->iBitsPerSample  = props->stream[i].bits_per_sample;
+    }
+    else if (m_streams[props->stream[i].id]->type == STREAM_VIDEO)
+    {
+      CDemuxStreamVideoPVRClient* st = (CDemuxStreamVideoPVRClient*) m_streams[props->stream[i].id];
+      st->iFpsScale       = props->stream[i].fpsscale;
+      st->iFpsRate        = props->stream[i].fpsrate;
+      st->iHeight         = props->stream[i].height;
+      st->iWidth          = props->stream[i].width;
+      st->fAspect         = props->stream[i].aspect;
+    }
+    else if (m_streams[props->stream[i].id]->type == STREAM_SUBTITLE)
+    {
+      CDemuxStreamSubtitlePVRClient* st = (CDemuxStreamSubtitlePVRClient*) m_streams[props->stream[i].id];
+      st->identifier      = props->stream[i].identifier;
+    }
+
+    m_streams[props->stream[i].id]->language[0] = props->stream[i].language[0];
+    m_streams[props->stream[i].id]->language[1] = props->stream[i].language[1];
+    m_streams[props->stream[i].id]->language[2] = props->stream[i].language[2];
+    m_streams[props->stream[i].id]->language[3] = props->stream[i].language[3];
+
+    CLog::Log(LOGDEBUG,"CDVDDemuxPVRClient::UpdateStreams(): update stream %d:%d with codec_id %d", m_streams[props->stream[i].id]->iId, m_streams[props->stream[i].id]->iPhysicalId, m_streams[props->stream[i].id]->codec);
+  }
+}
+
+int CDVDDemuxPVRClient::GetNrOfStreams()
+{
+  int i = 0;
+  while (i < MAX_PVR_STREAMS && m_streams[i]) i++;
+  return i;
+}
+
+std::string CDVDDemuxPVRClient::GetFileName()
+{
+  if(m_pInput)
+    return m_pInput->GetFileName();
+  else
+    return "";
+}
+
+void CDVDDemuxPVRClient::GetStreamCodecName(int iStreamId, CStdString &strName)
+{
+  CDemuxStream *stream = GetStream(iStreamId);
+  if (stream)
+  {
+    if (stream->codec == CODEC_ID_AC3)
+      strName = "ac3";
+    else if (stream->codec == CODEC_ID_MP2)
+      strName = "mp2";
+    else if (stream->codec == CODEC_ID_AAC)
+      strName = "aac";
+    else if (stream->codec == CODEC_ID_DTS)
+      strName = "dca";
+    else if (stream->codec == CODEC_ID_MPEG2VIDEO)
+      strName = "mpeg2video";
+    else if (stream->codec == CODEC_ID_H264)
+      strName = "h264";
+  }
+}
diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPVRClient.h b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPVRClient.h
new file mode 100644 (file)
index 0000000..f23edc3
--- /dev/null
@@ -0,0 +1,91 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "DVDDemux.h"
+#include <map>
+
+class CDVDDemuxPVRClient;
+struct PVR_STREAMPROPS;
+
+class CDemuxStreamVideoPVRClient : public CDemuxStreamVideo
+{
+  CDVDDemuxPVRClient *m_parent;
+public:
+  CDemuxStreamVideoPVRClient(CDVDDemuxPVRClient *parent)
+    : m_parent(parent)
+  {}
+  virtual void GetStreamInfo(std::string& strInfo);
+};
+
+class CDemuxStreamAudioPVRClient : public CDemuxStreamAudio
+{
+  CDVDDemuxPVRClient *m_parent;
+public:
+  CDemuxStreamAudioPVRClient(CDVDDemuxPVRClient *parent)
+    : m_parent(parent)
+  {}
+  virtual void GetStreamInfo(std::string& strInfo);
+};
+
+class CDemuxStreamSubtitlePVRClient : public CDemuxStreamSubtitle
+{
+  CDVDDemuxPVRClient *m_parent;
+public:
+  CDemuxStreamSubtitlePVRClient(CDVDDemuxPVRClient *parent)
+    : m_parent(parent)
+  {}
+  virtual void GetStreamInfo(std::string& strInfo);
+};
+
+
+class CDVDDemuxPVRClient : public CDVDDemux
+{
+public:
+
+  CDVDDemuxPVRClient();
+  ~CDVDDemuxPVRClient();
+
+  bool Open(CDVDInputStream* pInput);
+  void Dispose();
+  void Reset();
+  void Abort();
+  void Flush();
+  DemuxPacket* Read();
+  bool SeekTime(int time, bool backwords = false, double* startpts = NULL) { return false; }
+  void SetSpeed(int iSpeed) {};
+  int GetStreamLength() { return 0; }
+  CDemuxStream* GetStream(int iStreamId);
+  int GetNrOfStreams();
+  std::string GetFileName();
+  virtual void GetStreamCodecName(int iStreamId, CStdString &strName);
+
+protected:
+  CDVDInputStream* m_pInput;
+
+  #define MAX_PVR_STREAMS 42
+  CDemuxStream* m_streams[MAX_PVR_STREAMS]; // maximum number of streams that ffmpeg can handle
+
+private:
+  void RequestStreams();
+  void UpdateStreams(PVR_STREAMPROPS *props);
+};
+
diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPacket.h b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPacket.h
new file mode 100644 (file)
index 0000000..beadfcd
--- /dev/null
@@ -0,0 +1,37 @@
+#pragma once
+
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#define DMX_SPECIALID_STREAMINFO    -10
+#define DMX_SPECIALID_STREAMCHANGE  -11
+
+ typedef struct DemuxPacket
+{
+  unsigned char* pData;   // data
+  int iSize;     // data size
+  int iStreamId; // integer representing the stream index
+  int iGroupId;  // the group this data belongs to, used to group data from different streams together
+
+  double pts; // pts in DVD_TIME_BASE
+  double dts; // dts in DVD_TIME_BASE
+  double duration; // duration in DVD_TIME_BASE if available
+} DemuxPacket;
index e7d5f41..5d28810 100644 (file)
@@ -21,7 +21,7 @@
  *
  */
 
-#include "DVDDemux.h"
+#include "DVDDemuxPacket.h"
 
 class CDVDDemuxUtils
 {
index 7021661..79cbfbe 100644 (file)
 
 #include "DVDInputStreams/DVDInputStream.h"
 #include "DVDInputStreams/DVDInputStreamHttp.h"
+#include "DVDInputStreams/DVDInputStreamPVRManager.h"
 
 #include "DVDDemuxFFmpeg.h"
 #include "DVDDemuxShoutcast.h"
 #ifdef HAS_FILESYSTEM_HTSP
 #include "DVDDemuxHTSP.h"
 #endif
+#include "DVDDemuxPVRClient.h"
+#include "pvr/PVRManager.h"
 
 using namespace std;
 
@@ -62,6 +65,39 @@ CDVDDemux* CDVDFactoryDemuxer::CreateDemuxer(CDVDInputStream* pInputStream)
   }
 #endif
 
+  if (pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER))
+  {
+    CDVDInputStreamPVRManager* pInputStreamPVR = (CDVDInputStreamPVRManager*)pInputStream;
+    CDVDInputStream* pOtherStream = pInputStreamPVR->GetOtherStream();
+    if(pOtherStream)
+    {
+      /* Used for MediaPortal PVR addon (uses PVR otherstream for playback of rtsp streams) */
+      if (pOtherStream->IsStreamType(DVDSTREAM_TYPE_FFMPEG))
+      {
+        auto_ptr<CDVDDemuxFFmpeg> demuxer(new CDVDDemuxFFmpeg());
+        if(demuxer->Open(pOtherStream))
+          return demuxer.release();
+        else
+          return NULL;
+      }
+    }
+
+    std::string filename = pInputStream->GetFileName();
+    /* Use PVR demuxer only for live streams */
+    if (filename.substr(0, 14) == "pvr://channels")
+    {
+      PVR_SERVERPROPS *pProps = g_PVRManager.GetCurrentClientProps();
+      if (pProps && pProps->HandleDemuxing)
+      {
+        auto_ptr<CDVDDemuxPVRClient> demuxer(new CDVDDemuxPVRClient());
+        if(demuxer->Open(pInputStream))
+          return demuxer.release();
+        else
+          return NULL;
+      }
+    }
+  }
+
   auto_ptr<CDVDDemuxFFmpeg> demuxer(new CDVDDemuxFFmpeg());
   if(demuxer->Open(pInputStream))
     return demuxer.release();
index 8cebca3..25bb7b7 100644 (file)
@@ -10,6 +10,7 @@ SRCS= DVDDemux.cpp \
        DVDFactoryDemuxer.cpp \
        DVDDemuxVobsub.cpp \
        DVDDemuxHTSP.cpp \
+       DVDDemuxPVRClient.cpp \
 
 LIB=   DVDDemuxers.a
 
index 5fbf33a..320ad9c 100644 (file)
@@ -26,6 +26,7 @@
 #include "DVDInputStreamNavigator.h"
 #include "DVDInputStreamHttp.h"
 #include "DVDInputStreamFFmpeg.h"
+#include "DVDInputStreamPVRManager.h"
 #include "DVDInputStreamTV.h"
 #include "DVDInputStreamRTMP.h"
 #ifdef HAVE_LIBBLURAY
@@ -55,6 +56,8 @@ CDVDInputStream* CDVDFactoryInputStream::CreateInputStream(IDVDPlayer* pPlayer,
   {
     return (new CDVDInputStreamNavigator(pPlayer));
   }
+  else if(file.substr(0, 6) == "pvr://")
+    return new CDVDInputStreamPVRManager(pPlayer);
 #ifdef HAVE_LIBBLURAY
   else if (item.IsType(".bdmv") || item.IsType(".mpls") || content == "bluray/iso")
     return new CDVDInputStreamBluray();
index d68a30e..72d8e95 100644 (file)
@@ -38,8 +38,9 @@ enum DVDStreamType
   DVDSTREAM_TYPE_RTMP   = 7,
   DVDSTREAM_TYPE_HTSP   = 8,
   DVDSTREAM_TYPE_MMS    = 9,
-  DVDSTREAM_TYPE_MPLS   = 10,
-  DVDSTREAM_TYPE_BLURAY = 11,
+  DVDSTREAM_TYPE_PVRMANAGER = 10,
+  DVDSTREAM_TYPE_MPLS   = 11,
+  DVDSTREAM_TYPE_BLURAY = 12,
 };
 
 #define DVDSTREAM_BLOCK_SIZE_FILE (2048 * 16)
@@ -52,10 +53,16 @@ public:
   {
     public:
     virtual ~IChannel() {};
-    virtual bool NextChannel() = 0;
-    virtual bool PrevChannel() = 0;
+    virtual bool NextChannel(bool preview = false) = 0;
+    virtual bool PrevChannel(bool preview = false) = 0;
     virtual bool SelectChannel(unsigned int channel) = 0;
+    virtual int GetSelectedChannel() = 0;
+    virtual int GetTotalTime() = 0;
+    virtual int GetStartTime() = 0;
     virtual bool UpdateItem(CFileItem& item) = 0;
+    virtual bool CanRecord() = 0;
+    virtual bool IsRecording() = 0;
+    virtual bool Record(bool bOnOff) = 0;
   };
 
   class IDisplayTime
index 5a5d8f4..52d7d7a 100644 (file)
@@ -196,7 +196,7 @@ bool CDVDInputStreamHTSP::GetChannels(SChannelV &channels, SChannelV::iterator &
   return false;
 }
 
-bool CDVDInputStreamHTSP::NextChannel()
+bool CDVDInputStreamHTSP::NextChannel(bool preview/* = false*/)
 {
   SChannelV channels;
   SChannelV::iterator it;
@@ -210,7 +210,7 @@ bool CDVDInputStreamHTSP::NextChannel()
     return SetChannel(circ->id);
 }
 
-bool CDVDInputStreamHTSP::PrevChannel()
+bool CDVDInputStreamHTSP::PrevChannel(bool preview/* = false*/)
 {
   SChannelV channels;
   SChannelV::iterator it;
@@ -257,6 +257,33 @@ int CDVDInputStreamHTSP::GetTotalTime()
 {
   if(m_event.id == 0)
     return 0;
+
+  long duration = (time_t)m_event.stop - (time_t)m_event.start;
+  CDateTimeSpan time = CDateTimeSpan(0, 0, duration / 60, duration % 60);
+
+  return time.GetDays()    * 1000 * 60 * 60 * 24
+       + time.GetHours()   * 1000 * 60 * 60
+       + time.GetMinutes() * 1000 * 60
+       + time.GetSeconds() * 1000;
+}
+
+int CDVDInputStreamHTSP::GetStartTime()
+{
+  if(m_event.id == 0)
+    return 0;
+
+  time_t time_c;
+
+  CDateTime::GetCurrentDateTime().GetAsTime(time_c);
+
+  return (m_event.start - time_c) * 1000;
+}
+
+/*
+int CDVDInputStreamHTSP::GetTotalTime()
+{
+  if(m_event.id == 0)
+    return 0;
   return (m_event.stop - m_event.start) * 1000;
 }
 
@@ -271,6 +298,7 @@ int CDVDInputStreamHTSP::GetTime()
        + time.GetMinutes() * 1000 * 60
        + time.GetSeconds() * 1000;
 }
+*/
 
 void CDVDInputStreamHTSP::Abort()
 {
index 7f7781e..e8c378b 100644 (file)
@@ -26,7 +26,6 @@
 class CDVDInputStreamHTSP
   : public CDVDInputStream
   , public CDVDInputStream::IChannel
-  , public CDVDInputStream::IDisplayTime
 {
 public:
   CDVDInputStreamHTSP();
@@ -43,13 +42,18 @@ public:
 
   virtual void    Abort();
 
-  bool            NextChannel();
-  bool            PrevChannel();
+  bool            NextChannel(bool preview = false);
+  bool            PrevChannel(bool preview = false);
   bool            SelectChannel(unsigned int channel);
+  int             GetSelectedChannel() {return -1; }
   bool            UpdateItem(CFileItem& item);
 
+  bool            CanRecord()         { return false; }
+  bool            IsRecording()       { return false; }
+  bool            Record(bool bOnOff) { return false; }
+
   int             GetTotalTime();
-  int             GetTime();
+  int             GetStartTime();
 
   htsmsg_t* ReadStream();
 
index 9ddf69f..d09fa6f 100644 (file)
@@ -106,7 +106,10 @@ void CDVDInputStreamMMS::Close()
 {
   CDVDInputStream::Close();
   if (m_mms)
+  {
     mmsx_close(m_mms);
+    m_mms = NULL;
+  }
 }
 
 int CDVDInputStreamMMS::Read(BYTE* buf, int buf_size)
diff --git a/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamPVRManager.cpp b/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamPVRManager.cpp
new file mode 100644 (file)
index 0000000..33faa9c
--- /dev/null
@@ -0,0 +1,282 @@
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "DVDFactoryInputStream.h"
+#include "DVDInputStreamPVRManager.h"
+#include "FileSystem/PVRFile.h"
+#include "URL.h"
+#include "pvr/PVRManager.h"
+
+using namespace XFILE;
+
+/************************************************************************
+ * Description: Class constructor, initialize member variables
+ *              public class is CDVDInputStream
+ */
+CDVDInputStreamPVRManager::CDVDInputStreamPVRManager(IDVDPlayer* pPlayer) : CDVDInputStream(DVDSTREAM_TYPE_PVRMANAGER)
+{
+  m_pPlayer         = pPlayer;
+  m_pFile           = NULL;
+  m_pRecordable     = NULL;
+  m_pLiveTV         = NULL;
+  m_pOtherStream    = NULL;
+  m_eof             = true;
+}
+
+/************************************************************************
+ * Description: Class destructor
+ */
+CDVDInputStreamPVRManager::~CDVDInputStreamPVRManager()
+{
+  Close();
+}
+
+bool CDVDInputStreamPVRManager::IsEOF()
+{
+  if (m_pOtherStream)
+    return m_pOtherStream->IsEOF();
+  else
+    return !m_pFile || m_eof;
+}
+
+bool CDVDInputStreamPVRManager::Open(const char* strFile, const std::string& content)
+{
+  /* Open PVR File for both cases, to have access to ILiveTVInterface and
+   * IRecordable
+   */
+  m_pFile       = new CPVRFile();
+  m_pLiveTV     = ((CPVRFile*)m_pFile)->GetLiveTV();
+  m_pRecordable = ((CPVRFile*)m_pFile)->GetRecordable();
+
+  CURL url(strFile);
+  if (!CDVDInputStream::Open(strFile, content)) return false;
+  if (!m_pFile->Open(url))
+  {
+    delete m_pFile;
+    m_pFile = NULL;
+    return false;
+  }
+  m_eof = false;
+
+  /*
+   * Translate the "pvr://....." entry.
+   * The PVR Client can use http or whatever else is supported by DVDPlayer.
+   * to access streams.
+   * If after translation the file protocol is still "pvr://" use this class
+   * to read the stream data over the CPVRFile class and the PVR Library itself.
+   * Otherwise call CreateInputStream again with the translated filename and looks again
+   * for the right protocol stream handler and swap every call to this input stream
+   * handler.
+   */
+  std::string transFile = XFILE::CPVRFile::TranslatePVRFilename(strFile);
+  if(transFile.substr(0, 6) != "pvr://")
+  {
+    m_pOtherStream = CDVDFactoryInputStream::CreateInputStream(m_pPlayer, transFile, content);
+    if (!m_pOtherStream)
+    {
+      CLog::Log(LOGERROR, "CDVDInputStreamPVRManager::Open - unable to create input stream for [%s]", transFile.c_str());
+      return false;
+    }
+    else
+      m_pOtherStream->SetFileItem(m_item);
+
+    if (!m_pOtherStream->Open(transFile.c_str(), content))
+    {
+      CLog::Log(LOGERROR, "CDVDInputStreamPVRManager::Open - error opening [%s]", transFile.c_str());
+      delete m_pFile;
+      m_pFile = NULL;
+      delete m_pOtherStream;
+      m_pOtherStream = NULL;
+      return false;
+    }
+  }
+
+  return true;
+}
+
+// close file and reset everyting
+void CDVDInputStreamPVRManager::Close()
+{
+  if (m_pOtherStream)
+  {
+    m_pOtherStream->Close();
+    delete m_pOtherStream;
+  }
+
+  if (m_pFile)
+  {
+    m_pFile->Close();
+    delete m_pFile;
+  }
+
+  CDVDInputStream::Close();
+
+  m_pPlayer         = NULL;
+  m_pFile           = NULL;
+  m_pLiveTV         = NULL;
+  m_pRecordable     = NULL;
+  m_pOtherStream    = NULL;
+  m_eof             = true;
+}
+
+int CDVDInputStreamPVRManager::Read(BYTE* buf, int buf_size)
+{
+  if(!m_pFile) return -1;
+
+  if (m_pOtherStream)
+  {
+    return m_pOtherStream->Read(buf, buf_size);
+  }
+  else
+  {
+    unsigned int ret = m_pFile->Read(buf, buf_size);
+
+    /* we currently don't support non completing reads */
+    if( ret <= 0 ) m_eof = true;
+
+    return (int)(ret & 0xFFFFFFFF);
+  }
+}
+
+__int64 CDVDInputStreamPVRManager::Seek(__int64 offset, int whence)
+{
+  if(!m_pFile) return -1;
+
+  if (m_pOtherStream)
+  {
+    return m_pOtherStream->Seek(offset, whence);
+  }
+  else
+  {
+    __int64 ret = m_pFile->Seek(offset, whence);
+
+    /* if we succeed, we are not eof anymore */
+    if( ret >= 0 ) m_eof = false;
+
+    return ret;
+  }
+}
+
+__int64 CDVDInputStreamPVRManager::GetLength()
+{
+  if(!m_pFile) return -1;
+
+  if (m_pOtherStream)
+    return m_pOtherStream->GetLength();
+  else
+    return m_pFile->GetLength();
+}
+
+int CDVDInputStreamPVRManager::GetTotalTime()
+{
+  if (m_pLiveTV)
+    return m_pLiveTV->GetTotalTime();
+  return 0;
+}
+
+int CDVDInputStreamPVRManager::GetStartTime()
+{
+  if (m_pLiveTV)
+    return m_pLiveTV->GetStartTime();
+  return 0;
+}
+
+bool CDVDInputStreamPVRManager::NextChannel(bool preview/* = false*/)
+{
+  if (m_pLiveTV)
+    return m_pLiveTV->NextChannel(preview);
+  return false;
+}
+
+bool CDVDInputStreamPVRManager::PrevChannel(bool preview/* = false*/)
+{
+  if (m_pLiveTV)
+    return m_pLiveTV->PrevChannel(preview);
+  return false;
+}
+
+bool CDVDInputStreamPVRManager::SelectChannel(unsigned int channel)
+{
+  if (m_pLiveTV)
+    return m_pLiveTV->SelectChannel(channel);
+  return false;
+}
+
+int CDVDInputStreamPVRManager::GetSelectedChannel()
+{
+  int number = -1;
+  bool radio = false;
+  g_PVRManager.GetCurrentChannel(&number, &radio);
+  return number;
+}
+
+bool CDVDInputStreamPVRManager::UpdateItem(CFileItem& item)
+{
+  if (m_pLiveTV)
+    return m_pLiveTV->UpdateItem(item);
+  return false;
+}
+
+bool CDVDInputStreamPVRManager::NextStream()
+{
+  if(!m_pFile) return false;
+
+  if (m_pOtherStream)
+    return m_pOtherStream->NextStream();
+  else
+  {
+    if(m_pFile->SkipNext())
+    {
+      m_eof = false;
+      return true;
+    }
+  }
+  return false;
+}
+
+bool CDVDInputStreamPVRManager::CanRecord()
+{
+  if (m_pRecordable)
+    return m_pRecordable->CanRecord();
+  return false;
+}
+
+bool CDVDInputStreamPVRManager::IsRecording()
+{
+  if (m_pRecordable)
+    return m_pRecordable->IsRecording();
+  return false;
+}
+
+bool CDVDInputStreamPVRManager::Record(bool bOnOff)
+{
+  if (m_pRecordable)
+    return m_pRecordable->Record(bOnOff);
+  return false;
+}
+
+CStdString CDVDInputStreamPVRManager::GetInputFormat()
+{
+  if (m_pOtherStream)
+    return "";
+  else
+    return g_PVRManager.GetCurrentInputFormat();
+}
diff --git a/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamPVRManager.h b/xbmc/cores/dvdplayer/DVDInputStreams/DVDInputStreamPVRManager.h
new file mode 100644 (file)
index 0000000..af7d389
--- /dev/null
@@ -0,0 +1,106 @@
+#pragma once
+
+/*
+ *      Copyright (C) 2005-2008 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+/*
+* for DESCRIPTION see 'DVDInputStreamPVRManager.cpp'
+*/
+
+#include "DVDInputStream.h"
+#include "FileItem.h"
+
+namespace XFILE {
+class IFile;
+class ILiveTVInterface;
+class IRecordable;
+}
+
+class IDVDPlayer;
+
+class CDVDInputStreamPVRManager
+  : public CDVDInputStream
+  , public CDVDInputStream::IChannel
+{
+public:
+  CDVDInputStreamPVRManager(IDVDPlayer* pPlayer);
+  virtual ~CDVDInputStreamPVRManager();
+  virtual bool Open(const char* strFile, const std::string &content);
+  virtual void Close();
+  virtual int Read(BYTE* buf, int buf_size);
+  virtual __int64 Seek(__int64 offset, int whence);
+  virtual bool Pause(double dTime) { return false; }
+  virtual bool IsEOF();
+  virtual __int64 GetLength();
+
+  virtual bool    NextStream();
+
+  bool            SelectChannel(unsigned int iChannel);
+  bool            NextChannel(bool preview = false);
+  bool            PrevChannel(bool preview = false);
+  int             GetSelectedChannel();
+
+  int             GetTotalTime();
+  int             GetStartTime();
+
+  bool            CanRecord();
+  bool            IsRecording();
+  bool            Record(bool bOnOff);
+
+  bool            UpdateItem(CFileItem& item);
+
+  /* overloaded is streamtype to support m_pOtherStream */
+  bool            IsStreamType(DVDStreamType type) const;
+
+  /*! \brief Get the input format from the Backend
+   If it is empty ffmpeg scanning the stream to find the right input format.
+   See "xbmc/cores/dvdplayer/Codecs/ffmpeg/libavformat/allformats.c" for a
+   list of the input formats.
+   \return The name of the input format
+   */
+  CStdString      GetInputFormat();
+
+  /* returns m_pOtherStream */
+  CDVDInputStream* GetOtherStream();
+
+protected:
+  IDVDPlayer*               m_pPlayer;
+  CDVDInputStream*          m_pOtherStream;
+  XFILE::IFile*             m_pFile;
+  XFILE::ILiveTVInterface*  m_pLiveTV;
+  XFILE::IRecordable*       m_pRecordable;
+  bool                      m_eof;
+};
+
+
+inline bool CDVDInputStreamPVRManager::IsStreamType(DVDStreamType type) const
+{
+  if (m_pOtherStream)
+    return m_pOtherStream->IsStreamType(type);
+
+  return m_streamType == type;
+}
+
+inline CDVDInputStream* CDVDInputStreamPVRManager::GetOtherStream()
+{
+  return m_pOtherStream;
+};
+
index fdeb336..70184c7 100644 (file)
@@ -130,13 +130,13 @@ int CDVDInputStreamTV::GetStartTime()
   return m_pLiveTV->GetStartTime();
 }
 
-bool CDVDInputStreamTV::NextChannel()
+bool CDVDInputStreamTV::NextChannel(bool preview/* = false*/)
 {
   if(!m_pLiveTV) return false;
   return m_pLiveTV->NextChannel();
 }
 
-bool CDVDInputStreamTV::PrevChannel()
+bool CDVDInputStreamTV::PrevChannel(bool preview/* = false*/)
 {
   if(!m_pLiveTV) return false;
   return m_pLiveTV->PrevChannel();
index f19244a..b1d1471 100644 (file)
@@ -48,9 +48,10 @@ public:
   virtual int     GetBlockSize();
 
 
-  bool            NextChannel();
-  bool            PrevChannel();
+  bool            NextChannel(bool preview = false);
+  bool            PrevChannel(bool preview = false);
   bool            SelectChannel(unsigned int channel);
+  int             GetSelectedChannel() {return -1; }
 
   int             GetTotalTime();
   int             GetStartTime();
index ce613e7..5b4a866 100644 (file)
@@ -1,4 +1,4 @@
-INCLUDES=-I. -I.. -I../.. -I../../../ -I../../../linux -I../../../../guilib -I../../../lib/libRTMP -I../../../lib
+INCLUDES=-I. -I.. -I../.. -I../../../ -I../../../linux -I../../../../guilib -I../../../lib/libRTMP -I../../../lib -I../../../utils
 CXXFLAGS += -D__STDC_FORMAT_MACROS \
           -DENABLE_DVDINPUTSTREAM_STACK \
 
@@ -12,6 +12,7 @@ SRCS= DVDFactoryInputStream.cpp \
        DVDInputStreamFFmpeg.cpp \
        DVDInputStreamTV.cpp \
        DVDInputStreamRTMP.cpp \
+       DVDInputStreamPVRManager.cpp \
        DVDInputStreamStack.cpp \
        DVDInputStreamHTSP.cpp \
        DVDInputStreamMMS.cpp \
index ff96b3e..66918e0 100644 (file)
@@ -26,6 +26,7 @@
 #include "DVDInputStreams/DVDFactoryInputStream.h"
 #include "DVDInputStreams/DVDInputStreamNavigator.h"
 #include "DVDInputStreams/DVDInputStreamTV.h"
+#include "DVDInputStreams/DVDInputStreamPVRManager.h"
 
 #include "DVDDemuxers/DVDDemux.h"
 #include "DVDDemuxers/DVDDemuxUtils.h"
@@ -41,6 +42,7 @@
 #include "Util.h"
 #include "utils/GUIInfoManager.h"
 #include "GUIWindowManager.h"
+#include "GUIDialogFullScreenInfo.h"
 #include "Application.h"
 #include "DVDPerformanceCounter.h"
 #include "FileSystem/File.h"
@@ -65,6 +67,8 @@
 #include "utils/TimeUtils.h"
 #include "utils/StreamDetails.h"
 #include "MediaManager.h"
+#include "pvr/PVRManager.h"
+#include "FileSystem/PVRFile.h"
 #include "GUIDialogBusy.h"
 
 using namespace std;
@@ -465,6 +469,7 @@ retry:
 
   // find any available external subtitles for non dvd files
   if (!m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD)
+  &&  !m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER)
   &&  !m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV)
   &&  !m_pInputStream->IsStreamType(DVDSTREAM_TYPE_HTSP))
   {
@@ -504,6 +509,7 @@ retry:
   m_clock.Reset();
   m_dvd.Clear();
   m_errorCount = 0;
+  m_ChannelEntryTimeOut = 0;
 
   return true;
 }
@@ -521,7 +527,12 @@ bool CDVDPlayer::OpenDemuxStream()
     while(!m_bStop && attempts-- > 0)
     {
       m_pDemuxer = CDVDFactoryDemuxer::CreateDemuxer(m_pInputStream);
-      if(!m_pDemuxer && m_pInputStream->NextStream())
+      if(!m_pDemuxer && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER))
+      {
+        Sleep(200);
+        continue;
+      }
+      else if(!m_pDemuxer && m_pInputStream->NextStream())
       {
         CLog::Log(LOGDEBUG, "%s - New stream available from input, retry open", __FUNCTION__);
         continue;
@@ -665,7 +676,6 @@ void CDVDPlayer::OpenDefaultStreams()
     m_dvdPlayerVideo.EnableSubtitle(true);
   else
     m_dvdPlayerVideo.EnableSubtitle(false);
-
   // open teletext data stream
   count = m_SelectionStreams.Count(STREAM_TELETEXT);
   valid = false;
@@ -981,6 +991,26 @@ void CDVDPlayer::Process()
     // update application with our state
     UpdateApplication(1000);
 
+    if (m_ChannelEntryTimeOut > 0 && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER))
+    {
+      if (CTimeUtils::GetTimeMS() - m_ChannelEntryTimeOut > g_guiSettings.GetInt("pvrplayback.channelentrytimeout"))
+      {
+        m_ChannelEntryTimeOut = 0;
+        CDVDInputStreamPVRManager* pStream = static_cast<CDVDInputStreamPVRManager*>(m_pInputStream);
+        int channel = pStream->GetSelectedChannel();
+        if(channel > 0 && pStream->SelectChannel(channel))
+        {
+          FlushBuffers(false);
+          SAFE_DELETE(m_pDemuxer);
+          continue;
+        }
+        else
+        {
+          break;
+        }
+      }
+    }
+
     // if the queues are full, no need to read more
     if ((!m_dvdPlayerAudio.AcceptsData() && m_CurrentAudio.id >= 0)
     ||  (!m_dvdPlayerVideo.AcceptsData() && m_CurrentVideo.id >= 0))
@@ -1062,6 +1092,16 @@ void CDVDPlayer::Process()
         Sleep(100);
         continue;
       }
+      else if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER))
+      {
+        CDVDInputStreamPVRManager* pStream = static_cast<CDVDInputStreamPVRManager*>(m_pInputStream);
+
+        if (pStream->IsEOF())
+          break;
+
+        Sleep(100);
+        continue;
+      }
 
       // make sure we tell all players to finish it's data
       if(m_CurrentAudio.inited)
@@ -1369,7 +1409,8 @@ bool CDVDPlayer::CheckStartCaching(CCurrentStream& current)
       return false;
 
     if(m_pInputStream->IsStreamType(DVDSTREAM_TYPE_HTSP)
-    || m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV))
+    || m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV)
+    || m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER))
       SetCaching(CACHESTATE_INIT);
     else
     {
@@ -1474,7 +1515,7 @@ void CDVDPlayer::CheckContinuity(CCurrentStream& current, DemuxPacket* pPacket)
 #if 0
   // these checks seem to cause more harm, than good
   // looping stillframes are not common in normal files
-  // and a better fix for this behaviour would be to 
+  // and a better fix for this behaviour would be to
   // correct the timestamps with some offset
 
   if (current.type == STREAM_VIDEO
@@ -1550,6 +1591,7 @@ void CDVDPlayer::CheckContinuity(CCurrentStream& current, DemuxPacket* pPacket)
     /* normally don't need to sync players since video player will keep playing at normal fps */
     /* after a discontinuity */
     //SynchronizePlayers(dts, pts, MSGWAIT_ALL);
+
     m_CurrentAudio.inited = false;
     m_CurrentVideo.inited = false;
     m_CurrentSubtitle.inited = false;
@@ -1952,8 +1994,9 @@ void CDVDPlayer::HandleMessages()
       }
       else if (pMsg->IsType(CDVDMsg::PLAYER_SET_RECORD))
       {
-        if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV))
-          static_cast<CDVDInputStreamTV*>(m_pInputStream)->Record(*(CDVDMsgBool*)pMsg);
+        CDVDInputStream::IChannel* input = dynamic_cast<CDVDInputStream::IChannel*>(m_pInputStream);
+        if(input)
+          input->Record(*(CDVDMsgBool*)pMsg);
       }
       else if (pMsg->IsType(CDVDMsg::GENERAL_FLUSH))
       {
@@ -2000,30 +2043,48 @@ void CDVDPlayer::HandleMessages()
         if(m_pDemuxer)
           m_pDemuxer->SetSpeed(speed);
       }
-      else if (pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_NEXT) ||
-               pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_PREV) ||
-              (pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_SELECT) && m_messenger.GetPacketCount(CDVDMsg::PLAYER_CHANNEL_SELECT) == 0))
+      else if (pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_SELECT) && m_messenger.GetPacketCount(CDVDMsg::PLAYER_CHANNEL_SELECT) == 0)
       {
         CDVDInputStream::IChannel* input = dynamic_cast<CDVDInputStream::IChannel*>(m_pInputStream);
-        if(input)
+        if(input && input->SelectChannel(static_cast<CDVDMsgInt*>(pMsg)->m_value))
         {
-          g_infoManager.SetDisplayAfterSeek(100000);
 
+          FlushBuffers(false);
+          SAFE_DELETE(m_pDemuxer);
+        }
+      }
+      else if (pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_NEXT) || pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_PREV))
+      {
+        CDVDInputStream::IChannel* input = dynamic_cast<CDVDInputStream::IChannel*>(m_pInputStream);
+        if(input)
+        {
           bool result;
-          if (pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_SELECT))
-            result = input->SelectChannel(static_cast<CDVDMsgInt*>(pMsg)->m_value);
-          else if(pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_NEXT))
-            result = input->NextChannel();
+          bool fastSwitch = g_guiSettings.GetInt("pvrplayback.channelentrytimeout") > 0;
+
+          if(pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_NEXT))
+            result = input->NextChannel(fastSwitch);
           else
-            result = input->PrevChannel();
+            result = input->PrevChannel(fastSwitch);
 
           if(result)
           {
-            FlushBuffers(false);
-            SAFE_DELETE(m_pDemuxer);
+            if (fastSwitch)
+            {
+              CFileItem item(g_application.CurrentFileItem());
+              if(input->UpdateItem(item))
+              {
+                g_application.CurrentFileItem() = item;
+                g_infoManager.SetCurrentItem(item);
+              }
+              m_ChannelEntryTimeOut = CTimeUtils::GetTimeMS();
+            }
+            else
+            {
+              m_ChannelEntryTimeOut = 0;
+              FlushBuffers(false);
+              SAFE_DELETE(m_pDemuxer);
+            }
           }
-
-          g_infoManager.SetDisplayAfterSeek();
         }
       }
       else if (pMsg->IsType(CDVDMsg::GENERAL_GUI_ACTION))
@@ -3218,17 +3279,45 @@ bool CDVDPlayer::OnAction(const CAction &action)
   {
     switch (action.GetID())
     {
+      case ACTION_MOVE_UP:
       case ACTION_NEXT_ITEM:
       case ACTION_PAGE_UP:
         m_messenger.Put(new CDVDMsg(CDVDMsg::PLAYER_CHANNEL_NEXT));
         g_infoManager.SetDisplayAfterSeek();
+        if (g_guiSettings.GetBool("pvrmenu.infoswitch"))
+        {
+          CGUIDialogFullScreenInfo* pDialog = (CGUIDialogFullScreenInfo*)g_windowManager.GetWindow(WINDOW_DIALOG_FULLSCREEN_INFO);
+          if (pDialog)
+          {
+            if (g_guiSettings.GetBool("pvrmenu.infotimeout"))
+            {
+              pDialog->SetAutoClose(g_guiSettings.GetInt("pvrmenu.infotime")*1000);
+            }
+            if (m_ChannelEntryTimeOut == 0)
+              pDialog->DoModal();
+          }
+        }
         return true;
       break;
 
+      case ACTION_MOVE_DOWN:
       case ACTION_PREV_ITEM:
       case ACTION_PAGE_DOWN:
         m_messenger.Put(new CDVDMsg(CDVDMsg::PLAYER_CHANNEL_PREV));
         g_infoManager.SetDisplayAfterSeek();
+        if (g_guiSettings.GetBool("pvrmenu.infoswitch"))
+        {
+          CGUIDialogFullScreenInfo* pDialog = (CGUIDialogFullScreenInfo*)g_windowManager.GetWindow(WINDOW_DIALOG_FULLSCREEN_INFO);
+          if (pDialog)
+          {
+            if (g_guiSettings.GetBool("pvrmenu.infotimeout"))
+            {
+              pDialog->SetAutoClose(g_guiSettings.GetInt("pvrmenu.infotime")*1000);
+            }
+            if (m_ChannelEntryTimeOut == 0)
+              pDialog->DoModal();
+          }
+        }
         return true;
       break;
 
@@ -3238,6 +3327,18 @@ bool CDVDPlayer::OnAction(const CAction &action)
         int channel = action.GetAmount();
         m_messenger.Put(new CDVDMsgInt(CDVDMsg::PLAYER_CHANNEL_SELECT, channel));
         g_infoManager.SetDisplayAfterSeek();
+        if (g_guiSettings.GetBool("pvrmenu.infoswitch"))
+        {
+          CGUIDialogFullScreenInfo* pDialog = (CGUIDialogFullScreenInfo*)g_windowManager.GetWindow(WINDOW_DIALOG_FULLSCREEN_INFO);
+          if (pDialog)
+          {
+            if (g_guiSettings.GetBool("pvrmenu.infotimeout"))
+            {
+              pDialog->SetAutoClose(g_guiSettings.GetInt("pvrmenu.infotime")*1000);
+            }
+            pDialog->DoModal();
+          }
+        }
         return true;
       }
       break;
@@ -3487,13 +3588,16 @@ void CDVDPlayer::UpdatePlayState(double timeout)
     else
         m_State.player_state = "";
 
-
-    if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV))
+    CDVDInputStream::IChannel* input = dynamic_cast<CDVDInputStream::IChannel*>(m_pInputStream);
+    if(input)
     {
-      if(((CDVDInputStreamTV*)m_pInputStream)->GetTotalTime() > 0)
+      m_State.canrecord = input->CanRecord();
+      m_State.recording = input->IsRecording();
+
+      if(input->GetTotalTime() > 0)
       {
-        m_State.time      -= ((CDVDInputStreamTV*)m_pInputStream)->GetStartTime();
-        m_State.time_total = ((CDVDInputStreamTV*)m_pInputStream)->GetTotalTime();
+        m_State.time       = input->GetStartTime();
+        m_State.time_total = input->GetTotalTime();
       }
     }
   }
@@ -3563,7 +3667,8 @@ bool CDVDPlayer::IsRecording()
 
 bool CDVDPlayer::Record(bool bOnOff)
 {
-  if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV))
+  if (m_pInputStream && (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV) ||
+                         m_pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER)) )
   {
     m_messenger.Put(new CDVDMsgBool(CDVDMsg::PLAYER_SET_RECORD, bOnOff));
     return true;
index 2ba06ce..5602319 100644 (file)
@@ -295,6 +295,7 @@ protected:
   std::string m_mimetype;  // hold a hint to what content file contains (mime type)
   ECacheState m_caching;
   CFileItem   m_item;
+  long        m_ChannelEntryTimeOut;
 
   CCurrentStream m_CurrentAudio;
   CCurrentStream m_CurrentVideo;
diff --git a/xbmc/cores/dvdplayer/DVDSubtitles/SamiTagConvertor.cpp b/xbmc/cores/dvdplayer/DVDSubtitles/SamiTagConvertor.cpp
new file mode 100644 (file)
index 0000000..6199aaa
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ *      Copyright (C) 2005-2008 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "SamiTagConvertor.h"
+#include "DVDSubtitleStream.h"
+#include "DVDCodecs/Overlay/DVDOverlayText.h"
+#include "utils/RegExp.h"
+
+SamiTagConvertor::~SamiTagConvertor()
+{
+  delete m_tags;
+  delete m_tagOptions;
+}
+
+bool SamiTagConvertor::Init()
+{
+  m_tags = new CRegExp(true);
+  if (!m_tags->RegComp("(<[^>]*>)"))
+    return false;
+
+  m_tagOptions = new CRegExp(true);
+  if (!m_tagOptions->RegComp("([a-z]+)[ \t]*=[ \t]*(?:[\"'])?([^\"'> ]+)(?:[\"'])?(?:>)?"))
+    return false;
+
+  return true;
+}
+
+void SamiTagConvertor::ConvertLine(CDVDOverlayText* pOverlay, const char* line, int len, const char* lang)
+{
+  CStdStringA strUTF8;
+  strUTF8.assign(line, len);
+
+  int pos = 0;
+  int del_start = 0;
+  while ((pos=m_tags->RegFind(strUTF8.c_str(), pos)) >= 0)
+  {
+    // Parse Tags
+    CStdString fullTag = m_tags->GetMatch(0);
+    fullTag.ToLower();
+    strUTF8.erase(pos, fullTag.length());
+    if (fullTag == "<b>")
+    {
+      tag_flag[FLAG_BOLD] = true;
+      strUTF8.insert(pos, "[B]");
+      pos += 3;
+    }
+    else if (fullTag == "</b>" && tag_flag[FLAG_BOLD])
+    {
+      tag_flag[FLAG_BOLD] = false;
+      strUTF8.insert(pos, "[/B]");
+      pos += 4;
+    }
+    else if (fullTag == "<i>")
+    {
+      tag_flag[FLAG_ITALIC] = true;
+      strUTF8.insert(pos, "[I]");
+      pos += 3;
+    }
+    else if (fullTag == "</i>" && tag_flag[FLAG_ITALIC])
+    {
+      tag_flag[FLAG_ITALIC] = false;
+      strUTF8.insert(pos, "[/I]");
+      pos += 4;
+    }
+    else if (fullTag == "</font>" && tag_flag[FLAG_COLOR])
+    {
+      tag_flag[FLAG_COLOR] = false;
+      strUTF8.insert(pos, "[/COLOR]");
+      pos += 8;
+    }
+    else if (fullTag.Left(5) == "<font")
+    {
+      int pos2 = 5;
+      while ((pos2 = m_tagOptions->RegFind(fullTag.c_str(), pos2)) >= 0)
+      {
+        CStdString tagOptionName = m_tagOptions->GetMatch(1);
+        CStdString tagOptionValue = m_tagOptions->GetMatch(2);
+        pos2 += tagOptionName.length() + tagOptionValue.length();
+        if (tagOptionName == "color")
+        {
+          tag_flag[FLAG_COLOR] = true;
+          CStdString tempColorTag = "[COLOR ";
+          if (tagOptionValue[0] == '#')
+          {
+            tagOptionValue.erase(0, 1);
+            tempColorTag += "FF";
+          }
+          else if( tagOptionValue.size() == 6 )
+          {
+            bool bHex = true;
+            for( int i=0 ; i<6 ; i++ )
+            {
+              char temp = tagOptionValue[i];
+              if( !(('0' <= temp && temp <= '9') ||
+                ('a' <= temp && temp <= 'f') ||
+                ('A' <= temp && temp <= 'F') ))
+              {
+                bHex = false;
+                break;
+              }
+            }
+            if( bHex ) tempColorTag += "FF";
+          }
+          tempColorTag += tagOptionValue;
+          tempColorTag += "]";
+          strUTF8.insert(pos, tempColorTag);
+          pos += tempColorTag.length();
+        }
+      }
+    }
+    else if (lang && (fullTag.Left(3) == "<p "))
+    {
+      int pos2 = 3;
+      while ((pos2 = m_tagOptions->RegFind(fullTag.c_str(), pos2)) >= 0)
+      {
+        CStdString tagOptionName = m_tagOptions->GetMatch(1);
+        CStdString tagOptionValue = m_tagOptions->GetMatch(2);
+        pos2 += tagOptionName.length() + tagOptionValue.length();
+        if (tagOptionName == "class")
+        {
+          if (tag_flag[FLAG_LANGUAGE])
+          {
+            strUTF8.erase(del_start, pos - del_start);
+            pos = del_start;
+          }
+          if (!tagOptionValue.Compare(lang))
+          {
+            tag_flag[FLAG_LANGUAGE] = false;
+          }
+          else
+          {
+            tag_flag[FLAG_LANGUAGE] = true;
+            del_start = pos;
+          }
+          break;
+        }
+      }
+    }
+    else if (fullTag == "</p>" && tag_flag[FLAG_LANGUAGE])
+    {
+      strUTF8.erase(del_start, pos - del_start);
+      pos = del_start;
+      tag_flag[FLAG_LANGUAGE] = false;
+    }
+    else if (fullTag == "<br>" && !strUTF8.IsEmpty())
+    {
+      strUTF8.Insert(pos, "\n");
+      pos += 1;
+    }
+  }
+
+  if(tag_flag[FLAG_LANGUAGE])
+    strUTF8.erase(del_start);
+
+  if (strUTF8.IsEmpty())
+    return;
+
+  if( strUTF8[strUTF8.size()-1] == '\n' )
+    strUTF8.Delete(strUTF8.size()-1);
+
+  // add a new text element to our container
+  pOverlay->AddElement(new CDVDOverlayText::CElementText(strUTF8.c_str()));
+}
+
+void SamiTagConvertor::CloseTag(CDVDOverlayText* pOverlay)
+{
+  if (tag_flag[FLAG_BOLD])
+  {
+    pOverlay->AddElement(new CDVDOverlayText::CElementText("[/B]"));
+    tag_flag[FLAG_BOLD] = false;
+  }
+  if (tag_flag[FLAG_ITALIC])
+  {
+    pOverlay->AddElement(new CDVDOverlayText::CElementText("[/I]"));
+    tag_flag[FLAG_ITALIC] = false;
+  }
+  if (tag_flag[FLAG_COLOR])
+  {
+    pOverlay->AddElement(new CDVDOverlayText::CElementText("[/COLOR]"));
+    tag_flag[FLAG_COLOR] = false;
+  }
+  tag_flag[FLAG_LANGUAGE] = false;
+}
+
+void SamiTagConvertor::LoadHead(CDVDSubtitleStream* samiStream)
+{
+  char line[1024];
+  bool inSTYLE = false;
+  CRegExp reg(true);
+  if (!reg.RegComp("\\.([a-z]+)[ \t]*\\{[ \t]*name:([^;]*?);[ \t]*lang:([^;]*?);[ \t]*SAMIType:([^;]*?);[ \t]*\\}"))
+    return;
+
+  while (samiStream->ReadLine(line, sizeof(line)))
+  {
+    if (!strnicmp(line, "<BODY>", 6))
+      break;
+    if (inSTYLE)
+    {
+      if (!strnicmp(line, "</STYLE>", 8))
+        break;
+      else
+      {
+        if (reg.RegFind(line) > -1)
+        {
+          SLangclass lc;
+          lc.ID = reg.GetMatch(1);
+          lc.Name = reg.GetMatch(2);
+          lc.Lang = reg.GetMatch(3);
+          lc.SAMIType = reg.GetMatch(4);
+          lc.Name.Trim();
+          lc.Lang.Trim();
+          lc.SAMIType.Trim();
+          m_Langclass.push_back(lc);
+        }
+      }
+    }
+    else
+    {
+      if (!strnicmp(line, "<STYLE TYPE=\"text/css\">", 23))
+        inSTYLE = true;
+    }
+  }
+}
+
diff --git a/xbmc/cores/dvdplayer/DVDSubtitles/SamiTagConvertor.h b/xbmc/cores/dvdplayer/DVDSubtitles/SamiTagConvertor.h
new file mode 100644 (file)
index 0000000..93ee191
--- /dev/null
@@ -0,0 +1,68 @@
+#pragma once
+
+/*
+ *      Copyright (C) 2005-2008 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+#include <stdio.h>
+#include "StdString.h"
+
+#define FLAG_BOLD   0
+#define FLAG_ITALIC 1
+#define FLAG_COLOR  2
+#define FLAG_LANGUAGE   3
+
+class CDVDOverlayText;
+class CDVDSubtitleStream;
+class CRegExp;
+
+class SamiTagConvertor
+{
+public:
+  SamiTagConvertor()
+  {
+    m_tags = NULL;
+    m_tagOptions = NULL;
+    tag_flag[FLAG_BOLD] = false;
+    tag_flag[FLAG_ITALIC] = false;
+    tag_flag[FLAG_COLOR] = false;
+    tag_flag[FLAG_LANGUAGE] = false; //set to true when classID != lang
+  }
+  virtual ~SamiTagConvertor();
+  bool Init();
+  void ConvertLine(CDVDOverlayText* pOverlay, const char* line, int len, const char* lang = NULL);
+  void CloseTag(CDVDOverlayText* pOverlay);
+  void LoadHead(CDVDSubtitleStream* samiStream);
+
+  typedef struct
+  {
+    CStdString ID;
+    CStdString Name;
+    CStdString Lang;
+    CStdString SAMIType;
+  } SLangclass;
+
+  std::vector<SLangclass> m_Langclass;
+
+private:
+  CRegExp *m_tags;
+  CRegExp *m_tagOptions;
+  bool tag_flag[4];
+};
+
index 70d1c41..b2342db 100644 (file)
@@ -78,6 +78,8 @@ ICodec* CodecFactory::CreateCodec(const CStdString& strFileType)
 #else
     return new DVDPlayerCodec();
 #endif
+  else if (strFileType.Equals("pvr"))
+    return new DVDPlayerCodec();
   else if (strFileType.Equals("m4a") || strFileType.Equals("aac"))
     return new DVDPlayerCodec();
   else if (strFileType.Equals("wv"))
index 81ecedc..95dd2e6 100644 (file)
@@ -210,7 +210,7 @@ typedef uint64_t  uintmax_t;
 #define UINT8_C(val)  val##ui8
 #define UINT16_C(val) val##ui16
 #define UINT32_C(val) val##ui32
-//#define UINT64_C(val) val##ui64
+#define UINT64_C(val) val##ui64
 
 // 7.18.4.2 Macros for greatest-width integer constants
 #define INTMAX_C   INT64_C
index a36ac62..852e648 100644 (file)
@@ -59,7 +59,6 @@
       <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
     </ClCompile>
     <Lib>
-      <IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
       <IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
     </Lib>
   </ItemDefinitionGroup>
diff --git a/xbmc/pvr/Makefile b/xbmc/pvr/Makefile
new file mode 100644 (file)
index 0000000..3539a58
--- /dev/null
@@ -0,0 +1,21 @@
+INCLUDES=-I. -I.. -I../../ -I../linux -I../cores -I../../guilib -I../posix -I../utils
+
+SRCS=PVRChannel.cpp \
+       PVRChannelGroup.cpp \
+       PVRChannelGroups.cpp \
+       PVRChannels.cpp \
+       PVRChannelsContainer.cpp \
+       PVREpg.cpp \
+       PVREpgInfoTag.cpp \
+       PVREpgs.cpp \
+       PVREpgSearchFilter.cpp \
+       PVRManager.cpp \
+       PVRRecordings.cpp \
+       PVRTimerInfoTag.cpp \
+       PVRTimers.cpp \
+       PVRDatabase.cpp
+
+LIB=pvr.a
+
+include ../../Makefile.include
+-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS)))
diff --git a/xbmc/pvr/PVRChannel.cpp b/xbmc/pvr/PVRChannel.cpp
new file mode 100644 (file)
index 0000000..9ee16f7
--- /dev/null
@@ -0,0 +1,734 @@
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "FileItem.h"
+#include "LocalizeStrings.h"
+#include "utils/log.h"
+#include "Util.h"
+#include "FileSystem/File.h"
+#include "MusicInfoTag.h"
+
+#include "PVRChannelGroups.h"
+#include "PVRChannelGroup.h"
+#include "PVRChannel.h"
+#include "PVREpgs.h"
+#include "PVREpg.h"
+#include "PVREpgInfoTag.h"
+#include "PVRDatabase.h"
+#include "PVRManager.h"
+
+using namespace XFILE;
+using namespace MUSIC_INFO;
+
+const CPVREpgInfoTag *m_EmptyEpgInfoTag = new CPVREpgInfoTag();
+
+bool CPVRChannel::operator==(const CPVRChannel& right) const
+{
+  if (this == &right) return true;
+
+  return (m_iChannelId              == right.m_iChannelId &&
+          m_iChannelNumber          == right.m_iChannelNumber &&
+          m_iChannelGroupId         == right.m_iChannelGroupId &&
+          m_bIsRadio                == right.m_bIsRadio &&
+          m_bIsHidden               == right.m_bIsHidden &&
+          m_bClientIsRecording      == right.m_bClientIsRecording &&
+          m_strIconPath             == right.m_strIconPath &&
+          m_strChannelName          == right.m_strChannelName &&
+          m_bIsVirtual              == right.m_bIsVirtual &&
+
+          m_iUniqueId               == right.m_iUniqueId &&
+          m_iClientId               == right.m_iClientId &&
+          m_iClientChannelNumber    == right.m_iClientChannelNumber &&
+          m_strClientChannelName    == right.m_strClientChannelName &&
+          m_strStreamURL            == right.m_strStreamURL &&
+          m_strFileNameAndPath      == right.m_strFileNameAndPath &&
+          m_iClientEncryptionSystem == right.m_iClientEncryptionSystem);
+}
+
+bool CPVRChannel::operator!=(const CPVRChannel &right) const
+{
+  return !(*this == right);
+}
+
+CPVRChannel::CPVRChannel()
+{
+  m_iChannelId              = -1;
+  m_iChannelNumber          = -1;
+  m_iChannelGroupId         = -1;
+  m_bIsRadio                = false;
+  m_bIsHidden               = false;
+  m_bClientIsRecording      = false;
+  m_strIconPath             = "";
+  m_strChannelName          = "";
+  m_bIsVirtual              = false;
+
+  m_EPG                     = NULL;
+  m_EPGNow                  = NULL;
+  m_bEPGEnabled             = true;
+  m_strEPGScraper           = "client";
+
+  m_iUniqueId               = -1;
+  m_iClientId               = -1;
+  m_iClientChannelNumber    = -1;
+  m_strClientChannelName    = "";
+  m_strInputFormat          = "";
+  m_strStreamURL            = "";
+  m_strFileNameAndPath      = "";
+  m_iClientEncryptionSystem = -1;
+}
+
+/********** XBMC related channel methods **********/
+
+bool CPVRChannel::UpdateFromClient(const CPVRChannel &channel)
+{
+  bool bChanged = false;
+
+  bChanged = SetClientID(channel.ClientID()) || bChanged;
+  bChanged = SetClientChannelNumber(channel.ClientChannelNumber()) || bChanged;
+  bChanged = SetClientChannelName(channel.ClientChannelName()) || bChanged;
+  bChanged = SetInputFormat(channel.InputFormat()) || bChanged;
+  bChanged = SetStreamURL(channel.StreamURL()) || bChanged;
+  bChanged = SetEncryptionSystem(channel.EncryptionSystem()) || bChanged;
+
+  return bChanged;
+}
+
+bool CPVRChannel::Persist(bool bQueueWrite /* = false */)
+{
+  CPVRDatabase *database = g_PVRManager.GetTVDatabase();
+  if (database)
+  {
+    database->Open();
+    database->UpdateChannel(*this, bQueueWrite);
+    database->Close();
+
+    return true;
+  }
+
+  return false;
+}
+
+bool CPVRChannel::SetChannelID(long iChannelId, bool bSaveInDb /* = false */)
+{
+  bool bReturn = false;
+
+  if (m_iChannelId != iChannelId)
+  {
+    /* update the id */
+    m_iChannelId = iChannelId;
+    SetChanged();
+
+    /* persist the changes */
+    if (bSaveInDb)
+      Persist();
+
+    bReturn = true;
+  }
+
+  return bReturn;
+}
+
+bool CPVRChannel::SetChannelNumber(int iChannelNumber, bool bSaveInDb /* = false */)
+{
+  bool bReturn = false;
+
+  if (m_iChannelNumber != iChannelNumber)
+  {
+    /* update the channel number */
+    m_iChannelNumber = iChannelNumber;
+    SetChanged();
+
+    /* persist the changes */
+    if (bSaveInDb)
+      Persist();
+
+    bReturn = true;
+  }
+
+  UpdatePath();
+
+  return bReturn;
+}
+
+bool CPVRChannel::SetGroupID(int iChannelGroupId, bool bSaveInDb /* = false */)
+{
+  bool bRemoveFromOldGroup = true; // TODO support multiple groups and make this a parameter
+  bool bReturn = false;
+
+  if (m_iChannelGroupId != iChannelGroupId)
+  {
+    CPVRChannelGroups *groups = (IsRadio() ? &PVRChannelGroupsRadio : &PVRChannelGroupsTV);
+
+    if (bRemoveFromOldGroup)
+    {
+      CPVRChannelGroup *oldGroup = groups->GetGroupById(m_iChannelGroupId);
+      if (oldGroup)
+        oldGroup->RemoveFromGroup(this);
+    }
+
+    CPVRChannelGroup *newGroup = groups->GetGroupById(iChannelGroupId);
+    if (newGroup)
+      newGroup->AddToGroup(this);
+
+    /* update the group id */
+    m_iChannelGroupId = iChannelGroupId;
+    SetChanged();
+
+    /* persist the changes */
+    if (bSaveInDb)
+      Persist();
+
+    bReturn = true;
+  }
+
+  return bReturn;
+}
+
+bool CPVRChannel::SetRadio(bool bIsRadio, bool bSaveInDb /* = false */)
+{
+  bool bReturn = false;
+
+  if (m_bIsRadio != bIsRadio)
+  {
+    /* update the radio flag */
+    m_bIsRadio = bIsRadio;
+    SetChanged();
+
+    /* persist the changes */
+    if (bSaveInDb)
+      Persist();
+
+    bReturn = true;
+  }
+
+  UpdatePath();
+
+  return bReturn;
+}
+
+bool CPVRChannel::SetHidden(bool bIsHidden, bool bSaveInDb /* = false */)
+{
+  bool bReturn = false;
+
+  if (m_bIsHidden != bIsHidden)
+  {
+    /* update the hidden flag */
+    m_bIsHidden = bIsHidden;
+    SetChanged();
+
+    /* persist the changes */
+    if (bSaveInDb)
+      Persist();
+
+    bReturn = true;
+  }
+
+  return bReturn;
+}
+
+bool CPVRChannel::SetRecording(bool bClientIsRecording)
+{
+  bool bReturn = false;
+
+  if (m_bClientIsRecording != bClientIsRecording)
+  {
+    /* update the recording false */
+    m_bClientIsRecording = bClientIsRecording;
+    SetChanged();
+
+    bReturn = true;
+  }
+
+  return bReturn;
+}
+
+bool CPVRChannel::SetIconPath(const CStdString &strIconPath, bool bSaveInDb /* = false */)
+{
+  bool bReturn = true; // different from the behaviour of the rest of this class
+
+  /* check if the path is valid */
+  if (!CFile::Exists(strIconPath))
+    return false;
+
+  if (m_strIconPath != strIconPath)
+  {
+    /* update the path */
+    m_strIconPath = CStdString(strIconPath);
+    SetChanged();
+
+    /* persist the changes */
+    if (bSaveInDb)
+      Persist();
+
+    bReturn = true;
+  }
+
+  return bReturn;
+}
+
+bool CPVRChannel::SetChannelName(const CStdString &strChannelName, bool bSaveInDb /* = false */)
+{
+  bool bReturn = false;
+  CStdString strName(strChannelName);
+
+  if (strName.IsEmpty())
+  {
+    strName.Format(g_localizeStrings.Get(19085), ClientChannelNumber());
+  }
+
+  if (m_strChannelName != strName)
+  {
+    /* update the channel name */
+    m_strChannelName = strName;
+    SetChanged();
+
+    /* persist the changes */
+    if (bSaveInDb)
+      Persist();
+
+    bReturn = true;
+  }
+
+  return bReturn;
+}
+
+bool CPVRChannel::SetVirtual(bool bIsVirtual, bool bSaveInDb /* = false */)
+{
+  bool bReturn = false;
+
+  if (m_bIsVirtual != bIsVirtual)
+  {
+    /* update the virtual flag */
+    m_bIsVirtual = bIsVirtual;
+    SetChanged();
+
+    /* persist the changes */
+    if (bSaveInDb)
+      Persist();
+
+    bReturn = true;
+  }
+
+  return bReturn;
+}
+
+bool CPVRChannel::IsEmpty() const
+{
+  return (m_strFileNameAndPath.IsEmpty() ||
+          m_strStreamURL.IsEmpty());
+}
+
+/********** Client related channel methods **********/
+
+bool CPVRChannel::SetUniqueID(int iUniqueId, bool bSaveInDb /* = false */)
+{
+  bool bReturn = false;
+
+  if (m_iUniqueId != iUniqueId)
+  {
+    /* update the unique ID */
+    m_iUniqueId = iUniqueId;
+    SetChanged();
+
+    /* persist the changes */
+    if (bSaveInDb)
+      Persist();
+
+    bReturn = true;
+  }
+
+  return bReturn;
+}
+
+bool CPVRChannel::SetClientID(int iClientId, bool bSaveInDb /* = false */)
+{
+  bool bReturn = false;
+
+  if (m_iClientId != iClientId)
+  {
+    /* update the client ID */
+    m_iClientId = iClientId;
+    SetChanged();
+
+    /* persist the changes */
+    if (bSaveInDb)
+      Persist();
+
+    bReturn = true;
+  }
+
+  return bReturn;
+}
+
+bool CPVRChannel::SetClientChannelNumber(int iClientChannelNumber, bool bSaveInDb /* = false */)
+{
+  bool bReturn = false;
+
+  if (m_iClientChannelNumber != iClientChannelNumber)
+  {
+    /* update the client channel number */
+    m_iClientChannelNumber = iClientChannelNumber;
+    SetChanged();
+
+    /* persist the changes */
+    if (bSaveInDb)
+      Persist();
+
+    bReturn = true;
+  }
+
+  return bReturn;
+}
+
+bool CPVRChannel::SetClientChannelName(const CStdString &strClientChannelName, bool bSaveInDb /* = false */)
+{
+  bool bReturn = false;
+
+  if (m_strClientChannelName != strClientChannelName)
+  {
+    /* update the client channel name */
+    m_strClientChannelName = CStdString(strClientChannelName);
+    SetChanged();
+
+    /* persist the changes */
+    if (bSaveInDb)
+      Persist();
+
+    bReturn = true;
+  }
+
+  return bReturn;
+}
+
+bool CPVRChannel::SetInputFormat(const CStdString &strInputFormat, bool bSaveInDb /* = false */)
+{
+  bool bReturn = false;
+
+  if (m_strInputFormat != strInputFormat)
+  {
+    /* update the input format */
+    m_strInputFormat = CStdString(strInputFormat);
+    SetChanged();
+
+    /* persist the changes */
+    if (bSaveInDb)
+      Persist();
+
+    bReturn = true;
+  }
+
+  return bReturn;
+}
+
+bool CPVRChannel::SetStreamURL(const CStdString &strStreamURL, bool bSaveInDb /* = false */)
+{
+  bool bReturn = false;
+
+  if (m_strStreamURL != strStreamURL)
+  {
+    /* update the stream url */
+    m_strStreamURL = CStdString(strStreamURL);
+    SetChanged();
+
+    /* persist the changes */
+    if (bSaveInDb)
+      Persist();
+
+    bReturn = true;
+  }
+
+  return bReturn;
+}
+
+void CPVRChannel::UpdatePath(void)
+{
+  CStdString strFileNameAndPath;
+  strFileNameAndPath.Format("pvr://channels/%s/all/%i.pvr", (m_bIsRadio ? "radio" : "tv"), m_iChannelNumber);
+  if (m_strFileNameAndPath != strFileNameAndPath)
+  {
+    m_strFileNameAndPath = strFileNameAndPath;
+    SetChanged();
+  }
+}
+
+bool CPVRChannel::SetEncryptionSystem(int iClientEncryptionSystem, bool bSaveInDb /* = false */)
+{
+  bool bReturn = false;
+
+  if (m_iClientEncryptionSystem != iClientEncryptionSystem)
+  {
+    /* update the client encryption system */
+    m_iClientEncryptionSystem = iClientEncryptionSystem;
+    SetChanged();
+
+    /* persist the changes */
+    if (bSaveInDb)
+      Persist();
+
+    bReturn = true;
+  }
+
+  return bReturn;
+}
+
+CStdString CPVRChannel::EncryptionName() const
+{
+  // http://www.dvb.org/index.php?id=174
+  // http://en.wikipedia.org/wiki/Conditional_access_system
+  CStdString strName;
+
+  if (     m_iClientEncryptionSystem == 0x0000)
+    strName = g_localizeStrings.Get(19013); /* Free To Air */
+  else if (m_iClientEncryptionSystem <  0x0000)
+    strName = g_localizeStrings.Get(13205); /* Unknown */
+  else if (m_iClientEncryptionSystem >= 0x0001 &&
+           m_iClientEncryptionSystem <= 0x009F)
+    strName.Format("%s (%X)", g_localizeStrings.Get(19014).c_str(), m_iClientEncryptionSystem); /* Fixed */
+  else if (m_iClientEncryptionSystem >= 0x00A0 &&
+           m_iClientEncryptionSystem <= 0x00A1)
+    strName.Format("%s (%X)", g_localizeStrings.Get(338).c_str(), m_iClientEncryptionSystem); /* Analog */
+  else if (m_iClientEncryptionSystem >= 0x00A2 &&
+           m_iClientEncryptionSystem <= 0x00FF)
+    strName.Format("%s (%X)", g_localizeStrings.Get(19014).c_str(), m_iClientEncryptionSystem); /* Fixed */
+  else if (m_iClientEncryptionSystem >= 0x0100 &&
+           m_iClientEncryptionSystem <= 0x01FF)
+    strName.Format("%s (%X)", "SECA Mediaguard", m_iClientEncryptionSystem);
+  else if (m_iClientEncryptionSystem == 0x0464)
+    strName.Format("%s (%X)", "EuroDec", m_iClientEncryptionSystem);
+  else if (m_iClientEncryptionSystem >= 0x0500 &&
+           m_iClientEncryptionSystem <= 0x05FF)
+    strName.Format("%s (%X)", "Viaccess", m_iClientEncryptionSystem);
+  else if (m_iClientEncryptionSystem >= 0x0600 &&
+           m_iClientEncryptionSystem <= 0x06FF)
+    strName.Format("%s (%X)", "Irdeto", m_iClientEncryptionSystem);
+  else if (m_iClientEncryptionSystem >= 0x0900 &&
+           m_iClientEncryptionSystem <= 0x09FF)
+    strName.Format("%s (%X)", "NDS Videoguard", m_iClientEncryptionSystem);
+  else if (m_iClientEncryptionSystem >= 0x0B00 &&
+           m_iClientEncryptionSystem <= 0x0BFF)
+    strName.Format("%s (%X)", "Conax", m_iClientEncryptionSystem);
+  else if (m_iClientEncryptionSystem >= 0x0D00 &&
+           m_iClientEncryptionSystem <= 0x0DFF)
+    strName.Format("%s (%X)", "CryptoWorks", m_iClientEncryptionSystem);
+  else if (m_iClientEncryptionSystem >= 0x0E00 &&
+           m_iClientEncryptionSystem <= 0x0EFF)
+    strName.Format("%s (%X)", "PowerVu", m_iClientEncryptionSystem);
+  else if (m_iClientEncryptionSystem == 0x1000)
+    strName.Format("%s (%X)", "RAS", m_iClientEncryptionSystem);
+  else if (m_iClientEncryptionSystem >= 0x1200 &&
+           m_iClientEncryptionSystem <= 0x12FF)
+    strName.Format("%s (%X)", "NagraVision", m_iClientEncryptionSystem);
+  else if (m_iClientEncryptionSystem >= 0x1700 &&
+           m_iClientEncryptionSystem <= 0x17FF)
+    strName.Format("%s (%X)", "BetaCrypt", m_iClientEncryptionSystem);
+  else if (m_iClientEncryptionSystem >= 0x1800 &&
+           m_iClientEncryptionSystem <= 0x18FF)
+    strName.Format("%s (%X)", "NagraVision", m_iClientEncryptionSystem);
+  else if (m_iClientEncryptionSystem == 0x22F0)
+    strName.Format("%s (%X)", "Codicrypt", m_iClientEncryptionSystem);
+  else if (m_iClientEncryptionSystem == 0x2600)
+    strName.Format("%s (%X)", "BISS", m_iClientEncryptionSystem);
+  else if (m_iClientEncryptionSystem == 0x4347)
+    strName.Format("%s (%X)", "CryptOn", m_iClientEncryptionSystem);
+  else if (m_iClientEncryptionSystem == 0x4800)
+    strName.Format("%s (%X)", "Accessgate", m_iClientEncryptionSystem);
+  else if (m_iClientEncryptionSystem == 0x4900)
+    strName.Format("%s (%X)", "China Crypt", m_iClientEncryptionSystem);
+  else if (m_iClientEncryptionSystem == 0x4A10)
+    strName.Format("%s (%X)", "EasyCas", m_iClientEncryptionSystem);
+  else if (m_iClientEncryptionSystem == 0x4A20)
+    strName.Format("%s (%X)", "AlphaCrypt", m_iClientEncryptionSystem);
+  else if (m_iClientEncryptionSystem == 0x4A70)
+    strName.Format("%s (%X)", "DreamCrypt", m_iClientEncryptionSystem);
+  else if (m_iClientEncryptionSystem == 0x4A60)
+    strName.Format("%s (%X)", "SkyCrypt", m_iClientEncryptionSystem);
+  else if (m_iClientEncryptionSystem == 0x4A61)
+    strName.Format("%s (%X)", "Neotioncrypt", m_iClientEncryptionSystem);
+  else if (m_iClientEncryptionSystem == 0x4A62)
+    strName.Format("%s (%X)", "SkyCrypt", m_iClientEncryptionSystem);
+  else if (m_iClientEncryptionSystem == 0x4A63)
+    strName.Format("%s (%X)", "Neotion SHL", m_iClientEncryptionSystem);
+  else if (m_iClientEncryptionSystem >= 0x4A64 &&
+           m_iClientEncryptionSystem <= 0x4A6F)
+    strName.Format("%s (%X)", "SkyCrypt", m_iClientEncryptionSystem);
+  else if (m_iClientEncryptionSystem == 0x4A80)
+    strName.Format("%s (%X)", "ThalesCrypt", m_iClientEncryptionSystem);
+  else if (m_iClientEncryptionSystem == 0x4AA1)
+    strName.Format("%s (%X)", "KeyFly", m_iClientEncryptionSystem);
+  else if (m_iClientEncryptionSystem == 0x4ABF)
+    strName.Format("%s (%X)", "DG-Crypt", m_iClientEncryptionSystem);
+  else if (m_iClientEncryptionSystem >= 0x4AD0 &&
+           m_iClientEncryptionSystem <= 0x4AD1)
+    strName.Format("%s (%X)", "X-Crypt", m_iClientEncryptionSystem);
+  else if (m_iClientEncryptionSystem == 0x4AD4)
+    strName.Format("%s (%X)", "OmniCrypt", m_iClientEncryptionSystem);
+  else if (m_iClientEncryptionSystem == 0x4AE0)
+    strName.Format("%s (%X)", "RossCrypt", m_iClientEncryptionSystem);
+  else if (m_iClientEncryptionSystem == 0x5500)
+    strName.Format("%s (%X)", "Z-Crypt", m_iClientEncryptionSystem);
+  else if (m_iClientEncryptionSystem == 0x5501)
+    strName.Format("%s (%X)", "Griffin", m_iClientEncryptionSystem);
+  else
+    strName.Format("%s (%X)", g_localizeStrings.Get(19499).c_str(), m_iClientEncryptionSystem); /* Unknown */
+
+  return strName;
+}
+
+/********** EPG methods **********/
+
+CPVREpg *CPVRChannel::GetEPG(void)
+{
+  if (m_EPG == NULL)
+  {
+    /* will be cleaned up by CPVREpgs on exit */
+    m_EPG = new CPVREpg(this);
+    PVREpgs.push_back(m_EPG);
+  }
+
+  return m_EPG;
+}
+
+int CPVRChannel::GetEPG(CFileItemList *results)
+{
+  CPVREpg *epg = GetEPG();
+  if (!epg)
+  {
+    CLog::Log(LOGERROR, "PVR - %s - cannot get EPG for channel '%s'",
+        __FUNCTION__, m_strChannelName.c_str());
+    return -1;
+  }
+
+  return epg->Get(results);
+}
+
+bool CPVRChannel::ClearEPG()
+{
+  if (m_EPG != NULL)
+  {
+    GetEPG()->Clear();
+    m_EPGNow = NULL;
+  }
+
+  return true;
+}
+
+const CPVREpgInfoTag* CPVRChannel::GetEPGNow(void) const
+{
+  if (m_bIsHidden || !m_bEPGEnabled || m_EPGNow == NULL)
+    return m_EmptyEpgInfoTag;
+
+  return m_EPGNow;
+}
+
+const CPVREpgInfoTag* CPVRChannel::GetEPGNext(void) const
+{
+  if (m_bIsHidden || !m_bEPGEnabled || m_EPGNow == NULL)
+    return m_EmptyEpgInfoTag;
+
+  const CPVREpgInfoTag *nextTag = m_EPGNow->GetNextEvent();
+  return nextTag == NULL ?
+      m_EmptyEpgInfoTag :
+      nextTag;
+}
+
+bool CPVRChannel::SetEPGEnabled(bool bEPGEnabled /* = true */, bool bSaveInDb /* = false */)
+{
+  bool bReturn = false;
+
+  if (m_bEPGEnabled != bEPGEnabled)
+  {
+    /* update the EPG flag */
+    m_bEPGEnabled = bEPGEnabled;
+    SetChanged();
+
+    /* persist the changes */
+    if (bSaveInDb)
+      Persist();
+
+    /* clear the previous EPG entries if needed */
+    if (!m_bEPGEnabled && m_EPG)
+      m_EPG->Clear();
+
+    bReturn = true;
+  }
+
+  return bReturn;
+}
+
+bool CPVRChannel::SetEPGScraper(const CStdString &strScraper, bool bSaveInDb /* = false */)
+{
+  bool bReturn = false;
+
+  if (m_strEPGScraper != strScraper)
+  {
+    bool bCleanEPG = !m_strEPGScraper.IsEmpty() || strScraper.IsEmpty();
+
+    /* update the scraper name */
+    m_strEPGScraper = CStdString(strScraper);
+    SetChanged();
+
+    /* persist the changes */
+    if (bSaveInDb)
+      Persist();
+
+    /* clear the previous EPG entries if needed */
+    if (bCleanEPG && m_bEPGEnabled && m_EPG)
+      m_EPG->Clear();
+
+    bReturn = true;
+  }
+
+  return bReturn;
+}
+
+void CPVRChannel::UpdateEPGPointers(void)
+{
+  if (m_bIsHidden || !m_bEPGEnabled || m_iChannelId == 0 || m_iChannelNumber == 0)
+    return;
+
+  CPVREpg *epg = GetEPG();
+
+  if (epg == NULL)
+  {
+    CLog::Log(LOGDEBUG, "PVR - %s - could not get EPG reference", __FUNCTION__);
+    return;
+  }
+
+  if (!epg->IsUpdateRunning() &&
+      (m_EPGNow == NULL ||
+       m_EPGNow->End() <= CDateTime::GetCurrentDateTime()))
+  {
+    SetChanged();
+    m_EPGNow  = epg->InfoTagNow();
+    if (m_EPGNow)
+    {
+      CLog::Log(LOGDEBUG, "%s - EPG now pointer for channel '%s' updated to '%s'",
+          __FUNCTION__, m_strChannelName.c_str(), m_EPGNow->Title().c_str());
+    }
+    else
+    {
+      CLog::Log(LOGDEBUG, "%s - no EPG now pointer for channel '%s'",
+          __FUNCTION__, m_strChannelName.c_str());
+    }
+  }
+
+  NotifyObservers("epg");
+}
diff --git a/xbmc/pvr/PVRChannel.h b/xbmc/pvr/PVRChannel.h
new file mode 100644 (file)
index 0000000..021f9c2
--- /dev/null
@@ -0,0 +1,347 @@
+#pragma once
+
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "DateTime.h"
+#include "FileItem.h"
+#include "Observer.h"
+#include "../addons/include/xbmc_pvr_types.h"
+
+class CPVREpg;
+
+class CPVRChannel : public Observable
+{
+  friend class CPVREpgs;
+  friend class CPVRDatabase;
+
+private:
+  /********** XBMC related channel data **********/
+  long                           m_iChannelId;             /* the identifier given to this channel by the TV database */
+  int                            m_iChannelNumber;          /* the channel number used by XBMC */
+  int                            m_iChannelGroupId;         /* the identifier of the group where this channel belongs to */
+  bool                           m_bIsRadio;                /* true if this channel is a radio channel, false if not */
+  bool                           m_bIsHidden;               /* true if this channel is hidden, false if not */
+  bool                           m_bClientIsRecording;      /* true if a recording is currently running on this channel, false if not */
+  CStdString                     m_strIconPath;             /* the path to the icon for this channel */
+  CStdString                     m_strChannelName;          /* the name for this channel used by XBMC */
+  bool                           m_bIsVirtual;              /* true if this channel is marked as virtual, false if not */
+
+  /********** EPG related channel data **********/
+  CPVREpg *                      m_EPG;                     /* the EPG table for this channel */
+  mutable const CPVREpgInfoTag * m_EPGNow;                  /* the EPG tag that is active on this channel now */
+  bool                           m_bEPGEnabled;             /* don't use an EPG for this channel if set to false */
+  CStdString                     m_strEPGScraper;           /* the name of the scraper to be used for this channel */
+
+  /********** Client related channel data **********/
+  int                            m_iUniqueId;               /* the unique identifier for this channel */
+  int                            m_iClientId;               /* the identifier of the client that serves this channel */
+  int                            m_iClientChannelNumber;    /* the channel number on the client */
+  CStdString                     m_strClientChannelName;    /* the name of this channel on the client */
+  CStdString                     m_strInputFormat;          /* the stream input type based on ffmpeg/libavformat/allformats.c */
+  CStdString                     m_strStreamURL;            /* URL of the stream. Use the client to read stream if this is empty */
+  CStdString                     m_strFileNameAndPath;      /* the filename to be used by PVRManager to open and read the stream */
+  int                            m_iClientEncryptionSystem; /* the encryption system used by this channel. 0 for FreeToAir, -1 for unknown */
+
+public:
+  CPVRChannel();
+
+  bool operator ==(const CPVRChannel &right) const;
+  bool operator !=(const CPVRChannel &right) const;
+
+  /********** XBMC related channel methods **********/
+
+  /**
+   * Updates this channel tag with the data of the given channel tag.
+   * Returns true if something changed, false otherwise.
+   */
+  bool UpdateFromClient(const CPVRChannel &channel);
+
+  /**
+   * Persists the changes in the database.
+   * Returns true if the changes were saved succesfully, false otherwise.
+   */
+  bool Persist(bool bQueueWrite = false);
+
+  /**
+   * The identifier given to this channel by the TV database.
+   */
+  long ChannelID(void) const { return m_iChannelId; }
+
+  /**
+   * Set the identifier for this channel.
+   * Nothing will be changed in the database.
+   */
+  bool SetChannelID(long iDatabaseId, bool bSaveInDb = false);
+
+  /**
+   * The channel number used by XBMC.
+   */
+  int ChannelNumber(void) const { return m_iChannelNumber; }
+
+  /**
+   * Change the channel number used by XBMC.
+   * Nothing will be changed in the database.
+   */
+  bool SetChannelNumber(int iChannelNumber, bool bSaveInDb = false);
+
+  /**
+   * The identifier of the group this channel belongs to.
+   * -1 for undefined.
+   */
+  int GroupID(void) const { return m_iChannelGroupId; }
+
+  /**
+   * Set the identifier of the group this channel belongs to.
+   * Nothing will be changed in the database.
+   */
+  bool SetGroupID(int iChannelGroupId, bool bSaveInDb = false);
+
+  /**
+   * True if this channel is a radio channel, false if not.
+   */
+  bool IsRadio(void) const { return m_bIsRadio; }
+
+  /**
+   * Set to true if this channel is a radio channel. Set to false if not.
+   * Nothing will be changed in the database.
+   */
+  bool SetRadio(bool bIsRadio, bool bSaveInDb = false);
+
+  /**
+   * True if this channel is hidden. False if not.
+   */
+  bool IsHidden(void) const { return m_bIsHidden; }
+
+  /**
+   * Set to true to hide this channel. Set to false to unhide it.
+   * The EPG of hidden channels won't be updated.
+   * Nothing will be changed in the database.
+   */
+  bool SetHidden(bool bIsHidden, bool bSaveInDb = false);
+
+  /**
+   * True if a recording is currently running on this channel. False if not.
+   */
+  bool IsRecording(void) const { return m_bClientIsRecording; }
+
+  /**
+   * Set to true if a recording is currently running on this channel. Set to false if not.
+   * Nothing will be changed in the database.
+   */
+  bool SetRecording(bool bClientIsRecording);
+
+  /**
+   * The path to the icon for this channel.
+   */
+  CStdString IconPath(void) const { return m_strIconPath; }
+
+  /**
+   * Set the path to the icon for this channel.
+   */
+  bool SetIconPath(const CStdString &strIconPath, bool bSaveInDb = false);
+
+  /**
+   * The name for this channel used by XBMC.
+   */
+  CStdString ChannelName(void) const { return m_strChannelName; }
+
+  /**
+   * Set the name for this channel used by XBMC.
+   */
+  bool SetChannelName(const CStdString &strChannelName, bool bSaveInDb = false);
+
+  /**
+   * True if this channel is marked as virtual. False if not.
+   */
+  bool IsVirtual() const { return m_bIsVirtual; }
+
+  /**
+   * True if this channel is marked as virtual. False if not.
+   */
+  bool SetVirtual(bool bIsVirtual, bool bSaveInDb = false);
+
+  /**
+   * True if this channel has no file or stream name
+   */
+  bool IsEmpty() const;
+
+  /********** Client related channel methods **********/
+
+  /**
+   * A unique identifier for this channel.
+   * It can be used to find the same channel on different providers
+   */
+  int UniqueID(void) const { return m_iUniqueId; }
+
+  /**
+   * Change the unique identifier for this channel.
+   */
+  bool SetUniqueID(int iUniqueId, bool bSaveInDb = false);
+
+  /**
+   * The identifier of the client that serves this channel.
+   */
+  int ClientID(void) const { return m_iClientId; }
+
+  /**
+   * Set the identifier of the client that serves this channel.
+   */
+  bool SetClientID(int iClientId, bool bSaveInDb = false);
+
+  /**
+   * The channel number on the client.
+   */
+  int ClientChannelNumber(void) const { return m_iClientChannelNumber; }
+
+  /**
+   * Set the channel number on the client.
+   * It will only be changed in this tag and won't change anything on the client.
+   */
+  bool SetClientChannelNumber(int iClientChannelNumber, bool bSaveInDb = false);
+
+  /**
+   * The name of this channel on the client.
+   */
+  CStdString ClientChannelName(void) const { return m_strClientChannelName; }
+  /**
+   * Set the name of this channel on the client.
+   * It will only be changed in this tag and won't change anything on the client.
+   */
+  bool SetClientChannelName(const CStdString &strClientChannelName, bool bSaveInDb = false);
+
+  /**
+   * The stream input type
+   * If it is empty, ffmpeg will try to scan the stream to find the right input format.
+   * See "xbmc/cores/dvdplayer/Codecs/ffmpeg/libavformat/allformats.c" for a
+   * list of the input formats.
+   */
+  CStdString InputFormat(void) const { return m_strInputFormat; }
+
+  /**
+   * Set the stream input type
+   */
+  bool SetInputFormat(const CStdString &strInputFormat, bool bSaveInDb = false);
+
+  /**
+   * The stream URL to access this channel.
+   * If this is empty, then the client should be used to read from the channel.
+   */
+  CStdString StreamURL(void) const { return m_strStreamURL; }
+
+  /**
+   * Set the stream URL to access this channel.
+   * If this is empty, then the client should be used to read from the channel.
+   */
+  bool SetStreamURL(const CStdString &strStreamURL, bool bSaveInDb = false);
+
+  /**
+   * The path in the XBMC VFS to be used by PVRManager to open and read the stream.
+   */
+  CStdString Path(void) const { return m_strFileNameAndPath; }
+
+private:
+  /**
+   * Updates the path after the channel number or radio flag has changed
+   */
+  void UpdatePath(void);
+
+public:
+  /**
+   * Return true if this channel is encrypted. Does not inform whether XBMC can play the file.
+   * Decryption should be done by the client.
+   */
+  bool IsEncrypted(void) const { return m_iClientEncryptionSystem > 0; }
+
+  /**
+   * Return the encryption system ID for this channel. 0 for FTA. The values are documented
+   * on: http://www.dvb.org/index.php?id=174.
+   */
+  int EncryptionSystem(void) const { return m_iClientEncryptionSystem; }
+
+  /**
+   * Set the encryption ID (CAID) for this channel.
+   */
+  bool SetEncryptionSystem(int iClientEncryptionSystem, bool bSaveInDb = false);
+
+  /**
+   * Return a friendly name for the used encryption system.
+   */
+  CStdString EncryptionName() const;
+
+  /********** EPG methods **********/
+
+  /**
+   * Get the EPG table for this channel.
+   * Will be created if it doesn't exist.
+   */
+  CPVREpg *GetEPG();
+
+  /**
+   * Get the EPG table for this channel.
+   * Will be created if it doesn't exist.
+   */
+  int GetEPG(CFileItemList *results);
+
+  /**
+   * Clear the EPG for this channel.
+   */
+  bool ClearEPG();
+
+  /**
+   * Get the EPG tag that is active on this channel now.
+   * Will return an empty tag if there is none.
+   */
+  const CPVREpgInfoTag* GetEPGNow() const;
+
+  /**
+   * Get the EPG tag that is active on this channel next
+   * Will return an empty tag if there is none.
+   */
+  const CPVREpgInfoTag* GetEPGNext() const;
+
+  /**
+   * Don't use an EPG for this channel if set to false
+   */
+  bool EPGEnabled() const { return m_bEPGEnabled; }
+
+  /**
+   * Set to true if an EPG should be used for this channel. Set to false otherwise.
+   */
+  bool SetEPGEnabled(bool bEPGEnabled = true, bool bSaveInDb = false);
+
+  /**
+   * Get the name of the scraper to be used for this channel
+   */
+  CStdString EPGScraper(void) const { return m_strEPGScraper; }
+
+  /**
+   * Set the name of the scraper to be used for this channel.
+   * Set to "client" to load the EPG from the backend
+   */
+  bool SetEPGScraper(const CStdString &strScraper, bool bSaveInDb = false);
+
+private:
+  /**
+   * Updates the EPG "now" and "next" pointers
+   */
+  void UpdateEPGPointers();
+};
diff --git a/xbmc/pvr/PVRChannelGroup.cpp b/xbmc/pvr/PVRChannelGroup.cpp
new file mode 100644 (file)
index 0000000..acd3606
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "PVRChannelGroup.h"
+#include "PVRChannel.h"
+
+CPVRChannelGroup::CPVRChannelGroup(void)
+{
+  m_iGroupID = 0;
+  m_GroupName = "";
+}
+
+CPVRChannelGroup::~CPVRChannelGroup(void)
+{
+  erase(begin(), end());
+}
+
+bool CPVRChannelGroup::RemoveFromGroup(const CPVRChannel *channel)
+{
+  bool bReturn = false;
+
+  for (unsigned int iChannelPtr = 0; iChannelPtr < size(); iChannelPtr++)
+  {
+    if (*channel == *at(iChannelPtr))
+    {
+      erase(begin() + iChannelPtr);
+      bReturn = true;
+      break;
+    }
+  }
+
+  return bReturn;
+}
+
+bool CPVRChannelGroup::AddToGroup(CPVRChannel *channel)
+{
+  bool bReturn = false;
+
+  if (!IsGroupMember(channel))
+  {
+    push_back(channel);
+    bReturn = true;
+  }
+
+  return bReturn;
+}
+
+bool CPVRChannelGroup::IsGroupMember(const CPVRChannel *channel)
+{
+  bool bReturn = false;
+
+  for (unsigned int iChannelPtr = 0; iChannelPtr < size(); iChannelPtr++)
+  {
+    if (*channel == *at(iChannelPtr))
+    {
+      bReturn = true;
+      break;
+    }
+  }
+
+  return bReturn;
+}
diff --git a/xbmc/pvr/PVRChannelGroup.h b/xbmc/pvr/PVRChannelGroup.h
new file mode 100644 (file)
index 0000000..2bfadbc
--- /dev/null
@@ -0,0 +1,57 @@
+#pragma once
+
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "VideoInfoTag.h"
+#include "DateTime.h"
+#include "FileItem.h"
+#include "../addons/include/xbmc_pvr_types.h"
+
+class CPVRChannelGroup : public std::vector<CPVRChannel *>
+{
+private:
+  unsigned long m_iGroupID;
+  CStdString    m_GroupName;
+  int           m_iSortOrder;
+
+public:
+  CPVRChannelGroup(void);
+  virtual ~CPVRChannelGroup(void);
+
+  bool RemoveFromGroup(const CPVRChannel *channel);
+  bool AddToGroup(CPVRChannel *channel);
+  bool IsGroupMember(const CPVRChannel *channel);
+
+  /**
+   * Get a channel given it's channel number.
+   */
+  CPVRChannel *GetByChannelNumber(int iChannelNumber); // TODO
+  CPVRChannel *GetByChannelNumberUp(int iChannelNumber); // TODO
+  CPVRChannel *GetByChannelNumberDown(int iChannelNumber); // TODO
+
+  long GroupID(void) const { return m_iGroupID; }
+  void SetGroupID(long group) { m_iGroupID = group; }
+  CStdString GroupName(void) const { return m_GroupName; }
+  void SetGroupName(CStdString name) { m_GroupName = name; }
+  long SortOrder(void) const { return m_iSortOrder; }
+  void SetSortOrder(long sortorder) { m_iSortOrder = sortorder; }
+};
diff --git a/xbmc/pvr/PVRChannelGroups.cpp b/xbmc/pvr/PVRChannelGroups.cpp
new file mode 100644 (file)
index 0000000..5a61d52
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+ *      Copyright (C) 2005-2008 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "FileItem.h"
+#include "GUISettings.h"
+#include "GUIWindowManager.h"
+#include "GUIDialogYesNo.h"
+#include "GUIDialogOK.h"
+#include "LocalizeStrings.h"
+#include "utils/log.h"
+#include "Util.h"
+#include "URL.h"
+#include "FileSystem/File.h"
+#include "MusicInfoTag.h"
+
+#include "PVRChannelsContainer.h"
+#include "PVRChannelGroups.h"
+#include "PVRChannelGroup.h"
+#include "PVRDatabase.h"
+#include "PVRManager.h"
+
+using namespace XFILE;
+using namespace MUSIC_INFO;
+
+// --- CPVRChannelGroups ----------------------------------------------------------
+
+CPVRChannelGroups PVRChannelGroupsTV;
+CPVRChannelGroups PVRChannelGroupsRadio;
+
+CPVRChannelGroups::CPVRChannelGroups(void)
+{
+}
+
+bool CPVRChannelGroups::Load(bool radio)
+{
+  CPVRDatabase *database = g_PVRManager.GetTVDatabase();
+  database->Open();
+
+  m_bRadio = radio;
+  Clear();
+  database->GetChannelGroupList(*this, m_bRadio);
+  database->Close();
+  return true;
+}
+
+void CPVRChannelGroups::Unload()
+{
+  Clear();
+}
+
+int CPVRChannelGroups::GetGroupList(CFileItemList* results)
+{
+  for (unsigned int i = 0; i < size(); i++)
+  {
+    CFileItemPtr group(new CFileItem(at(i).GroupName()));
+    group->m_strTitle = at(i).GroupName();
+    group->m_strPath.Format("%i", at(i).GroupID());
+    results->Add(group);
+  }
+  return size();
+}
+
+CPVRChannelGroup *CPVRChannelGroups::GetGroupById(int iGroupId)
+{
+  CPVRChannelGroup *group = NULL;
+
+  if (iGroupId == -1)
+    return group;
+
+  for (int iGroupPtr = 0; iGroupPtr < size(); iGroupPtr++)
+  {
+    if (at(iGroupPtr).GroupID() == iGroupId)
+    {
+      group = &at(iGroupPtr);
+      break;
+    }
+  }
+
+  return group;
+}
+
+int CPVRChannelGroups::GetFirstChannelForGroupID(int GroupId)
+{
+  if (GroupId == -1)
+    return 1;
+
+  const CPVRChannels *channels = g_PVRChannels.Get(m_bRadio);
+
+  for (unsigned int i = 0; i < channels->size(); i++)
+  {
+    if (channels->at(i)->GroupID() == GroupId)
+      return i+1;
+  }
+  return 1;
+}
+
+int CPVRChannelGroups::GetPrevGroupID(int current_group_id)
+{
+  if (size() == 0)
+    return -1;
+
+  if ((current_group_id == -1) || (current_group_id == 0))
+    return at(size()-1).GroupID();
+
+  for (unsigned int i = 0; i < size(); i++)
+  {
+    if (current_group_id == at(i).GroupID())
+    {
+      if (i != 0)
+        return at(i-1).GroupID();
+      else
+        return -1;
+    }
+  }
+  return -1;
+}
+
+int CPVRChannelGroups::GetNextGroupID(int current_group_id)
+{
+  unsigned int i = 0;
+
+  if (size() == 0)
+    return -1;
+
+  if ((current_group_id == 0) || (current_group_id == -1))
+    return at(0).GroupID();
+
+  if (size() == 0)
+    return -1;
+
+  for (; i < size(); i++)
+  {
+    if (current_group_id == at(i).GroupID())
+      break;
+  }
+
+  if (i >= size()-1)
+    return -1;
+  else
+    return at(i+1).GroupID();
+}
+
+void CPVRChannelGroups::AddGroup(const CStdString &name)
+{
+  CPVRDatabase *database = g_PVRManager.GetTVDatabase();
+  database->Open();
+
+  Clear();
+  database->AddChannelGroup(name, -1, m_bRadio);
+  database->GetChannelGroupList(*this, m_bRadio);
+
+  database->Close();
+}
+
+bool CPVRChannelGroups::RenameGroup(int GroupId, const CStdString &newname)
+{
+  CPVRDatabase *database = g_PVRManager.GetTVDatabase();
+  database->Open();
+
+  Clear();
+  database->SetChannelGroupName(GroupId, newname, m_bRadio);
+  database->GetChannelGroupList(*this, m_bRadio);  database->Close();
+  return true;
+}
+
+bool CPVRChannelGroups::DeleteGroup(int GroupId)
+{
+  CPVRDatabase *database = g_PVRManager.GetTVDatabase();
+  database->Open();
+
+  Clear();
+
+  const CPVRChannels *channels = g_PVRChannels.Get(m_bRadio);
+
+  /* Delete the group inside Database */
+  database->DeleteChannelGroup(GroupId, m_bRadio);
+
+  /* Set all channels with this group to undefined */
+  for (unsigned int i = 0; i < channels->size(); i++)
+  {
+    if (channels->at(i)->GroupID() == GroupId)
+    {
+      channels->at(i)->SetGroupID(0, true);
+    }
+  }
+
+  /* Reload the group list */
+  database->GetChannelGroupList(*this, m_bRadio);
+
+  database->Close();
+  return true;
+}
+
+CStdString CPVRChannelGroups::GetGroupName(int GroupId)
+{
+  if (GroupId != -1)
+  {
+    for (unsigned int i = 0; i < size(); i++)
+    {
+      if (GroupId == at(i).GroupID())
+        return at(i).GroupName();
+    }
+  }
+
+  return g_localizeStrings.Get(593);
+}
+
+int CPVRChannelGroups::GetGroupId(CStdString GroupName)
+{
+  if (GroupName.IsEmpty() || GroupName == g_localizeStrings.Get(593) || GroupName == "all")
+    return -1;
+
+  for (unsigned int i = 0; i < size(); i++)
+  {
+    if (GroupName == at(i).GroupName())
+      return at(i).GroupID();
+  }
+  return -1;
+}
+
+bool CPVRChannelGroups::ChannelToGroup(const CPVRChannel &channel, int GroupId)
+{
+  const CPVRChannels *channels = g_PVRChannels.Get(channel.IsRadio());
+  channels->at(channel.ChannelNumber()-1)->SetGroupID(GroupId);
+  return channels->at(channel.ChannelNumber()-1)->Persist();
+}
+
+void CPVRChannelGroups::Clear()
+{
+  /* Clear all current present Channel groups inside list */
+  erase(begin(), end());
+  return;
+}
diff --git a/xbmc/pvr/PVRChannelGroups.h b/xbmc/pvr/PVRChannelGroups.h
new file mode 100644 (file)
index 0000000..d8ea89f
--- /dev/null
@@ -0,0 +1,58 @@
+#pragma once
+
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "VideoInfoTag.h"
+#include "DateTime.h"
+#include "FileItem.h"
+#include "../addons/include/xbmc_pvr_types.h"
+
+class CPVRChannelGroup;
+
+class CPVRChannelGroups : public std::vector<CPVRChannelGroup>
+{
+private:
+  bool  m_bRadio;
+
+public:
+  CPVRChannelGroups(void);
+
+  bool Load(bool radio = false);
+  void Unload();
+
+  int GetGroupList(CFileItemList* results);
+  CPVRChannelGroup *GetGroupById(int iGroupId);
+  int GetFirstChannelForGroupID(int GroupId);
+  int GetPrevGroupID(int current_group_id);
+  int GetNextGroupID(int current_group_id);
+
+  void AddGroup(const CStdString &name);
+  bool RenameGroup(int GroupId, const CStdString &newname);
+  bool DeleteGroup(int GroupId);
+  bool ChannelToGroup(const CPVRChannel &channel, int GroupId);
+  CStdString GetGroupName(int GroupId);
+  int GetGroupId(CStdString GroupName);
+  void Clear();
+};
+
+extern CPVRChannelGroups PVRChannelGroupsTV;
+extern CPVRChannelGroups PVRChannelGroupsRadio;
diff --git a/xbmc/pvr/PVRChannels.cpp b/xbmc/pvr/PVRChannels.cpp
new file mode 100644 (file)
index 0000000..15acad7
--- /dev/null
@@ -0,0 +1,814 @@
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+/**
+ * TODO:
+ * - move some logic to channel groups. now the channel group is mostly used as a property of channel
+ * - treat hidden channels as a channel group and remove the exceptions that we are making for them now
+ * - use Observervable here, so we can use event driven operations later
+ */
+
+#include "GUISettings.h"
+#include "GUIWindowManager.h"
+#include "GUIDialogYesNo.h"
+#include "GUIDialogOK.h"
+#include "MusicInfoTag.h"
+#include "log.h"
+
+#include "PVRChannelGroups.h"
+#include "PVRChannelGroup.h"
+#include "PVRChannelsContainer.h"
+#include "PVRDatabase.h"
+#include "PVRManager.h"
+#include "PVREpgInfoTag.h"
+
+using namespace MUSIC_INFO;
+
+CPVRChannels::CPVRChannels(bool bRadio)
+{
+  m_bRadio          = bRadio;
+  m_iHiddenChannels = 0;
+  m_bIsSorted       = false;
+}
+
+CPVRChannels::~CPVRChannels(void)
+{
+  for (int iChannelPtr = 0; iChannelPtr < size(); iChannelPtr++)
+  {
+    delete at(iChannelPtr);
+  }
+  erase(begin(), end());
+}
+
+int CPVRChannels::Load(void)
+{
+  /* make sure this container is empty before loading */
+  Unload();
+
+  int iChannelCount = LoadFromDb();
+
+  /* try to get the channels from clients if there are none in the database */
+  if (iChannelCount <= 0)
+  {
+    CLog::Log(LOGNOTICE, "%s - No %s channels stored in the database. Reading channels from clients",
+        __FUNCTION__, m_bRadio ? "Radio" : "TV");
+
+    iChannelCount = LoadFromClients();
+  }
+
+  return iChannelCount;
+}
+
+void CPVRChannels::Unload()
+{
+  clear();
+}
+
+bool CPVRChannels::Update()
+{
+  bool         bReturn  = false;
+  CPVRDatabase *database = g_PVRManager.GetTVDatabase();
+
+  if (database && database->Open())
+  {
+    CPVRChannels PVRChannels_tmp(m_bRadio);
+
+    PVRChannels_tmp.LoadFromClients(false);
+    bReturn = Update(&PVRChannels_tmp);
+
+    database->Close();
+  }
+
+  return bReturn;
+}
+
+bool CPVRChannels::Update(CPVRChannel *channel)
+{
+  // TODO notify observers
+  push_back(channel);
+  m_bIsSorted = false;
+  return true;
+}
+
+void CPVRChannels::MoveChannel(unsigned int iOldIndex, unsigned int iNewIndex)
+{
+  if (iNewIndex == iOldIndex || iNewIndex == 0)
+    return;
+
+  CPVRDatabase *database = g_PVRManager.GetTVDatabase();
+  database->Open();
+
+  CPVRChannels tempChannels(m_bRadio);
+
+  /* move the channel */
+  tempChannels.push_back(at(iOldIndex - 1));
+  erase(begin() + iOldIndex - 1);
+  if (iNewIndex < size())
+    insert(begin() + iNewIndex - 1, tempChannels[0]);
+  else
+    push_back(tempChannels[0]);
+
+  /* update the channel numbers */
+  for (unsigned int ptr = 0; ptr < size(); ptr++)
+  {
+    CPVRChannel *channel = at(ptr);
+
+    if (channel->ChannelNumber() != (int) ptr + 1)
+    {
+      channel->SetChannelNumber(ptr + 1, true);
+    }
+  }
+
+  CLog::Log(LOGNOTICE, "%s - %s channel '%d' moved to '%d'",
+      __FUNCTION__, (m_bRadio ? "radio" : "tv"), iOldIndex, iNewIndex);
+
+  database->Close();
+
+  /* update the timers with the new channel numbers */
+  for (unsigned int ptr = 0; ptr < PVRTimers.size(); ptr++)
+  {
+    CPVRTimerInfoTag timer = PVRTimers[ptr];
+    CPVRChannel *tag = GetByClient(timer.Number(), timer.ClientID());
+    if (tag)
+      timer.SetNumber(tag->ChannelNumber());
+  }
+
+  m_bIsSorted = false;
+}
+
+bool CPVRChannels::HideChannel(CPVRChannel *channel, bool bShowDialog /* = true */)
+{
+  if (!channel)
+    return false;
+
+  /* check if there are active timers on this channel if we are hiding it */
+  if (!channel->IsHidden() && PVRTimers.ChannelHasTimers(*channel))
+  {
+    if (bShowDialog)
+    {
+      CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
+      if (!pDialog)
+        return false;
+
+      pDialog->SetHeading(19098);
+      pDialog->SetLine(0, 19099);
+      pDialog->SetLine(1, "");
+      pDialog->SetLine(2, 19100);
+      pDialog->DoModal();
+
+      if (!pDialog->IsConfirmed())
+        return false;
+    }
+
+    /* delete the timers */
+    PVRTimers.DeleteTimersOnChannel(*channel, true);
+  }
+
+  /* check if this channel is currently playing if we are hiding it */
+  if (!channel->IsHidden() &&
+      (g_PVRManager.IsPlayingTV() || g_PVRManager.IsPlayingRadio()) &&
+      (g_PVRManager.GetCurrentPlayingItem()->GetPVRChannelInfoTag() == channel))
+  {
+    CGUIDialogOK::ShowAndGetInput(19098,19101,0,19102);
+    return false;
+  }
+
+  /* switch the hidden flag */
+  channel->SetHidden(!channel->IsHidden());
+
+  /* update the hidden channel counter */
+  if (channel->IsHidden())
+    ++m_iHiddenChannels;
+  else
+    --m_iHiddenChannels;
+
+  /* update the database entry */
+  channel->Persist();
+
+  /* move the channel to the end of the list */
+  MoveChannel(channel->ChannelNumber(), size());
+
+  return true;
+}
+
+void CPVRChannels::SearchAndSetChannelIcons(bool bUpdateDb /* = false */)
+{
+  if (g_guiSettings.GetString("pvrmenu.iconpath") == "")
+    return;
+
+  CPVRDatabase *database = g_PVRManager.GetTVDatabase();
+  database->Open();
+
+  for (unsigned int ptr = 0; ptr < size(); ptr++)
+  {
+    CPVRChannel *channel = at(ptr);
+
+    /* skip if an icon is already set */
+    if (channel->IconPath() != "")
+      continue;
+
+    CStdString strBasePath = g_guiSettings.GetString("pvrmenu.iconpath");
+
+    CStdString strIconPath = strBasePath + channel->ClientChannelName();
+    CStdString strIconPathLower = strBasePath + channel->ClientChannelName().ToLower();
+    CStdString strIconPathUid;
+    strIconPathUid.Format("%s/%08d", strBasePath, channel->UniqueID());
+
+    channel->SetIconPath(strIconPath      + ".tbn", bUpdateDb) ||
+    channel->SetIconPath(strIconPath      + ".jpg", bUpdateDb) ||
+    channel->SetIconPath(strIconPath      + ".png", bUpdateDb) ||
+
+    channel->SetIconPath(strIconPathLower + ".tbn", bUpdateDb) ||
+    channel->SetIconPath(strIconPathLower + ".jpg", bUpdateDb) ||
+    channel->SetIconPath(strIconPathLower + ".png", bUpdateDb) ||
+
+    channel->SetIconPath(strIconPathUid   + ".tbn", bUpdateDb) ||
+    channel->SetIconPath(strIconPathUid   + ".jpg", bUpdateDb) ||
+    channel->SetIconPath(strIconPathUid   + ".png", bUpdateDb);
+
+    /* TODO: start channel icon scraper here if nothing was found */
+  }
+
+  database->Close();
+}
+
+/********** sort methods **********/
+
+struct sortByClientChannelNumber
+{
+  bool operator()(CPVRChannel *channel1, CPVRChannel *channel2)
+  {
+    return channel1->ClientChannelNumber() < channel2->ClientChannelNumber();
+  }
+};
+
+struct sortByChannelNumber
+{
+  bool operator()(CPVRChannel *channel1, CPVRChannel *channel2)
+  {
+    return channel1->ChannelNumber() < channel2->ChannelNumber();
+  }
+};
+
+void CPVRChannels::SortByClientChannelNumber(void)
+{
+  sort(begin(), end(), sortByClientChannelNumber());
+  m_bIsSorted = false;
+}
+
+void CPVRChannels::SortByChannelNumber(void)
+{
+  if (!m_bIsSorted)
+  {
+    sort(begin(), end(), sortByChannelNumber());
+    m_bIsSorted = true;
+  }
+}
+
+/********** getters **********/
+
+CPVRChannel *CPVRChannels::GetByClient(int iClientChannelNumber, int iClientID)
+{
+  CPVRChannel *channel = NULL;
+
+  for (unsigned int ptr = 0; ptr < size(); ptr++)
+  {
+    CPVRChannel *checkChannel = at(ptr);
+    if (checkChannel->ClientChannelNumber() == iClientChannelNumber &&
+        checkChannel->ClientID() == iClientID)
+    {
+      channel = checkChannel;
+      break;
+    }
+  }
+
+  return channel;
+}
+
+CPVRChannel *CPVRChannels::GetByChannelID(long iChannelID)
+{
+  CPVRChannel *channel = NULL;
+
+  for (unsigned int ptr = 0; ptr < size(); ptr++)
+  {
+    if (at(ptr)->ChannelID() == iChannelID)
+    {
+      channel = at(ptr);
+      break;
+    }
+  }
+
+  return channel;
+}
+
+CPVRChannel *CPVRChannels::GetByUniqueID(int iUniqueID)
+{
+  CPVRChannel *channel = NULL;
+
+  for (unsigned int ptr = 0; ptr < size(); ptr++)
+  {
+    if (at(ptr)->UniqueID() == iUniqueID)
+    {
+      channel = at(ptr);
+      break;
+    }
+  }
+
+  return channel;
+}
+
+CPVRChannel *CPVRChannels::GetByChannelNumber(int iChannelNumber) // TODO: move to channelgroup
+{
+  CPVRChannel *channel = NULL;
+
+  if (iChannelNumber <= size())
+  {
+    SortByChannelNumber();
+    channel = at(iChannelNumber - 1);
+  }
+
+  return channel;
+}
+
+CPVRChannel *CPVRChannels::GetByChannelNumberUp(int iChannelNumber) // TODO: move to channelgroup
+{
+  int iGetChannel = iChannelNumber + 1;
+  if (iGetChannel > size())
+    iGetChannel = 1;
+
+  return GetByChannelNumber(iGetChannel - 1);
+}
+
+CPVRChannel *CPVRChannels::GetByChannelNumberDown(int iChannelNumber) // TODO: move to channelgroup
+{
+  int iGetChannel = iChannelNumber - 1;
+  if (iGetChannel <= 0)
+    iGetChannel = size();
+
+  return GetByChannelNumber(iGetChannel - 1);
+}
+
+CPVRChannel *CPVRChannels::GetByIndex(unsigned int iIndex)
+{
+  return iIndex < size() ?
+    at(iIndex) :
+    NULL;
+}
+
+int CPVRChannels::GetChannels(CFileItemList* results, int iGroupID /* = -1 */, bool bHidden /* = false */)
+{
+  int iAmount = 0;
+
+  SortByChannelNumber();
+
+  for (unsigned int ptr = 0; ptr < size(); ptr++)
+  {
+    CPVRChannel *channel = at(ptr);
+
+    if (channel->IsHidden() != bHidden)
+      continue;
+
+    if (iGroupID != -1 && channel->GroupID() != iGroupID)
+      continue;
+
+    CFileItemPtr channelFile(new CFileItem(*channel));
+
+    if (channel->IsRadio())
+    {
+      CMusicInfoTag* musictag = channelFile->GetMusicInfoTag();
+      if (musictag)
+      {
+        const CPVREpgInfoTag *epgNow = channel->GetEPGNow();
+        musictag->SetURL(channel->Path());
+        musictag->SetTitle(epgNow->Title());
+        musictag->SetArtist(channel->ChannelName());
+        musictag->SetAlbumArtist(channel->ChannelName());
+        musictag->SetGenre(epgNow->Genre());
+        musictag->SetDuration(epgNow->GetDuration());
+        musictag->SetLoaded(true);
+        musictag->SetComment("");
+        musictag->SetLyrics("");
+      }
+    }
+
+    results->Add(channelFile);
+    iAmount++;
+  }
+  return iAmount;
+}
+
+int CPVRChannels::GetHiddenChannels(CFileItemList* results)
+{
+  return GetChannels(results, -1, true);
+}
+
+/********** operations on all channels **********/
+
+void CPVRChannels::SearchMissingChannelIcons()
+{
+  CLog::Log(LOGINFO,"PVR: Manual Channel Icon search started...");
+  ((CPVRChannels *) g_PVRChannels.GetTV())->SearchAndSetChannelIcons(true);
+  ((CPVRChannels *) g_PVRChannels.GetRadio())->SearchAndSetChannelIcons(true);
+  // TODO: Add Process dialog here
+  CGUIDialogOK::ShowAndGetInput(19103,0,20177,0);
+}
+
+/********** static getters **********/
+
+CPVRChannel *CPVRChannels::GetByPath(const CStdString &strPath)
+{
+  CPVRChannels *channels = NULL;
+  int iChannelNumber = -1;
+
+  /* get the filename from curl */
+  CURL url(strPath);
+  CStdString strFileName = url.GetFileName();
+  CUtil::RemoveSlashAtEnd(strFileName);
+
+  if (strFileName.Left(16) == "channels/tv/all/")
+  {
+    strFileName.erase(0,16);
+    iChannelNumber = atoi(strFileName.c_str());
+    channels = (CPVRChannels *) g_PVRChannels.GetTV();
+  }
+  else if (strFileName.Left(19) == "channels/radio/all/")
+  {
+    strFileName.erase(0,19);
+    iChannelNumber = atoi(strFileName.c_str());
+    channels = (CPVRChannels *) g_PVRChannels.GetRadio();
+  }
+
+  return channels ? channels->GetByChannelNumber(iChannelNumber) : NULL;
+}
+
+bool CPVRChannels::GetDirectory(const CStdString& strPath, CFileItemList &results)
+{
+  CStdString strBase(strPath);
+  CUtil::RemoveSlashAtEnd(strBase);
+
+  /* get the filename from curl */
+  CURL url(strPath);
+  CStdString fileName = url.GetFileName();
+  CUtil::RemoveSlashAtEnd(fileName);
+
+  if (fileName == "channels")
+  {
+    CFileItemPtr item;
+
+    /* all tv channels */
+    item.reset(new CFileItem(strBase + "/tv/", true));
+    item->SetLabel(g_localizeStrings.Get(19020));
+    item->SetLabelPreformated(true);
+    results.Add(item);
+
+    /* all radio channels */
+    item.reset(new CFileItem(strBase + "/radio/", true));
+    item->SetLabel(g_localizeStrings.Get(19021));
+    item->SetLabelPreformated(true);
+    results.Add(item);
+
+    return true;
+  }
+  else if (fileName == "channels/tv")
+  {
+    return GetGroupsDirectory(strBase, &results, false);
+  }
+  else if (fileName == "channels/radio")
+  {
+    return GetGroupsDirectory(strBase, &results, true);
+  }
+  else if (fileName.Left(12) == "channels/tv/")
+  {
+    if (fileName.substr(12) == ".hidden")
+    {
+      ((CPVRChannels *) g_PVRChannels.GetTV())->GetChannels(&results, -1, true);
+    }
+    else
+    {
+      int iGroupID = PVRChannelGroupsTV.GetGroupId(fileName.substr(12));
+      ((CPVRChannels *) g_PVRChannels.GetTV())->GetChannels(&results, iGroupID, false);
+    }
+    return true;
+  }
+  else if (fileName.Left(15) == "channels/radio/")
+  {
+    if (fileName.substr(15) == ".hidden")
+    {
+      ((CPVRChannels *) g_PVRChannels.GetRadio())->GetChannels(&results, -1, true);
+    }
+    else
+    {
+      int iGroupID = PVRChannelGroupsRadio.GetGroupId(fileName.substr(15));
+      ((CPVRChannels *) g_PVRChannels.GetRadio())->GetChannels(&results, iGroupID, false);
+    }
+    return true;
+  }
+
+  return false;
+}
+
+int CPVRChannels::GetNumChannelsFromAll()
+{
+  return g_PVRChannels.GetTV()->GetNumChannels() + g_PVRChannels.GetRadio()->GetNumChannels();
+}
+
+
+CPVRChannel *CPVRChannels::GetByClientFromAll(int iClientChannelNumber, int iClientID)
+{
+  CPVRChannel *channel;
+
+  channel = ((CPVRChannels *) g_PVRChannels.GetTV())->GetByClient(iClientChannelNumber, iClientID);
+  if (channel != NULL)
+    return channel;
+
+  channel = ((CPVRChannels *) g_PVRChannels.GetRadio())->GetByClient(iClientChannelNumber, iClientID);
+  if (channel != NULL)
+    return channel;
+
+  return NULL;
+}
+
+CPVRChannel *CPVRChannels::GetByChannelIDFromAll(long iChannelID)
+{
+  CPVRChannel *channel;
+
+  channel = ((CPVRChannels *) g_PVRChannels.GetTV())->GetByChannelID(iChannelID);
+  if (channel != NULL)
+    return channel;
+
+  channel = ((CPVRChannels *) g_PVRChannels.GetRadio())->GetByChannelID(iChannelID);
+  if (channel != NULL)
+    return channel;
+
+  return NULL;
+}
+
+CPVRChannel *CPVRChannels::GetByUniqueIDFromAll(int iUniqueID)
+{
+  CPVRChannel *channel;
+
+  channel = ((CPVRChannels *) g_PVRChannels.GetTV())->GetByUniqueID(iUniqueID);
+  if (channel != NULL)
+    return channel;
+
+  channel = ((CPVRChannels *) g_PVRChannels.GetRadio())->GetByUniqueID(iUniqueID);
+  if (channel != NULL)
+    return channel;
+
+  return NULL;
+}
+
+/********** private methods **********/
+
+int CPVRChannels::LoadFromDb(bool bCompress /* = false */)
+{
+  CPVRDatabase *database = g_PVRManager.GetTVDatabase();
+  if (!database || !database->Open())
+    return -1;
+
+  int iChannelCount = size();
+
+  if (database->GetChannels(*this, m_bRadio) > 0)
+  {
+    if (bCompress)
+      database->Compress(true);
+
+    Update();
+  }
+
+  database->Close();
+
+  return size() - iChannelCount;
+}
+
+int CPVRChannels::LoadFromClients(bool bAddToDb /* = true */)
+{
+  CPVRDatabase *database = NULL;
+  int iCurSize = size();
+
+  if (bAddToDb)
+  {
+    database = g_PVRManager.GetTVDatabase();
+
+    if (!database || !database->Open())
+      return -1;
+  }
+
+  if (GetFromClients() == -1)
+    return -1;
+
+  SortByClientChannelNumber();
+  ReNumberAndCheck();
+  SearchAndSetChannelIcons();
+
+  if (bAddToDb)
+  {
+    /* add all channels to the database */
+    for (unsigned int ptr = 0; ptr < size(); ptr++)
+      database->UpdateChannel(*at(ptr));
+
+    database->Close();
+
+    clear();
+
+    return LoadFromDb(true);
+  }
+
+  return size() - iCurSize;
+}
+
+int CPVRChannels::GetFromClients(void)
+{
+  CLIENTMAP *clients = g_PVRManager.Clients();
+  if (!clients)
+    return 0;
+
+  int iCurSize = size();
+
+  /* get the channel list from each client */
+  CLIENTMAPITR itrClients = clients->begin();
+  while (itrClients != clients->end())
+  {
+    if ((*itrClients).second->ReadyToUse() && (*itrClients).second->GetNumChannels() > 0)
+      (*itrClients).second->GetChannelList(*this, m_bRadio);
+
+    itrClients++;
+  }
+
+  return size() - iCurSize;
+}
+
+bool CPVRChannels::RemoveByUniqueID(long iUniqueID)
+{
+  for (unsigned int ptr = 0; ptr < size(); ptr++)
+  {
+    if (at(ptr)->UniqueID() == iUniqueID)
+    {
+      erase(begin() + ptr);
+      return true;
+    }
+  }
+
+  return false;
+}
+
+bool CPVRChannels::Update(CPVRChannels *channels)
+{
+  /* the database has already been opened */
+  CPVRDatabase *database = g_PVRManager.GetTVDatabase();
+
+  int iSize = size();
+  for (int ptr = 0; ptr < iSize; ptr++)
+  {
+    CPVRChannel *channel = at(ptr);
+
+    /* ignore virtual channels */
+    if (channel->IsVirtual())
+      continue;
+
+    /* check if this channel is still present */
+    CPVRChannel *existingChannel = channels->GetByUniqueID(channel->UniqueID());
+    if (existingChannel)
+    {
+      /* if it's present, update the current tag */
+      if (channel->UpdateFromClient(*existingChannel))
+      {
+        channel->Persist(true);
+        CLog::Log(LOGINFO,"%s - updated %s channel '%s'",
+            __FUNCTION__, m_bRadio ? "radio" : "TV", channel->ChannelName().c_str());
+      }
+
+      /* remove this tag from the temporary channel list */
+      channels->RemoveByUniqueID(channel->UniqueID());
+    }
+    else
+    {
+      /* channel is no longer present */
+      CLog::Log(LOGINFO,"%s - removing %s channel '%s'",
+          __FUNCTION__, m_bRadio ? "radio" : "TV", channel->ChannelName().c_str());
+      database->RemoveChannel(*channel);
+      erase(begin() + ptr);
+      ptr--;
+      iSize--;
+    }
+  }
+
+  /* the temporary channel list only contains new channels now */
+  for (unsigned int ptr = 0; ptr < channels->size(); ptr++)
+  {
+    CPVRChannel *channel = channels->at(ptr);
+    channel->Persist(true);
+    push_back(channel);
+
+    CLog::Log(LOGINFO,"%s - added %s channel '%s'",
+        __FUNCTION__, m_bRadio ? "radio" : "TV", channel->ChannelName().c_str());
+  }
+
+  /* post the queries generated by the update */
+  database->CommitInsertQueries();
+
+  /* recount hidden channels */
+  m_iHiddenChannels = 0;
+  for (unsigned int i = 0; i < size(); i++)
+  {
+    if (at(i)->IsHidden())
+      m_iHiddenChannels++;
+  }
+
+  m_bIsSorted = false;
+  return true;
+}
+
+void CPVRChannels::RemoveInvalidChannels(void)
+{
+  for (unsigned int ptr = 0; ptr < size(); ptr--)
+  {
+    if (at(ptr)->IsVirtual())
+      continue;
+
+    if (at(ptr)->ClientChannelNumber() <= 0)
+    {
+      CLog::Log(LOGERROR, "%s - removing invalid channel '%s' from client '%i': no valid channel number",
+          __FUNCTION__, at(ptr)->ChannelName().c_str(), at(ptr)->ClientID());
+      erase(begin() + ptr);
+      ptr--;
+      continue;
+    }
+
+    if (at(ptr)->UniqueID() <= 0)
+    {
+      CLog::Log(LOGERROR, "%s - removing invalid channel '%s' from client '%i': no valid unique ID",
+          __FUNCTION__, at(ptr)->ChannelName().c_str(), at(ptr)->ClientID());
+      erase(begin() + ptr);
+      ptr--;
+      continue;
+    }
+  }
+}
+
+bool CPVRChannels::GetGroupsDirectory(const CStdString &strBase, CFileItemList *results, bool bRadio)
+{
+  const CPVRChannels * channels    = g_PVRChannels.Get(bRadio);
+  CPVRChannelGroups *channelGroups = bRadio ? &PVRChannelGroupsRadio : &PVRChannelGroupsTV;
+  CFileItemPtr item;
+
+  item.reset(new CFileItem(strBase + "/all/", true));
+  item->SetLabel(g_localizeStrings.Get(593));
+  item->SetLabelPreformated(true);
+  results->Add(item);
+
+  /* container has hidden channels */
+  if (channels->GetNumHiddenChannels() > 0)
+  {
+    item.reset(new CFileItem(strBase + "/.hidden/", true));
+    item->SetLabel(g_localizeStrings.Get(19022));
+    item->SetLabelPreformated(true);
+    results->Add(item);
+  }
+
+  /* add all groups */
+  for (unsigned int ptr = 0; ptr < channelGroups->size(); ptr++)
+  {
+    CPVRChannelGroup group = channelGroups->at(ptr);
+    CStdString strGroup = strBase + "/" + group.GroupName() + "/";
+    item.reset(new CFileItem(strGroup, true));
+    item->SetLabel(group.GroupName());
+    item->SetLabelPreformated(true);
+    results->Add(item);
+  }
+
+  return true;
+}
+
+void CPVRChannels::ReNumberAndCheck(void)
+{
+  RemoveInvalidChannels();
+
+  int iChannelNumber = 1;
+  for (unsigned int ptr = 0; ptr < size();  ptr++)
+  {
+    if (at(ptr)->IsHidden())
+      m_iHiddenChannels++;
+    else
+      at(ptr)->SetChannelNumber(iChannelNumber++);
+  }
+  m_bIsSorted = false;
+}
diff --git a/xbmc/pvr/PVRChannels.h b/xbmc/pvr/PVRChannels.h
new file mode 100644 (file)
index 0000000..6c6b6a0
--- /dev/null
@@ -0,0 +1,227 @@
+#pragma once
+
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "FileItem.h"
+#include "PVRChannel.h"
+#include "../addons/include/xbmc_pvr_types.h"
+
+class CPVRChannelGroups;
+class CPVREpg;
+
+class CPVRChannels : public std::vector<CPVRChannel *>
+{
+private:
+  bool m_bRadio;          /* true if this container holds radio channels, false if it holds TV channels */
+  int  m_iHiddenChannels; /* the amount of hidden channels in this container */
+  bool m_bIsSorted;       /* true if this container is sorted by channel number, false if not */
+
+  /**
+   * Load the channels stored in the database.
+   * Returns the amount of channels that were added.
+   */
+  int LoadFromDb(bool bCompress = false);
+
+  /**
+   * Load the channels from the clients.
+   * Returns the amount of channels that were added.
+   */
+  int LoadFromClients(bool bAddToDb = true);
+
+  /**
+   * Get the channels from the clients and add them to this container. Does not sort, renumber or add channels to the database.
+   * Returns the amount of channels that were added.
+   */
+  int GetFromClients(void);
+
+  /**
+   * Remove a channel.
+   * Returns true if the channel was found and removed, false otherwise
+   */
+  bool RemoveByUniqueID(long iUniqueID);
+
+  /**
+   * Update the current channel list with the given list.
+   * Only the new channels will be present in the passed list after this call.
+   * Return true if everything went well, false otherwise.
+   */
+  bool Update(CPVRChannels *channels);
+
+  /**
+   * Remove invalid channels from this container.
+   */
+  void RemoveInvalidChannels(void);
+
+  /**
+   * Called by GetDirectory to get the directory for a group
+   */
+  static bool GetGroupsDirectory(const CStdString &strBase, CFileItemList *results, bool bRadio);
+
+  /**
+   * Remove invalid channels and updates the channel numbers.
+   */
+  void ReNumberAndCheck(void);
+
+public:
+  CPVRChannels(bool bRadio);
+  virtual ~CPVRChannels(void);
+
+  /**
+   * Load the channels from the database. If no channels are stored in the database, then the channels will be loaded from the clients.
+   * Returns the amount of channels that were added.
+   */
+  int Load();
+
+  /**
+   * Clear this channel list.
+   */
+  void Unload();
+
+  /**
+   * Refresh the channel list from the clients.
+   */
+  bool Update();
+
+  /**
+   * Updates a channel in this container
+   */
+  bool Update(CPVRChannel *channel);
+
+  /**
+   * Move a channel from position iOldIndex to iNewIndex.
+   */
+  void MoveChannel(unsigned int iOldIndex, unsigned int iNewIndex);
+
+  /**
+   * Show a hidden channel or hide a visible channel.
+   */
+  bool HideChannel(CPVRChannel *channel, bool bShowDialog = true);
+
+  /**
+   * Search missing channel icons for all known channels.
+   */
+  void SearchAndSetChannelIcons(bool bUpdateDb = false);
+
+
+  /********** sort methods **********/
+
+  /**
+   * Sort the current channel list by client channel number.
+   */
+  void SortByClientChannelNumber(void);
+
+  /**
+   * Sort the current channel list by channel number.
+   */
+  void SortByChannelNumber(void);
+
+  /********** getters **********/
+
+  /**
+   * Get a channel given the channel number on the client.
+   */
+  CPVRChannel *GetByClient(int iClientChannelNumber, int iClientID);
+
+  /**
+   * Get a channel given it's channel ID.
+   */
+  CPVRChannel *GetByChannelID(long iChannelID);
+
+  /**
+   * Get a channel given it's unique ID.
+   */
+  CPVRChannel *GetByUniqueID(int iUniqueID);
+
+  /**
+   * Get a channel given it's channel number.
+   */
+  CPVRChannel *GetByChannelNumber(int iChannelNumber);
+  CPVRChannel *GetByChannelNumberUp(int iChannelNumber);
+  CPVRChannel *GetByChannelNumberDown(int iChannelNumber);
+
+  /**
+   * Get a channel given it's index in this container.
+   */
+  CPVRChannel *GetByIndex(unsigned int index);
+
+  /**
+   * Get the list of channels in a group.
+   */
+  int GetChannels(CFileItemList* results, int iGroupID = -1, bool bHidden = false);
+
+  /**
+   * The amount of channels in this container.
+   */
+  int GetNumChannels() const { return size(); }
+
+  /**
+   * Get the list of hidden channels.
+   */
+  int GetHiddenChannels(CFileItemList* results);
+
+  /**
+   * The amount of channels in this container.
+   */
+  int GetNumHiddenChannels() const { return m_iHiddenChannels; }
+
+  /********** operations on all channels **********/
+
+  /**
+   * Try to find missing channel icons automatically
+   */
+  static void SearchMissingChannelIcons();
+
+  /********** static getters **********/
+
+  /**
+   * Get a channel given it's path.
+   */
+  static CPVRChannel *GetByPath(const CStdString &strPath);
+
+  /**
+   * Get the directory for a path
+   */
+  static bool GetDirectory(const CStdString& strPath, CFileItemList &results);
+
+  /**
+   * The total amount of channels in all containers.
+   */
+  static int GetNumChannelsFromAll();
+
+  /**
+   * Get a channel given it's channel ID from all containers.
+   */
+  static CPVRChannel *GetByClientFromAll(int iClientChannelNumber, int iClientID);
+
+  /**
+   * Get a channel given it's channel ID from all containers.
+   */
+  static CPVRChannel *GetByChannelIDFromAll(long iChannelID);
+
+  /**
+   * Get a channel given it's unique ID.
+   */
+  static CPVRChannel *GetByUniqueIDFromAll(int iUniqueID);
+};
+
+extern CPVRChannels PVRChannelsTV;
+extern CPVRChannels PVRChannelsRadio;
diff --git a/xbmc/pvr/PVRChannelsContainer.cpp b/xbmc/pvr/PVRChannelsContainer.cpp
new file mode 100644 (file)
index 0000000..07d3ad0
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "PVRChannelsContainer.h"
+#include "PVRChannels.h"
+
+CPVRChannelsContainer::CPVRChannelsContainer()
+{
+  m_currentPlayingChannel = NULL;
+  InitializeCriticalSection(&m_criticalSection);
+
+  CPVRChannels *channelsTV = new CPVRChannels(false);
+  push_back(channelsTV);
+
+  CPVRChannels *channelsRadio = new CPVRChannels(true);
+  push_back(channelsRadio);
+}
+
+CPVRChannelsContainer::~CPVRChannelsContainer()
+{
+  erase(begin(), end());
+  DeleteCriticalSection(&m_criticalSection);
+}
+
+const CPVRChannels *CPVRChannelsContainer::Get(int iChannelsId)
+{
+  if (iChannelsId < 0 || (unsigned int) iChannelsId > size() - 1)
+    return NULL;
+
+  return at(iChannelsId);
+}
+
+const CPVRChannels *CPVRChannelsContainer::Get(bool bRadio)
+{
+  return bRadio ? g_PVRChannels.Get(RADIO) : g_PVRChannels.Get(TV);
+}
+
+const CPVRChannels *CPVRChannelsContainer::GetTV()
+{
+  return g_PVRChannels.Get(TV);
+}
+
+const CPVRChannels *CPVRChannelsContainer::GetRadio()
+{
+  return g_PVRChannels.Get(RADIO);
+}
+
+CPVRChannelsContainer g_PVRChannels;
diff --git a/xbmc/pvr/PVRChannelsContainer.h b/xbmc/pvr/PVRChannelsContainer.h
new file mode 100644 (file)
index 0000000..7d67cec
--- /dev/null
@@ -0,0 +1,45 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "PVRChannels.h"
+
+class CPVRChannelsContainer : public std::vector<CPVRChannels *>
+{
+private:
+  CFileItem       *m_currentPlayingChannel; /* the channel that is currently playing */
+  CRITICAL_SECTION m_criticalSection;       /* critical section for channel switching */
+
+public:
+  CPVRChannelsContainer(void);
+  virtual ~CPVRChannelsContainer(void);
+
+  const CPVRChannels *Get(int iChannelsId);
+  const CPVRChannels *Get(bool bRadio);
+
+  static const int TV    = 0;
+  static const int RADIO = 1;
+
+  static const CPVRChannels *GetTV();
+  static const CPVRChannels *GetRadio();
+};
+
+extern CPVRChannelsContainer g_PVRChannels;
diff --git a/xbmc/pvr/PVRDatabase.cpp b/xbmc/pvr/PVRDatabase.cpp
new file mode 100644 (file)
index 0000000..6070e8f
--- /dev/null
@@ -0,0 +1,1013 @@
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "PVRDatabase.h"
+#include "AdvancedSettings.h"
+#include "settings/VideoSettings.h"
+#include "utils/log.h"
+
+#include "PVREpgs.h"
+#include "PVRChannels.h"
+#include "PVREpgInfoTag.h"
+#include "PVRChannelGroups.h"
+#include "PVRChannelGroup.h"
+
+using namespace std;
+using namespace dbiplus;
+
+CPVRDatabase::CPVRDatabase(void)
+{
+  lastScanTime.SetValid(false);
+}
+
+CPVRDatabase::~CPVRDatabase(void)
+{
+}
+
+bool CPVRDatabase::Open()
+{
+  return CDatabase::Open(g_advancedSettings.m_databaseTV);
+}
+
+bool CPVRDatabase::CreateTables()
+{
+  bool bReturn = false;
+
+  try
+  {
+    CDatabase::CreateTables();
+
+    CLog::Log(LOGINFO, "PVRDB - %s - creating tables", __FUNCTION__);
+
+    CLog::Log(LOGDEBUG, "PVRDB - %s - creating table 'clients'", __FUNCTION__);
+    m_pDS->exec(
+        "CREATE TABLE clients ("
+          "idClient integer primary key, "
+          "sName    text, "
+          "sUid     text"
+        ");\n"
+    );
+    m_pDS->exec("CREATE INDEX idx_clients_sUid on clients(sUid);\n");
+
+    CLog::Log(LOGDEBUG, "PVRDB - %s - creating table 'channels'", __FUNCTION__);
+    m_pDS->exec(
+        "CREATE TABLE channels ("
+          "idChannel            integer primary key, "
+          "iUniqueId            integer, "
+          "iChannelNumber       integer, "
+          "idGroup              integer, " // TODO use mapping table
+          "bIsRadio             bool, "
+          "bIsHidden            bool, "
+          "sIconPath            text, "
+          "sChannelName         text, "
+          "bIsVirtual           bool, "
+          "bEPGEnabled          bool, "
+          "sEPGScraper          text, "
+          "iLastWatched         integer,"
+
+          // TODO use mapping table
+          "iClientId            integer, "
+          "iClientChannelNumber integer, "
+          "sInputFormat         text, "
+          "sStreamURL           text, "
+          "iEncryptionSystem    integer"
+        ");\n"
+    );
+    m_pDS->exec("CREATE UNIQUE INDEX idx_channels_iChannelNumber_bIsRadio on channels(iChannelNumber, bIsRadio);\n");
+    m_pDS->exec("CREATE INDEX idx_channels_iClientId on channels(iClientId);\n");
+    m_pDS->exec("CREATE INDEX idx_channels_iChannelNumber on channels(iChannelNumber);\n");
+    m_pDS->exec("CREATE INDEX idx_channels_iLastWatched on channels(iLastWatched);\n");
+    m_pDS->exec("CREATE INDEX idx_channels_bIsRadio on channels(bIsRadio);\n");
+    m_pDS->exec("CREATE INDEX idx_channels_bIsHidden on channels(bIsHidden);\n");
+
+    // TODO use a mapping table so multiple backends per channel can be implemented
+    //    CLog::Log(LOGDEBUG, "PVRDB - %s - creating table 'map_channels_clients'", __FUNCTION__);
+    //    m_pDS->exec(
+    //        "CREATE TABLE map_channels_clients ("
+    //          "idChannel             integer primary key, "
+    //          "idClient              integer, "
+    //          "iClientChannelNumber  integer,"
+    //          "sInputFormat          string,"
+    //          "sStreamURL            string,"
+    //          "iEncryptionSystem     integer"
+    //        ");\n"
+    //    );
+    //    m_pDS->exec("CREATE UNIQUE INDEX idx_idChannel_idClient on map_channels_clients(idChannel, idClient);\n");
+
+    CLog::Log(LOGDEBUG, "PVRDB - %s - creating view 'vw_last_watched'", __FUNCTION__);
+    m_pDS->exec(
+        "CREATE VIEW vw_last_watched "
+        "AS SELECT idChannel, iChannelNumber, sChannelName "
+        "FROM Channels "
+        "ORDER BY iLastWatched DESC;\n"
+    );
+
+    CLog::Log(LOGDEBUG, "PVRDB - %s - creating table 'channelgroups'", __FUNCTION__);
+    m_pDS->exec(
+        "CREATE TABLE channelgroups ("
+          "idGroup    integer primary key,"
+          "bIsRadio   bool, "
+          "sName      text,"
+          "iSortOrder integer"
+        ")\n"
+    );
+    m_pDS->exec("CREATE INDEX idx_channelgroups_bIsRadio on channelgroups(bIsRadio)\n");
+    m_pDS->exec("CREATE INDEX idx_channelgroups_sName on channelgroups(sName)\n");
+
+    // TODO use a mapping table so multiple groups per channel can be implemented
+    // replaces idGroup in the channels table
+    //    CLog::Log(LOGDEBUG, "PVRDB - %s - creating table 'map_channelgroups_channels'", __FUNCTION__);
+    //    m_pDS->exec(
+    //        "CREATE TABLE map_channelgroups_channels ("
+    //          "idChannel integer, "
+    //          "idGroup   integer"
+    //        ");\n"
+    //    );
+    //    m_pDS->exec("CREATE UNIQUE INDEX idx_idChannel_idGroup on map_channelgroups_channels(idChannel, idGroup);\n");
+
+    CLog::Log(LOGDEBUG, "PVRDB - %s - creating table 'channelsettings'", __FUNCTION__);
+    m_pDS->exec(
+        "CREATE TABLE channelsettings ("
+          "idChannel            integer primary key, "
+          "iInterlaceMethod     integer, "
+          "iViewMode            integer, "
+          "fCustomZoomAmount    float, "
+          "fPixelRatio          float, "
+          "iAudioStream         integer, "
+          "iSubtitleStream      integer,"
+          "fSubtitleDelay       float, "
+          "bSubtitles           bool, "
+          "fBrightness          float, "
+          "fContrast            float, "
+          "fGamma               float,"
+          "fVolumeAmplification float, "
+          "fAudioDelay          float, "
+          "bOutputToAllSpeakers bool, "
+          "bCrop                bool, "
+          "iCropLeft            integer, "
+          "iCropRight           integer, "
+          "iCropTop             integer, "
+          "iCropBottom          integer, "
+          "fSharpness           float, "
+          "fNoiseReduction      float"
+        ");\n"
+    );
+
+    CLog::Log(LOGDEBUG, "PVRDB - %s - creating table 'epg'", __FUNCTION__);
+    m_pDS->exec(
+        "CREATE TABLE epg ("
+          "idBroadcast     integer primary key, "
+          "iBroadcastUid   integer, "
+          "idChannel       integer, "
+          "sTitle          text, "
+          "sPlotOutline    text, "
+          "sPlot           text, "
+          "iStartTime      integer, "
+          "iEndTime        integer, "
+          "iGenreType      integer, "
+          "iGenreSubType   integer, "
+          "iFirstAired     integer, "
+          "iParentalRating integer, "
+          "iStarRating     integer, "
+          "bNotify         bool, "
+          "sSeriesId       text, "
+          "sEpisodeId      text, "
+          "sEpisodePart    text, "
+          "sEpisodeName    text"
+        ")\n"
+    );
+    m_pDS->exec("CREATE UNIQUE INDEX idx_epg_idChannel_iStartTime on epg(idChannel, iStartTime desc)\n");
+    m_pDS->exec("CREATE UNIQUE INDEX idx_epg_iBroadcastUid on epg(iBroadcastUid)\n");
+    m_pDS->exec("CREATE INDEX idx_epg_idChannel on epg(idChannel)\n");
+    m_pDS->exec("CREATE INDEX idx_epg_iStartTime on epg(iStartTime)\n");
+    m_pDS->exec("CREATE INDEX idx_epg_iEndTime on epg(iEndTime)\n");
+
+    // TODO keep separate value per epg table collection
+    CLog::Log(LOGDEBUG, "PVRDB - %s - creating table 'lastepgscan'", __FUNCTION__);
+    m_pDS->exec("CREATE TABLE lastepgscan ("
+          "idEpg integer primary key, "
+          "iLastScan integer"
+        ")\n"
+    );
+
+    bReturn = true;
+  }
+  catch (...)
+  {
+    CLog::Log(LOGERROR, "PVRDB - %s - unable to create TV tables:%i",
+        __FUNCTION__, (int)GetLastError());
+    bReturn = false;
+  }
+
+  return bReturn;
+}
+
+bool CPVRDatabase::UpdateOldVersion(int iVersion)
+{
+  if (iVersion < 7)
+  {
+    /* TODO since sqlite doesn't support all ALTER TABLE statements, we have to rename things by code. not supported at the moment */
+    CLog::Log(LOGERROR, "%s - Incompatible database version!", __FUNCTION__);
+    return false;
+  }
+
+  return true;
+}
+
+/********** Channel methods **********/
+
+bool CPVRDatabase::EraseChannels()
+{
+  CLog::Log(LOGDEBUG, "PVRDB - %s - deleting all channels from the database", __FUNCTION__);
+
+  return DeleteValues("channels");
+}
+
+bool CPVRDatabase::EraseClientChannels(long iClientId)
+{
+  /* invalid client Id */
+  if (iClientId <= 0)
+  {
+    CLog::Log(LOGERROR, "PVRDB - %s - invalid client id: %li",
+        __FUNCTION__, iClientId);
+    return false;
+  }
+
+  CLog::Log(LOGDEBUG, "PVRDB - %s - deleting all channels from client '%li' from the database",
+      __FUNCTION__, iClientId);
+
+  CStdString strWhereClause = FormatSQL("iClientId = %u", iClientId);
+  return DeleteValues("channels", strWhereClause);
+}
+
+long CPVRDatabase::UpdateChannel(const CPVRChannel &channel, bool bQueueWrite /* = false */)
+{
+  long iReturn = -1;
+
+  /* invalid channel */
+  if (channel.UniqueID() <= 0)
+  {
+    CLog::Log(LOGERROR, "PVRDB - %s - invalid channel uid: %d",
+        __FUNCTION__, channel.UniqueID());
+    return iReturn;
+  }
+
+  CStdString strQuery;
+
+  if (channel.ChannelID() <= 0)
+  {
+    /* new channel */
+    strQuery = FormatSQL("INSERT INTO channels ("
+        "iUniqueId, iChannelNumber, idGroup, bIsRadio, bIsHidden, "
+        "sIconPath, sChannelName, bIsVirtual, bEPGEnabled, sEPGScraper, iClientId, "
+        "iClientChannelNumber, sInputFormat, sStreamURL, iEncryptionSystem) "
+        "VALUES (%i, %i, %i, %i, %i, '%s', '%s', %i, %i, '%s', %i, %i, '%s', '%s', %i)\n",
+        channel.UniqueID(), channel.ChannelNumber(), channel.GroupID(), (channel.IsRadio() ? 1 :0), (channel.IsHidden() ? 1 : 0),
+        channel.IconPath().c_str(), channel.ChannelName().c_str(), (channel.IsVirtual() ? 1 : 0), (channel.EPGEnabled() ? 1 : 0), channel.EPGScraper().c_str(), channel.ClientID(),
+        channel.ClientChannelNumber(), channel.InputFormat().c_str(), channel.StreamURL().c_str(), channel.EncryptionSystem());
+  }
+  else
+  {
+    /* update channel */
+    strQuery = FormatSQL("REPLACE INTO channels ("
+        "iUniqueId, iChannelNumber, idGroup, bIsRadio, bIsHidden, "
+        "sIconPath, sChannelName, bIsVirtual, bEPGEnabled, sEPGScraper, iClientId, "
+        "iClientChannelNumber, sInputFormat, sStreamURL, iEncryptionSystem, idChannel) "
+        "VALUES (%i, %i, %i, %i, %i, '%s', '%s', %i, %i, '%s', %i, %i, '%s', '%s', %i, %i)\n",
+        channel.UniqueID(), channel.ChannelNumber(), channel.GroupID(), (channel.IsRadio() ? 1 :0), (channel.IsHidden() ? 1 : 0),
+        channel.IconPath().c_str(), channel.ChannelName().c_str(), (channel.IsVirtual() ? 1 : 0), (channel.EPGEnabled() ? 1 : 0), channel.EPGScraper().c_str(), channel.ClientID(),
+        channel.ClientChannelNumber(), channel.InputFormat().c_str(), channel.StreamURL().c_str(), channel.EncryptionSystem(), channel.ChannelID());
+  }
+
+  if (bQueueWrite)
+  {
+    QueueInsertQuery(strQuery);
+    iReturn = 0;
+  }
+  else if (ExecuteQuery(strQuery))
+  {
+    iReturn = (channel.ChannelID() <= 0) ? (long) m_pDS->lastinsertid() : channel.ChannelID();
+  }
+
+  return iReturn;
+}
+
+bool CPVRDatabase::RemoveChannel(const CPVRChannel &channel)
+{
+  /* invalid channel */
+  if (channel.ChannelID() <= 0)
+  {
+    CLog::Log(LOGERROR, "PVRDB - %s - invalid channel id: %li",
+        __FUNCTION__, channel.ChannelID());
+    return false;
+  }
+
+  CStdString strWhereClause = FormatSQL("idChannel = %u", channel.ChannelID());
+  return DeleteValues("channels", strWhereClause);
+}
+
+int CPVRDatabase::GetChannels(CPVRChannels &results, bool bIsRadio)
+{
+  int iReturn = -1;
+
+  CStdString strQuery = FormatSQL("SELECT * FROM channels WHERE bIsRadio = %u ORDER BY iChannelNumber\n", bIsRadio);
+  int iNumRows = ResultQuery(strQuery);
+
+  if (iNumRows > 0)
+  {
+    try
+    {
+      while (!m_pDS->eof())
+      {
+        CPVRChannel *channel = new CPVRChannel();
+
+        channel->m_iChannelId              = m_pDS->fv("idChannel").get_asInt();
+        channel->m_iUniqueId               = m_pDS->fv("iUniqueId").get_asInt();
+        channel->m_iChannelNumber          = m_pDS->fv("iChannelNumber").get_asInt();
+        channel->m_iChannelGroupId         = m_pDS->fv("idGroup").get_asInt();
+        channel->m_bIsRadio                = m_pDS->fv("bIsRadio").get_asBool();
+        channel->m_bIsHidden               = m_pDS->fv("bIsHidden").get_asBool();
+        channel->m_strIconPath             = m_pDS->fv("sIconPath").get_asString();
+        channel->m_strChannelName          = m_pDS->fv("sChannelName").get_asString();
+        channel->m_bIsVirtual              = m_pDS->fv("bIsVirtual").get_asBool();
+        channel->m_bEPGEnabled             = m_pDS->fv("bEPGEnabled").get_asBool();
+        channel->m_strEPGScraper           = m_pDS->fv("sEPGScraper").get_asString();
+        channel->m_iClientId               = m_pDS->fv("iClientId").get_asInt();
+        channel->m_iClientChannelNumber    = m_pDS->fv("iClientChannelNumber").get_asInt();
+        channel->m_strInputFormat          = m_pDS->fv("sInputFormat").get_asString();
+        channel->m_strStreamURL            = m_pDS->fv("sStreamURL").get_asString();
+        channel->m_iClientEncryptionSystem = m_pDS->fv("iEncryptionSystem").get_asInt();
+
+        channel->UpdatePath();
+
+        CLog::Log(LOGDEBUG, "PVRDB - %s - channel '%s' loaded from the database",
+            __FUNCTION__, channel->m_strChannelName.c_str());
+        results.push_back(channel);
+        m_pDS->next();
+        ++iReturn;
+      }
+    }
+    catch (...)
+    {
+      CLog::Log(LOGERROR, "PVRDB - %s - couldn't load channels from the database", __FUNCTION__);
+    }
+  }
+
+  m_pDS->close();
+  return iReturn;
+}
+
+int CPVRDatabase::GetChannelCount(bool bRadio, bool bHidden /* = false */)
+{
+  int iReturn = -1;
+  CStdString strQuery = FormatSQL("SELECT COUNT(1) FROM channels WHERE bIsRadio = %u AND bIsHidden = %u\n",
+      (bRadio ? 1 : 0), (bHidden ? 1 : 0));
+
+  if (ResultQuery(strQuery))
+  {
+    iReturn = m_pDS->fv(0).get_asInt();
+  }
+  m_pDS->close();
+
+  return iReturn;
+}
+
+int CPVRDatabase::GetLastChannel()
+{
+  CStdString strValue = GetSingleValue("vw_last_watched", "idChannel");
+
+  if (strValue.IsEmpty())
+    return -1;
+
+  return atoi(strValue.c_str());
+}
+
+bool CPVRDatabase::UpdateLastChannel(const CPVRChannel &channel)
+{
+  /* invalid channel */
+  if (channel.ChannelID() <= 0)
+  {
+    CLog::Log(LOGERROR, "PVRDB - %s - invalid channel id: %li",
+        __FUNCTION__, channel.ChannelID());
+    return false;
+  }
+
+  time_t tNow;
+  CDateTime::GetCurrentDateTime().GetAsTime(tNow);
+  CStdString strQuery = FormatSQL("UPDATE channels SET iLastWatched = %u WHERE idChannel = %u",
+      tNow, channel.ChannelID());
+
+  return ExecuteQuery(strQuery);
+}
+
+
+bool CPVRDatabase::EraseChannelSettings()
+{
+  CLog::Log(LOGDEBUG, "PVRDB - %s - deleting all channel settings from the database", __FUNCTION__);
+  return DeleteValues("channelsettings");
+}
+
+bool CPVRDatabase::GetChannelSettings(const CPVRChannel &channel, CVideoSettings &settings)
+{
+  bool bReturn = false;
+
+  /* invalid channel */
+  if (channel.ChannelID() <= 0)
+  {
+    CLog::Log(LOGERROR, "PVRDB - %s - invalid channel id: %li",
+        __FUNCTION__, channel.ChannelID());
+    return bReturn;
+  }
+
+  CStdString strQuery = FormatSQL("SELECT * FROM channelsettings WHERE idChannel = %u\n", channel.ChannelID());
+
+  if (ResultQuery(strQuery))
+  {
+    settings.m_AudioDelay           = m_pDS->fv("fAudioDelay").get_asFloat();
+    settings.m_AudioStream          = m_pDS->fv("iAudioStream").get_asInt();
+    settings.m_Brightness           = m_pDS->fv("fBrightness").get_asFloat();
+    settings.m_Contrast             = m_pDS->fv("fContrast").get_asFloat();
+    settings.m_CustomPixelRatio     = m_pDS->fv("fPixelRatio").get_asFloat();
+    settings.m_NoiseReduction       = m_pDS->fv("fNoiseReduction").get_asFloat();
+    settings.m_Sharpness            = m_pDS->fv("fSharpness").get_asFloat();
+    settings.m_CustomZoomAmount     = m_pDS->fv("fCustomZoomAmount").get_asFloat();
+    settings.m_Gamma                = m_pDS->fv("fGamma").get_asFloat();
+    settings.m_SubtitleDelay        = m_pDS->fv("fSubtitleDelay").get_asFloat();
+    settings.m_SubtitleOn           = m_pDS->fv("bSubtitles").get_asBool();
+    settings.m_SubtitleStream       = m_pDS->fv("iSubtitleStream").get_asInt();
+    settings.m_ViewMode             = m_pDS->fv("iViewMode").get_asInt();
+    settings.m_Crop                 = m_pDS->fv("bCrop").get_asBool();
+    settings.m_CropLeft             = m_pDS->fv("iCropLeft").get_asInt();
+    settings.m_CropRight            = m_pDS->fv("iCropRight").get_asInt();
+    settings.m_CropTop              = m_pDS->fv("iCropTop").get_asInt();
+    settings.m_CropBottom           = m_pDS->fv("iCropBottom").get_asInt();
+    settings.m_InterlaceMethod      = (EINTERLACEMETHOD)m_pDS->fv("iInterlaceMethod").get_asInt();
+    settings.m_VolumeAmplification  = m_pDS->fv("fVolumeAmplification").get_asFloat();
+    settings.m_OutputToAllSpeakers  = m_pDS->fv("bOutputToAllSpeakers").get_asBool();
+    settings.m_SubtitleCached       = false;
+
+    bReturn = true;
+  }
+  m_pDS->close();
+
+  return bReturn;
+}
+
+bool CPVRDatabase::SetChannelSettings(const CPVRChannel &channel, const CVideoSettings &settings)
+{
+  /* invalid channel */
+  if (channel.ChannelID() <= 0)
+  {
+    CLog::Log(LOGERROR, "PVRDB - %s - invalid channel id: %li",
+        __FUNCTION__, channel.ChannelID());
+    return false;
+  }
+
+  CStdString strQuery = FormatSQL(
+      "REPLACE INTO channelsettings "
+        "(idChannel, iInterlaceMethod, iViewMode, fCustomZoomAmount, fPixelRatio, iAudioStream, iSubtitleStream, fSubtitleDelay, "
+         "bSubtitles, fBrightness, fContrast, fGamma, fVolumeAmplification, fAudioDelay, bOutputToAllSpeakers, bCrop, iCropLeft, "
+         "iCropRight, iCropTop, iCropBottom, fSharpness, fNoiseReduction) VALUES "
+         "(%i, %i, %i, %f, %f, %i, %i, %f, %i, %f, %f, %f, %f, %f, %i, %i, %i, %i, %i, %i, %f, %f)\n",
+       channel.ChannelID(), settings.m_InterlaceMethod, settings.m_ViewMode, settings.m_CustomZoomAmount, settings.m_CustomPixelRatio,
+       settings.m_AudioStream, settings.m_SubtitleStream, settings.m_SubtitleDelay, settings.m_SubtitleOn,
+       settings.m_Brightness, settings.m_Contrast, settings.m_Gamma, settings.m_VolumeAmplification, settings.m_AudioDelay,
+       settings.m_OutputToAllSpeakers, settings.m_Crop, settings.m_CropLeft, settings.m_CropRight, settings.m_CropTop,
+       settings.m_CropBottom, settings.m_Sharpness, settings.m_NoiseReduction);
+
+  return ExecuteQuery(strQuery);
+}
+
+/********** Channel group methods **********/
+
+bool CPVRDatabase::EraseChannelGroups(bool bRadio /* = false */)
+{
+  CLog::Log(LOGDEBUG, "PVRDB - %s - deleting all channel groups from the database", __FUNCTION__);
+
+  CStdString strWhereClause = FormatSQL("bIsRadio = %u", (bRadio ? 1 : 0));
+  return DeleteValues("channelgroups", strWhereClause);
+}
+
+long CPVRDatabase::AddChannelGroup(const CStdString &strGroupName, int iSortOrder, bool bRadio /* = false */)
+{
+  long iReturn = -1;
+
+  /* invalid group name */
+  if (strGroupName.IsEmpty())
+  {
+    CLog::Log(LOGERROR, "PVRDB - %s - invalid group name", __FUNCTION__);
+    return iReturn;
+  }
+
+  iReturn = GetChannelGroupId(strGroupName, bRadio);
+  if (iReturn <= 0)
+  {
+    CStdString strQuery = FormatSQL("INSERT INTO channelgroups (sName, iSortOrder, bIsRadio) VALUES ('%s', %i, %i)\n",
+        strGroupName.c_str(), iSortOrder, (bRadio ? 1 : 0));
+
+    if (ExecuteQuery(strQuery))
+      iReturn = (long) m_pDS->lastinsertid();
+  }
+
+  return iReturn;
+}
+
+bool CPVRDatabase::DeleteChannelGroup(int iGroupId, bool bRadio /* = false */)
+{
+  /* invalid group id */
+  if (iGroupId <= 0)
+  {
+    CLog::Log(LOGERROR, "PVRDB - %s - invalid group id: %d",
+        __FUNCTION__, iGroupId);
+    return false;
+  }
+
+  CStdString strWhereClause = FormatSQL("idGroup = %u AND bIsRadio = %u", iGroupId, bRadio);
+  return DeleteValues("channelgroups", strWhereClause);
+}
+
+
+bool CPVRDatabase::GetChannelGroupList(CPVRChannelGroups &results, bool bRadio /* = false */)
+{
+  bool bReturn = false;
+  CStdString strQuery = FormatSQL("SELECT * from channelgroups WHERE bIsRadio = %u ORDER BY iSortOrder\n", bRadio);
+  int iNumRows = ResultQuery(strQuery);
+
+  if (iNumRows > 0)
+  {
+    try
+    {
+      while (!m_pDS->eof())
+      {
+        CPVRChannelGroup data;
+
+        data.SetGroupID(m_pDS->fv("idGroup").get_asInt());
+        data.SetGroupName(m_pDS->fv("sName").get_asString());
+        data.SetSortOrder(m_pDS->fv("iSortOrder").get_asInt());
+
+        results.push_back(data);
+        m_pDS->next();
+      }
+      bReturn = true;
+    }
+    catch (...)
+    {
+      CLog::Log(LOGERROR, "%s - couldn't load channels from the database", __FUNCTION__);
+    }
+  }
+
+  m_pDS->close();
+  return bReturn;
+}
+
+bool CPVRDatabase::SetChannelGroupName(int iGroupId, const CStdString &strNewName, bool bRadio /* = false */)
+{
+  bool bReturn = false;
+
+  /* invalid group id */
+  if (iGroupId <= 0)
+  {
+    CLog::Log(LOGERROR, "PVRDB - %s - invalid group id: %d",
+        __FUNCTION__, iGroupId);
+    return bReturn;
+  }
+
+  CStdString strQuery = FormatSQL("SELECT COUNT(1) FROM channelgroups WHERE idGroup = %u AND bIsRadio = %u\n", iGroupId, (bRadio ? 1 : 0));
+  if (ResultQuery(strQuery))
+  {
+    if (m_pDS->fv(0).get_asInt() > 0)
+    {
+      strQuery = FormatSQL("UPDATE channelgroups SET Name = '%s' WHERE idGroup = %i AND bIsRadio = %u\n", strNewName.c_str(), iGroupId, (bRadio ? 1 : 0));
+      bReturn = ExecuteQuery(strQuery);
+    }
+  }
+
+  return bReturn;
+}
+
+
+bool CPVRDatabase::SetChannelGroupSortOrder(int iGroupId, int iSortOrder, bool bRadio /* = false */)
+{
+  bool bReturn = false;
+
+  /* invalid group id */
+  if (iGroupId <= 0)
+  {
+    CLog::Log(LOGERROR, "PVRDB - %s - invalid group id: %d",
+        __FUNCTION__, iGroupId);
+    return bReturn;
+  }
+
+  CStdString strQuery = FormatSQL("SELECT COUNT(1) FROM channelgroups WHERE idGroup = %u AND bIsRadio = %u\n", iGroupId, (bRadio ? 1 : 0));
+  if (ResultQuery(strQuery))
+  {
+    if (m_pDS->fv(0).get_asInt() > 0)
+    {
+      strQuery = FormatSQL("UPDATE channelgroups SET iSortOrder = %i WHERE idGroup = %i AND bIsRadio = %u\n", iSortOrder, iGroupId, (bRadio ? 1 : 0));
+      bReturn = ExecuteQuery(strQuery);
+    }
+  }
+
+  return bReturn;
+}
+
+
+long CPVRDatabase::GetChannelGroupId(const CStdString &strGroupName, bool bRadio /* = false */)
+{
+  CStdString strWhereClause = FormatSQL("sName LIKE '%s' AND bIsRadio = %u", strGroupName.c_str(), (bRadio ? 1 : 0));
+  CStdString strReturn = GetSingleValue("channelgroups", "idGroup", strWhereClause);
+
+  m_pDS->close();
+
+  return atoi(strReturn);
+}
+
+/********** Client methods **********/
+
+bool CPVRDatabase::EraseClients()
+{
+  CLog::Log(LOGDEBUG, "PVRDB - %s - deleting all clients from the database", __FUNCTION__);
+
+  return DeleteValues("clients") &&
+      DeleteValues("map_channels_clients");
+}
+
+long CPVRDatabase::AddClient(const CStdString &strClientName, const CStdString &strClientUid)
+{
+  long iReturn = -1;
+
+  /* invalid client uid or name */
+  if (strClientName.IsEmpty() || strClientUid.IsEmpty())
+  {
+    CLog::Log(LOGERROR, "PVRDB - %s - invalid client uid or name", __FUNCTION__);
+    return iReturn;
+  }
+
+  /* only add this client if it's not already in the database */
+  iReturn = GetClientId(strClientUid);
+  if (iReturn <= 0)
+  {
+    CStdString strQuery = FormatSQL("INSERT INTO clients (sName, sUid) VALUES ('%s', '%s')\n",
+        strClientName.c_str(), strClientUid.c_str());
+
+    if (ExecuteQuery(strQuery))
+    {
+      iReturn = (long) m_pDS->lastinsertid();
+    }
+  }
+
+  return iReturn;
+}
+
+bool CPVRDatabase::RemoveClient(const CStdString &strClientUid)
+{
+  /* invalid client uid */
+  if (strClientUid.IsEmpty())
+  {
+    CLog::Log(LOGERROR, "PVRDB - %s - invalid client uid", __FUNCTION__);
+    return false;
+  }
+
+  CStdString strWhereClause = FormatSQL("sUid = '%s'", strClientUid.c_str());
+  return DeleteValues("clients", strWhereClause);
+}
+
+long CPVRDatabase::GetClientId(const CStdString &strClientUid)
+{
+  CStdString strWhereClause = FormatSQL("sUid = '%s'", strClientUid.c_str());
+  CStdString strValue = GetSingleValue("clients", "idClient", strWhereClause);
+
+  if (strValue.IsEmpty())
+    return -1;
+
+  return atol(strValue.c_str());
+}
+
+/********** EPG methods **********/
+
+bool CPVRDatabase::EraseEpg()
+{
+  CLog::Log(LOGDEBUG, "PVRDB - %s - deleting all EPG data from the database", __FUNCTION__);
+
+  if (DeleteValues("epg") && DeleteValues("lastepgscan"))
+  {
+    lastScanTime.SetValid(false);
+    return true;
+  }
+
+  return false;
+}
+
+bool CPVRDatabase::EraseEpgForChannel(const CPVRChannel &channel, const CDateTime &start /* = NULL */, const CDateTime &end /* = NULL */)
+{
+  /* invalid channel */
+  if (channel.ChannelID() <= 0)
+  {
+    CLog::Log(LOGERROR, "PVRDB - %s - invalid channel id: %li",
+        __FUNCTION__, channel.ChannelID());
+    return false;
+  }
+
+  CLog::Log(LOGDEBUG, "PVRDB - %s - clearing the EPG for channel '%s'",
+      __FUNCTION__, channel.ChannelName().c_str());
+
+  CStdString strWhereClause;
+  strWhereClause = FormatSQL("idChannel = %u", channel.ChannelID());
+
+  if (start != NULL)
+  {
+    time_t iStartTime;
+    start.GetAsTime(iStartTime);
+    strWhereClause.append(FormatSQL(" AND iStartTime < %u", iStartTime).c_str());
+  }
+
+  if (end != NULL)
+  {
+    time_t iEndTime;
+    end.GetAsTime(iEndTime);
+    strWhereClause.append(FormatSQL(" AND iEndTime > %u", iEndTime).c_str());
+  }
+
+  return DeleteValues("epg", strWhereClause);
+}
+
+bool CPVRDatabase::EraseOldEpgEntries()
+{
+  time_t iYesterday;
+  CDateTime yesterday = CDateTime::GetCurrentDateTime() - CDateTimeSpan(1, 0, 0, 0);
+  yesterday.GetAsTime(iYesterday);
+  CStdString strWhereClause = FormatSQL("iEndTime < %u", iYesterday);
+
+  return DeleteValues("epg", strWhereClause);
+}
+
+bool CPVRDatabase::RemoveEpgEntry(const CPVREpgInfoTag &tag)
+{
+  /* invalid tag */
+  if (tag.BroadcastId() <= 0 && tag.UniqueBroadcastID() <= 0)
+  {
+    CLog::Log(LOGERROR, "PVRDB - %s - invalid EPG tag", __FUNCTION__);
+    return false;
+  }
+
+  CStdString strWhereClause;
+
+  if (tag.BroadcastId() > 0)
+    strWhereClause = FormatSQL("idBroadcast = %u", tag.BroadcastId());
+  else
+    strWhereClause = FormatSQL("iBroadcastUid = %u", tag.UniqueBroadcastID());
+
+  return DeleteValues("epg", strWhereClause);
+}
+
+
+int CPVRDatabase::GetEpgForChannel(CPVREpg *epg, const CDateTime &start /* = NULL */, const CDateTime &end /* = NULL */)
+{
+  int iReturn = -1;
+  CPVRChannel *channel = epg->Channel();
+
+  if (!channel)
+  {
+    CLog::Log(LOGERROR, "PVRDB - %s - EPG doesn't contain a channel tag", __FUNCTION__);
+    return false;
+  }
+
+  CStdString strWhereClause;
+  strWhereClause = FormatSQL("idChannel = %u", channel->ChannelID());
+
+  if (start != NULL)
+  {
+    time_t iStartTime;
+    start.GetAsTime(iStartTime);
+    strWhereClause.append(FormatSQL(" AND iStartTime < %u", iStartTime).c_str());
+  }
+
+  if (end != NULL)
+  {
+    time_t iEndTime;
+    end.GetAsTime(iEndTime);
+    strWhereClause.append(FormatSQL(" AND iEndTime > %u", iEndTime).c_str());
+  }
+
+  CStdString strQuery;
+  strQuery.Format("SELECT * FROM epg WHERE %s ORDER BY iStartTime ASC\n", strWhereClause.c_str());
+
+  int iNumRows = ResultQuery(strQuery);
+
+  if (iNumRows > 0)
+  {
+    try
+    {
+      while (!m_pDS->eof())
+      {
+        int iBroadcastUid = m_pDS->fv("iBroadcastUid").get_asInt();
+        CPVREpgInfoTag newTag(iBroadcastUid);
+
+        time_t iStartTime, iEndTime, iFirstAired;
+        iStartTime = (time_t) m_pDS->fv("iStartTime").get_asInt();
+        CDateTime startTime(iStartTime);
+        newTag.SetStart(startTime);
+
+        iEndTime = (time_t) m_pDS->fv("iEndTime").get_asInt();
+        CDateTime endTime(iEndTime);
+        newTag.SetEnd(endTime);
+
+        iFirstAired = (time_t) m_pDS->fv("iFirstAired").get_asInt();
+        CDateTime firstAired(iFirstAired);
+        newTag.SetFirstAired(firstAired);
+
+        newTag.SetBroadcastId    (m_pDS->fv("idBroadcast").get_asInt());
+        newTag.SetTitle          (m_pDS->fv("sTitle").get_asString().c_str());
+        newTag.SetPlotOutline    (m_pDS->fv("sPlotOutline").get_asString().c_str());
+        newTag.SetPlot           (m_pDS->fv("sPlot").get_asString().c_str());
+        newTag.SetGenre          (m_pDS->fv("iGenreType").get_asInt(),
+                                  m_pDS->fv("iGenreSubType").get_asInt());
+        newTag.SetParentalRating (m_pDS->fv("iParentalRating").get_asInt());
+        newTag.SetStarRating     (m_pDS->fv("iStarRating").get_asInt());
+        newTag.SetNotify         (m_pDS->fv("bNotify").get_asBool());
+        newTag.SetEpisodeNum     (m_pDS->fv("sEpisodeId").get_asString().c_str());
+        newTag.SetEpisodePart    (m_pDS->fv("sEpisodePart").get_asString().c_str());
+        newTag.SetEpisodeName    (m_pDS->fv("sEpisodeName").get_asString().c_str());
+        newTag.SetSeriesNum      (m_pDS->fv("sSeriesId").get_asString().c_str());
+
+        epg->UpdateEntry(newTag, false);
+        m_pDS->next();
+        ++iReturn;
+      }
+    }
+    catch (...)
+    {
+      CLog::Log(LOGERROR, "%s - couldn't load EPG data from the database", __FUNCTION__);
+    }
+  }
+  return iReturn;
+}
+
+CDateTime CPVRDatabase::GetEpgDataStart(long iChannelId /* = -1 */)
+{
+  time_t iFirstProgramme;
+  CStdString strWhereClause;
+
+  if (iChannelId > 0)
+    strWhereClause = FormatSQL("idChannel = '%u'", iChannelId);
+
+  CStdString strReturn = GetSingleValue("epg", "iStartTime", strWhereClause, "iStartTime ASC");
+  if (!strReturn.IsEmpty())
+  {
+    iFirstProgramme = atoi(strReturn);
+    CDateTime firstProgramme(iFirstProgramme);
+
+    if (firstProgramme.IsValid())
+      return firstProgramme;
+  }
+
+  return CDateTime::GetCurrentDateTime();
+}
+
+CDateTime CPVRDatabase::GetEpgDataEnd(long iChannelId /* = -1 */)
+{
+  time_t iLastProgramme;
+  CStdString strWhereClause;
+
+  if (iChannelId > 0)
+    strWhereClause = FormatSQL("idChannel = '%u'", iChannelId);
+
+  CStdString strReturn = GetSingleValue("epg", "iEndTime", strWhereClause, "iEndTime DESC");
+  if (!strReturn.IsEmpty())
+  {
+    iLastProgramme = atoi(strReturn);
+    CDateTime lastProgramme(iLastProgramme);
+
+    if (lastProgramme.IsValid())
+      return lastProgramme;
+  }
+
+  return CDateTime::GetCurrentDateTime();
+}
+
+CDateTime CPVRDatabase::GetLastEpgScanTime()
+{
+  if (lastScanTime.IsValid())
+    return lastScanTime;
+
+  CStdString strValue = GetSingleValue("lastepgscan", "iLastScan", "idEpg = 0");
+
+  if (strValue.IsEmpty())
+    return -1;
+
+  time_t iLastScan = atoi(strValue.c_str());
+  CDateTime lastScan(iLastScan);
+  lastScanTime = lastScan;
+
+  return lastScan;
+}
+
+bool CPVRDatabase::UpdateLastEpgScanTime(void)
+{
+  CDateTime now = CDateTime::GetCurrentDateTime();
+  CLog::Log(LOGDEBUG, "PVRDB - %s - updating last scan time to '%s'",
+      __FUNCTION__, now.GetAsDBDateTime().c_str());
+  lastScanTime = now;
+
+  bool bReturn = true;
+  time_t iLastScan;
+  now.GetAsTime(iLastScan);
+  CStdString strQuery = FormatSQL("REPLACE INTO lastepgscan(idEpg, iLastScan) VALUES (0, %u)\n",
+      iLastScan);
+
+  bReturn = ExecuteQuery(strQuery);
+
+  return bReturn;
+}
+
+bool CPVRDatabase::UpdateEpgEntry(const CPVREpgInfoTag &tag, bool bSingleUpdate /* = true */, bool bLastUpdate /* = false */)
+{
+  int bReturn = false;
+
+  /* invalid tag */
+  if (tag.UniqueBroadcastID() <= 0)
+  {
+    CLog::Log(LOGERROR, "PVRDB - %s - invalid EPG tag", __FUNCTION__);
+    return bReturn;
+  }
+
+  time_t iStartTime, iEndTime, iFirstAired;
+  tag.Start().GetAsTime(iStartTime);
+  tag.End().GetAsTime(iEndTime);
+  tag.FirstAired().GetAsTime(iFirstAired);
+
+  int iBroadcastId = tag.BroadcastId();
+  if (iBroadcastId)
+  {
+    CStdString strWhereClause = FormatSQL("(iBroadcastUid = '%u' OR iStartTime = %u) AND idChannel = '%u'",
+        tag.UniqueBroadcastID(), iStartTime, tag.ChannelTag()->ChannelID());
+    CStdString strValue = GetSingleValue("epg", "idBroadcast", strWhereClause);
+
+    if (!strValue.IsEmpty())
+      iBroadcastId = atoi(strValue);
+  }
+
+  CStdString strQuery;
+
+  if (iBroadcastId < 0)
+  {
+    strQuery = FormatSQL("INSERT INTO epg (idChannel, iStartTime, "
+        "iEndTime, sTitle, sPlotOutline, sPlot, iGenreType, iGenreSubType, "
+        "iFirstAired, iParentalRating, iStarRating, bNotify, sSeriesId, "
+        "sEpisodeId, sEpisodePart, sEpisodeName, iBroadcastUid) "
+        "VALUES (%u, %u, %u, '%s', '%s', '%s', %i, %i, %u, %i, %i, %i, '%s', '%s', '%s', '%s', %i)\n",
+        tag.ChannelTag()->ChannelID(), iStartTime, iEndTime,
+        tag.Title().c_str(), tag.PlotOutline().c_str(), tag.Plot().c_str(), tag.GenreType(), tag.GenreSubType(),
+        iFirstAired, tag.ParentalRating(), tag.StarRating(), tag.Notify(),
+        tag.SeriesNum().c_str(), tag.EpisodeNum().c_str(), tag.EpisodePart().c_str(), tag.EpisodeName().c_str(),
+        tag.UniqueBroadcastID());
+  }
+  else
+  {
+    strQuery = FormatSQL("REPLACE INTO epg (idChannel, iStartTime, "
+        "iEndTime, sTitle, sPlotOutline, sPlot, iGenreType, iGenreSubType, "
+        "iFirstAired, iParentalRating, iStarRating, bNotify, sSeriesId, "
+        "sEpisodeId, sEpisodePart, sEpisodeName, iBroadcastUid, idBroadcast) "
+        "VALUES (%u, %u, %u, '%s', '%s', '%s', %i, %i, %u, %i, %i, %i, '%s', '%s', '%s', '%s', %i, %i)\n",
+        tag.ChannelTag()->ChannelID(), iStartTime, iEndTime,
+        tag.Title().c_str(), tag.PlotOutline().c_str(), tag.Plot().c_str(), tag.GenreType(), tag.GenreSubType(),
+        tag.FirstAired().GetAsDBDateTime().c_str(), tag.ParentalRating(), tag.StarRating(), tag.Notify(),
+        tag.SeriesNum().c_str(), tag.EpisodeNum().c_str(), tag.EpisodePart().c_str(), tag.EpisodeName().c_str(),
+        tag.UniqueBroadcastID(), iBroadcastId);
+  }
+
+  if (bSingleUpdate)
+  {
+    bReturn = ExecuteQuery(strQuery);
+  }
+  else
+  {
+    bReturn = QueueInsertQuery(strQuery);
+
+    if (bLastUpdate)
+      CommitInsertQueries();
+  }
+
+  if ((bSingleUpdate || bLastUpdate) && GetEpgDataEnd(tag.ChannelTag()->ChannelID()) > tag.End())
+    EraseEpgForChannel(*tag.ChannelTag(), NULL, tag.End());
+
+  return bReturn;
+}
diff --git a/xbmc/pvr/PVRDatabase.h b/xbmc/pvr/PVRDatabase.h
new file mode 100644 (file)
index 0000000..6b7652a
--- /dev/null
@@ -0,0 +1,224 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "Database.h"
+#include "DateTime.h"
+
+class CPVRChannels;
+class CPVRChannelsContainer;
+class CPVRChannel;
+class CPVRChannelGroups;
+class CPVREpg;
+class CPVREpgInfoTag;
+class CVideoSettings;
+
+class CPVRDatabase : public CDatabase
+{
+public:
+  CPVRDatabase(void);
+  virtual ~CPVRDatabase(void);
+
+  virtual bool Open();
+
+  virtual int GetMinVersion() const { return 7; };
+  const char *GetDefaultDBName() const { return "MyTV4.db"; };
+
+  /********** Channel methods **********/
+
+  /**
+   * Remove all channels from the database
+   */
+  bool EraseChannels();
+
+  /**
+   * Remove all channels from a client from the database
+   */
+  bool EraseClientChannels(long iClientId);
+
+  /**
+   * Add or update a channel entry in the database
+   */
+  long UpdateChannel(const CPVRChannel &channel, bool bQueueWrite = false);
+
+  /**
+   * Remove a channel entry from the database
+   */
+  bool RemoveChannel(const CPVRChannel &channel);
+
+  /**
+   * Get the list of channels from the database
+   */
+  int GetChannels(CPVRChannels &results, bool bIsRadio);
+
+  /**
+   * The amount of channels
+   */
+  int GetChannelCount(bool bRadio, bool bHidden = false);
+
+  /**
+   * Get the Id of the channel that was played last
+   */
+  int GetLastChannel();
+
+  /**
+   * Update the channel that was played last
+   */
+  bool UpdateLastChannel(const CPVRChannel &channel);
+
+  /********** Channel settings methods **********/
+
+  /**
+   * Remove all channel settings from the database
+   */
+  bool EraseChannelSettings();
+
+  /**
+   * Get channel settings from the database
+   */
+  bool GetChannelSettings(const CPVRChannel &channel, CVideoSettings &settings);
+
+  /**
+   * Store channel settings in the database
+   */
+  bool SetChannelSettings(const CPVRChannel &channel, const CVideoSettings &settings);
+
+  /********** Channel group methods **********/
+
+  /**
+   * Remove all channel groups from the database
+   */
+  bool EraseChannelGroups(bool bRadio = false);
+
+  /**
+   * Add a channel group to the database
+   */
+  long AddChannelGroup(const CStdString &strGroupName, int iSortOrder, bool bRadio = false);
+
+  /**
+   * Delete a channel group from the database
+   */
+  bool DeleteChannelGroup(int iGroupId, bool bRadio = false);
+
+  /**
+   * Get the channel groups
+   */
+  bool GetChannelGroupList(CPVRChannelGroups &results, bool bRadio = false);
+
+  /**
+   * Change the name of a channel group
+   */
+  bool SetChannelGroupName(int iGroupId, const CStdString &strNewName, bool bRadio = false);
+
+  /**
+   * Change the sort order of a channel group
+   */
+  bool SetChannelGroupSortOrder(int iGroupId, int iSortOrder, bool bRadio = false);
+
+protected:
+  /**
+   * Get the Id of a channel group
+   */
+  long GetChannelGroupId(const CStdString &strGroupName, bool bRadio = false);
+
+  /********** Client methods **********/
+public:
+  /**
+   * Remove all client information from the database
+   */
+  bool EraseClients();
+
+  /**
+   * Add a client to the database if it's not already in there.
+   */
+  long AddClient(const CStdString &strClientName, const CStdString &strGuid);
+
+  /**
+   * Remove a client from the database
+   */
+  bool RemoveClient(const CStdString &strGuid);
+
+protected:
+
+  /**
+   * Get the database ID of a client
+   */
+  long GetClientId(const CStdString &strClientUid);
+
+  /********** EPG methods **********/
+public:
+  /**
+   * Remove all EPG information from the database
+   */
+  bool EraseEpg();
+
+  /**
+   * Erases all EPG entries for a channel.
+   */
+  bool EraseEpgForChannel(const CPVRChannel &channel, const CDateTime &start = NULL, const CDateTime &end = NULL);
+
+  /**
+   * Erases all EPG entries older than 1 day.
+   */
+  bool EraseOldEpgEntries();
+
+  /**
+   * Remove a single EPG entry.
+   */
+  bool RemoveEpgEntry(const CPVREpgInfoTag &tag);
+
+  /**
+   * Get all EPG entries for a channel.
+   */
+  int GetEpgForChannel(CPVREpg *epg, const CDateTime &start = NULL, const CDateTime &end = NULL);
+
+  /**
+   * Get the start time of the first entry for a channel.
+   * If iChannelId is <= 0, then all entries will be searched.
+   */
+  CDateTime GetEpgDataStart(long iChannelId = -1);
+
+  /**
+   * Get the end time of the last entry for a channel.
+   * If iChannelId is <= 0, then all entries will be searched.
+   */
+  CDateTime GetEpgDataEnd(long iChannelId = -1);
+
+  /**
+   * Get the last stored EPG scan time.
+   */
+  CDateTime GetLastEpgScanTime();
+
+  /**
+   * Update the last scan time.
+   */
+  bool UpdateLastEpgScanTime(void);
+
+  /**
+   * Persist an infotag
+   */
+  bool UpdateEpgEntry(const CPVREpgInfoTag &tag, bool bSingleUpdate = true, bool bLastUpdate = false);
+
+private:
+  virtual bool CreateTables();
+  virtual bool UpdateOldVersion(int version);
+  CDateTime lastScanTime;
+};
diff --git a/xbmc/pvr/PVREpg.cpp b/xbmc/pvr/PVREpg.cpp
new file mode 100644 (file)
index 0000000..7218bd2
--- /dev/null
@@ -0,0 +1,482 @@
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "GUISettings.h"
+#include "utils/log.h"
+#include "utils/TimeUtils.h"
+
+#include "PVREpg.h"
+#include "PVREpgInfoTag.h"
+#include "PVRChannels.h"
+#include "PVRManager.h"
+
+struct sortEPGbyDate
+{
+  bool operator()(CPVREpgInfoTag* strItem1, CPVREpgInfoTag* strItem2)
+  {
+    if (!strItem1 || !strItem2)
+      return false;
+
+    return strItem1->Start() < strItem2->Start();
+  }
+};
+
+CPVREpg::CPVREpg(CPVRChannel *channel)
+{
+  m_Channel        = channel;
+  m_bUpdateRunning = false;
+  m_bIsSorted      = false;
+}
+
+CPVREpg::~CPVREpg(void)
+{
+  Clear();
+}
+
+bool CPVREpg::HasValidEntries(void) const
+{
+  ((CPVREpg *) this)->Sort();
+
+  return (m_Channel &&
+          m_Channel->ChannelID() > 0 && /* valid channel ID */
+          size() > 0 && /* contains at least 1 tag */
+          at(size()-1)->m_endTime >= CDateTime::GetCurrentDateTime()); /* the last end time hasn't passed yet */
+}
+
+bool CPVREpg::DeleteInfoTag(CPVREpgInfoTag *tag)
+{
+  /* check if we're the "owner" of this tag */
+  if (tag->m_Epg != this)
+    return false;
+
+  /* remove the tag */
+  for (unsigned int i = 0; i < size(); i++)
+  {
+    CPVREpgInfoTag *entry = at(i);
+    if (entry == tag)
+    {
+      erase(begin()+i);
+      m_bIsSorted = false;
+      return true;
+    }
+  }
+
+  return false;
+}
+
+void CPVREpg::Sort(void)
+{
+  /* no need to sort twice */
+  if (m_bIsSorted) return;
+
+  /* sort the EPG */
+  sort(begin(), end(), sortEPGbyDate());
+
+  /* reset the previous and next pointers on each tag */
+  int iTagAmount = size();
+  for (int ptr = 0; ptr < iTagAmount; ptr++)
+  {
+    CPVREpgInfoTag *tag = at(ptr);
+
+    if (ptr == 0)
+    {
+      tag->SetPreviousEvent(NULL);
+    }
+
+    if (ptr > 0)
+    {
+      CPVREpgInfoTag *previousTag = at(ptr-1);
+      previousTag->SetNextEvent(tag);
+      tag->SetPreviousEvent(previousTag);
+    }
+
+    if (ptr == iTagAmount - 1)
+    {
+      tag->SetNextEvent(NULL);
+    }
+  }
+  m_bIsSorted = true;
+}
+
+void CPVREpg::Clear(void)
+{
+  for (int iTagPtr = 0; iTagPtr < size(); iTagPtr++)
+  {
+    delete at(iTagPtr);
+  }
+  erase(begin(), end());
+}
+
+void CPVREpg::Cleanup(void)
+{
+  Cleanup(CDateTime::GetCurrentDateTime());
+}
+
+void CPVREpg::Cleanup(const CDateTime Time)
+{
+  m_bUpdateRunning = true;
+  for (unsigned int i = 0; i < size(); i++)
+  {
+    CPVREpgInfoTag *tag = at(i);
+    if ( tag && /* valid tag */
+        !tag->HasTimer() && /* no time set */
+        (tag->End() + CDateTimeSpan(0, PVREpgs.m_iLingerTime / 60 + 1, PVREpgs.m_iLingerTime % 60, 0) < Time)) /* adding one hour for safety */
+    {
+      DeleteInfoTag(tag);
+    }
+  }
+  m_bUpdateRunning = false;
+}
+
+const CPVREpgInfoTag *CPVREpg::InfoTagNow(void) const
+{
+  CDateTime now = CDateTime::GetCurrentDateTime();
+
+  /* one of the first items will always match */
+  for (unsigned int i = 0; i < size(); i++)
+  {
+    if ((at(i)->Start() <= now) && (at(i)->End() > now))
+      return at(i);
+  }
+  return NULL;
+}
+
+const CPVREpgInfoTag *CPVREpg::InfoTagNext(void) const
+{
+  const CPVREpgInfoTag *nowTag = InfoTagNow();
+
+  return nowTag ? nowTag->GetNextEvent() : NULL;
+}
+
+const CPVREpgInfoTag *CPVREpg::InfoTag(long uniqueID, CDateTime StartTime) const
+{
+  /* try to find the tag by UID */
+  if (uniqueID > 0)
+  {
+    for (unsigned int i = 0; i < size(); i++)
+    {
+      if (at(i)->UniqueBroadcastID() == uniqueID)
+        return at(i);
+    }
+  }
+
+  /* if we haven't found it, search by start time */
+  for (unsigned int i = 0; i < size(); i++)
+  {
+    if (at(i)->Start() == StartTime)
+      return at(i);
+  }
+
+  return NULL;
+}
+
+const CPVREpgInfoTag *CPVREpg::InfoTagBetween(CDateTime BeginTime, CDateTime EndTime) const
+{
+  for (unsigned int ptr = 0; ptr < size(); ptr++)
+  {
+    CPVREpgInfoTag *tag = at(ptr);
+    if (tag->Start() >= BeginTime && tag->End() <= EndTime)
+      return tag;
+  }
+  return NULL;
+}
+
+const CPVREpgInfoTag *CPVREpg::InfoTagAround(CDateTime Time) const
+{
+  for (unsigned int ptr = 0; ptr < size(); ptr++)
+  {
+    CPVREpgInfoTag *tag = at(ptr);
+    if ((tag->Start() <= Time) && (tag->End() >= Time))
+      return tag;
+  }
+  return NULL;
+}
+
+bool CPVREpg::UpdateEntry(const CPVREpgInfoTag &tag, bool bUpdateDatabase /* = false */)
+{
+  CPVREpgInfoTag *InfoTag = (CPVREpgInfoTag *) this->InfoTag(tag.UniqueBroadcastID(), tag.Start());
+  /* create a new tag if no tag with this ID exists */
+  if (!InfoTag)
+  {
+    InfoTag = new CPVREpgInfoTag(tag.UniqueBroadcastID());
+    if (!InfoTag)
+    {
+      CLog::Log(LOGERROR, "%s - Couldn't create new infotag", __FUNCTION__);
+      return false;
+    }
+    push_back(InfoTag);
+  }
+
+  InfoTag->m_Epg = this;
+  InfoTag->Update(tag);
+
+  /* update the cached first and last date in the table */
+  PVREpgs.UpdateFirstAndLastEPGDates(*InfoTag);
+
+  m_bIsSorted = false;
+
+  if (bUpdateDatabase)
+  {
+    bool retval;
+    CPVRDatabase *database = g_PVRManager.GetTVDatabase();
+    database->Open();
+    retval = database->UpdateEpgEntry(*InfoTag);
+    database->Close();
+    return retval;
+  }
+
+  return true;
+}
+
+bool CPVREpg::UpdateEntry(const PVR_PROGINFO *data, bool bUpdateDatabase /* = false */)
+{
+  if (!data)
+    return false;
+
+  CPVREpgInfoTag *InfoTag = (CPVREpgInfoTag *) this->InfoTag(data->uid, data->starttime);
+
+  /* create a new tag if no tag with this ID exists */
+  if (!InfoTag)
+  {
+    InfoTag = new CPVREpgInfoTag(data->uid);
+    if (!InfoTag)
+    {
+      CLog::Log(LOGERROR, "%s - Couldn't create new infotag", __FUNCTION__);
+      return false;
+    }
+    push_back(InfoTag);
+  }
+
+  /* update the tag's values */
+  InfoTag->m_Epg = this;
+  InfoTag->Update(data);
+
+  /* update the cached first and last date in the table */
+  PVREpgs.UpdateFirstAndLastEPGDates(*InfoTag);
+
+  m_bIsSorted = false;
+
+  if (bUpdateDatabase)
+  {
+    bool retval;
+    CPVRDatabase *database = g_PVRManager.GetTVDatabase();
+    database->Open();
+    retval = database->UpdateEpgEntry(*InfoTag);
+    database->Close();
+    return retval;
+  }
+
+  return true;
+}
+
+bool CPVREpg::FixOverlappingEvents(bool bStore /* = true */)
+{
+  CPVRDatabase *database = g_PVRManager.GetTVDatabase(); /* the database has already been opened */
+
+  Sort();
+
+  CPVREpgInfoTag *previousTag = NULL;
+  for (unsigned int ptr = 0; ptr < size(); ptr++)
+  {
+    if (previousTag == NULL)
+    {
+      previousTag = at(ptr);
+      continue;
+    }
+
+    CPVREpgInfoTag *currentTag = at(ptr);
+
+    if (previousTag->End() > currentTag->End())
+    {
+      /* previous tag completely overlaps current tag; delete the current tag */
+      CLog::Log(LOGNOTICE, "%s - Removing EPG event '%s' on channel '%s' at '%s' to '%s': overlaps with '%s' at '%s' to '%s'",
+          __FUNCTION__, currentTag->Title().c_str(), currentTag->ChannelTag()->ChannelName().c_str(),
+          currentTag->Start().GetAsLocalizedDateTime(false, false).c_str(),
+          currentTag->End().GetAsLocalizedDateTime(false, false).c_str(),
+          previousTag->Title().c_str(),
+          previousTag->Start().GetAsLocalizedDateTime(false, false).c_str(),
+          previousTag->End().GetAsLocalizedDateTime(false, false).c_str());
+
+      database->RemoveEpgEntry(*currentTag);
+      DeleteInfoTag(currentTag);
+      ptr--;
+    }
+    else if (previousTag->End() > currentTag->Start())
+    {
+      /* previous tag ends after the current tag starts; mediate */
+      CDateTimeSpan diff = previousTag->End() - currentTag->Start();
+      int iDiffSeconds = diff.GetSeconds() + diff.GetMinutes() * 60 + diff.GetHours() * 3600 + diff.GetDays() * 86400;
+      CDateTime newTime = previousTag->End() - CDateTimeSpan(0, 0, 0, (int) (iDiffSeconds / 2));
+
+      CLog::Log(LOGNOTICE, "%s - Mediating start and end times of EPG events '%s' on channel '%s' at '%s' to '%s' and '%s' at '%s' to '%s': using '%s'",
+          __FUNCTION__, currentTag->Title().c_str(), currentTag->ChannelTag()->ChannelName().c_str(),
+          currentTag->Start().GetAsLocalizedDateTime(false, false).c_str(),
+          currentTag->End().GetAsLocalizedDateTime(false, false).c_str(),
+          previousTag->Title().c_str(),
+          previousTag->Start().GetAsLocalizedDateTime(false, false).c_str(),
+          previousTag->End().GetAsLocalizedDateTime(false, false).c_str(),
+          newTime.GetAsLocalizedDateTime(false, false).c_str());
+
+      previousTag->SetEnd(newTime);
+      currentTag->SetStart(newTime);
+
+      if (bStore)
+      {
+        database->UpdateEpgEntry(*previousTag, false, false);
+        database->UpdateEpgEntry(*currentTag, false, true);
+      }
+    }
+
+    previousTag = at(ptr);
+  }
+
+  return true;
+}
+
+bool CPVREpg::UpdateFromClient(time_t start, time_t end)
+{
+  bool bGrabSuccess = false;
+
+  if (g_PVRManager.GetClientProps(m_Channel->ClientID())->SupportEPG &&
+      g_PVRManager.Clients()->find(m_Channel->ClientID())->second->ReadyToUse())
+  {
+    bGrabSuccess = g_PVRManager.Clients()->find(m_Channel->ClientID())->second->GetEPGForChannel(*m_Channel, this, start, end) == PVR_ERROR_NO_ERROR;
+  }
+  else
+  {
+    CLog::Log(LOGINFO, "%s - client '%s' on client '%i' does not support EPGs",
+        __FUNCTION__, m_Channel->ChannelName().c_str(), m_Channel->ClientID());
+  }
+
+  return bGrabSuccess;
+}
+
+bool CPVREpg::UpdateFromScraper(time_t start, time_t end)
+{
+  bool bGrabSuccess = false;
+
+  if (m_Channel->EPGScraper().IsEmpty()) /* no grabber defined */
+  {
+    CLog::Log(LOGERROR, "%s - no EPG grabber defined for channel '%s'",
+        __FUNCTION__, m_Channel->ChannelName().c_str());
+  }
+  else
+  {
+    CLog::Log(LOGINFO, "%s - the database contains no EPG data for channel '%s', loading with scraper '%s'",
+        __FUNCTION__, m_Channel->ChannelName().c_str(), m_Channel->EPGScraper().c_str());
+    CLog::Log(LOGERROR, "loading the EPG via scraper has not been implemented yet");
+    // TODO: Add Support for Web EPG Scrapers here
+  }
+
+  return bGrabSuccess;
+}
+
+bool CPVREpg::LoadFromDb()
+{
+  bool bReturn = false;
+
+  if (!m_Channel)
+      return bReturn;
+
+  CPVRDatabase *database = g_PVRManager.GetTVDatabase(); /* the database has already been opened */
+
+  /* check if this channel is marked for grabbing */
+  if (!m_Channel->EPGEnabled())
+    return bReturn;
+
+  /* request the epg for this channel from the database */
+  bReturn = (database->GetEpgForChannel(this, NULL, NULL) > 0);
+
+  return bReturn;
+}
+
+bool CPVREpg::Update(time_t start, time_t end, bool bStoreInDb /* = true */) // XXX add locking
+{
+  if (!m_Channel)
+      return false;
+
+  bool bGrabSuccess     = true;
+  CPVRDatabase *database = g_PVRManager.GetTVDatabase(); /* the database has already been opened */
+
+  /* check if this channel is marked for grabbing */
+  if (!m_Channel->EPGEnabled())
+    return false;
+
+  /* mark the EPG as being updated */
+  m_bUpdateRunning = true;
+
+  bGrabSuccess = (m_Channel->EPGScraper() == "client") ?
+      UpdateFromClient(start, end) || bGrabSuccess:
+      UpdateFromScraper(start, end) || bGrabSuccess;
+
+  /* store the loaded EPG entries in the database */
+  if (bGrabSuccess)
+  {
+    FixOverlappingEvents(bStoreInDb);
+
+    if (bStoreInDb)
+    {
+      for (unsigned int iTagPtr = 0; iTagPtr < size(); iTagPtr++)
+        database->UpdateEpgEntry(*at(iTagPtr), false, (iTagPtr == size() - 1));
+    }
+  }
+
+  m_bUpdateRunning = false;
+
+  return bGrabSuccess;
+}
+
+int CPVREpg::Get(CFileItemList *results)
+{
+  int iInitialSize = results->Size();
+
+  if (!HasValidEntries() || IsUpdateRunning())
+    return -1;
+
+  for (unsigned int iTagPtr = 0; iTagPtr < size(); iTagPtr++)
+  {
+    CFileItemPtr channel(new CFileItem(*at(iTagPtr)));
+    channel->SetLabel2(at(iTagPtr)->Start().GetAsLocalizedDateTime(false, false));
+    results->Add(channel);
+  }
+
+  return size() - iInitialSize;
+}
+
+int CPVREpg::Get(CFileItemList *results, const PVREpgSearchFilter &filter)
+{
+  int iInitialSize = results->Size();
+
+  if (!HasValidEntries() || IsUpdateRunning())
+    return -1;
+
+  for (unsigned int iTagPtr = 0; iTagPtr < size(); iTagPtr++)
+  {
+    if (filter.FilterEntry(*at(iTagPtr)))
+    {
+      CFileItemPtr channel(new CFileItem(*at(iTagPtr)));
+      channel->SetLabel2(at(iTagPtr)->Start().GetAsLocalizedDateTime(false, false));
+      results->Add(channel);
+    }
+  }
+
+  return size() - iInitialSize;
+}
diff --git a/xbmc/pvr/PVREpg.h b/xbmc/pvr/PVREpg.h
new file mode 100644 (file)
index 0000000..d6967ab
--- /dev/null
@@ -0,0 +1,154 @@
+#pragma once
+
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "PVRChannel.h"
+#include "PVREpgSearchFilter.h"
+#include "../addons/include/xbmc_pvr_types.h"
+
+class CPVREpgInfoTag;
+class CPVREpgs;
+
+class CPVREpg : public std::vector<CPVREpgInfoTag*>
+{
+  friend class CPVREpgs;
+
+private:
+  CPVRChannel * m_Channel;        /* the channel this EPG belongs to */
+  bool          m_bUpdateRunning; /* true if EPG is currently being updated */
+  bool          m_bIsSorted;      /* remember if we're sorted or not */
+
+  /**
+   * Update the EPG from a scraper set in the channel tag
+   * TODO: not implemented yet
+   */
+  bool UpdateFromScraper(time_t start, time_t end);
+
+  /**
+   * Update the EPG from a client
+   */
+  bool UpdateFromClient(time_t start, time_t end);
+
+public:
+  CPVREpg(CPVRChannel *channel);
+  virtual ~CPVREpg(void);
+
+  /**
+   * Check if this EPG contains valid entries
+   */
+  bool HasValidEntries(void) const;
+
+  /**
+   * The channel this EPG belongs to
+   */
+  CPVRChannel *Channel(void) const { return m_Channel; }
+
+  /**
+   * Delete an infotag from this EPG
+   */
+  bool DeleteInfoTag(CPVREpgInfoTag *tag);
+
+  /**
+   * Remove all entries from this EPG that finished before the given time
+   * and that have no timers set
+   */
+  void Cleanup(const CDateTime Time);
+
+  /**
+   * Remove all entries from this EPG that finished before the current time
+   * and that have no timers set
+   */
+  void Cleanup(void);
+
+  /**
+   * Remove all entries from this EPG
+   */
+  void Clear(void);
+
+  /**
+   * Sort all entries in this EPG by date
+   */
+  void Sort(void);
+
+  /**
+   * Get the event that is occurring now
+   */
+  const CPVREpgInfoTag *InfoTagNow(void) const;
+
+  /**
+   * Get the event that will occur next
+   */
+  const CPVREpgInfoTag *InfoTagNext(void) const;
+
+  /**
+   * Get the infotag with the given ID
+   * If it wasn't'found, try finding the event with the given start time
+   */
+  const CPVREpgInfoTag *InfoTag(long uniqueID, CDateTime StartTime) const;
+
+  /**
+   * Get the event that occurs at the given time
+   */
+  const CPVREpgInfoTag *InfoTagAround(CDateTime Time) const;
+
+  /**
+   * Get the event that occurs between the given begin and end time
+   */
+  const CPVREpgInfoTag *InfoTagBetween(CDateTime BeginTime, CDateTime EndTime) const;
+
+  /**
+   * True if this EPG is currently being updated, false otherwise
+   */
+  bool IsUpdateRunning(void) const { return m_bUpdateRunning; }
+
+  /**
+   * Mark the EPG as being update or no longer being updated
+   */
+  void SetUpdateRunning(bool OnOff) { m_bUpdateRunning = OnOff; }
+
+  /**
+   * Update an entry in this EPG
+   * If bUpdateDatabase is set to true, this event will be persisted in the database
+   */
+  bool UpdateEntry(const CPVREpgInfoTag &tag, bool bUpdateDatabase = false);
+
+  /**
+   * Update an entry in this EPG
+   * If bUpdateDatabase is set to true, this event will be persisted in the database
+   */
+  bool UpdateEntry(const PVR_PROGINFO *data, bool bUpdateDatabase = false);
+
+  /**
+   * Fix overlapping events from the tables
+   */
+  bool FixOverlappingEvents(bool bStore = true);
+
+  /**
+   * Update the EPG from 'start' till 'end'
+   */
+  bool Update(time_t start, time_t end, bool bStoreInDb = true);
+
+  bool LoadFromDb();
+
+  int Get(CFileItemList *results);
+  int Get(CFileItemList *results, const PVREpgSearchFilter &filter);
+};
diff --git a/xbmc/pvr/PVREpgInfoTag.cpp b/xbmc/pvr/PVREpgInfoTag.cpp
new file mode 100644 (file)
index 0000000..bf0e7b4
--- /dev/null
@@ -0,0 +1,240 @@
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "LocalizeStrings.h"
+
+#include "PVREpgInfoTag.h"
+#include "PVRTimers.h"
+#include "PVRTimerInfoTag.h"
+
+using namespace std;
+
+CPVREpgInfoTag::CPVREpgInfoTag(int uniqueBroadcastID)
+{
+  Reset();
+  m_iUniqueBroadcastID = uniqueBroadcastID;
+}
+
+CPVREpgInfoTag::~CPVREpgInfoTag()
+{
+  m_Epg           = NULL;
+  m_nextEvent     = NULL;
+  m_previousEvent = NULL;
+}
+
+void CPVREpgInfoTag::Reset()
+{
+  m_iBroadcastId        = -1;
+  m_strTitle            = g_localizeStrings.Get(19055);
+  m_strGenre            = "";
+  m_strPlotOutline      = "";
+  m_strPlot             = "";
+  m_iGenreType          = 0;
+  m_iGenreSubType       = 0;
+  m_strFileNameAndPath  = "";
+  m_strIconPath         = "";
+  m_isRecording         = false;
+  m_Timer               = NULL;
+  m_Epg                 = NULL;
+  m_iParentalRating     = 0;
+  m_iStarRating         = 0;
+  m_notify              = false;
+  m_seriesNum           = "";
+  m_episodeNum          = "";
+  m_episodePart         = "";
+  m_episodeName         = "";
+}
+
+void CPVREpgInfoTag::SetTimer(const CPVRTimerInfoTag *Timer)
+{
+  if (!Timer)
+    m_Timer = NULL;
+
+  m_Timer = Timer;
+}
+
+bool CPVREpgInfoTag::HasTimer(void) const
+{
+  for (unsigned int i = 0; i < PVRTimers.size(); ++i)
+  {
+    if (PVRTimers[i].EpgInfoTag() == this)
+      return true;
+  }
+  return false;
+}
+
+int CPVREpgInfoTag::GetDuration() const
+{
+  time_t start, end;
+  m_startTime.GetAsTime(start);
+  m_endTime.GetAsTime(end);
+  return end - start > 0 ? end - start : 3600;
+}
+
+void CPVREpgInfoTag::SetGenre(int ID, int subID)
+{
+  m_iGenreType    = ID;
+  m_iGenreSubType = subID;
+  m_strGenre      = ConvertGenreIdToString(ID, subID);
+}
+
+const CPVREpgInfoTag *CPVREpgInfoTag::GetNextEvent() const
+{
+  m_Epg->Sort();
+
+  return m_nextEvent;
+}
+
+const CPVREpgInfoTag *CPVREpgInfoTag::GetPreviousEvent() const
+{
+  m_Epg->Sort();
+
+  return m_previousEvent;
+}
+
+void CPVREpgInfoTag::SetStart(CDateTime Start)
+{
+  m_startTime = Start;
+  UpdatePath();
+}
+
+void CPVREpgInfoTag::UpdatePath()
+{
+  if (!m_Epg)
+    return;
+
+  CStdString path;
+  path.Format("pvr://guide/channel-%04i/%s.epg", m_Epg->Channel()->ChannelNumber(), m_startTime.GetAsDBDateTime().c_str());
+  SetPath(path);
+}
+
+CStdString CPVREpgInfoTag::ConvertGenreIdToString(int ID, int subID) const
+{
+  CStdString str = g_localizeStrings.Get(19499);
+  switch (ID)
+  {
+    case EVCONTENTMASK_MOVIEDRAMA:
+      if (subID <= 8)
+        str = g_localizeStrings.Get(19500 + subID);
+      else
+        str = g_localizeStrings.Get(19500) + " (undefined)";
+      break;
+    case EVCONTENTMASK_NEWSCURRENTAFFAIRS:
+      if (subID <= 4)
+        str = g_localizeStrings.Get(19516 + subID);
+      else
+        str = g_localizeStrings.Get(19516) + " (undefined)";
+      break;
+    case EVCONTENTMASK_SHOW:
+      if (subID <= 3)
+        str = g_localizeStrings.Get(19532 + subID);
+      else
+        str = g_localizeStrings.Get(19532) + " (undefined)";
+      break;
+    case EVCONTENTMASK_SPORTS:
+      if (subID <= 0x0B)
+        str = g_localizeStrings.Get(19548 + subID);
+      else
+        str = g_localizeStrings.Get(19548) + " (undefined)";
+      break;
+    case EVCONTENTMASK_CHILDRENYOUTH:
+      if (subID <= 5)
+        str = g_localizeStrings.Get(19564 + subID);
+      else
+        str = g_localizeStrings.Get(19564) + " (undefined)";
+      break;
+    case EVCONTENTMASK_MUSICBALLETDANCE:
+      if (subID <= 6)
+        str = g_localizeStrings.Get(19580 + subID);
+      else
+        str = g_localizeStrings.Get(19580) + " (undefined)";
+      break;
+    case EVCONTENTMASK_ARTSCULTURE:
+      if (subID <= 0x0B)
+        str = g_localizeStrings.Get(19596 + subID);
+      else
+        str = g_localizeStrings.Get(19596) + " (undefined)";
+      break;
+    case EVCONTENTMASK_SOCIALPOLITICALECONOMICS:
+      if (subID <= 0x03)
+        str = g_localizeStrings.Get(19612 + subID);
+      else
+        str = g_localizeStrings.Get(19612) + " (undefined)";
+      break;
+    case EVCONTENTMASK_EDUCATIONALSCIENCE:
+      if (subID <= 0x07)
+        str = g_localizeStrings.Get(19628 + subID);
+      else
+        str = g_localizeStrings.Get(19628) + " (undefined)";
+      break;
+    case EVCONTENTMASK_LEISUREHOBBIES:
+      if (subID <= 0x07)
+        str = g_localizeStrings.Get(19644 + subID);
+      else
+        str = g_localizeStrings.Get(19644) + " (undefined)";
+      break;
+    case EVCONTENTMASK_SPECIAL:
+      if (subID <= 0x03)
+        str = g_localizeStrings.Get(19660 + subID);
+      else
+        str = g_localizeStrings.Get(19660) + " (undefined)";
+      break;
+    case EVCONTENTMASK_USERDEFINED:
+      if (subID <= 0x03)
+        str = g_localizeStrings.Get(19676 + subID);
+      else
+        str = g_localizeStrings.Get(19676) + " (undefined)";
+      break;
+    default:
+      break;
+  }
+  return str;
+}
+
+void CPVREpgInfoTag::Update(const CPVREpgInfoTag &tag)
+{
+  SetBroadcastId(tag.BroadcastId());
+  SetTitle(tag.Title());
+  SetPlotOutline(tag.PlotOutline());
+  SetPlot(tag.Plot());
+  SetStart(tag.Start());
+  SetEnd(tag.End());
+  SetGenre(tag.GenreType(), tag.GenreSubType());
+  SetFirstAired(tag.FirstAired());
+  SetParentalRating(tag.ParentalRating());
+  SetStarRating(tag.StarRating());
+  SetNotify(tag.Notify());
+  SetEpisodeNum(tag.EpisodeNum());
+  SetEpisodePart(tag.EpisodePart());
+  SetEpisodeName(tag.EpisodeName());
+}
+
+void CPVREpgInfoTag::Update(const PVR_PROGINFO *data)
+{
+  SetStart((time_t)data->starttime);
+  SetEnd((time_t)data->endtime);
+  SetTitle(data->title);
+  SetPlotOutline(data->subtitle);
+  SetPlot(data->description);
+  SetGenre(data->genre_type, data->genre_sub_type);
+  SetParentalRating(data->parental_rating);
+//  SetIcon(m_Channel->IconPath());
+}
diff --git a/xbmc/pvr/PVREpgInfoTag.h b/xbmc/pvr/PVREpgInfoTag.h
new file mode 100644 (file)
index 0000000..2c99056
--- /dev/null
@@ -0,0 +1,147 @@
+#pragma once
+
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "DateTime.h"
+#include "PVREpg.h"
+
+class CPVRTimerInfoTag;
+
+class CPVREpgInfoTag
+{
+  friend class CPVREpg;
+
+private:
+  CPVREpg *                     m_Epg;     // The Schedule this event belongs to
+  const CPVRTimerInfoTag *      m_Timer;
+
+  int                           m_iBroadcastId;
+  CStdString                    m_strTitle;
+  CStdString                    m_strPlotOutline;
+  CStdString                    m_strPlot;
+  CStdString                    m_strGenre;
+  CDateTime                     m_startTime;
+  CDateTime                     m_endTime;
+  CDateTimeSpan                 m_duration;
+  CStdString                    m_strIconPath; // XXX not persisted?
+  CStdString                    m_strFileNameAndPath;
+  int                           m_iGenreType;
+  int                           m_iGenreSubType;
+  bool                          m_isRecording; //XXX
+  CDateTime                     m_strFirstAired;
+  int                           m_iParentalRating;
+  int                           m_iStarRating;
+  bool                          m_notify;
+  CStdString                    m_seriesNum;
+  CStdString                    m_episodeNum;
+  CStdString                    m_episodePart;
+  CStdString                    m_episodeName;
+
+  mutable const CPVREpgInfoTag *m_nextEvent;
+  mutable const CPVREpgInfoTag *m_previousEvent;
+  int                           m_iUniqueBroadcastID; // event's unique identifier for this tag
+
+public:
+  CPVREpgInfoTag(int uniqueBroadcastID);
+  CPVREpgInfoTag() { Reset(); };
+  ~CPVREpgInfoTag();
+  void Reset();
+
+  int UniqueBroadcastID(void) const { return m_iUniqueBroadcastID; }
+
+  int BroadcastId(void) const { return m_iBroadcastId; }
+  void SetBroadcastId(int iId) { m_iBroadcastId = iId; }
+
+  CDateTime Start(void) const { return m_startTime; }
+  void SetStart(CDateTime Start);
+
+  CDateTime End(void) const { return m_endTime; }
+  void SetEnd(CDateTime Stop) { m_endTime = Stop; }
+
+  int GetDuration() const;
+
+  CStdString Title(void) const { return m_strTitle; }
+  void SetTitle(CStdString name) { m_strTitle = name; }
+
+  CStdString PlotOutline(void) const { return m_strPlotOutline; }
+  void SetPlotOutline(CStdString PlotOutline) { m_strPlotOutline = PlotOutline; }
+
+  CStdString Plot(void) const { return m_strPlot; }
+  void SetPlot(CStdString Plot) { m_strPlot = Plot; }
+
+  int GenreType(void) const { return m_iGenreType; }
+  int GenreSubType(void) const { return m_iGenreSubType; }
+  CStdString Genre(void) const { return m_strGenre; }
+  void SetGenre(int ID, int subID);
+
+  CDateTime FirstAired(void) const { return m_strFirstAired; }
+  void SetFirstAired(CDateTime FirstAired) { m_strFirstAired = FirstAired; }
+
+  int ParentalRating(void) const { return m_iParentalRating; }
+  void SetParentalRating(int ParentalRating) { m_iParentalRating = ParentalRating; }
+
+  int StarRating(void) const { return m_iStarRating; }
+  void SetStarRating(int StarRating) { m_iStarRating = StarRating; }
+
+  bool Notify(void) const { return m_notify; }
+  void SetNotify(bool Notify) { m_notify = Notify; }
+
+  CStdString SeriesNum(void) const { return m_seriesNum; }
+  void SetSeriesNum(CStdString SeriesNum) { m_seriesNum = SeriesNum; }
+
+  CStdString EpisodeNum(void) const { return m_episodeNum; }
+  void SetEpisodeNum(CStdString EpisodeNum) { m_episodeNum = EpisodeNum; }
+
+  CStdString EpisodePart(void) const { return m_episodePart; }
+  void SetEpisodePart(CStdString EpisodePart) { m_episodePart = EpisodePart; }
+
+  CStdString EpisodeName(void) const { return m_episodeName; }
+  void SetEpisodeName(CStdString EpisodeName) { m_episodeName = EpisodeName; }
+
+  CStdString Icon(void) const { return m_strIconPath; }
+  void SetIcon(CStdString icon) { m_strIconPath = icon; }
+
+  CStdString Path(void) const { return m_strFileNameAndPath; }
+  void SetPath(CStdString Path) { m_strFileNameAndPath = Path; }
+  void UpdatePath();
+
+  /*! \brief Get the CPVRChannel class associated to this epg entry
+   \return the pointer to the info tag
+   */
+  const CPVRChannel *ChannelTag(void) const { return m_Epg->Channel(); }
+
+  /* Scheduled recording related Data */
+  bool HasTimer() const;
+  void SetTimer(const CPVRTimerInfoTag *Timer);
+  const CPVRTimerInfoTag *Timer(void) const { return m_Timer; }
+
+  CStdString ConvertGenreIdToString(int ID, int subID) const;
+
+  const CPVREpgInfoTag *GetNextEvent() const;
+  const CPVREpgInfoTag *GetPreviousEvent() const;
+
+  void SetNextEvent(const CPVREpgInfoTag *event) { m_nextEvent = event; }
+  void SetPreviousEvent(const CPVREpgInfoTag *event) { m_previousEvent = event; }
+
+  void Update(const CPVREpgInfoTag &tag);
+  void Update(const PVR_PROGINFO *data);
+};
diff --git a/xbmc/pvr/PVREpgSearchFilter.cpp b/xbmc/pvr/PVREpgSearchFilter.cpp
new file mode 100644 (file)
index 0000000..1e63bf3
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "LocalizeStrings.h"
+#include "TextSearch.h"
+
+#include "PVREpgSearchFilter.h"
+#include "PVREpgs.h"
+#include "PVREpgInfoTag.h"
+
+using namespace std;
+
+void PVREpgSearchFilter::Reset()
+{
+  m_strSearchTerm            = "";
+  m_bIsCaseSensitive         = false;
+  m_bSearchInDescription     = false;
+  m_iGenreType               = -1;
+  m_iGenreSubType            = -1;
+  m_iMinimumDuration         = -1;
+  m_iMaximumDuration         = -1;
+  PVREpgs.GetFirstEPGDate().GetAsSystemTime(m_startDate);
+  m_startDate.wHour          = 0;
+  m_startDate.wMinute        = 0;
+  PVREpgs.GetLastEPGDate().GetAsSystemTime(m_endDate);
+  m_endDate.wHour            = 23;
+  m_endDate.wMinute          = 59;
+  m_startTime                = m_startDate;
+  m_startTime.wHour          = 0;
+  m_startTime.wMinute        = 0;
+  m_endTime                  = m_endDate;
+  m_endTime.wHour            = 23;
+  m_endTime.wMinute          = 59;
+  m_iChannelNumber           = -1;
+  m_bFTAOnly                 = false;
+  m_bIncludeUnknownGenres    = true;
+  m_iChannelGroup            = -1;
+  m_bIgnorePresentTimers     = true;
+  m_bIgnorePresentRecordings = true;
+  m_bPreventRepeats          = false;
+}
+
+bool PVREpgSearchFilter::FilterEntry(const CPVREpgInfoTag &tag) const
+{
+  if (m_iGenreType != -1)
+  {
+    if (tag.GenreType() != m_iGenreType &&
+        (!m_bIncludeUnknownGenres &&
+        ((tag.GenreType() < EVCONTENTMASK_USERDEFINED || tag.GenreType() >= EVCONTENTMASK_MOVIEDRAMA))))
+    {
+      return false;
+    }
+  }
+  if (m_iMinimumDuration != -1)
+  {
+    if (tag.GetDuration() < (m_iMinimumDuration*60))
+      return false;
+  }
+  if (m_iMaximumDuration != -1)
+  {
+    if (tag.GetDuration() > (m_iMaximumDuration*60))
+      return false;
+  }
+  if (m_iChannelNumber != -1)
+  {
+    if (m_iChannelNumber == -2)
+    {
+      if (tag.ChannelTag()->IsRadio())
+        return false;
+    }
+    else if (m_iChannelNumber == -3)
+    {
+      if (!tag.ChannelTag()->IsRadio())
+        return false;
+    }
+    else if (tag.ChannelTag()->ChannelNumber() != m_iChannelNumber)
+      return false;
+  }
+  if (m_bFTAOnly && tag.ChannelTag()->IsEncrypted())
+  {
+    return false;
+  }
+  if (m_iChannelGroup != -1)
+  {
+    if (tag.ChannelTag()->GroupID() != m_iChannelGroup)
+      return false;
+  }
+
+  int timeTag = (tag.Start().GetHour()*60 + tag.Start().GetMinute());
+
+  if (timeTag < (m_startTime.wHour*60 + m_startTime.wMinute))
+    return false;
+
+  if (timeTag > (m_endTime.wHour*60 + m_endTime.wMinute))
+    return false;
+
+  if (tag.Start() < m_startDate)
+    return false;
+
+  if (tag.Start() > m_endDate)
+    return false;
+
+  if (m_strSearchTerm != "")
+  {
+    cTextSearch search(tag.Title(), m_strSearchTerm, m_bIsCaseSensitive);
+    if (!search.DoSearch())
+    {
+      if (m_bSearchInDescription)
+      {
+        search.SetText(tag.PlotOutline(), m_strSearchTerm, m_bIsCaseSensitive);
+        if (!search.DoSearch())
+        {
+          search.SetText(tag.Plot(), m_strSearchTerm, m_bIsCaseSensitive);
+          if (!search.DoSearch())
+            return false;
+        }
+      }
+      else
+        return false;
+    }
+  }
+  return true;
+}
diff --git a/xbmc/pvr/PVREpgSearchFilter.h b/xbmc/pvr/PVREpgSearchFilter.h
new file mode 100644 (file)
index 0000000..6b766e5
--- /dev/null
@@ -0,0 +1,52 @@
+#pragma once
+
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "DateTime.h"
+
+class CPVREpgInfoTag;
+
+/* Filter data to check with a EPGEntry */
+struct PVREpgSearchFilter
+{
+  void Reset();
+  bool FilterEntry(const CPVREpgInfoTag &tag) const;
+
+  CStdString    m_strSearchTerm;
+  bool          m_bIsCaseSensitive;
+  bool          m_bSearchInDescription;
+  int           m_iGenreType;
+  int           m_iGenreSubType;
+  int           m_iMinimumDuration;
+  int           m_iMaximumDuration;
+  SYSTEMTIME    m_startTime;
+  SYSTEMTIME    m_endTime;
+  SYSTEMTIME    m_startDate;
+  SYSTEMTIME    m_endDate;
+  int           m_iChannelNumber;
+  bool          m_bFTAOnly;
+  bool          m_bIncludeUnknownGenres;
+  int           m_iChannelGroup;
+  bool          m_bIgnorePresentTimers;
+  bool          m_bIgnorePresentRecordings;
+  bool          m_bPreventRepeats;
+};
diff --git a/xbmc/pvr/PVREpgs.cpp b/xbmc/pvr/PVREpgs.cpp
new file mode 100644 (file)
index 0000000..de93a0d
--- /dev/null
@@ -0,0 +1,585 @@
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "GUISettings.h"
+#include "GUIDialogPVRUpdateProgressBar.h"
+#include "GUIDialogProgress.h"
+#include "GUIWindowManager.h"
+#include "log.h"
+#include "TimeUtils.h"
+
+#include "PVREpgs.h"
+#include "PVREpg.h"
+#include "PVREpgInfoTag.h"
+#include "PVRManager.h"
+#include "PVREpgSearchFilter.h"
+#include "PVRChannel.h"
+#include "PVRChannelsContainer.h"
+#include "PVRTimerInfoTag.h"
+
+#define NOWPLAYINGUPDATEINTERVAL 30  /* update "now playing" tags every 30 seconds */
+#define EPGCLEANUPINTERVAL       900 /* remove old entries from the EPG every 15 minutes */
+
+using namespace std;
+
+CPVREpgs PVREpgs;
+
+CPVREpgs::CPVREpgs()
+{
+  m_bStop = true;
+  Reset();
+}
+
+CPVREpgs::~CPVREpgs()
+{
+  Clear();
+}
+
+void CPVREpgs::Clear(bool bClearDb /* = false */)
+{
+  // XXX stop the timers from being updated while clearing tags
+  /* remove all pointers to epg tables on timers */
+  for (unsigned int iTimerPtr = 0; iTimerPtr < PVRTimers.size(); iTimerPtr++)
+    PVRTimers[iTimerPtr].SetEpgInfoTag(NULL);
+
+  /* clear all epg tables and remove pointers to epg tables on channels */
+  for (unsigned int iEpgPtr = 0; iEpgPtr < size(); iEpgPtr++)
+  {
+    CPVREpg *epg = at(iEpgPtr);
+    epg->Clear();
+
+    CPVRChannel *channel = (CPVRChannel *) epg->Channel();
+    if (channel)
+      channel->m_EPG = NULL;
+  }
+
+  /* remove all EPG tables */
+  for (int iEpgPtr = 0; iEpgPtr < size(); iEpgPtr++)
+  {
+    delete at(iEpgPtr);
+  }
+  erase(begin(), end());
+
+  /* clear the database entries */
+  if (bClearDb)
+  {
+    CPVRDatabase *database = g_PVRManager.GetTVDatabase();
+    database->Open();
+    database->EraseEpg();
+    database->Close();
+  }
+
+  m_iLastEpgUpdate  = 0;
+  m_bDatabaseLoaded = false;
+}
+
+void CPVREpgs::Start()
+{
+  Clear();
+
+  g_guiSettings.AddObserver(this);
+
+  /* make sure the EPG is loaded before starting the thread */
+  CreateChannelEpgs();
+  LoadFromDb(true /* show progress */);
+
+  Create();
+  SetName("XBMC EPG thread");
+  SetPriority(-15);
+  CLog::Log(LOGNOTICE, "%s - EPG thread started", __FUNCTION__);
+}
+
+bool CPVREpgs::Stop()
+{
+  StopThread();
+
+  g_guiSettings.RemoveObserver(this);
+
+  return true;
+}
+
+bool CPVREpgs::Reset(bool bClearDb /* = false */)
+{
+  bool bThreadRunning = !m_bStop;
+
+  if (bThreadRunning && !Stop())
+    return false;
+
+  Clear(bClearDb);
+
+  m_bDatabaseLoaded = false;
+  m_RadioFirst      = CDateTime::GetCurrentDateTime();
+  m_RadioLast       = m_RadioFirst;
+  m_TVFirst         = m_RadioFirst;
+  m_TVLast          = m_RadioFirst;
+
+  if (bThreadRunning)
+    Start();
+
+  return true;
+}
+
+void CPVREpgs::Notify(const Observable &obs, const CStdString& msg)
+{
+  /* settings were updated */
+  if (msg == "settings")
+    LoadSettings();
+}
+
+void CPVREpgs::Process()
+{
+  time_t iNow          = 0;
+  m_iLastPointerUpdate = 0;
+  m_iLastEpgCleanup    = 0;
+  m_iLastEpgUpdate     = 0;
+
+  CPVRDatabase *database = g_PVRManager.GetTVDatabase();
+  if (!database->Open())
+  {
+    CLog::Log(LOGERROR, "PVREpgs - %s - cannot open the database",
+        __FUNCTION__);
+    return;
+  }
+
+  /* get the last EPG update time from the database */
+  database->GetLastEpgScanTime().GetAsTime(m_iLastEpgUpdate);
+  database->Close();
+
+  LoadSettings();
+
+  while (!m_bStop)
+  {
+    CDateTime::GetCurrentDateTime().GetAsTime(iNow);
+
+    /* update the EPG */
+    if (!m_bStop && (iNow > m_iLastEpgUpdate + m_iUpdateTime || !m_bDatabaseLoaded))
+      UpdateEPG(!m_bDatabaseLoaded);
+
+    if (!m_bStop && iNow > m_iLastPointerUpdate + NOWPLAYINGUPDATEINTERVAL)
+      UpdateAllChannelEPGPointers();
+
+    if (!m_bStop && iNow > m_iLastEpgCleanup + EPGCLEANUPINTERVAL)
+      RemoveOldEntries();
+
+    Sleep(1000);
+  }
+}
+
+bool CPVREpgs::LoadSettings()
+{
+  m_bIgnoreDbForClient = g_guiSettings.GetBool("pvrepg.ignoredbforclient");
+  m_iUpdateTime        = g_guiSettings.GetInt ("pvrepg.epgupdate") * 60;
+  m_iLingerTime        = g_guiSettings.GetInt ("pvrmenu.lingertime") * 60;
+  m_iDisplayTime       = g_guiSettings.GetInt ("pvrmenu.daystodisplay") * 24 * 60 * 60;
+
+  return true;
+}
+
+bool CPVREpgs::RemoveOldEntries()
+{
+  bool bReturn = false;
+  CLog::Log(LOGINFO, "PVREpgs - %s - removing old EPG entries",
+      __FUNCTION__);
+
+  CPVRDatabase *database = g_PVRManager.GetTVDatabase();
+  CDateTime now = CDateTime::GetCurrentDateTime();
+
+  if (!database->Open())
+  {
+    CLog::Log(LOGERROR, "PVREpgs - %s - cannot open the database",
+        __FUNCTION__);
+    return bReturn;
+  }
+
+  /* call Cleanup() on all known EPG tables */
+  for (unsigned int iEpgPtr = 0; iEpgPtr < size(); iEpgPtr++)
+  {
+    at(iEpgPtr)->Cleanup(now);
+  }
+
+  /* remove the old entries from the database */
+  bReturn = database->EraseOldEpgEntries();
+
+  if (bReturn)
+    CDateTime::GetCurrentDateTime().GetAsTime(m_iLastEpgCleanup);
+
+  return bReturn;
+}
+
+bool CPVREpgs::CreateChannelEpgs(void)
+{
+  for (int radio = 0; radio <= 1; radio++)
+  {
+    const CPVRChannels *channels = g_PVRChannels.Get(radio);
+    for (unsigned int iChannelPtr = 0; iChannelPtr < channels->size(); iChannelPtr++)
+    {
+      channels->at(iChannelPtr)->GetEPG();
+    }
+  }
+
+  return true;
+}
+
+bool CPVREpgs::LoadFromDb(bool bShowProgress /* = false */)
+{
+  if (m_bDatabaseLoaded)
+    return m_bDatabaseLoaded;
+
+  CPVRDatabase *database = g_PVRManager.GetTVDatabase();
+
+  /* show the progress bar */
+  CGUIDialogPVRUpdateProgressBar *scanner = NULL;
+  if (bShowProgress)
+  {
+    scanner = (CGUIDialogPVRUpdateProgressBar *)g_windowManager.GetWindow(WINDOW_DIALOG_EPG_SCAN);
+    scanner->Show();
+    scanner->SetHeader(g_localizeStrings.Get(19004));
+  }
+
+  /* open the database */
+  database->Open();
+
+  /* load all EPG tables */
+  bool bLoaded = false;
+  unsigned int iSize = size();
+  for (unsigned int iEpgPtr = 0; iEpgPtr < iSize; iEpgPtr++)
+  {
+    CPVREpg *epg = at(iEpgPtr);
+    CPVRChannel *channel = epg->Channel();
+
+    if (epg->LoadFromDb())
+    {
+      if (channel)
+        channel->UpdateEPGPointers();
+
+      bLoaded = true;
+    }
+
+    if (bShowProgress)
+    {
+      /* update the progress bar */
+      if (channel)
+        scanner->SetTitle(channel->ChannelName());
+
+      scanner->SetProgress(iEpgPtr, iSize);
+      scanner->UpdateState();
+    }
+  }
+
+  /* close the database */
+  database->Close();
+
+  if (bShowProgress)
+    scanner->Close();
+
+  m_bDatabaseLoaded = bLoaded;
+
+  return bLoaded;
+}
+
+bool CPVREpgs::UpdateEPG(bool bShowProgress /* = false */)
+{
+  long iStartTime                         = CTimeUtils::GetTimeMS();
+  int iEpgCount                           = size();
+  CPVRDatabase *database                   = g_PVRManager.GetTVDatabase();
+  bool bUpdateSuccess                     = true;
+  CGUIDialogPVRUpdateProgressBar *scanner = NULL;
+
+  /* set start and end time */
+  time_t start;
+  time_t end;
+  CDateTime::GetCurrentDateTime().GetAsTime(start); // NOTE: XBMC stores the EPG times as local time
+  end = start;
+  start -= m_iLingerTime;
+
+  if (!m_bDatabaseLoaded)
+  {
+    CLog::Log(LOGNOTICE, "PVREpgs - %s - loading initial EPG entries for %i tables from clients",
+        __FUNCTION__, iEpgCount);
+    end += 60 * 60 * 3; // load 3 hours
+  }
+  else
+  {
+    CLog::Log(LOGNOTICE, "PVREpgs - %s - starting EPG update for %i tables (update time = %d)",
+        __FUNCTION__, iEpgCount, m_iUpdateTime);
+    end += m_iDisplayTime;
+  }
+
+  /* show the progress bar */
+  if (bShowProgress)
+  {
+    scanner = (CGUIDialogPVRUpdateProgressBar *)g_windowManager.GetWindow(WINDOW_DIALOG_EPG_SCAN);
+    scanner->Show();
+    scanner->SetHeader(g_localizeStrings.Get(19004));
+  }
+
+  /* open the database */
+  database->Open();
+
+  /* update all EPG tables */
+  for (unsigned int iEpgPtr = 0; iEpgPtr < size(); iEpgPtr++)
+  {
+    /* interrupt the update on exit */
+    if (m_bStop)
+    {
+      bUpdateSuccess = false;
+      break;
+    }
+
+    CPVREpg *epg = at(iEpgPtr);
+
+    bUpdateSuccess = epg->Update(start, end, !m_bIgnoreDbForClient) && bUpdateSuccess;
+
+    if (bShowProgress)
+    {
+      /* update the progress bar */
+      scanner->SetProgress(iEpgPtr, iEpgCount);
+      scanner->SetTitle(epg->Channel()->ChannelName());
+      scanner->UpdateState();
+    }
+  }
+
+  /* update the last scan time if the update was successful and if we did a full update */
+  if (bUpdateSuccess && m_bDatabaseLoaded)
+  {
+    database->UpdateLastEpgScanTime();
+    CDateTime::GetCurrentDateTime().GetAsTime(m_iLastEpgUpdate);
+  }
+  database->Close();
+
+  if (!m_bDatabaseLoaded)
+  {
+    UpdateAllChannelEPGPointers();
+    m_bDatabaseLoaded = true;
+  }
+
+  if (bShowProgress)
+    scanner->Close();
+
+  long lUpdateTime = CTimeUtils::GetTimeMS() - iStartTime;
+  CLog::Log(LOGINFO, "PVREpgs - %s - finished updating the EPG after %li.%li seconds",
+      __FUNCTION__, lUpdateTime / 1000, lUpdateTime % 1000);
+
+  return bUpdateSuccess;
+}
+
+bool CPVREpgs::UpdateAllChannelEPGPointers()
+{
+  for (unsigned int epgPtr = 0; epgPtr < PVREpgs.size(); epgPtr++)
+  {
+    CPVRChannel *channel = PVREpgs.at(epgPtr)->Channel();
+    if (channel)
+      channel->UpdateEPGPointers();
+  }
+
+  CDateTime::GetCurrentDateTime().GetAsTime(m_iLastPointerUpdate);
+
+  return true;
+}
+
+int CPVREpgs::GetEPGAll(CFileItemList* results, bool bRadio /* = false */)
+{
+  int iInitialSize = results->Size();
+
+  for (unsigned int iEpgPtr = 0; iEpgPtr < size(); iEpgPtr++)
+  {
+    CPVREpg *epg = at(iEpgPtr);
+    CPVRChannel *channel = at(iEpgPtr)->Channel();
+    if (!channel || channel->IsRadio() != bRadio)
+      continue;
+
+    epg->Get(results);
+  }
+
+  return results->Size() - iInitialSize;
+}
+
+void CPVREpgs::UpdateFirstAndLastEPGDates(const CPVREpgInfoTag &tag)
+{
+  if (!tag.ChannelTag())
+    return;
+
+  if (tag.ChannelTag()->IsRadio())
+  {
+    if (tag.Start() < m_RadioFirst)
+      m_RadioFirst = tag.Start();
+
+    if (tag.End() > m_RadioLast)
+      m_RadioLast = tag.End();
+  }
+  else
+  {
+    if (tag.Start() < m_TVFirst)
+      m_TVFirst = tag.Start();
+
+    if (tag.End() > m_TVLast)
+      m_TVLast = tag.End();
+  }
+}
+
+CDateTime CPVREpgs::GetFirstEPGDate(bool bRadio /* = false */)
+{
+  return bRadio ? m_RadioFirst : m_TVFirst;
+}
+
+CDateTime CPVREpgs::GetLastEPGDate(bool bRadio /* = false */)
+{
+  return bRadio ? m_RadioLast : m_TVLast;
+}
+
+int CPVREpgs::GetEPGSearch(CFileItemList* results, const PVREpgSearchFilter &filter)
+{
+  for (unsigned int iEpgPtr = 0; iEpgPtr < size(); iEpgPtr++)
+  {
+    CPVREpg *epg = at(iEpgPtr);
+    epg->Get(results, filter);
+  }
+
+  /* filter recordings */
+  if (filter.m_bIgnorePresentRecordings && PVRRecordings.size() > 0)
+  {
+    for (unsigned int iRecordingPtr = 0; iRecordingPtr < PVRRecordings.size(); iRecordingPtr++)
+    {
+      for (int iResultPtr = 0; iResultPtr < results->Size(); iResultPtr++)
+      {
+        const CPVREpgInfoTag *epgentry  = results->Get(iResultPtr)->GetEPGInfoTag();
+        CPVRRecordingInfoTag *recording = &PVRRecordings[iRecordingPtr];
+        if (epgentry)
+        {
+          if (epgentry->Title()       != recording->Title() ||
+              epgentry->PlotOutline() != recording->PlotOutline() ||
+              epgentry->Plot()        != recording->Plot())
+            continue;
+
+          results->Remove(iResultPtr);
+          iResultPtr--;
+        }
+      }
+    }
+  }
+
+  /* filter timers */
+  if (filter.m_bIgnorePresentTimers && PVRTimers.size() > 0)
+  {
+    for (unsigned int iTimerPtr = 0; iTimerPtr < PVRTimers.size(); iTimerPtr++)
+    {
+      for (int iResultPtr = 0; iResultPtr < results->Size(); iResultPtr++)
+      {
+        const CPVREpgInfoTag *epgentry = results->Get(iResultPtr)->GetEPGInfoTag();
+        CPVRTimerInfoTag *timer        = &PVRTimers[iTimerPtr];
+        if (epgentry)
+        {
+          if (epgentry->ChannelTag()->ChannelNumber() != timer->ChannelNumber() ||
+              epgentry->Start()                       <  timer->Start() ||
+              epgentry->End()                         >  timer->Stop())
+            continue;
+
+          results->Remove(iResultPtr);
+          iResultPtr--;
+        }
+      }
+    }
+  }
+
+  /* remove duplicate entries */
+  if (filter.m_bPreventRepeats)
+  {
+    unsigned int iSize = results->Size();
+    for (unsigned int iResultPtr = 0; iResultPtr < iSize; iResultPtr++)
+    {
+      const CPVREpgInfoTag *epgentry_1 = results->Get(iResultPtr)->GetEPGInfoTag();
+      for (unsigned int iTagPtr = 0; iTagPtr < iSize; iTagPtr++)
+      {
+        const CPVREpgInfoTag *epgentry_2 = results->Get(iTagPtr)->GetEPGInfoTag();
+        if (iResultPtr == iTagPtr)
+          continue;
+
+        if (epgentry_1->Title()       != epgentry_2->Title() ||
+            epgentry_1->Plot()        != epgentry_2->Plot() ||
+            epgentry_1->PlotOutline() != epgentry_2->PlotOutline())
+          continue;
+
+        results->Remove(iTagPtr);
+        iSize = results->Size();
+        iResultPtr--;
+        iTagPtr--;
+      }
+    }
+  }
+
+  return results->Size();
+}
+
+int CPVREpgs::GetEPGNow(CFileItemList* results, bool bRadio)
+{
+  CPVRChannels *channels = (CPVRChannels *) g_PVRChannels.Get(bRadio);
+  int iInitialSize       = results->Size();
+
+  for (unsigned int iChannelPtr = 0; iChannelPtr < channels->size(); iChannelPtr++)
+  {
+    CPVRChannel *channel = channels->GetByIndex(iChannelPtr);
+    CPVREpg *epg = channel->GetEPG();
+    if (!epg->HasValidEntries() || epg->IsUpdateRunning())
+      continue;
+
+    const CPVREpgInfoTag *epgNow = epg->InfoTagNow();
+    if (!epgNow)
+    {
+      continue;
+    }
+
+    CFileItemPtr entry(new CFileItem(*epgNow));
+    entry->SetLabel2(epgNow->Start().GetAsLocalizedTime("", false));
+    entry->m_strPath = channel->ChannelName();
+    entry->SetThumbnailImage(channel->IconPath());
+    results->Add(entry);
+  }
+
+  return results->Size() - iInitialSize;
+}
+
+int CPVREpgs::GetEPGNext(CFileItemList* results, bool bRadio)
+{
+  CPVRChannels *channels = (CPVRChannels *) g_PVRChannels.Get(bRadio);
+  int iInitialSize       = results->Size();
+
+  for (unsigned int iChannelPtr = 0; iChannelPtr < channels->size(); iChannelPtr++)
+  {
+    CPVRChannel *channel = channels->GetByIndex(iChannelPtr);
+    CPVREpg *epg = channel->GetEPG();
+    if (!epg->HasValidEntries() || epg->IsUpdateRunning())
+      continue;
+
+    const CPVREpgInfoTag *epgNext = epg->InfoTagNext();
+    if (!epgNext)
+    {
+      continue;
+    }
+
+    CFileItemPtr entry(new CFileItem(*epgNext));
+    entry->SetLabel2(epgNext->Start().GetAsLocalizedTime("", false));
+    entry->m_strPath = channel->ChannelName();
+    entry->SetThumbnailImage(channel->IconPath());
+    results->Add(entry);
+  }
+
+  return results->Size() - iInitialSize;
+}
diff --git a/xbmc/pvr/PVREpgs.h b/xbmc/pvr/PVREpgs.h
new file mode 100644 (file)
index 0000000..d05f8d8
--- /dev/null
@@ -0,0 +1,138 @@
+#pragma once
+
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "DateTime.h"
+#include "Thread.h"
+#include "CriticalSection.h"
+#include "Observer.h"
+#include "../addons/include/xbmc_pvr_types.h"
+#include "utils/Thread.h"
+
+class CPVREpg;
+class CPVREpgInfoTag;
+class CPVRChannel;
+class CPVRDatabase;
+class CFileItemList;
+struct PVREpgSearchFilter;
+
+class CPVREpgs : public std::vector<CPVREpg*>,
+                 public Observer,
+                 private CThread
+{
+  friend class CPVREpg;
+
+private:
+  /* config settings */
+  bool             m_bIgnoreDbForClient;      /* don't save the EPG data in the database */
+  int              m_iLingerTime;             /* hours to keep old EPG data */
+  int              m_iDisplayTime;            /* hours of EPG data to fetch */
+  int              m_iUpdateTime;             /* update the full EPG after this period */
+
+  /* cached data */
+  CDateTime        m_RadioFirst;              /* the earliest EPG date in our radio channel tables */
+  CDateTime        m_RadioLast;               /* the latest EPG date in our radio channel tables */
+  CDateTime        m_TVFirst;                 /* the earliest EPG date in our tv channel tables */
+  CDateTime        m_TVLast;                  /* the latest EPG date in our tv channel tables */
+
+  /* class state properties */
+  bool             m_bDatabaseLoaded;         /* true if we already loaded the EPG from the database */
+  time_t           m_iLastEpgUpdate;          /* the time the EPG was updated */
+  time_t           m_iLastEpgCleanup;         /* the time the EPG was cleaned up */
+  time_t           m_iLastPointerUpdate;      /* the time the now playing pointers were updated */
+
+  /**
+   * Load the EPG for a channel using the pvr client
+   */
+  bool GrabEPGForChannelFromClient(const CPVRChannel &channel, CPVREpg *epg, time_t start, time_t end);
+
+  /**
+   * Load the EPG for a channel using a scraper
+   */
+  bool GrabEPGForChannelFromScraper(const CPVRChannel &channel, CPVREpg *epg, time_t start, time_t end);
+
+  /**
+   * Load the EPG for a channel using the pvr client or scraper
+   */
+  bool GrabEPGForChannel(const CPVRChannel &channel, CPVREpg *epg, time_t start, time_t end);
+
+  /**
+   * Load the EPG settings
+   */
+  bool LoadSettings();
+
+  /**
+   * Remove old EPG entries
+   */
+  bool RemoveOldEntries();
+
+  /**
+   * Update the last and first EPG date cache
+   */
+  void UpdateFirstAndLastEPGDates(const CPVREpgInfoTag &tag);
+
+  /**
+   * Update the EPG pointers for all channels
+   */
+  bool UpdateAllChannelEPGPointers();
+
+  /**
+   * Loads and updates the EPG data
+   */
+  bool UpdateEPG(bool bShowProgress = false);
+
+  /**
+   * Load all EPG entries from the database
+   */
+  bool LoadFromDb(bool bShowProgress = false);
+
+  /**
+   * Clear all EPG entries
+   */
+  void Clear(bool bClearDb = false);
+
+  /**
+   * Create an EPG table for each channel
+   */
+  bool CreateChannelEpgs(void);
+
+protected:
+  virtual void Process();
+
+public:
+  CPVREpgs();
+  virtual ~CPVREpgs();
+
+  void Start();
+  bool Stop();
+  bool Reset(bool bClearDb = false);
+  void Notify(const Observable &obs, const CStdString& msg);
+
+  int GetEPGSearch(CFileItemList* results, const PVREpgSearchFilter &filter);
+  int GetEPGAll(CFileItemList* results, bool bRadio = false);
+  int GetEPGNow(CFileItemList* results, bool bRadio = false);
+  int GetEPGNext(CFileItemList* results, bool bRadio = false);
+  CDateTime GetFirstEPGDate(bool bRadio = false);
+  CDateTime GetLastEPGDate(bool bRadio = false);
+};
+
+extern CPVREpgs PVREpgs;
diff --git a/xbmc/pvr/PVRManager.cpp b/xbmc/pvr/PVRManager.cpp
new file mode 100644 (file)
index 0000000..559934d
--- /dev/null
@@ -0,0 +1,1974 @@
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "Application.h"
+#include "GUISettings.h"
+#include "Util.h"
+#include "GUIWindowTV.h"
+#include "GUIWindowManager.h"
+#include "utils/GUIInfoManager.h"
+#ifdef HAS_VIDEO_PLAYBACK
+#include "cores/VideoRenderers/RenderManager.h"
+#endif
+#include "utils/log.h"
+#include "LocalizeStrings.h"
+#include "FileSystem/File.h"
+#include "StringUtils.h"
+#include "utils/TimeUtils.h"
+#include "MusicInfoTag.h"
+#include "Settings.h"
+
+/* GUI Messages includes */
+#include "GUIDialogOK.h"
+#include "GUIDialogProgress.h"
+#include "GUIDialogSelect.h"
+
+#include "PVRManager.h"
+#include "PVRChannelGroups.h"
+#include "PVRChannelGroup.h"
+#include "PVRChannelsContainer.h"
+#include "PVREpgInfoTag.h"
+#include "PVRTimerInfoTag.h"
+
+#define CHANNELCHECKDELTA     600 // seconds before checking for changes inside channels list
+#define RECORDINGCHECKDELTA   450 // seconds before checking for changes inside recordings list
+#define TIMERCHECKDELTA       60  // seconds before checking for changes inside timers list
+
+using namespace std;
+using namespace XFILE;
+using namespace MUSIC_INFO;
+using namespace ADDON;
+
+/**********************************************************************
+ * BEGIN OF CLASS **___ CPVRManager __________________________________*
+ **                                                                  **/
+
+/********************************************************************
+ * CPVRManager constructor
+ *
+ * It creates the PVRManager, which mostly handle all PVR related
+ * operations for XBMC
+ ********************************************************************/
+CPVRManager::CPVRManager()
+{
+  InitializeCriticalSection(&m_critSection);
+  m_bFirstStart = true;
+  CLog::Log(LOGDEBUG,"PVR: created");
+}
+
+/********************************************************************
+ * CPVRManager destructor
+ *
+ * Destroy this class
+ ********************************************************************/
+CPVRManager::~CPVRManager()
+{
+  /* First stop and remove any clients */
+  if (!m_clients.empty())
+    Stop();
+
+  DeleteCriticalSection(&m_critSection);
+  CLog::Log(LOGDEBUG,"PVR: destroyed");
+}
+
+void CPVRManager::Restart(void)
+{
+  Stop();
+  Start();
+}
+
+/********************************************************************
+ * CPVRManager Start
+ *
+ * PVRManager Startup
+ ********************************************************************/
+void CPVRManager::Start()
+{
+  /* First stop and remove any clients */
+  if (!m_clients.empty())
+    Stop();
+
+  /* Check if TV is enabled under Settings->Video->TV->Enable */
+  if (!g_guiSettings.GetBool("pvrmanager.enabled"))
+    return;
+
+  CLog::Log(LOGNOTICE, "PVR: PVRManager starting");
+
+  /* Reset Member variables and System Info swap counters */
+  m_hasRecordings           = false;
+  m_isRecording             = false;
+  m_hasTimers               = false;
+  m_CurrentGroupID          = -1;
+  m_currentPlayingChannel   = NULL;
+  m_currentPlayingRecording = NULL;
+  m_PreviousChannel[0]      = -1;
+  m_PreviousChannel[1]      = -1;
+  m_PreviousChannelIndex    = 0;
+  m_infoToggleStart         = NULL;
+  m_infoToggleCurrent       = 0;
+  m_recordingToggleStart    = NULL;
+  m_recordingToggleCurrent  = 0;
+  m_LastChannel             = 0;
+  m_bChannelScanRunning     = false;
+
+  /* Discover, load and create chosen Client add-on's. */
+  CAddonMgr::Get().RegisterAddonMgrCallback(ADDON_PVRDLL, this);
+  if (!LoadClients())
+  {
+    CLog::Log(LOGERROR, "PVR: couldn't load any clients");
+    return;
+  }
+
+  /* Create the supervisor thread to do all background activities */
+  Create();
+  SetName("XBMC PVR Supervisor");
+  SetPriority(-15);
+  CLog::Log(LOGNOTICE, "PVR: PVRManager started. Clients loaded = %u", (unsigned int) m_clients.size());
+  return;
+}
+
+/********************************************************************
+ * CPVRManager Stop
+ *
+ * PVRManager shutdown
+ ********************************************************************/
+void CPVRManager::Stop()
+{
+  CLog::Log(LOGNOTICE, "PVR: PVRManager stopping");
+  PVREpgs.Stop();
+  StopThread();
+
+  for (CLIENTMAPITR itr = m_clients.begin(); itr != m_clients.end(); itr++)
+  {
+    CLog::Log(LOGINFO, "PVR: sending destroy to addon:%s, GUID:%s", m_clients[(*itr).first]->Name().c_str(), m_clients[(*itr).first]->ID().c_str());
+
+    m_clients[(*itr).first]->Destroy();
+  }
+
+  m_clients.clear();
+  m_clientsProps.clear();
+  return;
+}
+
+/********************************************************************
+ * CPVRManager LoadClients
+ *
+ * Load the client drivers and doing the startup.
+ ********************************************************************/
+bool CPVRManager::LoadClients()
+{
+  /* Get all PVR Add on's */
+  VECADDONS addons;
+  if (!CAddonMgr::Get().GetAddons(ADDON_PVRDLL, addons, true))
+    return false;
+
+  m_database.Open();
+
+  /* load the clients */
+  for (unsigned i=0; i < addons.size(); i++)
+  {
+    const AddonPtr clientAddon = addons.at(i);
+    if (!clientAddon->Enabled())
+      continue;
+
+    /* Add client to TV-Database to identify different backend types,
+     * if client is already added his id is given.
+     */
+    long clientID = m_database.AddClient(clientAddon->Name(), clientAddon->ID());
+    if (clientID == -1)
+    {
+      CLog::Log(LOGERROR, "PVR: Can't Add/Get PVR Client '%s' to to TV Database", clientAddon->Name().c_str());
+      continue;
+    }
+
+    /* Load the Client libraries, and inside them into client list if
+     * success. Client initialization is also performed during loading.
+     */
+    boost::shared_ptr<CPVRClient> addon = boost::dynamic_pointer_cast<CPVRClient>(clientAddon);
+    if (addon && addon->Create(clientID, this))
+    {
+      m_clients.insert(std::make_pair(clientID, addon));
+    }
+  }
+
+  m_database.Close();
+
+  // Request each client's basic properties
+  GetClientProperties();
+
+  return !m_clients.empty();
+}
+
+/********************************************************************
+ * CPVRManager GetClientProperties
+ *
+ * Load the client Properties for every client
+ ********************************************************************/
+void CPVRManager::GetClientProperties()
+{
+  m_clientsProps.clear();
+  CLIENTMAPITR itr = m_clients.begin();
+  while (itr != m_clients.end())
+  {
+    GetClientProperties((*itr).first);
+    itr++;
+  }
+}
+
+/********************************************************************
+ * CPVRManager GetClientProperties
+ *
+ * Load the client Properties for the given client ID in the
+ * Properties list
+ ********************************************************************/
+void CPVRManager::GetClientProperties(long clientID)
+{
+  PVR_SERVERPROPS props;
+  if (m_clients[clientID]->GetProperties(&props) == PVR_ERROR_NO_ERROR)
+  {
+    // store the client's properties
+    m_clientsProps.insert(std::make_pair(clientID, props));
+  }
+}
+
+/********************************************************************
+ * CPVRManager GetFirstClientID
+ *
+ * Returns the first loaded client ID
+ ********************************************************************/
+unsigned long CPVRManager::GetFirstClientID()
+{
+  CLIENTMAPITR itr = m_clients.begin();
+  return m_clients[(*itr).first]->GetID();
+}
+
+/********************************************************************
+ * CPVRManager OnClientMessage
+ *
+ * Callback function from Client driver to inform about changed
+ * timers, channels, recordings or epg.
+ ********************************************************************/
+void CPVRManager::OnClientMessage(const long clientID, const PVR_EVENT clientEvent, const char* msg)
+{
+  /* here the manager reacts to messages sent from any of the clients via the IPVRClientCallback */
+  CStdString clientName = m_clients[clientID]->GetBackendName() + ":" + m_clients[clientID]->GetConnectionString();
+  switch (clientEvent)
+  {
+    case PVR_EVENT_UNKNOWN:
+      CLog::Log(LOGDEBUG, "%s - PVR: client_%ld unknown event : %s", __FUNCTION__, clientID, msg);
+      break;
+
+    case PVR_EVENT_TIMERS_CHANGE:
+      {
+        CLog::Log(LOGDEBUG, "%s - PVR: client_%ld timers changed", __FUNCTION__, clientID);
+        PVRTimers.Update();
+        UpdateRecordingsCache();
+
+        CGUIWindowTV *pTVWin = (CGUIWindowTV *)g_windowManager.GetWindow(WINDOW_TV);
+        if (pTVWin)
+        pTVWin->UpdateData(TV_WINDOW_TIMERS);
+      }
+      break;
+
+    case PVR_EVENT_RECORDINGS_CHANGE:
+      {
+        CLog::Log(LOGDEBUG, "%s - PVR: client_%ld recording list changed", __FUNCTION__, clientID);
+        UpdateRecordingsCache();
+
+        CGUIWindowTV *pTVWin = (CGUIWindowTV *)g_windowManager.GetWindow(WINDOW_TV);
+        if (pTVWin)
+        pTVWin->UpdateData(TV_WINDOW_RECORDINGS);
+      }
+      break;
+
+    case PVR_EVENT_CHANNELS_CHANGE:
+      {
+        CLog::Log(LOGDEBUG, "%s - PVR: client_%ld channel list changed", __FUNCTION__, clientID);
+        UpdateRecordingsCache();
+
+        CGUIWindowTV *pTVWin = (CGUIWindowTV *)g_windowManager.GetWindow(WINDOW_TV);
+        if (pTVWin)
+        {
+          pTVWin->UpdateData(TV_WINDOW_CHANNELS_TV);
+          pTVWin->UpdateData(TV_WINDOW_CHANNELS_RADIO);
+        }
+      }
+      break;
+
+    default:
+      break;
+  }
+}
+
+/********************************************************************
+ * CPVRManager RequestRestart
+ *
+ * Restart a client driver
+ ********************************************************************/
+bool CPVRManager::RequestRestart(AddonPtr addon, bool datachanged)
+{
+  if (!addon)
+    return false;
+
+  CLog::Log(LOGINFO, "PVR: requested restart of clientName:%s, clientGUID:%s", addon->Name().c_str(), addon->ID().c_str());
+  CLIENTMAPITR itr = m_clients.begin();
+  while (itr != m_clients.end())
+  {
+    if (m_clients[(*itr).first]->ID() == addon->ID())
+    {
+      if (m_clients[(*itr).first]->Name() == addon->Name())
+      {
+        CLog::Log(LOGINFO, "PVR: restarting clientName:%s, clientGUID:%s", addon->Name().c_str(), addon->ID().c_str());
+        StopThread();
+        m_clients[(*itr).first]->ReCreate();
+        Create();
+      }
+    }
+    itr++;
+  }
+  return true;
+}
+
+/********************************************************************
+ * CPVRManager RequestRemoval
+ *
+ * Unload a client driver
+ ********************************************************************/
+bool CPVRManager::RequestRemoval(AddonPtr addon)
+{
+  if (!addon)
+    return false;
+
+  CLog::Log(LOGINFO, "PVR: requested removal of clientName:%s, clientGUID:%s", addon->Name().c_str(), addon->ID().c_str());
+  CLIENTMAPITR itr = m_clients.begin();
+  while (itr != m_clients.end())
+  {
+    if (m_clients[(*itr).first]->ID() == addon->ID())
+    {
+      if (m_clients[(*itr).first]->Name() == addon->Name())
+      {
+        CLog::Log(LOGINFO, "PVR: removing clientName:%s, clientGUID:%s", addon->Name().c_str(), addon->ID().c_str());
+        StopThread();
+
+        m_clients[(*itr).first]->Destroy();
+        m_clients.erase((*itr).first);
+        if (!m_clients.empty())
+          Create();
+
+        return true;
+      }
+    }
+    itr++;
+  }
+
+  return false;
+}
+
+
+/*************************************************************/
+/** INTERNAL FUNCTIONS                                      **/
+/*************************************************************/
+
+bool CPVRManager::ContinueLastChannel()
+{
+  CLog::Log(LOGNOTICE,"PVR: Try to continue last channel");
+  m_bFirstStart = false;
+  bool bReturn = false;
+
+  m_database.Open();
+  int lastChannel = m_database.GetLastChannel();
+  m_database.Close();
+
+  if (lastChannel > 0)
+  {
+    CPVRChannel *tag = CPVRChannels::GetByChannelIDFromAll(lastChannel);
+    if (!tag)
+      return false;
+
+    const CPVRChannels *channels = g_PVRChannels.Get(tag->IsRadio());
+
+    if (g_guiSettings.GetInt("pvrplayback.startlast") == START_LAST_CHANNEL_MIN)
+      g_settings.m_bStartVideoWindowed = true;
+
+    if (g_application.PlayFile(CFileItem(*channels->at(tag->ChannelNumber()-1))))
+    {
+      CLog::Log(LOGNOTICE,"PVR: Continuing channel '%s'", tag->ChannelName().c_str());
+      bReturn = true;
+    }
+    else
+    {
+      CLog::Log(LOGERROR,"PVR: Can't continue playback on channel '%s'", tag->ChannelName().c_str());
+    }
+  }
+  else
+  {
+    CLog::Log(LOGNOTICE,"PVR: Can't find channel (ID=%i) to continue playback on at startup", lastChannel);
+  }
+
+  return bReturn;
+}
+
+/*************************************************************/
+/** PVRManager Update and control thread                    **/
+/*************************************************************/
+
+void CPVRManager::Process()
+{
+  ((CPVRChannels *) g_PVRChannels.GetTV())->Load();    /* Load the TV channels */
+  ((CPVRChannels *) g_PVRChannels.GetRadio())->Load(); /* Load the radio channels */
+  PVRChannelGroupsTV.Load(false);                      /* Load the TV channel group lists */
+  PVRChannelGroupsRadio.Load(true);                    /* Load the radio Channel group lists */
+
+  /* Continue last watched channel after first startup */
+  if (m_bFirstStart && g_guiSettings.GetInt("pvrplayback.startlast") != START_LAST_CHANNEL_OFF)
+    ContinueLastChannel();
+
+  PVRTimers.Load();     /* Get timers from the backends */
+  PVRRecordings.Load(); /* Get recordings from the backend */
+  PVREpgs.Start(); /* Start the EPG thread */
+
+  int Now = CTimeUtils::GetTimeMS()/1000;
+  m_LastTVChannelCheck     = Now;
+  m_LastRadioChannelCheck  = Now+CHANNELCHECKDELTA/2;
+  m_LastRecordingsCheck    = Now;
+  m_LastTimersCheck        = Now;
+
+  /* main loop */
+  while (!m_bStop)
+  {
+    Now = CTimeUtils::GetTimeMS()/1000;
+
+    /* Check for new or updated TV Channels */
+    if (Now - m_LastTVChannelCheck > CHANNELCHECKDELTA) // don't do this too often
+    {
+      CLog::Log(LOGDEBUG,"PVR: Updating TV Channel list");
+      ((CPVRChannels *) g_PVRChannels.GetTV())->Update();
+      m_LastTVChannelCheck = Now;
+    }
+
+    /* Check for new or updated Radio Channels */
+    if (Now - m_LastRadioChannelCheck > CHANNELCHECKDELTA) // don't do this too often
+    {
+      CLog::Log(LOGDEBUG,"PVR: Updating Radio Channel list");
+      ((CPVRChannels *) g_PVRChannels.GetTV())->Update();
+      m_LastRadioChannelCheck = Now;
+    }
+
+    /* Check for new or updated Recordings */
+    if (Now - m_LastRecordingsCheck > RECORDINGCHECKDELTA) // don't do this too often
+    {
+      CLog::Log(LOGDEBUG,"PVR: Updating Recordings list");
+      PVRRecordings.Update(true);
+      m_LastRecordingsCheck = Now;
+    }
+
+    /* Check for new or updated Timers */
+    if (Now - m_LastTimersCheck > TIMERCHECKDELTA) // don't do this too often
+    {
+      CLog::Log(LOGDEBUG,"PVR: Updating Timers list");
+      PVRTimers.Update();
+      UpdateRecordingsCache();
+      CGUIWindowTV *pTVWin = (CGUIWindowTV *)g_windowManager.GetWindow(WINDOW_TV);
+      if (pTVWin)
+        pTVWin->UpdateData(TV_WINDOW_TIMERS);
+      m_LastTimersCheck = Now;
+    }
+
+    EnterCriticalSection(&m_critSection);
+
+    /* Get Signal information of the current playing channel */
+    if (m_currentPlayingChannel && g_guiSettings.GetBool("pvrplayback.signalquality") && !m_currentPlayingChannel->GetPVRChannelInfoTag()->IsVirtual())
+    {
+      m_clients[m_currentPlayingChannel->GetPVRChannelInfoTag()->ClientID()]->SignalQuality(m_qualityInfo);
+    }
+    LeaveCriticalSection(&m_critSection);
+
+    Sleep(1000);
+  }
+
+  /* if a channel or recording is playing stop it first */
+  if (m_currentPlayingChannel || m_currentPlayingRecording)
+    g_application.StopPlaying();
+
+  PVREpgs.Stop(); /* Stop the EPG thread */
+
+  /* unload the rest */
+  PVRRecordings.Unload();
+  PVRTimers.Unload();
+  PVRChannelGroupsTV.Unload();
+  PVRChannelGroupsRadio.Unload();
+  ((CPVRChannels *) g_PVRChannels.GetRadio())->Unload();
+  ((CPVRChannels *) g_PVRChannels.GetTV())->Unload();
+}
+
+
+/*************************************************************/
+/** GUIInfoManager FUNCTIONS                                **/
+/*************************************************************/
+
+/********************************************************************
+ * CPVRManager UpdateRecordingsCache
+ *
+ * Updates the recordings and the "now" and "next" timers
+ ********************************************************************/
+void CPVRManager::UpdateRecordingsCache()
+{
+  EnterCriticalSection(&m_critSection);
+
+  PVRRecordings.GetNumRecordings() > 0 ? m_hasRecordings = true : m_hasRecordings = false;
+  PVRTimers.GetNumTimers()         > 0 ? m_hasTimers     = true : m_hasTimers = false;
+  m_isRecording = false;
+  m_NowRecording.clear();
+  m_NextRecording = NULL;
+
+  if (m_hasTimers)
+  {
+    CDateTime now = CDateTime::GetCurrentDateTime();
+    for (unsigned int i = 0; i < PVRTimers.size(); ++i)
+    {
+      CPVRTimerInfoTag *timerTag = &PVRTimers[i];
+      if (timerTag->Active() && timerTag->Start() <= now && timerTag->Stop() > now)
+      {
+        m_NowRecording.push_back(timerTag);
+        m_isRecording = true;
+      }
+      else if (timerTag->Active())
+      {
+        if (!m_NextRecording || m_NextRecording->Start() > timerTag->Start())
+          m_NextRecording = timerTag;
+      }
+    }
+  }
+
+  LeaveCriticalSection(&m_critSection);
+}
+
+/********************************************************************
+ * CPVRManager TranslateCharInfo
+ *
+ * Returns a GUIInfoManager Character String
+ ********************************************************************/
+#define INFO_TOGGLE_TIME    1500
+const char* CPVRManager::TranslateCharInfo(DWORD dwInfo)
+{
+  if      (dwInfo == PVR_NOW_RECORDING_TITLE)
+  {
+    if (m_recordingToggleStart == 0)
+    {
+      UpdateRecordingsCache();
+      m_recordingToggleStart = CTimeUtils::GetTimeMS();
+      m_recordingToggleCurrent = 0;
+    }
+    else
+    {
+      if (CTimeUtils::GetTimeMS() - m_recordingToggleStart > INFO_TOGGLE_TIME)
+      {
+        UpdateRecordingsCache();
+        if (m_NowRecording.size() > 0)
+        {
+          m_recordingToggleCurrent++;
+          if (m_recordingToggleCurrent > m_NowRecording.size()-1)
+            m_recordingToggleCurrent = 0;
+
+          m_recordingToggleStart = CTimeUtils::GetTimeMS();
+        }
+      }
+    }
+
+    if (m_NowRecording.size() >= m_recordingToggleCurrent + 1)
+      return m_NowRecording[m_recordingToggleCurrent]->Title();
+    else
+      return "";
+  }
+  else if (dwInfo == PVR_NOW_RECORDING_CHANNEL)
+  {
+    if (m_NowRecording.size() > 0)
+    {
+      CPVRTimerInfoTag * timerTag = m_NowRecording[m_recordingToggleCurrent];
+      return timerTag ? timerTag->ChannelName() : "";
+    }
+    else
+      return "";
+  }
+  else if (dwInfo == PVR_NOW_RECORDING_DATETIME)
+  {
+    if (m_NowRecording.size() > 0)
+    {
+      CPVRTimerInfoTag *timerTag = m_NowRecording[m_recordingToggleCurrent];
+      return timerTag ? timerTag->Start().GetAsLocalizedDateTime(false, false) : "";
+    }
+    else
+      return "";
+  }
+  else if (dwInfo == PVR_NEXT_RECORDING_TITLE)    return m_NextRecording ? m_NextRecording->Title() : "";
+  else if (dwInfo == PVR_NEXT_RECORDING_CHANNEL)  return m_NextRecording ? m_NextRecording->ChannelName() : "";
+  else if (dwInfo == PVR_NEXT_RECORDING_DATETIME) return m_NextRecording ? m_NextRecording->Start().GetAsLocalizedDateTime(false, false) : "";
+  else if (dwInfo == PVR_BACKEND_NAME)            return m_backendName;
+  else if (dwInfo == PVR_BACKEND_VERSION)         return m_backendVersion;
+  else if (dwInfo == PVR_BACKEND_HOST)            return m_backendHost;
+  else if (dwInfo == PVR_BACKEND_DISKSPACE)       return m_backendDiskspace;
+  else if (dwInfo == PVR_BACKEND_CHANNELS)        return m_backendChannels;
+  else if (dwInfo == PVR_BACKEND_TIMERS)          return m_backendTimers;
+  else if (dwInfo == PVR_BACKEND_RECORDINGS)      return m_backendRecordings;
+  else if (dwInfo == PVR_BACKEND_NUMBER)
+  {
+    if (m_infoToggleStart == 0)
+    {
+      m_infoToggleStart = CTimeUtils::GetTimeMS();
+      m_infoToggleCurrent = 0;
+    }
+    else
+    {
+      if (CTimeUtils::GetTimeMS() - m_infoToggleStart > INFO_TOGGLE_TIME)
+      {
+        if (m_clients.size() > 0)
+        {
+          m_infoToggleCurrent++;
+          if (m_infoToggleCurrent > m_clients.size()-1)
+            m_infoToggleCurrent = 0;
+
+          CLIENTMAPITR itr = m_clients.begin();
+          for (unsigned int i = 0; i < m_infoToggleCurrent; i++)
+            itr++;
+
+          long long kBTotal = 0;
+          long long kBUsed  = 0;
+          if (m_clients[(*itr).first]->GetDriveSpace(&kBTotal, &kBUsed) == PVR_ERROR_NO_ERROR)
+          {
+            kBTotal /= 1024; // Convert to MBytes
+            kBUsed /= 1024;  // Convert to MBytes
+            m_backendDiskspace.Format("%s %.1f GByte - %s: %.1f GByte", g_localizeStrings.Get(20161), (float) kBTotal / 1024, g_localizeStrings.Get(20162), (float) kBUsed / 1024);
+          }
+          else
+          {
+            m_backendDiskspace = g_localizeStrings.Get(19055);
+          }
+
+          int NumChannels = m_clients[(*itr).first]->GetNumChannels();
+          if (NumChannels >= 0)
+            m_backendChannels.Format("%i", NumChannels);
+          else
+            m_backendChannels = g_localizeStrings.Get(161);
+
+          int NumTimers = m_clients[(*itr).first]->GetNumTimers();
+          if (NumTimers >= 0)
+            m_backendTimers.Format("%i", NumTimers);
+          else
+            m_backendTimers = g_localizeStrings.Get(161);
+
+          int NumRecordings = m_clients[(*itr).first]->GetNumRecordings();
+          if (NumRecordings >= 0)
+            m_backendRecordings.Format("%i", NumRecordings);
+          else
+            m_backendRecordings = g_localizeStrings.Get(161);
+
+          m_backendName         = m_clients[(*itr).first]->GetBackendName();
+          m_backendVersion      = m_clients[(*itr).first]->GetBackendVersion();
+          m_backendHost         = m_clients[(*itr).first]->GetConnectionString();
+        }
+        else
+        {
+          m_backendName         = "";
+          m_backendVersion      = "";
+          m_backendHost         = "";
+          m_backendDiskspace    = "";
+          m_backendTimers       = "";
+          m_backendRecordings   = "";
+          m_backendChannels     = "";
+        }
+        m_infoToggleStart = CTimeUtils::GetTimeMS();
+      }
+    }
+
+    static CStdString backendClients;
+    if (m_clients.size() > 0)
+      backendClients.Format("%u %s %u", m_infoToggleCurrent+1, g_localizeStrings.Get(20163), m_clients.size());
+    else
+      backendClients = g_localizeStrings.Get(14023);
+
+    return backendClients;
+  }
+  else if (dwInfo == PVR_TOTAL_DISKSPACE)
+  {
+    long long kBTotal = 0;
+    long long kBUsed  = 0;
+    CLIENTMAPITR itr = m_clients.begin();
+    while (itr != m_clients.end())
+    {
+      long long clientKBTotal = 0;
+      long long clientKBUsed  = 0;
+
+      if (m_clients[(*itr).first]->GetDriveSpace(&clientKBTotal, &clientKBUsed) == PVR_ERROR_NO_ERROR)
+      {
+        kBTotal += clientKBTotal;
+        kBUsed += clientKBUsed;
+      }
+      itr++;
+    }
+    kBTotal /= 1024; // Convert to MBytes
+    kBUsed /= 1024;  // Convert to MBytes
+    m_totalDiskspace.Format("%s %0.1f GByte - %s: %0.1f GByte", g_localizeStrings.Get(20161), (float) kBTotal / 1024, g_localizeStrings.Get(20162), (float) kBUsed / 1024);
+    return m_totalDiskspace;
+  }
+  else if (dwInfo == PVR_NEXT_TIMER)
+  {
+    CPVRTimerInfoTag *next = PVRTimers.GetNextActiveTimer();
+    if (next != NULL)
+    {
+      m_nextTimer.Format("%s %s %s %s", g_localizeStrings.Get(19106)
+                         , next->Start().GetAsLocalizedDate(true)
+                         , g_localizeStrings.Get(19107)
+                         , next->Start().GetAsLocalizedTime("HH:mm", false));
+      return m_nextTimer;
+    }
+  }
+  else if (dwInfo == PVR_PLAYING_DURATION)
+  {
+    m_playingDuration = StringUtils::SecondsToTimeString(GetTotalTime()/1000, TIME_FORMAT_GUESS);
+    return m_playingDuration.c_str();
+  }
+  else if (dwInfo == PVR_PLAYING_TIME)
+  {
+    m_playingTime = StringUtils::SecondsToTimeString(GetStartTime()/1000, TIME_FORMAT_GUESS);
+    return m_playingTime.c_str();
+  }
+  else if (dwInfo == PVR_ACTUAL_STREAM_VIDEO_BR)
+  {
+    static CStdString strBitrate = "";
+    if (m_qualityInfo.video_bitrate > 0)
+      strBitrate.Format("%.2f Mbit/s", m_qualityInfo.video_bitrate);
+    return strBitrate;
+  }
+  else if (dwInfo == PVR_ACTUAL_STREAM_AUDIO_BR)
+  {
+    CStdString strBitrate = "";
+    if (m_qualityInfo.audio_bitrate > 0)
+      strBitrate.Format("%.0f kbit/s", m_qualityInfo.audio_bitrate);
+    return strBitrate;
+  }
+  else if (dwInfo == PVR_ACTUAL_STREAM_DOLBY_BR)
+  {
+    CStdString strBitrate = "";
+    if (m_qualityInfo.dolby_bitrate > 0)
+      strBitrate.Format("%.0f kbit/s", m_qualityInfo.dolby_bitrate);
+    return strBitrate;
+  }
+  else if (dwInfo == PVR_ACTUAL_STREAM_SIG)
+  {
+    CStdString strSignal = "";
+    if (m_qualityInfo.signal > 0)
+      strSignal.Format("%d %%", m_qualityInfo.signal / 655);
+    return strSignal;
+  }
+  else if (dwInfo == PVR_ACTUAL_STREAM_SNR)
+  {
+    CStdString strSNR = "";
+    if (m_qualityInfo.snr > 0)
+      strSNR.Format("%d %%", m_qualityInfo.snr / 655);
+    return strSNR;
+  }
+  else if (dwInfo == PVR_ACTUAL_STREAM_BER)
+  {
+    CStdString strBER;
+    strBER.Format("%08X", m_qualityInfo.ber);
+    return strBER;
+  }
+  else if (dwInfo == PVR_ACTUAL_STREAM_UNC)
+  {
+    CStdString strUNC;
+    strUNC.Format("%08X", m_qualityInfo.unc);
+    return strUNC;
+  }
+  else if (dwInfo == PVR_ACTUAL_STREAM_CLIENT)
+  {
+    return m_playingClientName;
+  }
+  else if (dwInfo == PVR_ACTUAL_STREAM_DEVICE)
+  {
+    CStdString string = m_qualityInfo.frontend_name;
+    if (string == "")
+      return g_localizeStrings.Get(13205);
+    else
+      return string;
+  }
+  else if (dwInfo == PVR_ACTUAL_STREAM_STATUS)
+  {
+    CStdString string = m_qualityInfo.frontend_status;
+    if (string == "")
+      return g_localizeStrings.Get(13205);
+    else
+      return string;
+  }
+  else if (dwInfo == PVR_ACTUAL_STREAM_CRYPTION)
+  {
+    if (m_currentPlayingChannel)
+      return m_currentPlayingChannel->GetPVRChannelInfoTag()->EncryptionName();
+  }
+  return "";
+}
+
+/********************************************************************
+ * CPVRManager TranslateIntInfo
+ *
+ * Returns a GUIInfoManager integer value
+ ********************************************************************/
+int CPVRManager::TranslateIntInfo(DWORD dwInfo)
+{
+  if (dwInfo == PVR_PLAYING_PROGRESS)
+  {
+    return (float)(((float)GetStartTime() / GetTotalTime()) * 100);
+  }
+  else if (dwInfo == PVR_ACTUAL_STREAM_SIG_PROGR)
+  {
+    return (float)(((float)m_qualityInfo.signal / 0xFFFF) * 100);
+  }
+  else if (dwInfo == PVR_ACTUAL_STREAM_SNR_PROGR)
+  {
+    return (float)(((float)m_qualityInfo.snr / 0xFFFF) * 100);
+  }
+  return 0;
+}
+
+/********************************************************************
+ * CPVRManager TranslateBoolInfo
+ *
+ * Returns a GUIInfoManager boolean value
+ ********************************************************************/
+bool CPVRManager::TranslateBoolInfo(DWORD dwInfo)
+{
+  if (dwInfo == PVR_IS_RECORDING)
+    return m_isRecording;
+  else if (dwInfo == PVR_HAS_TIMER)
+    return m_hasTimers;
+  else if (dwInfo == PVR_IS_PLAYING_TV)
+    return IsPlayingTV();
+  else if (dwInfo == PVR_IS_PLAYING_RADIO)
+    return IsPlayingRadio();
+  else if (dwInfo == PVR_IS_PLAYING_RECORDING)
+    return IsPlayingRecording();
+  else if (dwInfo == PVR_ACTUAL_STREAM_ENCRYPTED)
+  {
+    if (m_currentPlayingChannel)
+      return m_currentPlayingChannel->GetPVRChannelInfoTag()->IsEncrypted();
+  }
+
+  return false;
+}
+
+
+/*************************************************************/
+/** GENERAL FUNCTIONS                                       **/
+/*************************************************************/
+
+void CPVRManager::StartChannelScan()
+{
+  std::vector<long> clients;
+  long scanningClientID = -1;
+  m_bChannelScanRunning = true;
+
+  CLIENTMAPITR itr = m_clients.begin();
+  while (itr != m_clients.end())
+  {
+    if (m_clients[(*itr).first]->ReadyToUse() && GetClientProps(m_clients[(*itr).first]->GetID())->SupportChannelScan)
+    {
+      clients.push_back(m_clients[(*itr).first]->GetID());
+    }
+    itr++;
+  }
+
+  if (clients.size() > 1)
+  {
+    CGUIDialogSelect* pDialog= (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
+
+    pDialog->Reset();
+    pDialog->SetHeading(19119);
+
+    for (unsigned int i = 0; i < clients.size(); i++)
+    {
+      pDialog->Add(m_clients[clients[i]]->GetBackendName() + ":" + m_clients[clients[i]]->GetConnectionString());
+    }
+
+    pDialog->DoModal();
+
+    int selection = pDialog->GetSelectedLabel();
+    if (selection >= 0)
+    {
+      scanningClientID = clients[selection];
+    }
+
+  }
+  else if (clients.size() == 1)
+  {
+    scanningClientID = clients[0];
+  }
+
+  if (scanningClientID < 0)
+  {
+    CGUIDialogOK::ShowAndGetInput(19033,0,19192,0);
+    return;
+  }
+
+  CLog::Log(LOGNOTICE,"PVR: Starting to scan for channels on client %s:%s", m_clients[scanningClientID]->GetBackendName().c_str(), m_clients[scanningClientID]->GetConnectionString().c_str());
+  long perfCnt = CTimeUtils::GetTimeMS();
+
+  if (m_currentPlayingRecording || m_currentPlayingChannel)
+  {
+    CLog::Log(LOGNOTICE,"PVR: Is playing data, stopping playback");
+    g_application.StopPlaying();
+  }
+  /* Stop the supervisor thread */
+  StopThread();
+
+  if (m_clients[scanningClientID]->StartChannelScan() != PVR_ERROR_NO_ERROR)
+  {
+    CGUIDialogOK::ShowAndGetInput(19111,0,19193,0);
+  }
+
+  /* Create the supervisor thread again */
+  Create();
+  CLog::Log(LOGNOTICE, "PVR: Channel scan finished after %li.%li seconds", (CTimeUtils::GetTimeMS()-perfCnt)/1000, (CTimeUtils::GetTimeMS()-perfCnt)%1000);
+  m_bChannelScanRunning = false;
+}
+
+void CPVRManager::ResetDatabase()
+{
+  CLog::Log(LOGNOTICE,"PVR: TV Database is now set to it's initial state");
+
+  CGUIDialogProgress* pDlgProgress = (CGUIDialogProgress*)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
+  pDlgProgress->SetLine(0, "");
+  pDlgProgress->SetLine(1, g_localizeStrings.Get(19186));
+  pDlgProgress->SetLine(2, "");
+  pDlgProgress->StartModal();
+  pDlgProgress->Progress();
+
+  if (m_currentPlayingRecording || m_currentPlayingChannel)
+  {
+    CLog::Log(LOGNOTICE,"PVR: Is playing data, stopping playback");
+    g_application.StopPlaying();
+  }
+  pDlgProgress->SetPercentage(10);
+
+  Stop();
+  pDlgProgress->SetPercentage(20);
+
+  m_database.Open();
+  m_database.EraseEpg();
+  pDlgProgress->SetPercentage(30);
+
+  m_database.EraseChannelGroups(false);
+  pDlgProgress->SetPercentage(50);
+
+  m_database.EraseChannelGroups(true);
+  pDlgProgress->SetPercentage(60);
+
+  m_database.EraseChannels();
+  pDlgProgress->SetPercentage(70);
+
+  m_database.EraseChannelSettings();
+  pDlgProgress->SetPercentage(80);
+
+  m_database.EraseClients();
+  pDlgProgress->SetPercentage(90);
+
+  m_database.Close();
+  CLog::Log(LOGNOTICE,"PVR: TV Database reset finished, starting PVR Subsystem again");
+  Start();
+  PVREpgs.Start();
+  pDlgProgress->SetPercentage(100);
+  pDlgProgress->Close();
+}
+
+void CPVRManager::ResetEPG()
+{
+  PVREpgs.Reset(true);
+}
+
+bool CPVRManager::IsPlayingTV()
+{
+  if (!m_currentPlayingChannel)
+    return false;
+
+  return !m_currentPlayingChannel->GetPVRChannelInfoTag()->IsRadio();
+}
+
+bool CPVRManager::IsPlayingRadio()
+{
+  if (!m_currentPlayingChannel)
+    return false;
+
+  return m_currentPlayingChannel->GetPVRChannelInfoTag()->IsRadio();
+}
+
+bool CPVRManager::IsPlayingRecording()
+{
+  if (m_currentPlayingRecording)
+    return false;
+  else
+    return true;
+}
+
+PVR_SERVERPROPS *CPVRManager::GetCurrentClientProps()
+{
+  if (m_currentPlayingChannel)
+    return &m_clientsProps[m_currentPlayingChannel->GetPVRChannelInfoTag()->ClientID()];
+  else if (m_currentPlayingRecording)
+    return &m_clientsProps[m_currentPlayingRecording->GetPVRRecordingInfoTag()->ClientID()];
+  else
+    return NULL;
+}
+
+long CPVRManager::GetCurrentPlayingClientID()
+{
+  if (m_currentPlayingChannel)
+    return m_currentPlayingChannel->GetPVRChannelInfoTag()->ClientID();
+  else if (m_currentPlayingRecording)
+    return m_currentPlayingRecording->GetPVRRecordingInfoTag()->ClientID();
+  else
+    return -1;
+}
+
+PVR_STREAMPROPS *CPVRManager::GetCurrentStreamProps()
+{
+  if (m_currentPlayingChannel)
+  {
+    int cid = m_currentPlayingChannel->GetPVRChannelInfoTag()->ClientID();
+    m_clients[cid]->GetStreamProperties(&m_streamProps[cid]);
+
+    return &m_streamProps[cid];
+  }
+  else
+    return NULL;
+}
+
+CFileItem *CPVRManager::GetCurrentPlayingItem()
+{
+  if (m_currentPlayingChannel)
+    return m_currentPlayingChannel;
+  else if (m_currentPlayingRecording)
+    return m_currentPlayingRecording;
+  else
+    return NULL;
+}
+
+CStdString CPVRManager::GetCurrentInputFormat()
+{
+  if (m_currentPlayingChannel)
+    return m_currentPlayingChannel->GetPVRChannelInfoTag()->InputFormat();
+
+  return "";
+}
+
+bool CPVRManager::GetCurrentChannel(const CPVRChannel *channel)
+{
+  if (m_currentPlayingChannel)
+  {
+    channel = m_currentPlayingChannel->GetPVRChannelInfoTag();
+    CLog::Log(LOGDEBUG,"%s - current channel '%s'", __FUNCTION__, channel->ChannelName().c_str());
+    return true;
+  }
+  else
+  {
+    CLog::Log(LOGDEBUG,"%s - no current channel set", __FUNCTION__);
+    channel = NULL;
+    return false;
+  }
+}
+
+bool CPVRManager::GetCurrentChannel(int *number, bool *radio)
+{
+  if (m_currentPlayingChannel)
+  {
+    if (number)
+      *number = m_currentPlayingChannel->GetPVRChannelInfoTag()->ChannelNumber();
+    if (radio)
+      *radio  = m_currentPlayingChannel->GetPVRChannelInfoTag()->IsRadio();
+    return true;
+  }
+  else
+  {
+    if (number)
+      *number = 1;
+    if (radio)
+      *radio  = false;
+    return false;
+  }
+}
+
+bool CPVRManager::HaveActiveClients()
+{
+  if (m_clients.empty())
+    return false;
+
+  int ready = 0;
+  CLIENTMAPITR itr = m_clients.begin();
+  while (itr != m_clients.end())
+  {
+    if (m_clients[(*itr).first]->ReadyToUse())
+      ready++;
+    itr++;
+  }
+  return ready > 0 ? true : false;
+}
+
+bool CPVRManager::HaveMenuHooks(long clientID)
+{
+  if (clientID < 0)
+    clientID = GetCurrentPlayingClientID();
+  if (clientID < 0)
+    return false;
+  return m_clients[clientID]->HaveMenuHooks();
+}
+
+void CPVRManager::ProcessMenuHooks(long clientID)
+{
+  if (m_clients[clientID]->HaveMenuHooks())
+  {
+    PVR_MENUHOOKS *hooks = m_clients[clientID]->GetMenuHooks();
+    std::vector<long> hookIDs;
+
+    CGUIDialogSelect* pDialog = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
+
+    pDialog->Reset();
+    pDialog->SetHeading(19196);
+
+    for (unsigned int i = 0; i < hooks->size(); i++)
+    {
+      pDialog->Add(m_clients[clientID]->GetString(hooks->at(i).string_id));
+    }
+
+    pDialog->DoModal();
+
+    int selection = pDialog->GetSelectedLabel();
+    if (selection >= 0)
+    {
+      m_clients[clientID]->CallMenuHook(hooks->at(selection));
+    }
+  }
+}
+
+int CPVRManager::GetPreviousChannel()
+{
+  if (m_currentPlayingChannel == NULL)
+    return -1;
+
+  int LastChannel = m_currentPlayingChannel->GetPVRChannelInfoTag()->ChannelNumber();
+
+  if ((m_PreviousChannel[m_PreviousChannelIndex ^ 1] == LastChannel || LastChannel != m_PreviousChannel[0]) && LastChannel != m_PreviousChannel[1])
+    m_PreviousChannelIndex ^= 1;
+
+  return m_PreviousChannel[m_PreviousChannelIndex ^= 1];
+}
+
+bool CPVRManager::CanRecordInstantly()
+{
+  if (!m_currentPlayingChannel)
+    return false;
+
+  const CPVRChannel* tag = m_currentPlayingChannel->GetPVRChannelInfoTag();
+  if (m_clientsProps[tag->ClientID()].SupportTimers)
+    return true;
+  else
+    return false;
+}
+
+bool CPVRManager::IsRecordingOnPlayingChannel()
+{
+  if (!m_currentPlayingChannel)
+    return false;
+
+  const CPVRChannel* tag = m_currentPlayingChannel->GetPVRChannelInfoTag();
+  return tag->IsRecording();
+}
+
+bool CPVRManager::StartRecordingOnPlayingChannel(bool bOnOff)
+{
+  if (!m_currentPlayingChannel)
+    return false;
+
+  CPVRChannel* tag = m_currentPlayingChannel->GetPVRChannelInfoTag();
+  if (m_clientsProps[tag->ClientID()].SupportTimers)
+  {
+    const CPVRChannels *channels = g_PVRChannels.Get(m_currentPlayingChannel->GetPVRChannelInfoTag()->IsRadio());
+
+    if (bOnOff && tag->IsRecording() == false)
+    {
+      CPVRTimerInfoTag *newtimer = CPVRTimerInfoTag::InstantTimer();
+      newtimer->SetTitle(tag->ChannelName());
+      CFileItem *item = new CFileItem(*newtimer);
+
+      if (!CPVRTimers::AddTimer(*item))
+      {
+        CGUIDialogOK::ShowAndGetInput(19033,0,19164,0);
+        return false;
+      }
+
+      channels->at(tag->ChannelNumber()-1)->SetRecording(true); /* Set in channel list */
+      tag->SetRecording(true);                          /* and also in current playing item */
+      return true;
+    }
+    else if (tag->IsRecording() == true)
+    {
+      for (unsigned int i = 0; i < PVRTimers.size(); ++i)
+      {
+        if (!PVRTimers[i].IsRepeating() && PVRTimers[i].Active() &&
+            (PVRTimers[i].Number() == tag->ChannelNumber()) &&
+            (PVRTimers[i].Start() <= CDateTime::GetCurrentDateTime()) &&
+            (PVRTimers[i].Stop() >= CDateTime::GetCurrentDateTime()))
+        {
+          if (CPVRTimers::DeleteTimer(PVRTimers[i], true))
+          {
+            channels->at(tag->ChannelNumber()-1)->SetRecording(false);  /* Set in channel list */
+            tag->SetRecording(false);                           /* and also in current playing item */
+            return true;
+          }
+        }
+      }
+    }
+  }
+  return false;
+}
+
+void CPVRManager::SaveCurrentChannelSettings()
+{
+  if (m_currentPlayingChannel)
+  {
+    // save video settings
+    if (g_settings.m_currentVideoSettings != g_settings.m_defaultVideoSettings)
+    {
+      m_database.Open();
+      m_database.SetChannelSettings(*m_currentPlayingChannel->GetPVRChannelInfoTag(), g_settings.m_currentVideoSettings);
+      m_database.Close();
+    }
+  }
+}
+
+void CPVRManager::LoadCurrentChannelSettings()
+{
+  if (m_currentPlayingChannel)
+  {
+    CVideoSettings loadedChannelSettings;
+
+    // Switch to default options
+    g_settings.m_currentVideoSettings = g_settings.m_defaultVideoSettings;
+
+    m_database.Open();
+    if (m_database.GetChannelSettings(*m_currentPlayingChannel->GetPVRChannelInfoTag(), loadedChannelSettings))
+    {
+      if (loadedChannelSettings.m_AudioDelay != g_settings.m_currentVideoSettings.m_AudioDelay)
+      {
+        g_settings.m_currentVideoSettings.m_AudioDelay = loadedChannelSettings.m_AudioDelay;
+
+        if (g_application.m_pPlayer)
+          g_application.m_pPlayer->SetAVDelay(g_settings.m_currentVideoSettings.m_AudioDelay);
+      }
+
+      if (loadedChannelSettings.m_AudioStream != g_settings.m_currentVideoSettings.m_AudioStream)
+      {
+        g_settings.m_currentVideoSettings.m_AudioStream = loadedChannelSettings.m_AudioStream;
+
+        // only change the audio stream if a different one has been asked for
+        if (g_application.m_pPlayer->GetAudioStream() != g_settings.m_currentVideoSettings.m_AudioStream)
+        {
+          g_application.m_pPlayer->SetAudioStream(g_settings.m_currentVideoSettings.m_AudioStream);    // Set the audio stream to the one selected
+        }
+      }
+
+      if (loadedChannelSettings.m_Brightness != g_settings.m_currentVideoSettings.m_Brightness)
+      {
+        g_settings.m_currentVideoSettings.m_Brightness = loadedChannelSettings.m_Brightness;
+      }
+
+      if (loadedChannelSettings.m_Contrast != g_settings.m_currentVideoSettings.m_Contrast)
+      {
+        g_settings.m_currentVideoSettings.m_Contrast = loadedChannelSettings.m_Contrast;
+      }
+
+      if (loadedChannelSettings.m_Gamma != g_settings.m_currentVideoSettings.m_Gamma)
+      {
+        g_settings.m_currentVideoSettings.m_Gamma = loadedChannelSettings.m_Gamma;
+      }
+
+      if (loadedChannelSettings.m_Crop != g_settings.m_currentVideoSettings.m_Crop)
+      {
+        g_settings.m_currentVideoSettings.m_Crop = loadedChannelSettings.m_Crop;
+        // AutoCrop changes will get picked up automatically by dvdplayer
+      }
+
+      if (loadedChannelSettings.m_CropLeft != g_settings.m_currentVideoSettings.m_CropLeft)
+      {
+        g_settings.m_currentVideoSettings.m_CropLeft = loadedChannelSettings.m_CropLeft;
+      }
+
+      if (loadedChannelSettings.m_CropRight != g_settings.m_currentVideoSettings.m_CropRight)
+      {
+        g_settings.m_currentVideoSettings.m_CropRight = loadedChannelSettings.m_CropRight;
+      }
+
+      if (loadedChannelSettings.m_CropTop != g_settings.m_currentVideoSettings.m_CropTop)
+      {
+        g_settings.m_currentVideoSettings.m_CropTop = loadedChannelSettings.m_CropTop;
+      }
+
+      if (loadedChannelSettings.m_CropBottom != g_settings.m_currentVideoSettings.m_CropBottom)
+      {
+        g_settings.m_currentVideoSettings.m_CropBottom = loadedChannelSettings.m_CropBottom;
+      }
+
+      if (loadedChannelSettings.m_VolumeAmplification != g_settings.m_currentVideoSettings.m_VolumeAmplification)
+      {
+        g_settings.m_currentVideoSettings.m_VolumeAmplification = loadedChannelSettings.m_VolumeAmplification;
+
+        if (g_application.m_pPlayer)
+          g_application.m_pPlayer->SetDynamicRangeCompression((long)(g_settings.m_currentVideoSettings.m_VolumeAmplification * 100));
+      }
+
+      if (loadedChannelSettings.m_OutputToAllSpeakers != g_settings.m_currentVideoSettings.m_OutputToAllSpeakers)
+      {
+        g_settings.m_currentVideoSettings.m_OutputToAllSpeakers = loadedChannelSettings.m_OutputToAllSpeakers;
+      }
+
+      if (loadedChannelSettings.m_ViewMode != g_settings.m_currentVideoSettings.m_ViewMode)
+      {
+        g_settings.m_currentVideoSettings.m_ViewMode = loadedChannelSettings.m_ViewMode;
+
+        g_renderManager.SetViewMode(g_settings.m_currentVideoSettings.m_ViewMode);
+        g_settings.m_currentVideoSettings.m_CustomZoomAmount = g_settings.m_fZoomAmount;
+        g_settings.m_currentVideoSettings.m_CustomPixelRatio = g_settings.m_fPixelRatio;
+      }
+
+      if (loadedChannelSettings.m_CustomPixelRatio != g_settings.m_currentVideoSettings.m_CustomPixelRatio)
+      {
+        g_settings.m_currentVideoSettings.m_CustomPixelRatio = loadedChannelSettings.m_CustomPixelRatio;
+      }
+
+      if (loadedChannelSettings.m_CustomZoomAmount != g_settings.m_currentVideoSettings.m_CustomZoomAmount)
+      {
+        g_settings.m_currentVideoSettings.m_CustomZoomAmount = loadedChannelSettings.m_CustomZoomAmount;
+      }
+
+      if (loadedChannelSettings.m_NoiseReduction != g_settings.m_currentVideoSettings.m_NoiseReduction)
+      {
+        g_settings.m_currentVideoSettings.m_NoiseReduction = loadedChannelSettings.m_NoiseReduction;
+      }
+
+      if (loadedChannelSettings.m_Sharpness != g_settings.m_currentVideoSettings.m_Sharpness)
+      {
+        g_settings.m_currentVideoSettings.m_Sharpness = loadedChannelSettings.m_Sharpness;
+      }
+
+      if (loadedChannelSettings.m_SubtitleDelay != g_settings.m_currentVideoSettings.m_SubtitleDelay)
+      {
+        g_settings.m_currentVideoSettings.m_SubtitleDelay = loadedChannelSettings.m_SubtitleDelay;
+
+        g_application.m_pPlayer->SetSubTitleDelay(g_settings.m_currentVideoSettings.m_SubtitleDelay);
+      }
+
+      if (loadedChannelSettings.m_SubtitleOn != g_settings.m_currentVideoSettings.m_SubtitleOn)
+      {
+        g_settings.m_currentVideoSettings.m_SubtitleOn = loadedChannelSettings.m_SubtitleOn;
+
+        g_application.m_pPlayer->SetSubtitleVisible(g_settings.m_currentVideoSettings.m_SubtitleOn);
+      }
+
+      if (loadedChannelSettings.m_SubtitleStream != g_settings.m_currentVideoSettings.m_SubtitleStream)
+      {
+        g_settings.m_currentVideoSettings.m_SubtitleStream = loadedChannelSettings.m_SubtitleStream;
+
+        g_application.m_pPlayer->SetSubtitle(g_settings.m_currentVideoSettings.m_SubtitleStream);
+      }
+
+      if (loadedChannelSettings.m_InterlaceMethod != g_settings.m_currentVideoSettings.m_InterlaceMethod)
+      {
+        g_settings.m_currentVideoSettings.m_InterlaceMethod = loadedChannelSettings.m_InterlaceMethod;
+      }
+    }
+    m_database.Close();
+  }
+}
+
+void CPVRManager::SetPlayingGroup(int GroupId)
+{
+  m_CurrentGroupID = GroupId;
+}
+
+void CPVRManager::ResetQualityData()
+{
+  if (g_guiSettings.GetBool("pvrplayback.signalquality"))
+  {
+    strncpy(m_qualityInfo.frontend_name, g_localizeStrings.Get(13205).c_str(), 1024);
+    strncpy(m_qualityInfo.frontend_status, g_localizeStrings.Get(13205).c_str(), 1024);
+  }
+  else
+  {
+    strncpy(m_qualityInfo.frontend_name, g_localizeStrings.Get(13106).c_str(), 1024);
+    strncpy(m_qualityInfo.frontend_status, g_localizeStrings.Get(13106).c_str(), 1024);
+  }
+  m_qualityInfo.snr           = 0;
+  m_qualityInfo.signal        = 0;
+  m_qualityInfo.ber           = 0;
+  m_qualityInfo.unc           = 0;
+  m_qualityInfo.video_bitrate = 0;
+  m_qualityInfo.audio_bitrate = 0;
+  m_qualityInfo.dolby_bitrate = 0;
+}
+
+int CPVRManager::GetPlayingGroup()
+{
+  return m_CurrentGroupID;
+}
+
+void CPVRManager::TriggerRecordingsUpdate(bool force)
+{
+  m_LastRecordingsCheck = CTimeUtils::GetTimeMS()/1000-RECORDINGCHECKDELTA + (force ? 0 : 5);
+}
+
+void CPVRManager::TriggerTimersUpdate(bool force)
+{
+  m_LastTimersCheck = CTimeUtils::GetTimeMS()/1000-TIMERCHECKDELTA + (force ? 0 : 5);
+}
+
+bool CPVRManager::OpenLiveStream(const CPVRChannel* tag)
+{
+  if (tag == NULL)
+    return false;
+
+  EnterCriticalSection(&m_critSection);
+
+  CLog::Log(LOGDEBUG,"PVR: opening live stream on channel '%s'", tag->ChannelName().c_str());
+
+  /* Check if a channel or recording is already opened and clear it if yes */
+  if (m_currentPlayingChannel)
+    delete m_currentPlayingChannel;
+  if (m_currentPlayingRecording)
+    delete m_currentPlayingRecording;
+
+  /* Set the new channel information */
+  m_currentPlayingChannel   = new CFileItem(*tag);
+  m_currentPlayingRecording = NULL;
+  m_scanStart               = CTimeUtils::GetTimeMS();  /* Reset the stream scan timer */
+  ResetQualityData();
+
+  /* Open the stream on the Client */
+  if (tag->StreamURL().IsEmpty())
+  {
+    if (!m_clientsProps[tag->ClientID()].HandleInputStream ||
+        !m_clients[tag->ClientID()]->OpenLiveStream(*tag))
+    {
+      delete m_currentPlayingChannel;
+      m_currentPlayingChannel = NULL;
+      LeaveCriticalSection(&m_critSection);
+      return false;
+    }
+  }
+
+  /* Load now the new channel settings from Database */
+  LoadCurrentChannelSettings();
+
+  LeaveCriticalSection(&m_critSection);
+  return true;
+}
+
+bool CPVRManager::OpenRecordedStream(const CPVRRecordingInfoTag* tag)
+{
+  if (tag == NULL)
+    return false;
+
+  EnterCriticalSection(&m_critSection);
+
+  /* Check if a channel or recording is already opened and clear it if yes */
+  if (m_currentPlayingChannel)
+    delete m_currentPlayingChannel;
+  if (m_currentPlayingRecording)
+    delete m_currentPlayingRecording;
+
+  /* Set the new recording information */
+  m_currentPlayingRecording = new CFileItem(*tag);
+  m_currentPlayingChannel   = NULL;
+  m_scanStart               = CTimeUtils::GetTimeMS();  /* Reset the stream scan timer */
+  m_playingClientName       = m_clients[tag->ClientID()]->GetBackendName() + ":" + m_clients[tag->ClientID()]->GetConnectionString();
+
+  /* Open the recording stream on the Client */
+  bool ret = m_clients[tag->ClientID()]->OpenRecordedStream(*tag);
+
+  LeaveCriticalSection(&m_critSection);
+  return ret;
+}
+
+CStdString CPVRManager::GetLiveStreamURL(const CPVRChannel* tag)
+{
+  CStdString stream_url;
+
+  EnterCriticalSection(&m_critSection);
+
+  /* Check if a channel or recording is already opened and clear it if yes */
+  if (m_currentPlayingChannel)
+    delete m_currentPlayingChannel;
+  if (m_currentPlayingRecording)
+    delete m_currentPlayingRecording;
+
+  /* Set the new channel information */
+  m_currentPlayingChannel   = new CFileItem(*tag);
+  m_currentPlayingRecording = NULL;
+  m_scanStart               = CTimeUtils::GetTimeMS();  /* Reset the stream scan timer */
+  ResetQualityData();
+
+  /* Retrieve the dynamily generated stream URL from the Client */
+  stream_url = m_clients[tag->ClientID()]->GetLiveStreamURL(*tag);
+  if (stream_url.IsEmpty())
+  {
+    delete m_currentPlayingChannel;
+    m_currentPlayingChannel = NULL;
+    LeaveCriticalSection(&m_critSection);
+    return "";
+  }
+
+  LeaveCriticalSection(&m_critSection);
+
+  return stream_url;
+}
+
+void CPVRManager::CloseStream()
+{
+  EnterCriticalSection(&m_critSection);
+
+  if (m_currentPlayingChannel)
+  {
+    m_playingClientName = "";
+
+    /* Save channel number in database */
+    m_database.Open();
+    m_database.UpdateLastChannel(*m_currentPlayingChannel->GetPVRChannelInfoTag());
+    m_database.Close();
+
+    /* Store current settings inside Database */
+    SaveCurrentChannelSettings();
+
+    /* Set quality data to undefined defaults */
+    ResetQualityData();
+
+    /* Close the Client connection */
+    if ((m_currentPlayingChannel->GetPVRChannelInfoTag()->StreamURL().IsEmpty()) || (m_currentPlayingChannel->GetPVRChannelInfoTag()->StreamURL().compare(0,13, "pvr://stream/") == 0))
+      m_clients[m_currentPlayingChannel->GetPVRChannelInfoTag()->ClientID()]->CloseLiveStream();
+    delete m_currentPlayingChannel;
+    m_currentPlayingChannel = NULL;
+  }
+  else if (m_currentPlayingRecording)
+  {
+    /* Close the Client connection */
+    m_clients[m_currentPlayingRecording->GetPVRRecordingInfoTag()->ClientID()]->CloseRecordedStream();
+    delete m_currentPlayingRecording;
+    m_currentPlayingRecording = NULL;
+  }
+
+  LeaveCriticalSection(&m_critSection);
+  return;
+}
+
+int CPVRManager::ReadStream(void* lpBuf, int64_t uiBufSize)
+{
+  EnterCriticalSection(&m_critSection);
+
+  int bytesReaded = 0;
+
+  /* Check stream for available video or audio data, if after the scantime no stream
+     is present playback is canceled and returns to the window */
+  if (m_scanStart)
+  {
+    if (CTimeUtils::GetTimeMS() - m_scanStart > (unsigned int) g_guiSettings.GetInt("pvrplayback.scantime")*1000)
+    {
+      CLog::Log(LOGERROR,"PVR: No video or audio data available after %i seconds, playback stopped", g_guiSettings.GetInt("pvrplayback.scantime"));
+      LeaveCriticalSection(&m_critSection);
+      return 0;
+    }
+    else if (g_application.IsPlayingVideo() || g_application.IsPlayingAudio())
+      m_scanStart = NULL;
+  }
+
+  /* Process LiveTV Reading */
+  if (m_currentPlayingChannel)
+  {
+    bytesReaded = m_clients[m_currentPlayingChannel->GetPVRChannelInfoTag()->ClientID()]->ReadLiveStream(lpBuf, uiBufSize);
+  }
+  /* Process Recording Reading */
+  else if (m_currentPlayingRecording)
+  {
+    bytesReaded = m_clients[m_currentPlayingRecording->GetPVRRecordingInfoTag()->ClientID()]->ReadRecordedStream(lpBuf, uiBufSize);
+  }
+
+  LeaveCriticalSection(&m_critSection);
+  return bytesReaded;
+}
+
+void CPVRManager::DemuxReset()
+{
+  EnterCriticalSection(&m_critSection);
+  if (m_currentPlayingChannel)
+    m_clients[m_currentPlayingChannel->GetPVRChannelInfoTag()->ClientID()]->DemuxReset();
+  LeaveCriticalSection(&m_critSection);
+}
+
+void CPVRManager::DemuxAbort()
+{
+  EnterCriticalSection(&m_critSection);
+  if (m_currentPlayingChannel)
+    m_clients[m_currentPlayingChannel->GetPVRChannelInfoTag()->ClientID()]->DemuxAbort();
+  LeaveCriticalSection(&m_critSection);
+}
+
+void CPVRManager::DemuxFlush()
+{
+  EnterCriticalSection(&m_critSection);
+  if (m_currentPlayingChannel)
+    m_clients[m_currentPlayingChannel->GetPVRChannelInfoTag()->ClientID()]->DemuxFlush();
+  LeaveCriticalSection(&m_critSection);
+}
+
+DemuxPacket* CPVRManager::ReadDemuxStream()
+{
+  DemuxPacket* packet = NULL;
+
+  EnterCriticalSection(&m_critSection);
+  if (m_currentPlayingChannel)
+  {
+    packet = m_clients[m_currentPlayingChannel->GetPVRChannelInfoTag()->ClientID()]->DemuxRead();
+  }
+  LeaveCriticalSection(&m_critSection);
+  return packet;
+}
+
+int64_t CPVRManager::LengthStream(void)
+{
+  int64_t streamLength = 0;
+
+  EnterCriticalSection(&m_critSection);
+
+  if (m_currentPlayingChannel)
+  {
+    streamLength = 0;
+  }
+  else if (m_currentPlayingRecording)
+  {
+    streamLength = m_clients[m_currentPlayingRecording->GetPVRRecordingInfoTag()->ClientID()]->LengthRecordedStream();
+  }
+
+  LeaveCriticalSection(&m_critSection);
+  return streamLength;
+}
+
+int64_t CPVRManager::SeekStream(int64_t iFilePosition, int iWhence/* = SEEK_SET*/)
+{
+  int64_t streamNewPos = 0;
+
+  EnterCriticalSection(&m_critSection);
+
+  if (m_currentPlayingChannel)
+  {
+    streamNewPos = 0;
+  }
+  else if (m_currentPlayingRecording)
+  {
+    streamNewPos = m_clients[m_currentPlayingRecording->GetPVRRecordingInfoTag()->ClientID()]->SeekRecordedStream(iFilePosition, iWhence);
+  }
+
+  LeaveCriticalSection(&m_critSection);
+  return streamNewPos;
+}
+
+int64_t CPVRManager::GetStreamPosition()
+{
+  int64_t streamPos = 0;
+
+  EnterCriticalSection(&m_critSection);
+
+  if (m_currentPlayingChannel)
+  {
+    streamPos = 0;
+  }
+  else if (m_currentPlayingRecording)
+  {
+    streamPos = m_clients[m_currentPlayingRecording->GetPVRRecordingInfoTag()->ClientID()]->PositionRecordedStream();
+  }
+
+  LeaveCriticalSection(&m_critSection);
+  return streamPos;
+}
+
+bool CPVRManager::UpdateItem(CFileItem& item)
+{
+  /* Don't update if a recording is played */
+  if (item.IsPVRRecording())
+    return false;
+
+  if (!item.IsPVRChannel())
+  {
+    CLog::Log(LOGERROR, "CPVRManager: UpdateItem no TVChannelTag given!");
+    return false;
+  }
+
+  g_application.CurrentFileItem() = *m_currentPlayingChannel;
+  g_infoManager.SetCurrentItem(*m_currentPlayingChannel);
+
+  CPVRChannel* channelTag = item.GetPVRChannelInfoTag();
+  const CPVREpgInfoTag* epgTagNow = channelTag->GetEPGNow();
+
+  if (channelTag->IsRadio())
+  {
+    CMusicInfoTag* musictag = item.GetMusicInfoTag();
+    if (musictag)
+    {
+      musictag->SetTitle(epgTagNow->Title());
+      musictag->SetGenre(epgTagNow->Genre());
+      musictag->SetDuration(epgTagNow->GetDuration());
+      musictag->SetURL(channelTag->Path());
+      musictag->SetArtist(channelTag->ChannelName());
+    //    musictag->SetAlbum(epgTagNow->m_strBouquet);
+      musictag->SetAlbumArtist(channelTag->ChannelName());
+      musictag->SetLoaded(true);
+      musictag->SetComment("");
+      musictag->SetLyrics("");
+    }
+  }
+
+  CPVRChannel* tagPrev = item.GetPVRChannelInfoTag();
+  if (tagPrev && tagPrev->ChannelNumber() != m_LastChannel)
+  {
+    m_LastChannel         = tagPrev->ChannelNumber();
+    m_LastChannelChanged  = CTimeUtils::GetTimeMS();
+    if (channelTag->ClientID() == 999)
+      m_playingClientName = g_localizeStrings.Get(19209);
+    else if (!channelTag->IsVirtual())
+      m_playingClientName = m_clients[channelTag->ClientID()]->GetBackendName() + ":" + m_clients[channelTag->ClientID()]->GetConnectionString();
+    else
+      m_playingClientName = g_localizeStrings.Get(13205);
+  }
+  if (CTimeUtils::GetTimeMS() - m_LastChannelChanged >= (unsigned int) g_guiSettings.GetInt("pvrplayback.channelentrytimeout") && m_LastChannel != m_PreviousChannel[m_PreviousChannelIndex])
+     m_PreviousChannel[m_PreviousChannelIndex ^= 1] = m_LastChannel;
+
+  return false;
+}
+
+bool CPVRManager::ChannelSwitch(unsigned int iChannel)
+{
+  if (!m_currentPlayingChannel)
+    return false;
+
+  const CPVRChannels *channels = g_PVRChannels.Get(m_currentPlayingChannel->GetPVRChannelInfoTag()->IsRadio());
+
+  if (iChannel > channels->size()+1)
+  {
+    CGUIDialogOK::ShowAndGetInput(19033,19136,0,0);
+    return false;
+  }
+
+  EnterCriticalSection(&m_critSection);
+
+  const CPVRChannel* tag = channels->at(iChannel-1);
+
+  /* Store current settings inside Database */
+  SaveCurrentChannelSettings();
+
+  /* Perform Channelswitch */
+  if (tag->StreamURL().IsEmpty() && !m_clients[tag->ClientID()]->SwitchChannel(*tag))
+  {
+    CGUIDialogOK::ShowAndGetInput(19033,0,19136,0);
+    LeaveCriticalSection(&m_critSection);
+    return false;
+  }
+
+  /* Update the Playing channel data and the current epg data if it was not previewed */
+  delete m_currentPlayingChannel;
+  m_currentPlayingChannel = new CFileItem(*tag);
+
+  /* Reset the Audio/Video detection counter */
+  m_scanStart = CTimeUtils::GetTimeMS();
+
+  /* Load now the new channel settings from Database */
+  LoadCurrentChannelSettings();
+
+  /* Set quality data to undefined defaults */
+  ResetQualityData();
+
+  LeaveCriticalSection(&m_critSection);
+  return true;
+}
+
+bool CPVRManager::ChannelUp(unsigned int *newchannel, bool preview/* = false*/)
+{
+   if (m_currentPlayingChannel)
+   {
+     const CPVRChannels *channels = g_PVRChannels.Get(m_currentPlayingChannel->GetPVRChannelInfoTag()->IsRadio());
+
+    EnterCriticalSection(&m_critSection);
+
+    /* Store current settings inside Database */
+    SaveCurrentChannelSettings();
+
+    unsigned int currentTVChannel = m_currentPlayingChannel->GetPVRChannelInfoTag()->ChannelNumber();
+    const CPVRChannel* tag;
+    for (unsigned int i = 1; i < channels->size(); i++)
+    {
+      currentTVChannel += 1;
+
+      if (currentTVChannel > channels->size())
+        currentTVChannel = 1;
+
+      tag = channels->at(currentTVChannel-1);
+
+      if ((m_CurrentGroupID != -1) && (m_CurrentGroupID != tag->GroupID()))
+        continue;
+
+      /* Perform Channelswitch */
+      if (preview)
+      {
+        /* Update the Playing channel data and the current epg data */
+        delete m_currentPlayingChannel;
+        m_currentPlayingChannel = new CFileItem(*tag);
+
+        /* Load now the new channel settings from Database */
+        LoadCurrentChannelSettings();
+
+        *newchannel = currentTVChannel;
+        LeaveCriticalSection(&m_critSection);
+        return true;
+      }
+      else if (!tag->StreamURL().IsEmpty() || m_clients[tag->ClientID()]->SwitchChannel(*tag))
+      {
+        /* Update the Playing channel data and the current epg data */
+        delete m_currentPlayingChannel;
+        m_currentPlayingChannel = new CFileItem(*tag);
+        m_scanStart             = CTimeUtils::GetTimeMS();
+
+        /* Load now the new channel settings from Database */
+        LoadCurrentChannelSettings();
+
+        /* Set quality data to undefined defaults */
+        ResetQualityData();
+
+        *newchannel = currentTVChannel;
+        LeaveCriticalSection(&m_critSection);
+        return true;
+      }
+    }
+    LeaveCriticalSection(&m_critSection);
+  }
+
+  return false;
+}
+
+bool CPVRManager::ChannelDown(unsigned int *newchannel, bool preview/* = false*/)
+{
+  if (m_currentPlayingChannel)
+  {
+    const CPVRChannels *channels = g_PVRChannels.Get(m_currentPlayingChannel->GetPVRChannelInfoTag()->IsRadio());
+
+    EnterCriticalSection(&m_critSection);
+
+    /* Store current settings inside Database */
+    SaveCurrentChannelSettings();
+
+    int currentTVChannel = m_currentPlayingChannel->GetPVRChannelInfoTag()->ChannelNumber();
+    const CPVRChannel* tag;
+    for (unsigned int i = 1; i < channels->size(); i++)
+    {
+      currentTVChannel -= 1;
+
+      if (currentTVChannel <= 0)
+        currentTVChannel = channels->size();
+
+      tag = channels->at(currentTVChannel-1);
+
+      if ((m_CurrentGroupID != -1) && (m_CurrentGroupID != tag->GroupID()))
+        continue;
+
+      /* Perform Channelswitch */
+      if (preview)
+      {
+        /* Update the Playing channel data and the current epg data */
+        delete m_currentPlayingChannel;
+        m_currentPlayingChannel = new CFileItem(*tag);
+
+        /* Load now the new channel settings from Database */
+        LoadCurrentChannelSettings();
+
+        *newchannel = currentTVChannel;
+        LeaveCriticalSection(&m_critSection);
+        return true;
+      }
+      else if (!tag->StreamURL().IsEmpty() || m_clients[tag->ClientID()]->SwitchChannel(*tag))
+      {
+        /* Update the Playing channel data and the current epg data */
+        delete m_currentPlayingChannel;
+        m_currentPlayingChannel = new CFileItem(*tag);
+        m_scanStart             = CTimeUtils::GetTimeMS();
+
+        /* Load now the new channel settings from Database */
+        LoadCurrentChannelSettings();
+
+        /* Set quality data to undefined defaults */
+        ResetQualityData();
+
+        *newchannel = currentTVChannel;
+        LeaveCriticalSection(&m_critSection);
+        return true;
+      }
+    }
+    LeaveCriticalSection(&m_critSection);
+  }
+  return false;
+}
+
+int CPVRManager::GetTotalTime()
+{
+  if (m_currentPlayingChannel)
+    return m_currentPlayingChannel->GetPVRChannelInfoTag()->GetEPGNow()->GetDuration() * 1000;
+
+  return 0;
+}
+
+int CPVRManager::GetStartTime()
+{
+  /* If it is called without a opened TV channel return with NULL */
+  if (!m_currentPlayingChannel)
+    return 0;
+
+  /* GetStartTime() is frequently called by DVDPlayer during playback of Live TV, so we
+   * check here if the end of the current running event is reached, if yes update the
+   * playing file item with the newest EPG data of the now running event.
+   */
+  const CPVREpgInfoTag* tag = m_currentPlayingChannel->GetPVRChannelInfoTag()->GetEPGNow();
+  if (tag && (tag->End() < CDateTime::GetCurrentDateTime() || tag->Title().IsEmpty()))
+  {
+    EnterCriticalSection(&m_critSection);
+    UpdateItem(*m_currentPlayingChannel);
+    LeaveCriticalSection(&m_critSection);
+  }
+
+  /* Calculate here the position we have of the running live TV event.
+   * "position in ms" = ("current local time" - "event start local time") * 1000
+   */
+  CDateTimeSpan time = CDateTime::GetCurrentDateTime() - tag->Start();
+  return time.GetDays()    * 1000 * 60 * 60 * 24
+       + time.GetHours()   * 1000 * 60 * 60
+       + time.GetMinutes() * 1000 * 60
+       + time.GetSeconds() * 1000;
+}
+
+CPVRManager g_PVRManager;
diff --git a/xbmc/pvr/PVRManager.h b/xbmc/pvr/PVRManager.h
new file mode 100644 (file)
index 0000000..b5d6916
--- /dev/null
@@ -0,0 +1,410 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "FileItem.h"
+#include "PVRDatabase.h"
+#include "addons/Addon.h"
+#include "addons/PVRClient.h"
+#include "addons/AddonManager.h"
+#include "utils/Thread.h"
+
+#include <vector>
+#include <deque>
+
+class CPVRChannels;
+class CPVRRecordings;
+class CPVRTimers;
+
+typedef std::map< long, boost::shared_ptr<CPVRClient> >           CLIENTMAP;
+typedef std::map< long, boost::shared_ptr<CPVRClient> >::iterator CLIENTMAPITR;
+typedef std::map< long, PVR_SERVERPROPS >       CLIENTPROPS;
+typedef std::map< long, PVR_STREAMPROPS >       STREAMPROPS;
+
+class CPVRManager : IPVRClientCallback
+                  , public ADDON::IAddonMgrCallback
+                  , private CThread
+{
+public:
+  CPVRManager();
+  ~CPVRManager();
+
+  /*! \name Startup functions
+   */
+  void Start();
+  void Stop();
+  void Restart(void);
+
+  /*! \name External Client access functions
+   */
+  unsigned long GetFirstClientID();
+  CLIENTMAP* Clients() { return &m_clients; }
+  CPVRDatabase *GetTVDatabase() { return &m_database; }
+
+  /*! \name Addon related functions
+   */
+  bool RequestRestart(ADDON::AddonPtr addon, bool datachanged);
+  bool RequestRemoval(ADDON::AddonPtr addon);
+  void OnClientMessage(const long clientID, const PVR_EVENT clientEvent, const char* msg);
+
+  /*! \name GUIInfoManager functions
+   */
+  void UpdateRecordingsCache();
+  const char* TranslateCharInfo(DWORD dwInfo);
+  int TranslateIntInfo(DWORD dwInfo);
+  bool TranslateBoolInfo(DWORD dwInfo);
+
+  /*! \name General functions
+   */
+
+  /*! \brief Open a selection Dialog and start a channelscan on
+   selected Client.
+   */
+  void StartChannelScan();
+
+  /*! \brief Get info about a running channel scan
+   \return true if scan is running
+   */
+  bool ChannelScanRunning() { return m_bChannelScanRunning; }
+
+  /*! \brief Set the TV Database to it's initial state and delete all
+   the data inside.
+   */
+  void ResetDatabase();
+
+  /*! \brief Set the EPG data inside TV Database to it's initial state
+   and reload it from Clients.
+   */
+  void ResetEPG();
+
+  /*! \brief Returns true if a tv channel is playing
+   \return true during TV playback
+   */
+  bool IsPlayingTV();
+
+  /*! \brief Returns true if a radio channel is playing
+   \return true during radio playback
+   */
+  bool IsPlayingRadio();
+
+  /*! \brief Returns true if a recording is playing over the client
+   \return true during recording playback
+   */
+  bool IsPlayingRecording();
+
+  /*! \brief Returns the properties of the current playing client
+   \return pointer to properties (NULL if no stream is playing)
+   */
+  PVR_SERVERPROPS *GetCurrentClientProps();
+
+  /*! \brief Return the current playing client identifier
+   \return the identifier or -1 if no playing item ist present
+   */
+  long GetCurrentPlayingClientID();
+
+  /*! \brief Returns the properties of the given client identifier
+   \param clientID The identifier of the client
+   \return pointer to properties (NULL if no stream is playing)
+   */
+  PVR_SERVERPROPS *GetClientProps(int clientID) { return &m_clientsProps[clientID]; }
+
+  /*! \brief Returns the properties of the current playing stream content
+   \return pointer to properties (NULL if no stream is playing)
+   */
+  PVR_STREAMPROPS *GetCurrentStreamProps();
+
+  /*! \brief Returns the current playing file item
+   \return pointer to file item class (NULL if no stream is playing)
+   */
+  CFileItem *GetCurrentPlayingItem();
+
+  /*! \brief Get the input format name of the current playing channel
+   \return the name of the input format or empty if unknown
+   */
+  CStdString GetCurrentInputFormat();
+
+  /*! \brief Returns the current playing channel number
+   \param number Address to integer value to write playing channel number
+   \param radio Address to boolean value to set it true if it is a radio channel
+   \return true if channel is playing
+   */
+  bool GetCurrentChannel(int *number, bool *radio);
+
+  bool GetCurrentChannel(const CPVRChannel *channel);
+
+   /*! \brief Returns if a minimum one client is active
+   \return true if minimum one client is started
+   */
+  bool HaveActiveClients();
+
+   /*! \brief Returns the presence of PVR specific Menu entries
+   \param clientID identifier of the client to ask or < 0 for playing channel
+   \return true if menu hooks are present
+   */
+  bool HaveMenuHooks(long clientID);
+
+   /*! \brief Open selection and progress pvr actions
+   \param clientID identifier to process
+   */
+  void ProcessMenuHooks(long clientID);
+
+  /*! \brief Returns the previous selected channel
+   \return the number of the previous channel or -1 if no channel was selected before
+   */
+  int GetPreviousChannel();
+
+  /*! \brief Get the possibility to start a recording of the current playing
+   channel.
+   \return true if a recording can be started
+   */
+  bool CanRecordInstantly();
+
+  /*! \brief Get the presence of timers.
+   \return true if timers are present
+   */
+  bool HasTimer() { return m_hasTimers; }
+
+  /*! \brief Get the presence of a running recording.
+   \return true if a recording is running
+   */
+  bool IsRecording() { return m_isRecording; }
+
+  /*! \brief Get the presence of a running recording on current playing channel.
+   \return true if a recording is running
+   */
+  bool IsRecordingOnPlayingChannel();
+
+  /*! \brief Start a instant recording on playing channel
+   \return true if it success
+   */
+  bool StartRecordingOnPlayingChannel(bool bOnOff);
+
+  /*! \brief Set the current playing group ID, used to load the right channel
+   lists.
+   */
+  void SetPlayingGroup(int GroupId);
+
+  /*! \brief Get the current playing group ID, used to load the
+    right channel lists
+   \return current playing group identifier
+   */
+  int GetPlayingGroup();
+
+  /*! \brief Trigger a recordings list update
+   */
+  void TriggerRecordingsUpdate(bool force=true);
+
+  /*! \brief Trigger a timer list update
+   */
+  void TriggerTimersUpdate(bool force=true);
+
+
+  /*! \name Stream reading functions
+   PVR Client internal input stream access, is used if
+   inside PVR_SERVERPROPS the HandleInputStream is true
+   */
+
+  /*! \brief Open the Channel stream on the given channel info tag
+   \return true if opening was succesfull
+   */
+  bool OpenLiveStream(const CPVRChannel* tag);
+
+  /*! \brief Open a recording by a index number passed to this function.
+   \return true if opening was succesfull
+   */
+  bool OpenRecordedStream(const CPVRRecordingInfoTag* tag);
+
+  /*! \brief Returns runtime generated stream URL
+   Returns a during runtime generated stream URL from the PVR Client.
+   Backends like Mediaportal generates the URL for the RTSP streams
+   during opening.
+   \return Stream URL
+   */
+  CStdString GetLiveStreamURL(const CPVRChannel* tag);
+
+  /*! \brief Close the stream on the PVR Client.
+   */
+  void CloseStream();
+
+  /*! \brief Read the stream
+   Read the stream to the buffer pointer passed defined by "buf" and
+   a maximum site passed with "buf_size".
+   \return the amount of readed bytes is returned
+   */
+  int ReadStream(void* lpBuf, int64_t uiBufSize);
+
+  /*! \brief Reset the client demuxer
+   */
+  void DemuxReset();
+
+  /*! \brief Aborts any internal reading that might be stalling main thread
+   * NOTICE - this can be called from another thread
+   */
+  void DemuxAbort();
+
+  /*! \brief Flush the demuxer, if any data is kept in buffers, this should be freed now
+   */
+  void DemuxFlush();
+
+  /*! \brief Read the stream from demuxer
+   Read the stream from pvr client own demuxer
+   \return a allocated demuxer packet
+   */
+  DemuxPacket* ReadDemuxStream();
+
+  /*! \brief Return the filesize of the current running stream.
+   Limited to recordings playback at the moment.
+   \return the size of the actual running stream
+   */
+  int64_t LengthStream(void);
+
+  /*! \brief Seek to a position inside stream
+   It is currently limited to playback of recordings, no live tv seek is possible.
+   \param pos the position to seek to
+   \param whence how we want to seek ("new position=pos", or "new position=pos+actual postion" or "new position=filesize-pos
+   \return the new position inside stream
+   */
+  int64_t SeekStream(int64_t iFilePosition, int iWhence = SEEK_SET);
+
+  /*! \brief Get the current playing position in stream
+   \return the current position inside stream
+   */
+  int64_t GetStreamPosition(void);
+
+  /*! \brief Update the current running channel
+   It is called during a channel  change to refresh the global file item.
+   \param item the file item with the current running item
+   \return true if success
+   */
+  bool UpdateItem(CFileItem& item);
+
+  /*! \brief Switch to a channel by the given number
+   Used only for live TV channels
+   \param channel the channel number to switch
+   \return true if success
+   */
+  bool ChannelSwitch(unsigned int channel);
+
+  /*! \brief Switch to the next channel in list
+   It switch to the next channel and return the new channel to
+   the pointer passed by this function, if preview is true no client
+   channel switch is performed, only the data of the new channel is
+   loaded, is used to show new event information in fast channel zapping.
+   \param newchannel pointer to store the new selected channel number
+   \param preview if set the channel is only switched inside XBMC without client action
+   \return true if success
+   */
+  bool ChannelUp(unsigned int *newchannel, bool preview = false);
+
+  /*! \brief Switch the to previous channel in list
+   It switch to the previous channel and return the new channel to
+   the pointer passed by this function, if preview is true no client
+   channel switch is performed, only the data of the new channel is
+   loaded, is used to show new event information in fast channel zapping.
+   \param newchannel pointer to store the new selected channel number
+   \param preview if set the channel is only switched inside XBMC without client action
+   \return true if success
+   */
+  bool ChannelDown(unsigned int *newchannel, bool preview = false);
+
+  /*! \brief Returns the duration of the current playing channel
+   Used only for live TV channels
+   \return duration in milliseconds or NULL if no channel is playing.
+   */
+  int GetTotalTime();
+
+  /*! \brief Returns the current position from 0 in milliseconds
+   Used only for live TV channels
+   \return position in milliseconds or NULL if no channel is playing.
+   */
+  int GetStartTime();
+
+protected:
+  virtual void Process();
+
+private:
+  /*--- Handling functions ---*/
+  bool LoadClients();
+  void GetClientProperties();                   /*! \brief call GetClientProperties(long clientID) for each client connected */
+  void GetClientProperties(long clientID);      /*! \brief request the PVR_SERVERPROPS struct from each client */
+  void SaveCurrentChannelSettings();            /*! \brief Write the current Video and Audio settings of
+                                                 playing channel to the TV Database */
+  void LoadCurrentChannelSettings();            /*! \brief Read and set the Video and Audio settings of
+                                                 playing channel from the TV Database */
+  void ResetQualityData();                      /*! \brief Reset the Signal Quality data structure to initial values */
+  bool ContinueLastChannel();
+
+  /*--- General PVRManager data ---*/
+  CLIENTMAP           m_clients;                /* pointer to each enabled client's interface */
+  CLIENTPROPS         m_clientsProps;           /* store the properties of each client locally */
+  STREAMPROPS         m_streamProps;
+  CPVRDatabase         m_database;
+  CRITICAL_SECTION    m_critSection;
+  bool                m_bFirstStart;            /* Is set if this is first startup of PVRManager */
+  bool                m_bChannelScanRunning;    /* Is set if a channel scan is running */
+
+  /*--- GUIInfoManager information data ---*/
+  DWORD               m_infoToggleStart;        /* Time to toogle pvr infos like in System info */
+  unsigned int        m_infoToggleCurrent;      /* The current item showed by the GUIInfoManager */
+  DWORD               m_recordingToggleStart;   /* Time to toogle currently running pvr recordings */
+  unsigned int        m_recordingToggleCurrent; /* The current item showed by the GUIInfoManager */
+
+  std::vector<CPVRTimerInfoTag *> m_NowRecording;
+  CPVRTimerInfoTag *  m_NextRecording;
+  CStdString          m_backendName;
+  CStdString          m_backendVersion;
+  CStdString          m_backendHost;
+  CStdString          m_backendDiskspace;
+  CStdString          m_backendTimers;
+  CStdString          m_backendRecordings;
+  CStdString          m_backendChannels;
+  CStdString          m_totalDiskspace;
+  CStdString          m_nextTimer;
+  CStdString          m_playingDuration;
+  CStdString          m_playingTime;
+  CStdString          m_playingClientName;
+  bool                m_isRecording;
+  bool                m_hasRecordings;
+  bool                m_hasTimers;
+
+  /*--- Thread Update Timers ---*/
+  int                 m_LastTVChannelCheck;
+  int                 m_LastRadioChannelCheck;
+  int                 m_LastRecordingsCheck;
+  int                 m_LastTimersCheck;
+  int                 m_LastEPGUpdate;
+  int                 m_LastEPGScan;
+
+  /*--- Previous Channel data ---*/
+  int                 m_PreviousChannel[2];
+  int                 m_PreviousChannelIndex;
+  int                 m_LastChannel;
+  unsigned int        m_LastChannelChanged;
+
+  /*--- Stream playback data ---*/
+  CFileItem          *m_currentPlayingChannel;    /* The current playing channel or NULL */
+  CFileItem          *m_currentPlayingRecording;  /* The current playing recording or NULL */
+  int                 m_CurrentGroupID;           /* The current selected Channel group list */
+  DWORD               m_scanStart;                /* Scan start time to check for non present streams */
+  PVR_SIGNALQUALITY   m_qualityInfo;              /* Stream quality information */
+};
+
+extern CPVRManager g_PVRManager;
diff --git a/xbmc/pvr/PVRRecordings.cpp b/xbmc/pvr/PVRRecordings.cpp
new file mode 100644 (file)
index 0000000..59c772b
--- /dev/null
@@ -0,0 +1,485 @@
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+/*
+ * DESCRIPTION:
+ *
+ * CPVRRecordingInfoTag is part of the XBMC PVR system to support recording entrys,
+ * stored on a other Backend like VDR or MythTV.
+ *
+ * The recording information tag holds data about name, length, recording time
+ * and so on of recorded stream stored on the backend.
+ *
+ * The filename string is used to by the PVRManager and passed to DVDPlayer
+ * to stream data from the backend to XBMC.
+ *
+ * It is a also CVideoInfoTag and some of his variables must be set!
+ *
+ */
+
+#include "FileItem.h"
+#include "GUIDialogOK.h"
+#include "GUIWindowManager.h"
+#include "LocalizeStrings.h"
+#include "Util.h"
+#include "URL.h"
+#include "utils/log.h"
+#include "utils/SingleLock.h"
+
+#include "PVRRecordings.h"
+#include "PVRManager.h"
+
+/**
+ * Create a blank unmodified recording tag
+ */
+CPVRRecordingInfoTag::CPVRRecordingInfoTag()
+{
+  Reset();
+}
+
+bool CPVRRecordingInfoTag::operator ==(const CPVRRecordingInfoTag& right) const
+{
+
+  if (this == &right) return true;
+
+  return (m_clientIndex         == right.m_clientIndex &&
+          m_clientID            == right.m_clientID &&
+          m_strChannel          == right.m_strChannel &&
+          m_recordingTime       == right.m_recordingTime &&
+          m_duration            == right.m_duration &&
+          m_strPlotOutline      == right.m_strPlotOutline &&
+          m_strPlot             == right.m_strPlot &&
+          m_strStreamURL        == right.m_strStreamURL &&
+          m_Priority            == right.m_Priority &&
+          m_Lifetime            == right.m_Lifetime &&
+          m_strDirectory        == right.m_strDirectory &&
+          m_strFileNameAndPath  == right.m_strFileNameAndPath &&
+          m_strTitle            == right.m_strTitle);
+}
+
+bool CPVRRecordingInfoTag::operator !=(const CPVRRecordingInfoTag& right) const
+{
+
+  if (this == &right) return false;
+  return !(*this == right);
+}
+
+/**
+ * Initialize blank CPVRRecordingInfoTag
+ */
+void CPVRRecordingInfoTag::Reset(void)
+{
+  m_clientIndex           = -1;
+  m_clientID              = g_PVRManager.GetFirstClientID(); // Temporary until we support multiple backends
+  m_strChannel            = "";
+  m_strDirectory          = "";
+  m_recordingTime         = NULL;
+  m_strStreamURL          = "";
+  m_Priority              = -1;
+  m_Lifetime              = -1;
+  m_strFileNameAndPath    = "";
+
+  CVideoInfoTag::Reset();
+}
+
+int CPVRRecordingInfoTag::GetDuration() const
+{
+  int duration;
+  duration =  m_duration.GetDays()*60*60*24;
+  duration += m_duration.GetHours()*60*60;
+  duration += m_duration.GetMinutes()*60;
+  duration += m_duration.GetSeconds();
+  duration /= 60;
+  return duration;
+}
+
+bool CPVRRecordingInfoTag::Delete(void) const
+{
+  try
+  {
+    CLIENTMAP *clients = g_PVRManager.Clients();
+
+    /* and write it to the backend */
+    PVR_ERROR err = clients->find(m_clientID)->second->DeleteRecording(*this);
+
+    if (err != PVR_ERROR_NO_ERROR)
+      throw err;
+
+    PVRRecordings.Update();
+    return true;
+  }
+  catch (PVR_ERROR err)
+  {
+    DisplayError(err);
+  }
+  return false;
+}
+
+bool CPVRRecordingInfoTag::Rename(CStdString &newName) const
+{
+  try
+  {
+    CLIENTMAP *clients = g_PVRManager.Clients();
+
+    /* and write it to the backend */
+    PVR_ERROR err = clients->find(m_clientID)->second->RenameRecording(*this, newName);
+
+    if (err != PVR_ERROR_NO_ERROR)
+      throw err;
+
+    PVRRecordings.Update();
+    return true;
+  }
+  catch (PVR_ERROR err)
+  {
+    DisplayError(err);
+  }
+  return false;
+}
+
+void CPVRRecordingInfoTag::DisplayError(PVR_ERROR err) const
+{
+  if (err == PVR_ERROR_SERVER_ERROR)
+    CGUIDialogOK::ShowAndGetInput(19033,19111,19110,0); /* print info dialog "Server error!" */
+  else if (err == PVR_ERROR_NOT_SYNC)
+    CGUIDialogOK::ShowAndGetInput(19033,19108,19110,0); /* print info dialog "Recordings not in sync!" */
+  else if (err == PVR_ERROR_NOT_DELETED)
+    CGUIDialogOK::ShowAndGetInput(19033,19068,19110,0); /* print info dialog "Couldn't delete recording!" */
+  else
+    CGUIDialogOK::ShowAndGetInput(19033,19147,19110,0); /* print info dialog "Unknown error!" */
+
+  return;
+}
+
+
+// --- CPVRRecordings ---------------------------------------------------------------
+
+CPVRRecordings PVRRecordings;
+
+CPVRRecordings::CPVRRecordings(void)
+{
+
+}
+
+void CPVRRecordings::Process()
+{
+  CSingleLock lock(m_critSection);
+  CLIENTMAP *clients = g_PVRManager.Clients();
+
+  Clear();
+
+  /* Go thru all clients and receive there Recordings */
+  CLIENTMAPITR itr = clients->begin();
+  int cnt = 0;
+  while (itr != clients->end())
+  {
+    /* Load only if the client have Recordings */
+    if ((*itr).second->GetNumRecordings() > 0)
+    {
+      (*itr).second->GetAllRecordings(this);
+    }
+    itr++;
+    cnt++;
+  }
+
+  for (unsigned int i = 0; i < size(); ++i)
+  {
+    CFileItemPtr pFileItem(new CFileItem(at(i)));
+
+    CStdString Path;
+    CStdString strTitle = at(i).Title();
+    strTitle.Replace('/','-');
+
+    Path.Format("pvr://recordings/client_%04i/", at(i).ClientID());
+    if (at(i).Directory() != "")
+      Path += at(i).Directory();
+
+    CUtil::AddSlashAtEnd(Path);
+    Path += strTitle + ".pvr";
+    at(i).SetPath(Path);
+  }
+  return;
+}
+
+void CPVRRecordings::Unload()
+{
+  CSingleLock lock(m_critSection);
+  Clear();
+}
+
+bool CPVRRecordings::Update(bool Wait)
+{
+  if (Wait)
+  {
+    Process();
+    return true;
+  }
+  else
+  {
+    Create();
+    SetName("PVR Recordings Update");
+    SetPriority(-5);
+  }
+  return false;
+}
+
+int CPVRRecordings::GetNumRecordings()
+{
+  CSingleLock lock(m_critSection);
+  return size();
+}
+
+int CPVRRecordings::GetRecordings(CFileItemList* results)
+{
+  CSingleLock lock(m_critSection);
+  for (unsigned int i = 0; i < size(); ++i)
+  {
+    CFileItemPtr pFileItem(new CFileItem(at(i)));
+    results->Add(pFileItem);
+  }
+  return size();
+}
+
+bool CPVRRecordings::DeleteRecording(const CFileItem &item)
+{
+  /* Check if a CPVRRecordingInfoTag is inside file item */
+  if (!item.IsPVRRecording())
+  {
+    CLog::Log(LOGERROR, "cPVRRecordings: DeleteRecording no RecordingInfoTag given!");
+    return false;
+  }
+
+  const CPVRRecordingInfoTag* tag = item.GetPVRRecordingInfoTag();
+  return tag->Delete();
+}
+
+bool CPVRRecordings::RenameRecording(CFileItem &item, CStdString &newname)
+{
+  /* Check if a CPVRRecordingInfoTag is inside file item */
+  if (!item.IsPVRRecording())
+  {
+    CLog::Log(LOGERROR, "cPVRRecordings: RenameRecording no RecordingInfoTag given!");
+    return false;
+  }
+
+  CPVRRecordingInfoTag* tag = item.GetPVRRecordingInfoTag();
+  return tag->Rename(newname);
+}
+
+bool CPVRRecordings::RemoveRecording(const CFileItem &item)
+{
+  CSingleLock lock(m_critSection);
+
+  if (!item.IsPVRRecording())
+  {
+    CLog::Log(LOGERROR, "cPVRRecordings: RemoveRecording no RecordingInfoTag given!");
+    return false;
+  }
+
+  const CPVRRecordingInfoTag* tag = item.GetPVRRecordingInfoTag();
+
+  for (unsigned int i = 0; i < size(); ++i)
+  {
+    if (tag == &at(i))
+    {
+      erase(begin()+i);
+      return true;
+    }
+  }
+  return false;
+}
+
+bool CPVRRecordings::GetDirectory(const CStdString& strPath, CFileItemList &items)
+{
+  CSingleLock lock(m_critSection);
+
+  CStdString base(strPath);
+  CUtil::RemoveSlashAtEnd(base);
+
+  CURL url(strPath);
+  CStdString fileName = url.GetFileName();
+  CUtil::RemoveSlashAtEnd(fileName);
+
+  if (fileName == "recordings")
+  {
+//    if (g_PVRManager.Clients()->size() > 1)
+    {
+      CLIENTMAPITR itr = g_PVRManager.Clients()->begin();
+      while (itr != g_PVRManager.Clients()->end())
+      {
+        CFileItemPtr item;
+        CStdString dirName;
+        CStdString clientName;
+
+        int clientID = g_PVRManager.Clients()->find((*itr).first)->second->GetID();
+        clientName.Format(g_localizeStrings.Get(19016), clientID, g_PVRManager.Clients()->find((*itr).first)->second->GetBackendName());
+        dirName.Format("%s/client_%04i/", base, clientID);
+        item.reset(new CFileItem(dirName, true));
+        item->SetLabel(clientName);
+        item->SetLabelPreformated(true);
+        items.Add(item);
+
+        itr++;
+      }
+      return true;
+    }
+  }
+  else if (fileName.Left(18) == "recordings/client_")
+  {
+    fileName.erase(0,18);
+    int clientID = atoi(fileName.c_str());
+    CStdString curDir = url.GetFileName();
+    CUtil::AddSlashAtEnd(curDir);
+
+    CStdString strBuffer;
+    CStdString strSkip;
+    std::vector<CStdString> baseTokens;
+    if (!curDir.IsEmpty())
+      CUtil::Tokenize(curDir, baseTokens, "/");
+
+    for (unsigned int i = 0; i < size(); ++i)
+    {
+      if (clientID != at(i).ClientID())
+        continue;
+
+      CStdString strEntryName;
+      CStdString strTitle = at(i).Title();
+      strEntryName.Format("recordings/client_%04i/%s", clientID, at(i).Directory());
+      CUtil::AddSlashAtEnd(strEntryName);
+
+      strTitle.Replace('/','-');
+      strEntryName += strTitle;
+      strEntryName.Replace('\\','/');
+      CStdString strOrginalName = strEntryName;
+
+      if (strEntryName == curDir) // skip the listed dir
+        continue;
+
+      std::vector<CStdString> pathTokens;
+      CUtil::Tokenize(strEntryName, pathTokens, "/");
+      if (pathTokens.size() < baseTokens.size()+1)
+        continue;
+
+      bool bAdd = true;
+      strEntryName = "";
+      for (unsigned int j = 0; j < baseTokens.size(); ++j)
+      {
+        if (pathTokens[j] != baseTokens[j])
+        {
+          bAdd = false;
+          break;
+        }
+        strEntryName += pathTokens[j] + "/";
+      }
+      if (!bAdd)
+        continue;
+
+      strEntryName += pathTokens[baseTokens.size()];
+      char c=strOrginalName[strEntryName.size()];
+      if (c == '/' || c == '\\')
+        strEntryName += '/';
+
+      CFileItemPtr pFileItem;
+      bool bIsFolder = false;
+      if (strEntryName[strEntryName.size()-1] != '/') // this is a file
+      {
+        pFileItem.reset(new CFileItem(at(i)));
+        pFileItem->SetLabel(pathTokens[baseTokens.size()]);
+        pFileItem->SetLabel2(at(i).RecordingTime().GetAsLocalizedDateTime(true, false));
+        pFileItem->m_dateTime = at(i).RecordingTime();
+        pFileItem->m_strPath.Format("pvr://%s-%05i.pvr", strEntryName, at(i).ClientIndex());
+      }
+      else
+      { // this is new folder. add if not already added
+        bIsFolder = true;
+        strBuffer = "pvr://" + strEntryName;
+        if (items.Contains(strBuffer)) // already added
+          continue;
+
+        pFileItem.reset(new CFileItem(strBuffer, true));
+        pFileItem->SetLabel(pathTokens[baseTokens.size()]);
+      }
+      pFileItem->SetLabelPreformated(true);
+      items.Add(pFileItem);
+    }
+    return true;
+  }
+  return false;
+}
+
+CPVRRecordingInfoTag *CPVRRecordings::GetByPath(CStdString &path)
+{
+  CSingleLock lock(m_critSection);
+
+  CURL url(path);
+  CStdString fileName = url.GetFileName();
+  CUtil::RemoveSlashAtEnd(fileName);
+
+  if (fileName.Left(18) == "recordings/client_")
+  {
+    fileName.erase(0,18);
+    int clientID = atoi(fileName.c_str());
+    fileName.erase(0,5);
+
+    if (fileName.IsEmpty())
+      return NULL;
+
+    CStdString title;
+    CStdString dir;
+    size_t found = fileName.find_last_of("/");
+    if (found != CStdString::npos)
+    {
+      title = fileName.substr(found+1);
+      dir = fileName.substr(0, found);
+    }
+    else
+    {
+      title = fileName;
+      dir = "";
+    }
+    CUtil::RemoveExtension(title);
+    int index = atoi(title.substr(title.size()-5).c_str());
+    title.erase(title.size()-6);
+
+    for (unsigned int i = 0; i < size(); ++i)
+    {
+      if (index > 0)
+      {
+        if (index == at(i).ClientIndex())
+          return &at(i);
+      }
+      else
+      {
+        if ((title == at(i).Title()) && (dir == at(i).Directory()) && (clientID == at(i).ClientID()))
+          return &at(i);
+      }
+    }
+  }
+
+  return NULL;
+}
+
+void CPVRRecordings::Clear()
+{
+  /* Clear all current present Recordings inside list */
+  erase(begin(), end());
+  return;
+}
diff --git a/xbmc/pvr/PVRRecordings.h b/xbmc/pvr/PVRRecordings.h
new file mode 100644 (file)
index 0000000..8e30cfa
--- /dev/null
@@ -0,0 +1,111 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2008 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+/*
+ * for DESCRIPTION see 'TVRecordInfoTag.cpp'
+ */
+
+#include "VideoInfoTag.h"
+#include "Thread.h"
+#include "DateTime.h"
+#include "settings/VideoSettings.h"
+#include "../addons/include/xbmc_pvr_types.h"
+
+class CPVRRecordingInfoTag : public CVideoInfoTag
+{
+private:
+  int           m_clientID;           /// ID of the backend
+  int           m_clientIndex;        /// Index number of the reecording on the client, given by the backend, -1 for unknown
+  CStdString    m_strChannel;         /// Channel name where recording from
+  CDateTime     m_recordingTime;      /// Recording start time
+  CDateTimeSpan m_duration;           /// Duration
+  int           m_Priority;
+  int           m_Lifetime;
+  CStdString    m_strStreamURL;       ///> Stream URL if empty use pvr client
+  CStdString    m_strDirectory;       ///> Directory of this recording on the client
+  CStdString    m_strFileNameAndPath; ///> Filename for PVRManager to open and read stream
+
+  void DisplayError(PVR_ERROR err) const;
+
+public:
+  CPVRRecordingInfoTag();
+  bool operator ==(const CPVRRecordingInfoTag& right) const;
+  bool operator !=(const CPVRRecordingInfoTag& right) const;
+  void Reset(void);
+
+  CStdString Title(void) const { return m_strTitle; }
+  void SetTitle(CStdString Title) { m_strTitle = Title; }
+  CStdString Directory(void) const { return m_strDirectory; }
+  void SetDirectory(CStdString path) { m_strDirectory = path; }
+  CStdString PlotOutline(void) const { return m_strPlotOutline; }
+  void SetPlotOutline(CStdString PlotOutline) { m_strPlotOutline = PlotOutline; }
+  CStdString Plot(void) const { return m_strPlot; }
+  void SetPlot(CStdString Plot) { m_strPlot = Plot; }
+  CStdString ChannelName(void) const { return m_strChannel; }
+  void SetChannelName(CStdString name) { m_strChannel = name; }
+  CDateTime RecordingTime(void) const { return m_recordingTime; }
+  void SetRecordingTime(CDateTime time) { m_recordingTime = time; }
+  int GetDuration() const;
+  void SetDuration(CDateTimeSpan duration) { m_duration = duration; }
+  int Lifetime(void) const { return m_Lifetime; }
+  void SetLifetime(int Lifetime) { m_Lifetime = Lifetime; }
+  int Priority(void) const { return m_Priority; }
+  void SetPriority(int Priority) { m_Priority = Priority; }
+  CStdString Path(void) const { return m_strFileNameAndPath; }
+  void SetPath(CStdString path) { m_strFileNameAndPath = path; }
+
+  long ClientID(void) const { return m_clientID; }
+  void SetClientID(int ClientId) { m_clientID = ClientId; }
+  long ClientIndex(void) const { return m_clientIndex; }
+  void SetClientIndex(int ClientIndex) { m_clientIndex = ClientIndex; }
+  CStdString StreamURL(void) const { return m_strStreamURL; }
+  void SetStreamURL(CStdString stream) { m_strStreamURL = stream; }
+
+  bool Delete(void) const;
+  bool Rename(CStdString &newName) const;
+
+};
+
+
+class CPVRRecordings : public std::vector<CPVRRecordingInfoTag>
+                     , private CThread
+{
+private:
+  CCriticalSection  m_critSection;
+  virtual void Process();
+
+public:
+  CPVRRecordings(void);
+  bool Load() { return Update(true); }
+  void Unload();
+  bool Update(bool Wait = false);
+  int GetNumRecordings();
+  int GetRecordings(CFileItemList* results);
+  static bool DeleteRecording(const CFileItem &item);
+  static bool RenameRecording(CFileItem &item, CStdString &newname);
+  bool RemoveRecording(const CFileItem &item);
+  bool GetDirectory(const CStdString& strPath, CFileItemList &items);
+  CPVRRecordingInfoTag *GetByPath(CStdString &path);
+  void Clear();
+};
+
+extern CPVRRecordings PVRRecordings;
diff --git a/xbmc/pvr/PVRTimerInfoTag.cpp b/xbmc/pvr/PVRTimerInfoTag.cpp
new file mode 100644 (file)
index 0000000..cbbc35d
--- /dev/null
@@ -0,0 +1,486 @@
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "GUISettings.h"
+#include "GUIDialogOK.h"
+#include "GUIDialogYesNo.h"
+
+#include "PVRTimerInfoTag.h"
+#include "PVRChannel.h"
+#include "PVRChannelsContainer.h"
+#include "PVRManager.h"
+#include "PVREpgInfoTag.h"
+
+CPVRTimerInfoTag::CPVRTimerInfoTag()
+{
+  m_strTitle           = "";
+  m_strDir             = "/";
+  m_strSummary         = "";
+  m_bValidSummary      = false;
+  m_bIsActive          = false;
+  m_iChannelNumber     = -1;
+  m_iClientID          = g_PVRManager.GetFirstClientID();
+  m_iClientIndex       = -1;
+  m_iClientNumber      = -1;
+  m_bIsRadio           = false;
+  m_bIsRecording       = false;
+  m_StartTime          = NULL;
+  m_StopTime           = NULL;
+  m_bIsRepeating       = false;
+  m_FirstDay           = NULL;
+  m_iWeekdays          = 0;
+  m_iPriority          = -1;
+  m_iLifetime          = -1;
+  m_EpgInfo            = NULL;
+  m_strFileNameAndPath = "";
+}
+
+bool CPVRTimerInfoTag::operator ==(const CPVRTimerInfoTag& right) const
+{
+  if (this == &right) return true;
+
+  return (m_iClientIndex       == right.m_iClientIndex &&
+          m_bIsActive          == right.m_bIsActive &&
+          m_strSummary         == right.m_strSummary &&
+          m_iChannelNumber     == right.m_iChannelNumber &&
+          m_iClientNumber      == right.m_iClientNumber &&
+          m_bIsRadio           == right.m_bIsRadio &&
+          m_bIsRepeating       == right.m_bIsRepeating &&
+          m_StartTime          == right.m_StartTime &&
+          m_StopTime           == right.m_StopTime &&
+          m_FirstDay           == right.m_FirstDay &&
+          m_iWeekdays          == right.m_iWeekdays &&
+          m_bIsRecording       == right.m_bIsRecording &&
+          m_iPriority          == right.m_iPriority &&
+          m_iLifetime          == right.m_iLifetime &&
+          m_strFileNameAndPath == right.m_strFileNameAndPath &&
+          m_strTitle           == right.m_strTitle &&
+          m_iClientID          == right.m_iClientID);
+}
+
+/**
+ * Compare not equal for two CPVRTimerInfoTag
+ */
+bool CPVRTimerInfoTag::operator !=(const CPVRTimerInfoTag& right) const
+{
+  if (this == &right) return false;
+
+  return !(*this == right);
+}
+
+int CPVRTimerInfoTag::Compare(const CPVRTimerInfoTag &timer) const
+{
+  int iTimerDelta = StartTime() - timer.StartTime();
+
+  /* if the start times are equal, compare the priority of the timers */
+  return iTimerDelta == 0 ?
+    timer.m_iPriority - m_iPriority :
+    iTimerDelta;
+}
+
+time_t CPVRTimerInfoTag::StartTime(void) const
+{
+  time_t start;
+  m_StartTime.GetAsTime(start);
+  return start;
+}
+
+time_t CPVRTimerInfoTag::StopTime(void) const
+{
+  time_t stop;
+  m_StopTime.GetAsTime(stop);
+  return stop;
+}
+
+time_t CPVRTimerInfoTag::FirstDayTime(void) const
+{
+  time_t firstday;
+  m_FirstDay.GetAsTime(firstday);
+  return firstday;
+}
+
+void CPVRTimerInfoTag::SetStart(CDateTime Start)
+{
+  m_StartTime = Start;
+  m_bValidSummary = false;
+}
+
+void CPVRTimerInfoTag::SetStop(CDateTime Start)
+{
+  m_StopTime = Start;
+  m_bValidSummary = false;
+}
+
+void CPVRTimerInfoTag::SetWeekdays(int Weekdays)
+{
+  m_iWeekdays = Weekdays;
+  m_bValidSummary = false;
+}
+
+void CPVRTimerInfoTag::UpdateSummary(void)
+{
+  m_strSummary.clear();
+
+  if (!m_bIsRepeating)
+  {
+    m_strSummary.Format("%s %s %s %s %s",
+        m_StartTime.GetAsLocalizedDate(),
+        g_localizeStrings.Get(19159),
+        m_StartTime.GetAsLocalizedTime("", false),
+        g_localizeStrings.Get(19160),
+        m_StopTime.GetAsLocalizedTime("", false));
+  }
+  else if (m_FirstDay != NULL)
+  {
+    m_strSummary.Format("%s-%s-%s-%s-%s-%s-%s %s %s %s %s %s %s",
+        m_iWeekdays & 0x01 ? g_localizeStrings.Get(19149) : "__",
+        m_iWeekdays & 0x02 ? g_localizeStrings.Get(19150) : "__",
+        m_iWeekdays & 0x04 ? g_localizeStrings.Get(19151) : "__",
+        m_iWeekdays & 0x08 ? g_localizeStrings.Get(19152) : "__",
+        m_iWeekdays & 0x10 ? g_localizeStrings.Get(19153) : "__",
+        m_iWeekdays & 0x20 ? g_localizeStrings.Get(19154) : "__",
+        m_iWeekdays & 0x40 ? g_localizeStrings.Get(19155) : "__",
+        g_localizeStrings.Get(19156),
+        m_FirstDay.GetAsLocalizedDate(false),
+        g_localizeStrings.Get(19159),
+        m_StartTime.GetAsLocalizedTime("", false),
+        g_localizeStrings.Get(19160),
+        m_StopTime.GetAsLocalizedTime("", false));
+  }
+  else
+  {
+    m_strSummary.Format("%s-%s-%s-%s-%s-%s-%s %s %s %s %s",
+        m_iWeekdays & 0x01 ? g_localizeStrings.Get(19149) : "__",
+        m_iWeekdays & 0x02 ? g_localizeStrings.Get(19150) : "__",
+        m_iWeekdays & 0x04 ? g_localizeStrings.Get(19151) : "__",
+        m_iWeekdays & 0x08 ? g_localizeStrings.Get(19152) : "__",
+        m_iWeekdays & 0x10 ? g_localizeStrings.Get(19153) : "__",
+        m_iWeekdays & 0x20 ? g_localizeStrings.Get(19154) : "__",
+        m_iWeekdays & 0x40 ? g_localizeStrings.Get(19155) : "__",
+        g_localizeStrings.Get(19159),
+        m_StartTime.GetAsLocalizedTime("", false),
+        g_localizeStrings.Get(19160),
+        m_StopTime.GetAsLocalizedTime("", false));
+  }
+
+  m_bValidSummary = true;
+}
+
+/**
+ * Get the status string of this Timer, is used by the GUIInfoManager
+ */
+const CStdString CPVRTimerInfoTag::GetStatus() const
+{
+  if (m_strFileNameAndPath == "pvr://timers/add.timer")
+    return g_localizeStrings.Get(19026);
+  else if (!m_bIsActive)
+    return g_localizeStrings.Get(13106);
+  else if (m_bIsActive && (m_StartTime < CDateTime::GetCurrentDateTime() && m_StopTime > CDateTime::GetCurrentDateTime()))
+    return g_localizeStrings.Get(19162);
+  else
+    return g_localizeStrings.Get(305);
+}
+
+bool CPVRTimerInfoTag::AddToClient() const
+{
+  try
+  {
+    CLIENTMAP *clients = g_PVRManager.Clients();
+
+    /* and write it to the backend */
+    PVR_ERROR err = clients->find(m_iClientID)->second->AddTimer(*this);
+    if (err != PVR_ERROR_NO_ERROR)
+      throw err;
+
+    if (m_StartTime < CDateTime::GetCurrentDateTime() && m_StopTime > CDateTime::GetCurrentDateTime())
+      g_PVRManager.TriggerRecordingsUpdate(false);
+
+    return true;
+  }
+  catch (PVR_ERROR err)
+  {
+    DisplayError(err);
+  }
+  return false;
+}
+
+bool CPVRTimerInfoTag::DeleteFromClient(bool force) const
+{
+  try
+  {
+    CLIENTMAP *clients = g_PVRManager.Clients();
+
+    /* and write it to the backend */
+    PVR_ERROR err = clients->find(m_iClientID)->second->DeleteTimer(*this, force);
+
+    if (err == PVR_ERROR_RECORDING_RUNNING)
+    {
+      if (CGUIDialogYesNo::ShowAndGetInput(122,0,19122,0))
+        err = clients->find(m_iClientID)->second->DeleteTimer(*this, true);
+    }
+
+    if (err != PVR_ERROR_NO_ERROR)
+      throw err;
+
+    return true;
+  }
+  catch (PVR_ERROR err)
+  {
+    DisplayError(err);
+  }
+  return false;
+}
+
+bool CPVRTimerInfoTag::RenameOnClient(const CStdString &newname) const
+{
+  try
+  {
+    CLIENTMAP *clients = g_PVRManager.Clients();
+
+    /* and write it to the backend */
+    PVR_ERROR err = clients->find(m_iClientID)->second->RenameTimer(*this, newname);
+
+    if (err == PVR_ERROR_NOT_IMPLEMENTED)
+      err = clients->find(m_iClientID)->second->UpdateTimer(*this);
+
+    if (err != PVR_ERROR_NO_ERROR)
+      throw err;
+
+    return true;
+  }
+  catch (PVR_ERROR err)
+  {
+    DisplayError(err);
+  }
+
+  return false;
+}
+
+bool CPVRTimerInfoTag::UpdateOnClient() const
+{
+  try
+  {
+    CLIENTMAP *clients = g_PVRManager.Clients();
+
+    /* and write it to the backend */
+    PVR_ERROR err = clients->find(m_iClientID)->second->UpdateTimer(*this);
+    if (err != PVR_ERROR_NO_ERROR)
+      throw err;
+
+    if (m_StartTime < CDateTime::GetCurrentDateTime() && m_StopTime > CDateTime::GetCurrentDateTime())
+      g_PVRManager.TriggerRecordingsUpdate(false);
+
+    return true;
+  }
+  catch (PVR_ERROR err)
+  {
+    DisplayError(err);
+  }
+  return false;
+}
+
+void CPVRTimerInfoTag::DisplayError(PVR_ERROR err) const
+{
+  if (err == PVR_ERROR_SERVER_ERROR)
+    CGUIDialogOK::ShowAndGetInput(19033,19111,19110,0); /* print info dialog "Server error!" */
+  else if (err == PVR_ERROR_NOT_SYNC)
+    CGUIDialogOK::ShowAndGetInput(19033,19112,19110,0); /* print info dialog "Timers not in sync!" */
+  else if (err == PVR_ERROR_NOT_SAVED)
+    CGUIDialogOK::ShowAndGetInput(19033,19109,19110,0); /* print info dialog "Couldn't delete timer!" */
+  else if (err == PVR_ERROR_ALREADY_PRESENT)
+    CGUIDialogOK::ShowAndGetInput(19033,19109,0,19067); /* print info dialog */
+  else
+    CGUIDialogOK::ShowAndGetInput(19033,19147,19110,0); /* print info dialog "Unknown error!" */
+
+  return;
+}
+
+void CPVRTimerInfoTag::SetEpgInfoTag(const CPVREpgInfoTag *tag)
+{
+  if (m_EpgInfo != tag)
+  {
+    if (tag)
+      CLog::Log(LOGINFO, "cPVRTimerInfoTag: timer %s set to epg event %s", Title().c_str(), tag->Title().c_str());
+    else
+      CLog::Log(LOGINFO, "cPVRTimerInfoTag: timer %s set to no epg event", Title().c_str());
+    m_EpgInfo = tag;
+  }
+}
+
+int CPVRTimerInfoTag::ChannelNumber() const
+{
+  CPVRChannel *channeltag = CPVRChannels::GetByClientFromAll(m_iClientNumber, m_iClientID);
+  if (channeltag)
+    return channeltag->ChannelNumber();
+  else
+    return 0;
+}
+
+CStdString CPVRTimerInfoTag::ChannelName() const
+{
+  CPVRChannel *channeltag = CPVRChannels::GetByClientFromAll(m_iClientNumber, m_iClientID);
+  if (channeltag)
+    return channeltag->ChannelName();
+  else
+    return "";
+}
+
+bool CPVRTimerInfoTag::SetDuration(int iDuration)
+{
+  if (m_StartTime != NULL)
+  {
+    m_StopTime = m_StartTime + CDateTimeSpan(0, iDuration / 60, iDuration % 60, 0);
+    return true;
+  }
+
+  return false;
+}
+
+CPVRTimerInfoTag *CPVRTimerInfoTag::InstantTimer()
+{
+  /* create a new timer */
+  CPVRTimerInfoTag *newTag = new CPVRTimerInfoTag();
+  if (!newTag)
+  {
+    CLog::Log(LOGERROR, "%s - couldn't create new timer", __FUNCTION__);
+    return NULL;
+  }
+
+  CFileItem *curPlayingChannel = g_PVRManager.GetCurrentPlayingItem();
+  CPVRChannel *channel = (curPlayingChannel) ? curPlayingChannel->GetPVRChannelInfoTag(): NULL;
+  if (!channel)
+  {
+    CLog::Log(LOGDEBUG, "%s - couldn't find current playing channel", __FUNCTION__);
+    channel = ((CPVRChannels *) g_PVRChannels.GetTV())->GetByChannelNumber(1);
+
+    if (!channel)
+    {
+      CLog::Log(LOGERROR, "%s - cannot find any channels",
+          __FUNCTION__);
+    }
+  }
+
+  int iDuration = g_guiSettings.GetInt("pvrrecord.instantrecordtime");
+  if (!iDuration)
+    iDuration   = 180; /* default to 180 minutes */
+
+  int iPriority = g_guiSettings.GetInt("pvrrecord.defaultpriority");
+  if (!iPriority)
+    iPriority   = 50;  /* default to 50 */
+
+  int iLifetime = g_guiSettings.GetInt("pvrrecord.defaultlifetime");
+  if (!iLifetime)
+    iLifetime   = 30;  /* default to 30 days */
+
+  /* set the timer data */
+  newTag->m_iClientIndex   = -1;
+  newTag->m_bIsActive      = true;
+  newTag->m_strTitle       = g_localizeStrings.Get(19056);
+  newTag->m_iChannelNumber = channel->ChannelNumber();
+  newTag->m_iClientNumber  = channel->ClientChannelNumber();
+  newTag->m_iClientID      = channel->ClientID();
+  newTag->m_bIsRadio       = channel->IsRadio();
+  newTag->m_StartTime      = CDateTime::GetCurrentDateTime();
+  newTag->SetDuration(iDuration);
+  newTag->m_iPriority      = iPriority;
+  newTag->m_iLifetime      = iLifetime;
+
+  /* generate summary string */
+  newTag->m_strSummary.Format("%s %s %s %s %s",
+      newTag->m_StartTime.GetAsLocalizedDate(),
+      g_localizeStrings.Get(19159),
+      newTag->m_StartTime.GetAsLocalizedTime("", false),
+      g_localizeStrings.Get(19160),
+      newTag->m_StopTime.GetAsLocalizedTime("", false));
+
+  /* unused only for reference */
+  newTag->m_strFileNameAndPath = "pvr://timers/new";
+
+  return newTag;
+}
+
+CPVRTimerInfoTag *CPVRTimerInfoTag::CreateFromEpg(const CPVREpgInfoTag &tag)
+{
+  /* create a new timer */
+  CPVRTimerInfoTag *newTag = new CPVRTimerInfoTag();
+  if (!newTag)
+  {
+    CLog::Log(LOGERROR, "%s - couldn't create new timer", __FUNCTION__);
+    return NULL;
+  }
+
+  /* check if a valid channel is set */
+  const CPVRChannel *channel = tag.ChannelTag();
+  if (channel == NULL)
+  {
+    CLog::Log(LOGERROR, "%s - no channel set", __FUNCTION__);
+    return NULL;
+  }
+
+  /* check if the epg end date is in the future */
+  if (tag.End() < CDateTime::GetCurrentDateTime())
+  {
+    CLog::Log(LOGERROR, "%s - end time is in the past", __FUNCTION__);
+    return NULL;
+  }
+
+  int iPriority    = g_guiSettings.GetInt("pvrrecord.defaultpriority");
+  if (!iPriority)
+    iPriority      = 50; /* default to 50 */
+
+  int iLifetime    = g_guiSettings.GetInt("pvrrecord.defaultlifetime");
+  if (!iLifetime)
+    iLifetime      = 30; /* default to 30 days */
+
+  int iMarginStart = g_guiSettings.GetInt("pvrrecord.marginstart");
+  if (!iMarginStart)
+    iMarginStart   = 5;  /* default to 5 minutes */
+
+  int iMarginStop  = g_guiSettings.GetInt("pvrrecord.marginstop");
+  if (!iMarginStop)
+    iMarginStop    = 10; /* default to 10 minutes */
+
+  /* set the timer data */
+  newTag->m_iClientIndex   = tag.UniqueBroadcastID();
+  newTag->m_bIsActive      = true;
+  newTag->m_strTitle       = tag.Title().IsEmpty() ? channel->ChannelName() : tag.Title();
+  newTag->m_iChannelNumber = channel->ChannelNumber();
+  newTag->m_iClientNumber  = channel->ClientChannelNumber();
+  newTag->m_iClientID      = channel->ClientID();
+  newTag->m_bIsRadio       = channel->IsRadio();
+  newTag->m_StartTime      = tag.Start() - CDateTimeSpan(0, iMarginStart / 60, iMarginStart % 60, 0);
+  newTag->m_StopTime       = tag.End() + CDateTimeSpan(0, iMarginStop / 60, iMarginStop % 60, 0);
+  newTag->m_iPriority      = iPriority;
+  newTag->m_iLifetime      = iLifetime;
+
+  /* generate summary string */
+  newTag->m_strSummary.Format("%s %s %s %s %s",
+      newTag->m_StartTime.GetAsLocalizedDate(),
+      g_localizeStrings.Get(19159),
+      newTag->m_StartTime.GetAsLocalizedTime("", false),
+      g_localizeStrings.Get(19160),
+      newTag->m_StopTime.GetAsLocalizedTime("", false));
+
+  /* unused only for reference */
+  newTag->m_strFileNameAndPath = "pvr://timers/new";
+
+  return newTag;
+}
diff --git a/xbmc/pvr/PVRTimerInfoTag.h b/xbmc/pvr/PVRTimerInfoTag.h
new file mode 100644 (file)
index 0000000..b416b30
--- /dev/null
@@ -0,0 +1,178 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+/*
+ * DESCRIPTION:
+ *
+ * CPVRTimerInfoTag is part of the PVRManager to support sheduled recordings.
+ *
+ * The timer information tag holds data about current programmed timers for
+ * the PVRManager. It is possible to create timers directly based upon
+ * a EPG entry by giving the EPG information tag or as instant timer
+ * on currently tuned channel, or give a blank tag to modify later.
+ *
+ * With exception of the blank one, the tag can easily and unmodified added
+ * by the PVRManager function "bool AddTimer(const CFileItem &item)" to
+ * the backend server.
+ *
+ * The filename inside the tag is for reference only and gives the index
+ * number of the tag reported by the PVR backend and can not be played!
+ *
+ *
+ * USED SETUP VARIABLES:
+ *
+ * ------------- Name -------------|---Type--|-default-|--Description-----
+ * pvrmanager.instantrecordtime    = Integer = 180     = Length of a instant timer in minutes
+ * pvrmanager.defaultpriority      = Integer = 50      = Default Priority
+ * pvrmanager.defaultlifetime      = Integer = 99      = Liftime of the timer in days
+ * pvrmanager.marginstart          = Integer = 2       = Minutes to start record earlier
+ * pvrmanager.marginstop           = Integer = 10      = Minutes to stop record later
+ *
+ */
+
+#include "DateTime.h"
+#include "../addons/include/xbmc_pvr_types.h"
+
+class CFileItem;
+class CPVREpgInfoTag;
+class CGUIDialogPVRTimerSettings;
+class CPVRChannel;
+
+class CPVRTimerInfoTag
+{
+private:
+  friend class CGUIDialogPVRTimerSettings;
+
+  CStdString      m_strTitle;           /* name of this timer */
+  CStdString      m_strDir;             /* directory where the recording must be stored */
+
+  CStdString      m_strSummary;         /* summary string with the time to show inside a GUI list */
+  bool            m_bValidSummary;
+
+  bool            m_bIsActive;          /* active flag, if it is false backend ignore the timer */
+  int             m_iChannelNumber;     /* integer value of the channel number */
+  int             m_iClientID;          /* ID of the backend */
+  int             m_iClientIndex;       /* index number of the tag, given by the backend, -1 for new */
+  int             m_iClientNumber;      /* integer value of the client number */
+  bool            m_bIsRadio;           /* is radio channel if set */
+  bool            m_bIsRecording;       /* is this timer recording? */
+  int             m_iPriority;          /* priority of the timer */
+  int             m_iLifetime;          /* lifetime of the timer in days */
+  bool            m_bIsRepeating;       /* repeating timer if true, use the m_FirstDay and repeat flags */
+  CDateTime       m_StartTime;          /* start time */
+  CDateTime       m_StopTime;           /* stop time */
+  CDateTime       m_FirstDay;           /* if it is a repeating timer the first date it starts */
+  int             m_iWeekdays;          /* bit based store of weekdays to repeat */
+  CStdString      m_strFileNameAndPath; /* filename is only for reference */
+
+  const CPVREpgInfoTag *m_EpgInfo;
+
+  void UpdateSummary(void);
+
+  void DisplayError(PVR_ERROR err) const;
+
+public:
+  CPVRTimerInfoTag();
+
+  bool operator ==(const CPVRTimerInfoTag& right) const;
+  bool operator !=(const CPVRTimerInfoTag& right) const;
+  int Compare(const CPVRTimerInfoTag &timer) const;
+
+  void Reset();
+
+  const CStdString GetStatus() const;
+
+  CDateTime Start(void) const { return m_StartTime; }
+  time_t StartTime(void) const;
+  void SetStart(CDateTime Start);
+
+  CDateTime Stop(void) const { return m_StopTime; }
+  time_t StopTime(void) const;
+  void SetStop(CDateTime Stop);
+
+  bool SetDuration(int iDuration);
+
+  CStdString Title(void) const { return m_strTitle; }
+  void SetTitle(CStdString name) { m_strTitle = name; }
+
+  CStdString Dir(void) const { return m_strDir; }
+  void SetDir(CStdString dir) { m_strDir = dir; }
+
+  int Number(void) const { return m_iChannelNumber; }
+  void SetNumber(int Number) { m_iChannelNumber = Number; }
+
+  bool Active(void) const { return m_bIsActive; }
+  void SetActive(bool Active) { m_bIsActive = Active; }
+
+  bool IsRadio(void) const { return m_bIsRadio; }
+  void SetRadio(bool Radio) { m_bIsRadio = Radio; }
+
+  int ClientNumber(void) const { return m_iClientNumber; }
+  void SetClientNumber(int Number) { m_iClientNumber = Number; }
+
+  long ClientIndex(void) const { return m_iClientIndex; }
+  void SetClientIndex(int ClientIndex) { m_iClientIndex = ClientIndex; }
+
+  long ClientID(void) const { return m_iClientID; }
+  void SetClientID(int ClientId) { m_iClientID = ClientId; }
+
+  bool IsRecording(void) const { return m_bIsRecording; }
+  void SetRecording(bool Recording) { m_bIsRecording = Recording; }
+
+  int Lifetime(void) const { return m_iLifetime; }
+  void SetLifetime(int Lifetime) { m_iLifetime = Lifetime; }
+
+  int Priority(void) const { return m_iPriority; }
+  void SetPriority(int Priority) { m_iPriority = Priority; }
+
+  bool IsRepeating(void) const { return m_bIsRepeating; }
+  void SetRepeating(bool Repeating) { m_bIsRepeating = Repeating; }
+
+  int Weekdays(void) const { return m_iWeekdays; }
+  void SetWeekdays(int Weekdays);
+
+  CDateTime FirstDay(void) const { return m_FirstDay; }
+  time_t FirstDayTime(void) const;
+  void SetFirstDay(CDateTime FirstDay) { m_FirstDay = FirstDay; }
+
+  CStdString Summary(void) const { return m_strSummary; }
+  void SetSummary(CStdString Summary) { m_strSummary = Summary; }
+
+  CStdString Path(void) const { return m_strFileNameAndPath; }
+  void SetPath(CStdString path) { m_strFileNameAndPath = path; }
+
+  static CPVRTimerInfoTag *InstantTimer();
+  static CPVRTimerInfoTag *CreateFromEpg(const CPVREpgInfoTag &tag);
+
+  const CPVREpgInfoTag *EpgInfoTag() const { return m_EpgInfo;}
+  void SetEpgInfoTag(const CPVREpgInfoTag *tag);
+
+  /* Channel related Info data */
+  int ChannelNumber(void) const;
+  CStdString ChannelName(void) const;
+
+  /* Client control functions */
+  bool AddToClient() const;
+  bool DeleteFromClient(bool force = false) const;
+  bool RenameOnClient(const CStdString &newname) const;
+  bool UpdateOnClient() const;
+};
diff --git a/xbmc/pvr/PVRTimers.cpp b/xbmc/pvr/PVRTimers.cpp
new file mode 100644 (file)
index 0000000..072ec3f
--- /dev/null
@@ -0,0 +1,344 @@
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "FileItem.h"
+#include "GUIDialogOK.h"
+#include "utils/SingleLock.h"
+
+#include "PVRTimers.h"
+#include "PVRTimerInfoTag.h"
+#include "PVRChannel.h"
+#include "PVRManager.h"
+#include "PVREpgInfoTag.h"
+
+CPVRTimers PVRTimers;
+
+CPVRTimers::CPVRTimers(void)
+{
+
+}
+
+int CPVRTimers::Load()
+{
+  Unload();
+  return Update();
+}
+
+void CPVRTimers::Unload()
+{
+  clear();
+}
+
+int CPVRTimers::Update()
+{
+  CSingleLock lock(m_critSection);
+
+  CLog::Log(LOGDEBUG, "PVRTimers - %s - updating timers",
+      __FUNCTION__);
+
+  int iCurSize = size();
+
+  /* clear channel timers */
+  for (unsigned int iTimerPtr = 0; iTimerPtr < size(); iTimerPtr++)
+  {
+    CPVRTimerInfoTag *timerTag = &at(iTimerPtr);
+    if (!timerTag || !timerTag->Active())
+      continue;
+
+    CPVREpgInfoTag *epgTag = (CPVREpgInfoTag *)timerTag->EpgInfoTag();
+    if (!epgTag)
+      continue;
+
+    epgTag->SetTimer(NULL);
+  }
+
+  /* clear timers */
+  clear();
+
+  /* get all timers from the clients */
+  CLIENTMAP *clients = g_PVRManager.Clients();
+  CLIENTMAPITR itr = clients->begin();
+  while (itr != clients->end())
+  {
+    if (g_PVRManager.GetClientProps((*itr).second->GetID())->SupportTimers)
+    {
+      if ((*itr).second->GetNumTimers() > 0)
+      {
+        (*itr).second->GetAllTimers(this);
+      }
+    }
+    itr++;
+  }
+
+  //XXX
+  g_PVRManager.UpdateRecordingsCache();
+
+  /* set channel timers */
+  for (unsigned int ptr = 0; ptr < size(); ptr++)
+  {
+    /* get the timer tag */
+    CPVRTimerInfoTag *timerTag = &at(ptr);
+    if (!timerTag || !timerTag->Active())
+      continue;
+
+    /* try to get the channel */
+    CPVRChannel *channel = CPVRChannels::GetByClientFromAll(timerTag->Number(), timerTag->ClientID());
+    if (!channel)
+      continue;
+
+    /* try to get the EPG */
+    CPVREpg *epg = channel->GetEPG();
+    if (!epg)
+      continue;
+
+    /* try to set the timer on the epg tag that matches */
+    CPVREpgInfoTag *epgTag = (CPVREpgInfoTag *) epg->InfoTagBetween(timerTag->Start(), timerTag->Stop());
+    if (epgTag)
+      epgTag->SetTimer(timerTag);
+  }
+
+  return size() - iCurSize;
+}
+
+bool CPVRTimers::Update(const CPVRTimerInfoTag &timer)
+{
+  // TODO currently just adds the timer to this container
+  push_back(timer);
+
+  return true;
+}
+
+/********** getters **********/
+
+int CPVRTimers::GetTimers(CFileItemList* results)
+{
+  Update();
+
+  for (unsigned int i = 0; i < size(); ++i)
+  {
+    CFileItemPtr timer(new CFileItem(at(i)));
+    results->Add(timer);
+  }
+
+  return size();
+}
+
+CPVRTimerInfoTag *CPVRTimers::GetNextActiveTimer(void)
+{
+  CSingleLock lock(m_critSection);
+  CPVRTimerInfoTag *t0 = NULL;
+  for (unsigned int i = 0; i < size(); i++)
+  {
+    if ((at(i).Active()) && (!t0 || (at(i).Stop() > CDateTime::GetCurrentDateTime() && at(i).Compare(*t0) < 0)))
+    {
+      t0 = &at(i);
+    }
+  }
+  return t0;
+}
+
+int CPVRTimers::GetNumTimers()
+{
+  return size();
+}
+
+bool CPVRTimers::GetDirectory(const CStdString& strPath, CFileItemList &items)
+{
+  CStdString base(strPath);
+  CUtil::RemoveSlashAtEnd(base);
+
+  CURL url(strPath);
+  CStdString fileName = url.GetFileName();
+  CUtil::RemoveSlashAtEnd(fileName);
+
+  if (fileName == "timers")
+  {
+    CFileItemPtr item;
+
+    Update();
+
+    item.reset(new CFileItem(base + "/add.timer", false));
+    item->SetLabel(g_localizeStrings.Get(19026));
+    item->SetLabelPreformated(true);
+    items.Add(item);
+
+    for (unsigned int i = 0; i < size(); ++i)
+    {
+      item.reset(new CFileItem(at(i)));
+      items.Add(item);
+    }
+
+    return true;
+  }
+  return false;
+}
+
+/********** channel methods **********/
+
+bool CPVRTimers::ChannelHasTimers(const CPVRChannel &channel)
+{
+  for (unsigned int ptr = 0; ptr < size(); ptr++)
+  {
+    CPVRTimerInfoTag timer = at(ptr);
+
+    if (timer.ChannelNumber() == channel.ChannelNumber() && timer.IsRadio() == channel.IsRadio())
+      return true;
+  }
+
+  return false;
+}
+
+
+bool CPVRTimers::DeleteTimersOnChannel(const CPVRChannel &channel, bool bForce /* = false */)
+{
+  bool bReturn = true;
+
+  for (unsigned int ptr = 0; ptr < size(); ptr++)
+  {
+    CPVRTimerInfoTag timer = at(ptr);
+
+    if (timer.ChannelNumber() == channel.ChannelNumber() && timer.IsRadio() == channel.IsRadio())
+    {
+      bReturn = timer.DeleteFromClient(bForce) && bReturn;
+      erase(begin() + ptr);
+      ptr--;
+    }
+  }
+
+  return bReturn;
+}
+
+/********** static methods **********/
+
+bool CPVRTimers::AddTimer(const CFileItem &item)
+{
+  /* Check if a CPVRTimerInfoTag is inside file item */
+  if (!item.IsPVRTimer())
+  {
+    CLog::Log(LOGERROR, "cPVRTimers: AddTimer no TimerInfoTag given!");
+    return false;
+  }
+
+  const CPVRTimerInfoTag* tag = item.GetPVRTimerInfoTag();
+  if (!tag)
+    return false;
+
+  return AddTimer(*tag);
+}
+
+bool CPVRTimers::AddTimer(const CPVRTimerInfoTag &item)
+{
+  if (!g_PVRManager.GetClientProps(item.ClientID())->SupportTimers)
+  {
+    CGUIDialogOK::ShowAndGetInput(19033,0,19215,0);
+    return false;
+  }
+
+  return item.AddToClient();
+}
+
+bool CPVRTimers::DeleteTimer(const CFileItem &item, bool bForce /* = false */)
+{
+  /* Check if a CPVRTimerInfoTag is inside file item */
+  if (!item.IsPVRTimer())
+  {
+    CLog::Log(LOGERROR, "cPVRTimers: DeleteTimer no TimerInfoTag given!");
+    return false;
+  }
+
+  const CPVRTimerInfoTag* tag = item.GetPVRTimerInfoTag();
+  if (!tag)
+    return false;
+
+  return DeleteTimer(*tag, bForce);
+}
+
+bool CPVRTimers::DeleteTimer(const CPVRTimerInfoTag &item, bool bForce /* = false */)
+{
+  return item.DeleteFromClient(bForce);
+}
+
+bool CPVRTimers::RenameTimer(CFileItem &item, const CStdString &strNewName)
+{
+  /* Check if a CPVRTimerInfoTag is inside file item */
+  if (!item.IsPVRTimer())
+  {
+    CLog::Log(LOGERROR, "cPVRTimers: RenameTimer no TimerInfoTag given!");
+    return false;
+  }
+
+  CPVRTimerInfoTag* tag = item.GetPVRTimerInfoTag();
+  if (!tag)
+    return false;
+
+  return RenameTimer(*tag, strNewName);
+}
+
+bool CPVRTimers::RenameTimer(CPVRTimerInfoTag &item, const CStdString &strNewName)
+{
+  if (item.RenameOnClient(strNewName))
+  {
+    item.SetTitle(strNewName);
+    return true;
+  }
+
+  return false;
+}
+
+bool CPVRTimers::UpdateTimer(const CFileItem &item)
+{
+  /* Check if a CPVRTimerInfoTag is inside file item */
+  if (!item.IsPVRTimer())
+  {
+    CLog::Log(LOGERROR, "cPVRTimers: UpdateTimer no TimerInfoTag given!");
+    return false;
+  }
+
+  const CPVRTimerInfoTag* tag = item.GetPVRTimerInfoTag();
+  if (!tag)
+    return false;
+
+  return UpdateTimer(*tag);
+}
+
+bool CPVRTimers::UpdateTimer(const CPVRTimerInfoTag &item)
+{
+  return item.UpdateOnClient();
+}
+
+CPVRTimerInfoTag *CPVRTimers::GetMatch(CDateTime t)
+{
+  // TODO
+  return NULL;
+}
+
+CPVRTimerInfoTag *CPVRTimers::GetMatch(time_t t)
+{
+  // TODO
+  return NULL;
+}
+
+CPVRTimerInfoTag *CPVRTimers::GetMatch(const CPVREpgInfoTag *Epg, int *Match)
+{
+  // TODO
+  return NULL;
+}
+
diff --git a/xbmc/pvr/PVRTimers.h b/xbmc/pvr/PVRTimers.h
new file mode 100644 (file)
index 0000000..28d6658
--- /dev/null
@@ -0,0 +1,152 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "DateTime.h"
+#include "../addons/include/xbmc_pvr_types.h"
+
+class CFileItem;
+class CPVREpgInfoTag;
+class CGUIDialogPVRTimerSettings;
+class CPVRTimerInfoTag;
+
+class CPVRTimers : public std::vector<CPVRTimerInfoTag>
+{
+private:
+  CCriticalSection m_critSection;
+
+public:
+  CPVRTimers(void);
+
+  /**
+   * Load the timers from the clients.
+   * Returns the amount of timers that were added.
+   */
+  int Load();
+
+  /**
+   * Clear this timer list.
+   */
+  void Unload();
+
+  /**
+   * Refresh the channel list from the clients.
+   * Returns the amount of timers that were added.
+   */
+  int Update();
+
+  /**
+   * Update a timer entry in this container.
+   */
+  bool Update(const CPVRTimerInfoTag &timer);
+
+  /********** getters **********/
+
+  /**
+   * Get all known timers.
+   */
+  int GetTimers(CFileItemList* results);
+
+  /**
+   * The timer that will be active next.
+   * Returns null if there is none.
+   */
+  CPVRTimerInfoTag *GetNextActiveTimer(void);
+
+  /**
+   * The amount of timers in this container.
+   */
+  int GetNumTimers();
+
+  /**
+   * Get the directory for a path.
+   */
+  bool GetDirectory(const CStdString& strPath, CFileItemList &items);
+
+  /********** channel methods **********/
+
+  /**
+   * Check if there are any active timers on a channel.
+   */
+  bool ChannelHasTimers(const CPVRChannel &channel);
+
+  /**
+   * Delete all active timers on a channel.
+   */
+  bool DeleteTimersOnChannel(const CPVRChannel &channel, bool bForce = false);
+
+  /********** static methods **********/
+
+  /**
+   * Add a timer to the client.
+   * True if it was sent correctly, false if not.
+   */
+  static bool AddTimer(const CFileItem &item);
+
+  /**
+   * Add a timer to the client.
+   * True if it was sent correctly, false if not.
+   */
+  static bool AddTimer(const CPVRTimerInfoTag &item);
+
+  /**
+   * Delete a timer on the client.
+   * True if it was sent correctly, false if not.
+   */
+  static bool DeleteTimer(const CFileItem &item, bool bForce = false);
+
+  /**
+   * Delete a timer on the client.
+   * True if it was sent correctly, false if not.
+   */
+  static bool DeleteTimer(const CPVRTimerInfoTag &item, bool bForce = false);
+
+  /**
+   * Rename a timer on the client.
+   * True if it was sent correctly, false if not.
+   */
+  static bool RenameTimer(CFileItem &item, const CStdString &strNewName);
+
+  /**
+   * Rename a timer on the client.
+   * True if it was sent correctly, false if not.
+   */
+  static bool RenameTimer(CPVRTimerInfoTag &item, const CStdString &strNewName);
+
+  /**
+   * Get updated timer information from the client.
+   * True if it was requested correctly, false if not.
+   */
+  static bool UpdateTimer(const CFileItem &item);
+
+  /**
+   * Get updated timer information from the client.
+   * True if it was requested correctly, false if not.
+   */
+  static bool UpdateTimer(const CPVRTimerInfoTag &item);
+
+  // TODO
+  CPVRTimerInfoTag *GetMatch(CDateTime t);
+  CPVRTimerInfoTag *GetMatch(time_t t);
+  CPVRTimerInfoTag *GetMatch(const CPVREpgInfoTag *Epg, int *Match = NULL);
+};
+
+extern CPVRTimers PVRTimers;
diff --git a/xbmc/pvrclients/Makefile.include.in b/xbmc/pvrclients/Makefile.include.in
new file mode 100644 (file)
index 0000000..1b8b30b
--- /dev/null
@@ -0,0 +1,30 @@
+#
+# Makefile include for PVR AddOns
+#
+
+## INCLUDES
+INCLUDES  = -I. -I../.. -I../../cores/dvdplayer/DVDDemuxers -I../../addons/include
+INCLUDES += -I../../lib -I../../../guilib -I ../../../addons/library.xbmc.pvr -I../../../addons/library.xbmc.addon
+ifneq (@USE_EXTERNAL_FFMPEG@,1)
+       INCLUDES += -I../../cores/dvdplayer/Codecs/ffmpeg
+endif
+ifeq ($(findstring Darwin,$(shell uname -a)), Darwin)
+       INCLUDES += -I/opt/local/include
+endif
+
+## DEFINES
+DEFINES += -D_LINUX -fPIC -DUSE_DEMUX
+
+## CXXFLAGS
+ifeq ($(findstring Darwin,$(shell uname -a)), Darwin)
+       CXXFLAGS ?= -g -O2 -Wall -Woverloaded-virtual -Wno-parentheses -dynamiclib -single_module -undefined dynamic_lookup
+       DEFINES += -D__APPLE__ -isysroot /Developer/SDKs/MacOSX10.4u.sdk -mmacosx-version-min=10.4 -fno-common
+else
+       CXXFLAGS ?= -g -O2 -Wall -Woverloaded-virtual -Wno-parentheses
+endif
+
+include ../../../Makefile.include
+-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS)))
+
+$(LIB): $(OBJS)
+       $(CXX) $(CXXFLAGS) $(LDFLAGS) -shared -g $(OBJS) $(LIBS) $(LIBDIRS) $(SILIB) -o $(LIB)
diff --git a/xbmc/pvrclients/MediaPortal/Makefile.in b/xbmc/pvrclients/MediaPortal/Makefile.in
new file mode 100644 (file)
index 0000000..434d271
--- /dev/null
@@ -0,0 +1,21 @@
+#
+# Makefile for the XBMC MediaPortal PVR AddOn
+#
+# See the README for copyright information and
+# how to reach the author.
+#
+
+LIBS   =-ldl
+LIBDIR = ../../../addons/pvr.team-mediaportal.tvserver
+LIB    = ../../../addons/pvr.team-mediaportal.tvserver/XBMC_MPTV.pvr
+
+SRCS   = channels.cpp \
+       client.cpp \
+       epg.cpp \
+       pvrclient-mediaportal.cpp \
+       recordings.cpp \
+       timers.cpp \
+       utils.cpp \
+       Socket.cpp
+
+include ../Makefile.include
diff --git a/xbmc/pvrclients/MediaPortal/README b/xbmc/pvrclients/MediaPortal/README
new file mode 100644 (file)
index 0000000..a4f321b
--- /dev/null
@@ -0,0 +1,150 @@
+XBMC MediaPortal TV-client ('MPTV') PVR Add-on
+----------------------------------------------
+Supported platforms (pvrclient): Windows
+(Linux, OSX: work in progress)
+Dependencies:
+- MediaPortal TVServer 1.1.0 beta1 or higher
+- TVServerXBMC v1.0.7.0 or higher
+
+THIS IS A PRELIMINARY README AND IS SUBJECT TO CHANGE!!!
+
+Written by:                  Marcel Groothuis
+
+Project's homepage:          http://www.scintilla.utwente.nl/~marcelg/xbmc/
+
+Latest version available at: XBMC's pvr-testing2 branch
+
+The MediaPortal TV-Server plugin "TVServerXMBC", status updates, screenshots, 
+last-minute patches can be found at:
+
+http://www.scintilla.utwente.nl/~marcelg/xbmc/
+
+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 of the License, or
+any later version.
+See the file LICENSE.GPL for more information.
+
+----------------------------------------------
+General description:
+
+This is a PVR Add-on for XBMC to access/control the MediaPortal TV-Server
+backend from XBMC. It consists of two plugins: one for XBMC-PVR (1) and one for
+the MediaPortal TV Server (2).
+
+1. PVR client "XBMC_MPTV_win32.pvr" (for Windows):
+   This is the addon in this directory. After building the XBMC solution (and
+   the "pvrclient_mptv") this plugin file should be in addons\pvr\MediaPortal\
+
+The MediaPortal TV Server is written in C#, making it difficult for XBMC to
+control it directly. The TV Server connection of this AddOn depends on a
+special plugin at the TV Server side, called TVServerXBMC.
+
+
+2. TV Server plugin TVServerXBMC.dll/.exe (Windows only):
+   The TVServerXBMC plugin for the TV Server provides a socket interface to
+   XBMC to control the TV Server.
+   You can download it separately from:
+   http://www.scintilla.utwente.nl/~marcelg/xbmc
+   (It is not included by default in the XBMC pvr-testing2 branch, because it
+   will introduce a dependency on Visual C#.)
+   Please read the "readme.txt" file included in the TVServerXBMC zip files
+   for more information.
+
+----------------------------------------------
+Detailed instructions:
+
+(Preliminary...)
+
+1. Install MediaPortal & MediaPortal TV Server (1.1.0 beta1 or daily build 01-08-2010)
+   (for older versions, you will need to recompile the TVServerXBMC plugin from source)
+2. Use MediaPortal to make sure that the TV Server is working fine
+3. Download the TVServerXBMC plugin for the TV Server (see my website)
+4. Run the TVServerXBMC.exe (standalone version of the TV Server plugin) and
+   test it (see the readme.txt file included in the TVServerXBMC zip file).
+   Test for example the commands "ListTVChannels", "TimeshiftChannel"
+   (id is the first field returned by "ListTVChannels"). The "TimeshiftChannel"
+   command should return a URL like "rtsp://xxx.xxx.xxx.xx/stream2.0" on a
+   successful timeshift start. You can test this URL in VLC player. This should
+   work before proceeding to the next steps.
+5. Test the rtsp stream in XBMC (you can just use the standard 9.11)
+   Create a playlist file "rtsp-stream.m3u" with the rtsp:// URL as content and
+   start it from inside XBMC.
+   Example contents rtsp-stream.m3u:
+-----
+rtsp://192.168.2.5/steam2.0
+-----
+When this is all working fine, you can finally build XBMC-pvr-testing2:
+
+6. Build the XBMC solution in VC Express (pvr-testing2)
+  (check if XBMC_MPTV_win32.pvr was created succesfully in addons\pvr\MediaPortal)
+
+----------------------------------------------
+MediaPortal PVR-addon settings:
+
+Names are taken from addons/pvr/MediaPortal/settings.xml
+
+host: "Mediaportal Hostname"
+  IP-address of the machine that runs the TVServerXBMC tool
+  Default: 127.0.0.1 (localhost)
+port: "Mediaportal XBMC plugin Port"
+  Port number for the TVServerXBMC.
+  Default: 9596
+ftaonly: "Free-to-air only"
+  Fetch/show only Free-to-air channels from MediaPortal TV
+  Default: false
+useradio: "Include Radio"
+  Fetch also radio channels
+  Default: true
+convertchar: "Character Set Conversion"
+  Enable character conversion to UTF-8.
+  Does nothing. Not yet implemented.
+  Default: false
+timeout: "Connect timeout (s)"
+  Timeout on XBMC<->TVServerXBMC communication. After the selected timeout, 
+  XBMC won't wait any longer for an answer from TVServerXBMC and abort the 
+  selected action. Bottleneck is the timeshift start for TV channels. This 
+  can take a long time, so don't make this
+  value too small.
+  Default: 6
+tvgroup: "Import only TV Channels from group"
+  Allows you to fetch only the TV channels in a specific MediaPortal TVServer 
+  group. E.g. you can create a "XBMC" group at the TVServer side that contains
+  only the TV channels that you want to appear at the XBMC side. 
+  Default: <empty>
+radiogroup: "Import only Radio Channels from group"
+  Allows you to fetch only the radio channels in a specific MediaPortal TVServer 
+  group. E.g. you can create a "XBMC" group at the TVServer side that contains
+  only the radio channels that you want to appear at the XBMC side. 
+  Default: <empty>
+resolvertsphostname: "Convert hostname to IP-adress"
+  Resolve the TVServer hostname in the rtsp:// streaming URLs to an ip-address
+  at the TVServerXBMC side. May help you with connection problems.
+  Default: false
+readgenre: "EPG: Read genre strings (slow)"
+  Try to translate the EPG genre strings from MediaPortal into XBMC compatible
+  genre id's. However, depending on your EPG source, MediaPortal may return
+  strings in your local language. In this case, you can skip the genre
+  translation via readgenre=false.
+  The current implementation translates only English strings as workaround for
+  the mismatch between XBMC's genre ids and MediaPortals genre strings.
+  Default: false (= don't read and translate the genre strings)
+sleeponurl: "Wait after tuning a channel (ms)"
+  Adds an additional waiting time between the request to start a timeshift for
+  the selected channel and opening the rtsp:// stream in XBMC. You may need this
+  in case XBMC tries to open the returned rtsp:// stream before it is really
+  available. Typical symptom: the channel doesn't play the first time, but it
+  does play the second time.
+  Default: 0 (milliseconds)
+
+----------------------------------------------
+Troubleshooting:
+TODO...
+
+
+----------------------------------------------
+Links:
+
+MediaPortal:             http://www.team-mediaportal.com
+TVServer plugin:         http://www.scintilla.utwente.nl/~marcelg/xbmc
+
diff --git a/xbmc/pvrclients/MediaPortal/Socket.cpp b/xbmc/pvrclients/MediaPortal/Socket.cpp
new file mode 100644 (file)
index 0000000..e95b69b
--- /dev/null
@@ -0,0 +1,638 @@
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <string>
+#include "xbmc_pvr_types.h"
+#include "libXBMC_addon.h"
+#include "pvrclient-mediaportal_os.h"
+#include "utils.h"
+#include "client.h"
+#include "Socket.h"
+
+using namespace std;
+
+/* Master defines for client control */
+#define RECEIVE_TIMEOUT 6 //sec
+
+Socket::Socket(const enum SocketFamily family, const enum SocketDomain domain, const enum SocketType type, const enum SocketProtocol protocol)
+{
+  _sd = INVALID_SOCKET;
+  _family = family;
+  _domain = domain;
+  _type = type;
+  _protocol = protocol;
+  memset (&_sockaddr, 0, sizeof( _sockaddr ) );
+}
+
+
+Socket::Socket()
+{
+  // Default constructor, default settings
+  _sd = INVALID_SOCKET;
+  _family = af_inet;
+  _domain = pf_inet;
+  _type = sock_stream;
+  _protocol = tcp;
+  memset (&_sockaddr, 0, sizeof( _sockaddr ) );
+}
+
+
+Socket::~Socket()
+{
+  close();
+}
+
+bool Socket::setHostname ( const std::string host )
+{
+  if (isalpha(host.c_str()[0]))
+  {
+    // host address is a name
+    struct hostent *he = NULL;
+    if ((he = gethostbyname( host.c_str() )) == 0)
+    {
+      //errormessage( WSAGetLastError(), "Socket::setHostname");
+
+      return false;
+    }
+
+    _sockaddr.sin_addr = *((in_addr *) he->h_addr);
+  }
+  else
+  {
+    _sockaddr.sin_addr.s_addr = inet_addr(host.c_str());
+  }
+  return true;
+}
+
+
+bool Socket::close()
+{
+  if (is_valid())
+  {
+    closesocket(_sd);
+    _sd = INVALID_SOCKET;
+#if defined(_WIN32) || defined(_WIN64)
+    WSACleanup();
+#endif //WINDOWS
+    return true;
+  }
+  return false;
+}
+
+bool Socket::create()
+{
+  if( is_valid() )
+  {
+    close();
+  }
+
+#if defined(_WIN32) || defined(_WIN64)
+  // initialize winsock:
+  if (WSAStartup(MAKEWORD(2,2),&_wsaData) != 0)
+  {
+    return false;
+  }
+
+  WORD wVersionRequested = MAKEWORD(2,2);
+
+  // check version
+  if (_wsaData.wVersion != wVersionRequested)
+  {
+    return false;
+  }
+#endif //WINDOWS
+
+  _sd = socket(_family, _type, _protocol );
+  //0 indicates that the default protocol for the type selected is to be used.
+  //For example, IPPROTO_TCP is chosen for the protocol if the type  was set to
+  //SOCK_STREAM and the address family is AF_INET.
+
+  if (_sd == INVALID_SOCKET)
+  {
+    //errormessage( WSAGetLastError(), "Socket::create" );
+    return false;
+  }
+
+  return true;
+}
+
+
+bool Socket::bind ( const unsigned short port )
+{
+
+  if (!is_valid())
+  {
+    return false;
+  }
+
+  _sockaddr.sin_family = _family;
+  _sockaddr.sin_addr.s_addr = INADDR_ANY;  //listen to all
+  _sockaddr.sin_port = htons( port );
+
+  int bind_return = ::bind(_sd, (sockaddr*)(&_sockaddr), sizeof(_sockaddr));
+
+  if ( bind_return == -1 )
+  {
+    //errormessage( WSAGetLastError(), "Socket::bind" );
+    return false;
+  }
+
+  return true;
+}
+
+
+bool Socket::listen() const
+{
+
+  if (!is_valid())
+  {
+    return false;
+  }
+
+  int listen_return = ::listen (_sd, SOMAXCONN);
+  //This is defined as 5 in winsock.h, and 0x7FFFFFFF in winsock2.h.
+  //linux 128//MAXCONNECTIONS =1
+
+  if (listen_return == -1)
+  {
+    //errormessage( WSAGetLastError(), "Socket::listen" );
+    return false;
+  }
+
+  return true;
+}
+
+
+bool Socket::accept ( Socket& new_socket ) const
+{
+  if (!is_valid())
+  {
+    return false;
+  }
+
+  socklen_t addr_length = sizeof( _sockaddr );
+  new_socket._sd = ::accept(_sd, const_cast<sockaddr*>( (const sockaddr*) &_sockaddr), &addr_length );
+
+  if (new_socket._sd <= 0)
+  {
+    //errormessage( WSAGetLastError(), "Socket::accept" );
+    return false;
+  }
+
+  return true;
+}
+
+
+int Socket::send ( const std::string data )
+{
+  if (!is_valid())
+  {
+    return 0;
+  }
+
+  int status = Socket::send( (const char*) data.c_str(), (const unsigned int) data.size());
+
+  return status;
+}
+
+
+int Socket::send ( const char* data, const unsigned int len )
+{
+  fd_set set_w, set_e;
+  struct timeval tv;
+  int  result;
+
+  if (!is_valid())
+  {
+    return 0;
+  }
+
+  // fill with new data
+  tv.tv_sec  = 0;
+  tv.tv_usec = 0;
+
+  FD_ZERO(&set_w);
+  FD_ZERO(&set_e);
+  FD_SET(_sd, &set_w);
+  FD_SET(_sd, &set_e);
+
+  result = select(FD_SETSIZE, &set_w, NULL, &set_e, &tv);
+
+  if (result < 0)
+  {
+    XBMC->Log(LOG_ERROR, "Socket::send  - select failed");
+    _sd = INVALID_SOCKET;
+    return 0;
+  }
+  if (FD_ISSET(_sd, &set_w))
+  {
+    XBMC->Log(LOG_ERROR, "Socket::send  - failed to send data");
+    _sd = INVALID_SOCKET;
+    return 0;
+  }
+
+  int status = ::send(_sd, data, len, 0 );
+
+  if (status == -1)
+  {
+    //errormessage( WSAGetLastError(), "Socket::send");
+    XBMC->Log(LOG_ERROR, "Socket::send  - failed to send data");
+    _sd = INVALID_SOCKET;
+  }
+  return status;
+}
+
+
+int Socket::sendto ( const char* data, unsigned int size, bool sendcompletebuffer) const
+{
+  int sentbytes = 0;
+  int i;
+
+  do
+  {
+    i = ::sendto(_sd, data, size, 0, (const struct sockaddr*) &_sockaddr, sizeof( _sockaddr ) );
+
+    if (i <= 0)
+    {
+#if defined(_WIN32) || defined(_WIN64)
+      errormessage( WSAGetLastError(), "Socket::sendto");
+      WSACleanup();
+#endif //WINDOWS
+      return i;
+    }
+    sentbytes += i;
+  } while ( (sentbytes < (int) size) && (sendcompletebuffer == true));
+
+  return i;
+}
+
+
+int Socket::receive ( std::string& data, unsigned int minpacketsize ) const
+{
+  char * buf = NULL;
+  int status = 0;
+
+  if (!is_valid())
+  {
+    return 0;
+  }
+
+  buf = new char [ minpacketsize + 1 ];
+  memset ( buf, 0, minpacketsize + 1 );
+
+  status = receive( buf, minpacketsize, minpacketsize );
+
+  data = buf;
+
+  delete[] buf;
+  return status;
+}
+
+//Receive until error or \n
+bool Socket::ReadResponse (int &code, vector<string> &lines)
+{
+  fd_set         set_r, set_e;
+  timeval        timeout;
+  int            result;
+  int            retries = 6;
+  char           buffer[2048];
+  char           cont = 0;
+  string         line;
+  size_t         pos1 = 0, pos2 = 0, pos3 = 0;
+
+  code = 0;
+
+  while (true)
+  {
+    while ((pos1 = line.find("\r\n", pos3)) != std::string::npos)
+    {
+      pos2 = line.find(cont);
+
+      lines.push_back(line.substr(pos2+1, pos1-pos2-1));
+
+      line.erase(0, pos1 + 2);
+      pos3 = 0;
+      return true;
+    }
+
+    // we only need to recheck 1 byte
+    if (line.size() > 0)
+    {
+      pos3 = line.size() - 1;
+    }
+    else
+    {
+      pos3 = 0;
+    }
+
+    if (cont == ' ')
+    {
+      break;
+    }
+
+    timeout.tv_sec  = RECEIVE_TIMEOUT;
+    timeout.tv_usec = 0;
+
+    // fill with new data
+    FD_ZERO(&set_r);
+    FD_ZERO(&set_e);
+    FD_SET(_sd, &set_r);
+    FD_SET(_sd, &set_e);
+    result = select(FD_SETSIZE, &set_r, NULL, &set_e, &timeout);
+
+    if (result < 0)
+    {
+      XBMC->Log(LOG_DEBUG, "CVTPTransceiver::ReadResponse - select failed");
+      lines.push_back("ERROR: select failed");
+      code = 1; //error
+      _sd = INVALID_SOCKET;
+      return false;
+    }
+
+    if (result == 0)
+    {
+      if (retries != 0)
+      {
+         XBMC->Log(LOG_DEBUG, "CVTPTransceiver::ReadResponse - timeout waiting for response, retrying... (%i)", retries);
+         retries--;
+        continue;
+      } else {
+         XBMC->Log(LOG_DEBUG, "CVTPTransceiver::ReadResponse - timeout waiting for response. Failed after 10 retries.");
+         lines.push_back("ERROR: failed after 10 retries");
+         code = 1; //error
+        _sd = INVALID_SOCKET;
+         return false;
+      }
+    }
+
+    result = recv(_sd, buffer, sizeof(buffer) - 1, 0);
+    if (result < 0)
+    {
+      XBMC->Log(LOG_DEBUG, "CVTPTransceiver::ReadResponse - recv failed");
+      lines.push_back("ERROR: recv failed");
+      code = 1; //error
+      _sd = INVALID_SOCKET;
+      return false;
+    }
+    buffer[result] = 0;
+
+    line.append(buffer);
+  }
+
+  return true;
+}
+
+int Socket::receive ( std::string& data) const
+{
+  char buf[MAXRECV + 1];
+  int status = 0;
+
+  if ( !is_valid() )
+  {
+    return 0;
+  }
+
+  memset ( buf, 0, MAXRECV + 1 );
+  status = receive( buf, MAXRECV, 0 );
+  data = buf;
+
+  return status;
+}
+
+int Socket::receive ( char* data, const unsigned int buffersize, const unsigned int minpacketsize ) const
+{
+
+  unsigned int receivedsize = 0;
+  int status = 0;
+
+  if ( !is_valid() )
+  {
+    return 0;
+  }
+
+  while ( (receivedsize <= minpacketsize) && (receivedsize < buffersize) )
+  {
+    status = ::recv(_sd, data+receivedsize, (buffersize - receivedsize), 0 );
+
+    if ( status == SOCKET_ERROR )
+    {
+      //errormessage( WSAGetLastError(), "Socket::receive" );
+      return status;
+    }
+
+    receivedsize += status;
+  }
+
+  return receivedsize;
+}
+
+
+int Socket::recvfrom ( char* data, const int buffersize, const int minpacketsize, struct sockaddr* from, socklen_t* fromlen) const
+{
+  int status = ::recvfrom(_sd, data, buffersize, 0, from, fromlen);
+
+  return status;
+}
+
+
+bool Socket::connect ( const std::string host, const unsigned short port )
+{
+  if ( !is_valid() )
+  {
+    return false;
+  }
+
+  _sockaddr.sin_family = _family;
+  _sockaddr.sin_port = htons ( port );
+
+  if ( !setHostname( host ) )
+  {
+    return false;
+  }
+
+  int status = ::connect ( _sd, reinterpret_cast<sockaddr*>(&_sockaddr), sizeof ( _sockaddr ) );
+
+  if ( status == SOCKET_ERROR )
+  {
+    //errormessage( WSAGetLastError(), "Socket::connect" );
+    return false;
+  }
+
+  return true;
+}
+
+bool Socket::reconnect()
+{
+  if ( _sd != INVALID_SOCKET )
+  {
+    return true;
+  }
+
+  if( !create() )
+    return false;
+
+  int status = ::connect ( _sd, reinterpret_cast<sockaddr*>(&_sockaddr), sizeof ( _sockaddr ) );
+
+  if ( status == SOCKET_ERROR )
+  {
+    //errormessage( WSAGetLastError(), "Socket::connect" );
+    return false;
+  }
+
+  return true;
+}
+
+#if defined(_WIN32) || defined(_WIN64)
+bool Socket::set_non_blocking ( const bool b )
+{
+  u_long iMode;
+
+  if ( b )
+    iMode = 1;  // enable non_blocking
+  else
+    iMode = 0;  // disable non_blocking
+
+  if (ioctlsocket(_sd, FIONBIO, &iMode) == -1)
+  {
+    XBMC->Log(LOG_ERROR, "Socket::set_non_blocking - Can't set socket condition to: %i", iMode);
+    return false;
+  }
+
+  return true;
+}
+#elif defined(_LINUX)
+bool Socket::set_non_blocking ( const bool b )
+{
+  int opts;
+
+  opts = fcntl(_sd, F_GETFL);
+
+  if ( opts < 0 )
+  {
+    return false;
+  }
+
+  if ( b )
+    opts = ( opts | O_NONBLOCK );
+  else
+    opts = ( opts & ~O_NONBLOCK );
+
+  if(fcntl (_sd , F_SETFL, opts) == -1)
+  {
+    XBMC->Log(LOG_ERROR, "Socket::set_non_blocking - Can't set socket flags to: %i", opts);
+    return false;
+  }
+  return true;
+}
+#endif
+
+bool Socket::is_valid() const
+{
+  return (_sd != INVALID_SOCKET);
+}
+
+#if defined(_WIN32) || defined(_WIN64)
+void Socket::errormessage( int errnum, const char* functionname) const
+{
+  const char* errmsg = NULL;
+
+  switch (errnum)
+  {
+  case WSANOTINITIALISED:
+    errmsg = "A successful WSAStartup call must occur before using this function.";
+    break;
+  case WSAENETDOWN:
+    errmsg = "The network subsystem or the associated service provider has failed";
+    break;
+  case WSA_NOT_ENOUGH_MEMORY:
+    errmsg = "Insufficient memory available";
+    break;
+  case WSA_INVALID_PARAMETER:
+    errmsg = "One or more parameters are invalid";
+    break;
+  case WSA_OPERATION_ABORTED:
+    errmsg = "Overlapped operation aborted";
+    break;
+  case WSAEINTR:
+    errmsg = "Interrupted function call";
+    break;
+  case WSAEBADF:
+    errmsg = "File handle is not valid";
+    break;
+  case WSAEACCES:
+    errmsg = "Permission denied";
+    break;
+  case WSAEFAULT:
+    errmsg = "Bad address";
+    break;
+  case WSAEINVAL:
+    errmsg = "Invalid argument";
+    break;
+  case WSAENOTSOCK:
+    errmsg = "Socket operation on nonsocket";
+    break;
+  case WSAEDESTADDRREQ:
+    errmsg = "Destination address required";
+    break;
+  case WSAEMSGSIZE:
+    errmsg = "Message too long";
+    break;
+  case WSAEPROTOTYPE:
+    errmsg = "Protocol wrong type for socket";
+    break;
+  case WSAENOPROTOOPT:
+    errmsg = "Bad protocol option";
+    break;
+  case WSAEPFNOSUPPORT:
+    errmsg = "Protocol family not supported";
+    break;
+  case WSAEAFNOSUPPORT:
+    errmsg = "Address family not supported by protocol family";
+    break;
+  case WSAEADDRINUSE:
+    errmsg = "Address already in use";
+    break;
+  case WSAECONNRESET:
+    errmsg = "Connection reset by peer";
+    break;
+  case WSAHOST_NOT_FOUND:
+    errmsg = "Authoritative answer host not found";
+    break;
+  case WSATRY_AGAIN:
+    errmsg = "Nonauthoritative host not found, or server failure";
+    break;
+  case WSAEISCONN:
+    errmsg = "Socket is already connected";
+    break;
+  case WSAETIMEDOUT:
+    errmsg = "Connection timed out";
+    break;
+  case WSAECONNREFUSED:
+    errmsg = "Connection refused";
+    break;
+  default:
+    errmsg = "WSA Error";
+  }
+  XBMC->Log(LOG_ERROR, "%s: (errno=%i) %s\n", functionname, errnum, errmsg);
+}
+#elif defined _LINUX
+//TODO
+#endif //_WINDOWS || _LINUX
diff --git a/xbmc/pvrclients/MediaPortal/Socket.h b/xbmc/pvrclients/MediaPortal/Socket.h
new file mode 100644 (file)
index 0000000..a4e93ed
--- /dev/null
@@ -0,0 +1,299 @@
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+#pragma once
+
+//Include platform specific datatypes, header files, defines and constants:
+#if defined __WINDOWS__ || defined WIN32 || defined _WINDOWS
+  #ifdef _WINSOCKAPI_
+    #undef _WINSOCKAPI_
+  #endif
+  #include <winsock2.h>
+  #include <windows.h>
+
+  #ifndef NI_MAXHOST
+    #define NI_MAXHOST 1025
+  #endif
+
+  #ifndef socklen_t
+    typedef int socklen_t;
+  #endif
+  #ifndef ipaddr_t
+    typedef unsigned long ipaddr_t;
+  #endif
+  #ifndef port_t
+    typedef unsigned short port_t;
+  #endif
+#elif defined _LINUX
+  #include <sys/types.h>     /* for socket,connect */
+  #include <sys/socket.h>    /* for socket,connect */
+  #include <sys/un.h>        /* for Unix socket */
+  #include <arpa/inet.h>     /* for inet_pton */
+  #include <netdb.h>         /* for gethostbyname */
+  #include <netinet/in.h>    /* for htons */
+  #include <unistd.h>        /* for read, write, close */
+  #include <errno.h>
+  #include <fcntl.h>
+
+  typedef int SOCKET;
+  typedef sockaddr SOCKADDR;
+  typedef sockaddr_in SOCKADDR_IN;
+
+  #define INVALID_SOCKET -1
+  #define SOCKET_ERROR -1
+
+  #define closesocket ::close
+#else
+  #error Platform specific socket support is not yet available on this platform!
+#endif
+
+using namespace std;
+
+#include <vector>
+
+#define MAXCONNECTIONS 1  ///< Maximum number of pending connections before "Connection refused"
+#define MAXRECV 1500      ///< Maximum packet size
+
+enum SocketFamily
+{
+  #ifdef CONFIG_SOCKET_IPV6
+    af_inet6  = AF_INET6,
+    af_unspec = AF_UNSPEC,    ///< Either INET or INET6
+  #endif
+  af_inet = AF_INET
+};
+
+enum SocketDomain
+{
+  #if defined _LINUX
+    pf_unix  = PF_UNIX,
+    pf_local = PF_LOCAL,
+  #endif
+  #ifdef CONFIG_SOCKET_IPV6
+    pf_inet6  = PF_INET6,
+    pf_unspec = PF_UNSPEC,    //< Either INET or INET6
+  #endif
+  pf_inet = PF_INET
+};
+
+enum SocketType
+{
+  sock_stream = SOCK_STREAM,
+  sock_dgram = SOCK_DGRAM
+};
+
+enum SocketProtocol
+{
+  tcp = IPPROTO_TCP,
+  udp = IPPROTO_UDP
+  #ifdef CONFIG_SOCKET_IPV6
+    , ipv6 = IPPROTO_IPV6
+  #endif
+};
+
+class Socket
+{
+  public:
+
+    /*!
+     * An unconnected socket may be created directly on the local
+     * machine. The socket type (SOCK_STREAM, SOCK_DGRAM) and
+     * protocol may also be specified.
+     * If the socket cannot be created, an exception is thrown.
+     *
+     * \param family Socket family (IPv4 or IPv6)
+     * \param domain The domain parameter specifies a communications domain within which communication will take place;
+     * this selects the protocol family which should be used.
+     * \param type base type and protocol family of the socket.
+     * \param protocol specific protocol to apply.
+     */
+    Socket(const enum SocketFamily family, const enum SocketDomain domain, const enum SocketType type, const enum SocketProtocol protocol = tcp);
+    Socket(void);
+    virtual ~Socket();
+
+    //Socket settings
+
+    /*!
+     * Socket setFamily
+     * \param family    Can be af_inet or af_inet6. Default: af_inet
+     */
+    void setFamily(const enum SocketFamily family)
+    {
+      _family = family;
+    };
+
+    /*!
+     * Socket setDomain
+     * \param domain    Can be pf_unix, pf_local, pf_inet or pf_inet6. Default: pf_inet
+     */
+    void setDomain(const enum SocketDomain domain)
+    {
+      _domain = domain;
+    };
+
+    /*!
+     * Socket setType
+     * \param type    Can be sock_stream or sock_dgram. Default: sock_stream.
+     */
+    void setType(const enum SocketType type)
+    {
+      _type = type;
+    };
+
+    /*!
+     * Socket setProtocol
+     * \param protocol    Can be tcp or udp. Default: tcp.
+     */
+    void setProtocol(const enum SocketProtocol protocol)
+    {
+      _protocol = protocol;
+    };
+
+    /*!
+     * Socket setPort
+     * \param port    port number for socket communication
+     */
+    void setPort (const unsigned short port)
+    {
+      _sockaddr.sin_port = htons ( port );
+    };
+
+    bool setHostname ( const std::string host );
+
+    // Server initialization
+
+    /*!
+     * Socket create
+     * Create a new socket
+     * \return     True if succesful
+     */
+    bool create();
+
+    /*!
+     * Socket close
+     * Close the socket
+     * \return     True if succesful
+     */
+    bool close();
+
+    /*!
+     * Socket bind
+     */
+    bool bind ( const unsigned short port );
+    bool listen() const;
+    bool accept ( Socket& socket ) const;
+
+    // Client initialization
+    bool connect ( const std::string host, const unsigned short port );
+
+    bool reconnect();
+
+    // Data Transmission
+
+    /*!
+     * Socket send function
+     *
+     * \param data    Reference to a std::string with the data to transmit
+     * \return    Number of bytes send or -1 in case of an error
+     */
+    int send ( const std::string data );
+
+    /*!
+     * Socket send function
+     *
+     * \param data    Pointer to a character array of size 'size' with the data to transmit
+     * \param size    Length of the data to transmit
+     * \return    Number of bytes send or -1 in case of an error
+     */
+    int send ( const char* data, const unsigned int size );
+
+    /*!
+     * Socket sendto function
+     *
+     * \param data    Reference to a std::string with the data to transmit
+     * \param size    Length of the data to transmit
+     * \param sendcompletebuffer    If 'true': do not return until the complete buffer is transmitted
+     * \return    Number of bytes send or -1 in case of an error
+     */
+    int sendto ( const char* data, unsigned int size, bool sendcompletebuffer = false) const;
+    // Data Receive
+
+    /*!
+     * Socket receive function
+     *
+     * \param data    Reference to a std::string for storage of the received data.
+     * \param minpacketsize    The minimum number of bytes that should be received before returning from this function
+     * \return    Number of bytes received or SOCKET_ERROR
+     */
+    int receive ( std::string& data, unsigned int minpacketsize ) const;
+
+    /*!
+     * Socket receive function
+     *
+     * \param data    Reference to a std::string for storage of the received data.
+     * \return    Number of bytes received or SOCKET_ERROR
+     */
+    int receive ( std::string& data ) const;
+
+    /*!
+     * Socket receive function
+     *
+     * \param data    Pointer to a character array of size buffersize. Used to store the received data.
+     * \param buffersize    Size of the 'data' buffer
+     * \param minpacketsize    Specifies the minimum number of bytes that need to be received before returning
+     * \return    Number of bytes received or SOCKET_ERROR
+     */
+    int receive ( char* data, const unsigned int buffersize, const unsigned int minpacketsize ) const;
+
+    /*!
+     * Socket recvfrom function
+     *
+     * \param data    Pointer to a character array of size buffersize. Used to store the received data.
+     * \param buffersize    Size of the 'data' buffer
+     * \param minpacketsize    Do not return before at least 'minpacketsize' bytes are in the buffer.
+     * \param from        Optional: pointer to a sockaddr struct that will get the address from which the data is received
+     * \param fromlen    Optional, only required if 'from' is given: length of from struct
+     * \return    Number of bytes received or SOCKET_ERROR
+     */
+    int recvfrom ( char* data, const int buffersize, const int minpacketsize, struct sockaddr* from = NULL, socklen_t* fromlen = NULL) const;
+
+    bool set_non_blocking ( const bool );
+
+    bool ReadResponse (int &code, vector<string> &lines);
+
+    bool is_valid() const;
+
+  private:
+
+    SOCKET _sd;                         ///< Socket Descriptor
+    SOCKADDR_IN _sockaddr;              ///< Socket Address
+
+    enum SocketFamily _family;          ///< Socket Address Family
+    enum SocketProtocol _protocol;      ///< Socket Protocol
+    enum SocketType _type;              ///< Socket Type
+    enum SocketDomain _domain;          ///< Socket domain
+
+    #ifdef _WINDOWS
+      WSADATA _wsaData;                 ///< Windows Socket data
+    #endif
+
+    void errormessage( int errornum, const char* functionname = NULL) const;
+};
+
diff --git a/xbmc/pvrclients/MediaPortal/StdString.h b/xbmc/pvrclients/MediaPortal/StdString.h
new file mode 100644 (file)
index 0000000..6b3755f
--- /dev/null
@@ -0,0 +1,4329 @@
+#pragma once
+#include <string>
+#include "pvrclient-mediaportal_os.h"
+
+// =============================================================================
+//  FILE:  StdString.h
+//  AUTHOR:  Joe O'Leary (with outside help noted in comments)
+//
+//    If you find any bugs in this code, please let me know:
+//
+//        jmoleary@earthlink.net
+//        http://www.joeo.net/stdstring.htm (a bit outdated)
+//
+//      The latest version of this code should always be available at the
+//      following link:
+//
+//              http://www.joeo.net/code/StdString.zip (Dec 6, 2003)
+//
+//
+//  REMARKS:
+//    This header file declares the CStdStr template.  This template derives
+//    the Standard C++ Library basic_string<> template and add to it the
+//    the following conveniences:
+//      - The full MFC CString set of functions (including implicit cast)
+//      - writing to/reading from COM IStream interfaces
+//      - Functional objects for use in STL algorithms
+//
+//    From this template, we intstantiate two classes:  CStdStringA and
+//    CStdStringW.  The name "CStdString" is just a #define of one of these,
+//    based upone the UNICODE macro setting
+//
+//    This header also declares our own version of the MFC/ATL UNICODE-MBCS
+//    conversion macros.  Our version looks exactly like the Microsoft's to
+//    facilitate portability.
+//
+//  NOTE:
+//    If you you use this in an MFC or ATL build, you should include either
+//    afx.h or atlbase.h first, as appropriate.
+//
+//  PEOPLE WHO HAVE CONTRIBUTED TO THIS CLASS:
+//
+//    Several people have helped me iron out problems and othewise improve
+//    this class.  OK, this is a long list but in my own defense, this code
+//    has undergone two major rewrites.  Many of the improvements became
+//    necessary after I rewrote the code as a template.  Others helped me
+//    improve the CString facade.
+//
+//    Anyway, these people are (in chronological order):
+//
+//      - Pete the Plumber (???)
+//      - Julian Selman
+//      - Chris (of Melbsys)
+//      - Dave Plummer
+//      - John C Sipos
+//      - Chris Sells
+//      - Nigel Nunn
+//      - Fan Xia
+//      - Matthew Williams
+//      - Carl Engman
+//      - Mark Zeren
+//      - Craig Watson
+//      - Rich Zuris
+//      - Karim Ratib
+//      - Chris Conti
+//      - Baptiste Lepilleur
+//      - Greg Pickles
+//      - Jim Cline
+//      - Jeff Kohn
+//      - Todd Heckel
+//      - Ullrich Poll�hne
+//      - Joe Vitaterna
+//      - Joe Woodbury
+//      - Aaron (no last name)
+//      - Joldakowski (???)
+//      - Scott Hathaway
+//      - Eric Nitzche
+//      - Pablo Presedo
+//      - Farrokh Nejadlotfi
+//      - Jason Mills
+//      - Igor Kholodov
+//      - Mike Crusader
+//      - John James
+//      - Wang Haifeng
+//      - Tim Dowty
+//          - Arnt Witteveen
+//          - Glen Maynard
+//          - Paul DeMarco
+//          - Bagira (full name?)
+//          - Ronny Schulz
+//          - Jakko Van Hunen
+//      - Charles Godwin
+//      - Henk Demper
+//      - Greg Marr
+//      - Bill Carducci
+//      - Brian Groose
+//      - MKingman
+//      - Don Beusee
+//
+//  REVISION HISTORY
+//
+//    2005-JAN-10 - Thanks to Don Beusee for pointing out the danger in mapping
+//          length-checked formatting functions to non-length-checked
+//          CRT equivalents.  Also thanks to him for motivating me to
+//          optimize my implementation of Replace()
+//
+//    2004-APR-22 - A big, big thank you to "MKingman" (whoever you are) for
+//          finally spotting a silly little error in StdCodeCvt that
+//          has been causing me (and users of CStdString) problems for
+//          years in some relatively rare conversions.  I had reversed
+//          two length arguments.
+//
+//    2003-NOV-24 - Thanks to a bunch of people for helping me clean up many
+//          compiler warnings (and yes, even a couple of actual compiler
+//          errors).  These include Henk Demper for figuring out how
+//          to make the Intellisense work on with CStdString on VC6,
+//          something I was never able to do.  Greg Marr pointed out
+//          a compiler warning about an unreferenced symbol and a
+//          problem with my version of Load in MFC builds.  Bill
+//          Carducci took a lot of time with me to help me figure out
+//          why some implementations of the Standard C++ Library were
+//          returning error codes for apparently successful conversions
+//          between ASCII and UNICODE.  Finally thanks to Brian Groose
+//          for helping me fix compiler signed unsigned warnings in
+//          several functions.
+//
+//    2003-JUL-10 - Thanks to Charles Godwin for making me realize my 'FmtArg'
+//          fixes had inadvertently broken the DLL-export code (which is
+//                  normally commented out.  I had to move it up higher.  Also
+//          this helped me catch a bug in ssicoll that would prevent
+//                  compilation, otherwise.
+//
+//    2003-MAR-14 - Thanks to Jakko Van Hunen for pointing out a copy-and-paste
+//                  bug in one of the overloads of FmtArg.
+//
+//    2003-MAR-10 - Thanks to Ronny Schulz for (twice!) sending me some changes
+//                  to help CStdString build on SGI and for pointing out an
+//                  error in placement of my preprocessor macros for ssfmtmsg.
+//
+//    2002-NOV-26 - Thanks to Bagira for pointing out that my implementation of
+//                  SpanExcluding was not properly handling the case in which
+//                  the string did NOT contain any of the given characters
+//
+//    2002-OCT-21 - Many thanks to Paul DeMarco who was invaluable in helping me
+//                  get this code working with Borland's free compiler as well
+//                  as the Dev-C++ compiler (available free at SourceForge).
+//
+//    2002-SEP-13 - Thanks to Glen Maynard who helped me get rid of some loud
+//                  but harmless warnings that were showing up on g++.  Glen
+//                  also pointed out that some pre-declarations of FmtArg<>
+//                  specializations were unnecessary (and no good on G++)
+//
+//    2002-JUN-26 - Thanks to Arnt Witteveen for pointing out that I was using
+//                  static_cast<> in a place in which I should have been using
+//                  reinterpret_cast<> (the ctor for unsigned char strings).
+//                  That's what happens when I don't unit-test properly!
+//                  Arnt also noticed that CString was silently correcting the
+//                  'nCount' argument to Left() and Right() where CStdString was
+//                  not (and crashing if it was bad).  That is also now fixed!
+//
+//    2002-FEB-25 - Thanks to Tim Dowty for pointing out (and giving me the fix
+//          for) a conversion problem with non-ASCII MBCS characters.
+//          CStdString is now used in my favorite commercial MP3 player!
+//
+//    2001-DEC-06 - Thanks to Wang Haifeng for spotting a problem in one of the
+//          assignment operators (for _bstr_t) that would cause compiler
+//          errors when refcounting protection was turned off.
+//
+//    2001-NOV-27 - Remove calls to operator!= which involve reverse_iterators
+//          due to a conflict with the rel_ops operator!=.  Thanks to
+//          John James for pointing this out.
+//
+//    2001-OCT-29 - Added a minor range checking fix for the Mid function to
+//          make it as forgiving as CString's version is.  Thanks to
+//          Igor Kholodov for noticing this.
+//          - Added a specialization of std::swap for CStdString.  Thanks
+//          to Mike Crusader for suggesting this!  It's commented out
+//          because you're not supposed to inject your own code into the
+//          'std' namespace.  But if you don't care about that, it's
+//          there if you want it
+//          - Thanks to Jason Mills for catching a case where CString was
+//          more forgiving in the Delete() function than I was.
+//
+//    2001-JUN-06 - I was violating the Standard name lookup rules stated
+//          in [14.6.2(3)].  None of the compilers I've tried so
+//          far apparently caught this but HP-UX aCC 3.30 did.  The
+//          fix was to add 'this->' prefixes in many places.
+//          Thanks to Farrokh Nejadlotfi for this!
+//
+//    2001-APR-27 - StreamLoad was calculating the number of BYTES in one
+//          case, not characters.  Thanks to Pablo Presedo for this.
+//
+//    2001-FEB-23 - Replace() had a bug which caused infinite loops if the
+//          source string was empty.  Fixed thanks to Eric Nitzsche.
+//
+//    2001-FEB-23 - Scott Hathaway was a huge help in providing me with the
+//          ability to build CStdString on Sun Unix systems.  He
+//          sent me detailed build reports about what works and what
+//          does not.  If CStdString compiles on your Unix box, you
+//          can thank Scott for it.
+//
+//    2000-DEC-29 - Joldakowski noticed one overload of Insert failed to do a
+//          range check as CString's does.  Now fixed -- thanks!
+//
+//    2000-NOV-07 - Aaron pointed out that I was calling static member
+//          functions of char_traits via a temporary.  This was not
+//          technically wrong, but it was unnecessary and caused
+//          problems for poor old buggy VC5.  Thanks Aaron!
+//
+//    2000-JUL-11 - Joe Woodbury noted that the CString::Find docs don't match
+//          what the CString::Find code really ends up doing.   I was
+//          trying to match the docs.  Now I match the CString code
+//          - Joe also caught me truncating strings for GetBuffer() calls
+//          when the supplied length was less than the current length.
+//
+//    2000-MAY-25 - Better support for STLPORT's Standard library distribution
+//          - Got rid of the NSP macro - it interfered with Koenig lookup
+//          - Thanks to Joe Woodbury for catching a TrimLeft() bug that
+//          I introduced in January.  Empty strings were not getting
+//          trimmed
+//
+//    2000-APR-17 - Thanks to Joe Vitaterna for pointing out that ReverseFind
+//          is supposed to be a const function.
+//
+//    2000-MAR-07 - Thanks to Ullrich Poll�hne for catching a range bug in one
+//          of the overloads of assign.
+//
+//    2000-FEB-01 - You can now use CStdString on the Mac with CodeWarrior!
+//          Thanks to Todd Heckel for helping out with this.
+//
+//    2000-JAN-23 - Thanks to Jim Cline for pointing out how I could make the
+//          Trim() function more efficient.
+//          - Thanks to Jeff Kohn for prompting me to find and fix a typo
+//          in one of the addition operators that takes _bstr_t.
+//          - Got rid of the .CPP file -  you only need StdString.h now!
+//
+//    1999-DEC-22 - Thanks to Greg Pickles for helping me identify a problem
+//          with my implementation of CStdString::FormatV in which
+//          resulting string might not be properly NULL terminated.
+//
+//    1999-DEC-06 - Chris Conti pointed yet another basic_string<> assignment
+//          bug that MS has not fixed.  CStdString did nothing to fix
+//          it either but it does now!  The bug was: create a string
+//          longer than 31 characters, get a pointer to it (via c_str())
+//          and then assign that pointer to the original string object.
+//          The resulting string would be empty.  Not with CStdString!
+//
+//    1999-OCT-06 - BufferSet was erasing the string even when it was merely
+//          supposed to shrink it.  Fixed.  Thanks to Chris Conti.
+//          - Some of the Q172398 fixes were not checking for assignment-
+//          to-self.  Fixed.  Thanks to Baptiste Lepilleur.
+//
+//    1999-AUG-20 - Improved Load() function to be more efficient by using
+//          SizeOfResource().  Thanks to Rich Zuris for this.
+//          - Corrected resource ID constructor, again thanks to Rich.
+//          - Fixed a bug that occurred with UNICODE characters above
+//          the first 255 ANSI ones.  Thanks to Craig Watson.
+//          - Added missing overloads of TrimLeft() and TrimRight().
+//          Thanks to Karim Ratib for pointing them out
+//
+//    1999-JUL-21 - Made all calls to GetBuf() with no args check length first.
+//
+//    1999-JUL-10 - Improved MFC/ATL independence of conversion macros
+//          - Added SS_NO_REFCOUNT macro to allow you to disable any
+//          reference-counting your basic_string<> impl. may do.
+//          - Improved ReleaseBuffer() to be as forgiving as CString.
+//          Thanks for Fan Xia for helping me find this and to
+//          Matthew Williams for pointing it out directly.
+//
+//    1999-JUL-06 - Thanks to Nigel Nunn for catching a very sneaky bug in
+//          ToLower/ToUpper.  They should call GetBuf() instead of
+//          data() in order to ensure the changed string buffer is not
+//          reference-counted (in those implementations that refcount).
+//
+//    1999-JUL-01 - Added a true CString facade.  Now you can use CStdString as
+//          a drop-in replacement for CString.  If you find this useful,
+//          you can thank Chris Sells for finally convincing me to give
+//          in and implement it.
+//          - Changed operators << and >> (for MFC CArchive) to serialize
+//          EXACTLY as CString's do.  So now you can send a CString out
+//          to a CArchive and later read it in as a CStdString.   I have
+//          no idea why you would want to do this but you can.
+//
+//    1999-JUN-21 - Changed the CStdString class into the CStdStr template.
+//          - Fixed FormatV() to correctly decrement the loop counter.
+//          This was harmless bug but a bug nevertheless.  Thanks to
+//          Chris (of Melbsys) for pointing it out
+//          - Changed Format() to try a normal stack-based array before
+//          using to _alloca().
+//          - Updated the text conversion macros to properly use code
+//          pages and to fit in better in MFC/ATL builds.  In other
+//          words, I copied Microsoft's conversion stuff again.
+//          - Added equivalents of CString::GetBuffer, GetBufferSetLength
+//          - new sscpy() replacement of CStdString::CopyString()
+//          - a Trim() function that combines TrimRight() and TrimLeft().
+//
+//    1999-MAR-13 - Corrected the "NotSpace" functional object to use _istpace()
+//          instead of _isspace()   Thanks to Dave Plummer for this.
+//
+//    1999-FEB-26 - Removed errant line (left over from testing) that #defined
+//          _MFC_VER.  Thanks to John C Sipos for noticing this.
+//
+//    1999-FEB-03 - Fixed a bug in a rarely-used overload of operator+() that
+//          caused infinite recursion and stack overflow
+//          - Added member functions to simplify the process of
+//          persisting CStdStrings to/from DCOM IStream interfaces
+//          - Added functional objects (e.g. StdStringLessNoCase) that
+//          allow CStdStrings to be used as keys STL map objects with
+//          case-insensitive comparison
+//          - Added array indexing operators (i.e. operator[]).  I
+//          originally assumed that these were unnecessary and would be
+//          inherited from basic_string.  However, without them, Visual
+//          C++ complains about ambiguous overloads when you try to use
+//          them.  Thanks to Julian Selman to pointing this out.
+//
+//    1998-FEB-?? - Added overloads of assign() function to completely account
+//          for Q172398 bug.  Thanks to "Pete the Plumber" for this
+//
+//    1998-FEB-?? - Initial submission
+//
+// COPYRIGHT:
+//    2002 Joseph M. O'Leary.  This code is 100% free.  Use it anywhere you
+//      want.  Rewrite it, restructure it, whatever.  If you can write software
+//      that makes money off of it, good for you.  I kinda like capitalism.
+//      Please don't blame me if it causes your $30 billion dollar satellite
+//      explode in orbit.  If you redistribute it in any form, I'd appreciate it
+//      if you would leave this notice here.
+// =============================================================================
+
+// Avoid multiple inclusion
+
+#ifndef STDSTRING_H
+#define STDSTRING_H
+
+// When using VC, turn off browser references
+// Turn off unavoidable compiler warnings
+
+#if defined(_MSC_VER) && (_MSC_VER > 1100)
+  #pragma component(browser, off, references, "CStdString")
+  #pragma warning (disable : 4290) // C++ Exception Specification ignored
+  #pragma warning (disable : 4127) // Conditional expression is constant
+  #pragma warning (disable : 4097) // typedef name used as synonym for class name
+#endif
+
+// Borland warnings to turn off
+
+#ifdef __BORLANDC__
+    #pragma option push -w-inl
+//  #pragma warn -inl   // Turn off inline function warnings
+#endif
+
+// SS_IS_INTRESOURCE
+// -----------------
+//    A copy of IS_INTRESOURCE from VC7.  Because old VC6 version of winuser.h
+//    doesn't have this.
+
+#define SS_IS_INTRESOURCE(_r) (false)
+
+#if !defined (SS_ANSI) && defined(_MSC_VER)
+  #undef SS_IS_INTRESOURCE
+  #if defined(_WIN64)
+    #define SS_IS_INTRESOURCE(_r) (((unsigned __int64)(_r) >> 16) == 0)
+  #else
+    #define SS_IS_INTRESOURCE(_r) (((unsigned long)(_r) >> 16) == 0)
+  #endif
+#endif
+
+
+// MACRO: SS_UNSIGNED
+// ------------------
+//      This macro causes the addition of a constructor and assignment operator
+//      which take unsigned characters.  CString has such functions and in order
+//      to provide maximum CString-compatability, this code needs them as well.
+//      In practice you will likely never need these functions...
+
+//#define SS_UNSIGNED
+
+#ifdef SS_ALLOW_UNSIGNED_CHARS
+  #define SS_UNSIGNED
+#endif
+
+// MACRO: SS_SAFE_FORMAT
+// ---------------------
+//      This macro provides limited compatability with a questionable CString
+//      "feature".  You can define it in order to avoid a common problem that
+//      people encounter when switching from CString to CStdString.
+//
+//      To illustrate the problem -- With CString, you can do this:
+//
+//          CString sName("Joe");
+//          CString sTmp;
+//          sTmp.Format("My name is %s", sName);                    // WORKS!
+//
+//      However if you were to try this with CStdString, your program would
+//      crash.
+//
+//          CStdString sName("Joe");
+//          CStdString sTmp;
+//          sTmp.Format("My name is %s", sName);                    // CRASHES!
+//
+//      You must explicitly call c_str() or cast the object to the proper type
+//
+//          sTmp.Format("My name is %s", sName.c_str());            // WORKS!
+//          sTmp.Format("My name is %s", static_cast<PCSTR>(sName));// WORKS!
+//          sTmp.Format("My name is %s", (PCSTR)sName);        // WORKS!
+//
+//      This is because it is illegal to pass anything but a POD type as a
+//      variadic argument to a variadic function (i.e. as one of the "..."
+//      arguments).  The type const char* is a POD type.  The type CStdString
+//      is not.  Of course, neither is the type CString, but CString lets you do
+//      it anyway due to the way they laid out the class in binary.  I have no
+//      control over this in CStdString since I derive from whatever
+//      implementation of basic_string is available.
+//
+//      However if you have legacy code (which does this) that you want to take
+//      out of the MFC world and you don't want to rewrite all your calls to
+//      Format(), then you can define this flag and it will no longer crash.
+//
+//      Note however that this ONLY works for Format(), not sprintf, fprintf,
+//      etc.  If you pass a CStdString object to one of those functions, your
+//      program will crash.  Not much I can do to get around this, short of
+//      writing substitutes for those functions as well.
+
+#define SS_SAFE_FORMAT  // use new template style Format() function
+
+
+// MACRO: SS_NO_IMPLICIT_CAST
+// --------------------------
+//      Some people don't like the implicit cast to const char* (or rather to
+//      const CT*) that CStdString (and MFC's CString) provide.  That was the
+//      whole reason I created this class in the first place, but hey, whatever
+//      bakes your cake.  Just #define this macro to get rid of the the implicit
+//      cast.
+
+//#define SS_NO_IMPLICIT_CAST // gets rid of operator const CT*()
+
+
+// MACRO: SS_NO_REFCOUNT
+// ---------------------
+//    turns off reference counting at the assignment level.  Only needed
+//    for the version of basic_string<> that comes with Visual C++ versions
+//    6.0 or earlier, and only then in some heavily multithreaded scenarios.
+//    Uncomment it if you feel you need it.
+
+//#define SS_NO_REFCOUNT
+
+// MACRO: SS_WIN32
+// ---------------
+//      When this flag is set, we are building code for the Win32 platform and
+//      may use Win32 specific functions (such as LoadString).  This gives us
+//      a couple of nice extras for the code.
+//
+//      Obviously, Microsoft's is not the only compiler available for Win32 out
+//      there.  So I can't just check to see if _MSC_VER is defined to detect
+//      if I'm building on Win32.  So for now, if you use MS Visual C++ or
+//      Borland's compiler, I turn this on.  Otherwise you may turn it on
+//      yourself, if you prefer
+
+#if defined(_MSC_VER) || defined(__BORLANDC__) || defined(_WIN32)
+ #define SS_WIN32
+#endif
+
+// MACRO: SS_ANSI
+// --------------
+//      When this macro is defined, the code attempts only to use ANSI/ISO
+//      standard library functions to do it's work.  It will NOT attempt to use
+//      any Win32 of Visual C++ specific functions -- even if they are
+//      available.  You may define this flag yourself to prevent any Win32
+//      of VC++ specific functions from being called.
+
+// If we're not on Win32, we MUST use an ANSI build
+
+#ifndef SS_WIN32
+    #if !defined(SS_NO_ANSI)
+        #define SS_ANSI
+    #endif
+#endif
+
+// MACRO: SS_ALLOCA
+// ----------------
+//      Some implementations of the Standard C Library have a non-standard
+//      function known as alloca().  This functions allows one to allocate a
+//      variable amount of memory on the stack.  It is needed to implement
+//      the ASCII/MBCS conversion macros.
+//
+//      I wanted to find some way to determine automatically if alloca() is
+//    available on this platform via compiler flags but that is asking for
+//    trouble.  The crude test presented here will likely need fixing on
+//    other platforms.  Therefore I'll leave it up to you to fiddle with
+//    this test to determine if it exists.  Just make sure SS_ALLOCA is or
+//    is not defined as appropriate and you control this feature.
+
+#if defined(_MSC_VER) && !defined(SS_ANSI)
+  #define SS_ALLOCA
+#endif
+
+
+// MACRO: SS_MBCS
+// --------------
+//    Setting this macro means you are using MBCS characters.  In MSVC builds,
+//    this macro gets set automatically by detection of the preprocessor flag
+//    _MBCS.  For other platforms you may set it manually if you wish.  The
+//    only effect it currently has is to cause the allocation of more space
+//    for wchar_t --> char conversions.
+//    Note that MBCS does not mean UNICODE.
+//
+//  #define SS_MBCS
+//
+
+#ifdef _MBCS
+  #define SS_MBCS
+#endif
+
+
+// MACRO SS_NO_LOCALE
+// ------------------
+// If your implementation of the Standard C++ Library lacks the <locale> header,
+// you can #define this macro to make your code build properly.  Note that this
+// is some of my newest code and frankly I'm not very sure of it, though it does
+// pass my unit tests.
+
+// #define SS_NO_LOCALE
+
+
+// Compiler Error regarding _UNICODE and UNICODE
+// -----------------------------------------------
+// Microsoft header files are screwy.  Sometimes they depend on a preprocessor
+// flag named "_UNICODE".  Other times they check "UNICODE" (note the lack of
+// leading underscore in the second version".  In several places, they silently
+// "synchronize" these two flags this by defining one of the other was defined.
+// In older version of this header, I used to try to do the same thing.
+//
+// However experience has taught me that this is a bad idea.  You get weird
+// compiler errors that seem to indicate things like LPWSTR and LPTSTR not being
+// equivalent in UNICODE builds, stuff like that (when they MUST be in a proper
+// UNICODE  build).  You end up scratching your head and saying, "But that HAS
+// to compile!".
+//
+// So what should you do if you get this error?
+//
+// Make sure that both macros (_UNICODE and UNICODE) are defined before this
+// file is included.  You can do that by either
+//
+//    a) defining both yourself before any files get included
+//    b) including the proper MS headers in the proper order
+//    c) including this file before any other file, uncommenting
+//       the #defines below, and commenting out the #errors
+//
+//  Personally I recommend solution a) but it's your call.
+
+#ifdef _MSC_VER
+  #if defined (_UNICODE) && !defined (UNICODE)
+    #error UNICODE defined  but not UNICODE
+  //  #define UNICODE  // no longer silently fix this
+  #endif
+  #if defined (UNICODE) && !defined (_UNICODE)
+    #error Warning, UNICODE defined  but not _UNICODE
+  //  #define _UNICODE  // no longer silently fix this
+  #endif
+#endif
+
+
+// -----------------------------------------------------------------------------
+// MIN and MAX.  The Standard C++ template versions go by so many names (at
+// at least in the MS implementation) that you never know what's available
+// -----------------------------------------------------------------------------
+template<class Type>
+inline const Type& SSMIN(const Type& arg1, const Type& arg2)
+{
+  return arg2 < arg1 ? arg2 : arg1;
+}
+template<class Type>
+inline const Type& SSMAX(const Type& arg1, const Type& arg2)
+{
+  return arg2 > arg1 ? arg2 : arg1;
+}
+
+// If they have not #included W32Base.h (part of my W32 utility library) then
+// we need to define some stuff.  Otherwise, this is all defined there.
+
+#if !defined(W32BASE_H)
+
+  // If they want us to use only standard C++ stuff (no Win32 stuff)
+
+  #ifdef SS_ANSI
+
+    // On Win32 we have TCHAR.H so just include it.  This is NOT violating
+        // the spirit of SS_ANSI as we are not calling any Win32 functions here.
+
+    #ifdef SS_WIN32
+
+      #include <TCHAR.H>
+      #include <WTYPES.H>
+      #ifndef STRICT
+        #define STRICT
+      #endif
+
+        // ... but on non-Win32 platforms, we must #define the types we need.
+
+    #else
+
+      typedef const char*    PCSTR;
+      typedef char*      PSTR;
+      typedef const wchar_t*  PCWSTR;
+      typedef wchar_t*    PWSTR;
+      #ifdef UNICODE
+        typedef wchar_t    TCHAR;
+      #else
+        typedef char    TCHAR;
+      #endif
+      typedef wchar_t      OLECHAR;
+
+    #endif  // #ifndef _WIN32
+
+
+    // Make sure ASSERT and verify are defined using only ANSI stuff
+
+    #ifndef ASSERT
+      #include <assert.h>
+      #define ASSERT(f) assert((f))
+    #endif
+    #ifndef VERIFY
+      #ifdef _DEBUG
+        #define VERIFY(x) ASSERT((x))
+      #else
+        #define VERIFY(x) x
+      #endif
+    #endif
+
+  #else // ...else SS_ANSI is NOT defined
+
+    #include <TCHAR.H>
+    #include <WTYPES.H>
+    #ifndef STRICT
+      #define STRICT
+    #endif
+
+    // Make sure ASSERT and verify are defined
+
+    #ifndef ASSERT
+      #include <crtdbg.h>
+      #define ASSERT(f) _ASSERTE((f))
+    #endif
+    #ifndef VERIFY
+      #ifdef _DEBUG
+        #define VERIFY(x) ASSERT((x))
+      #else
+        #define VERIFY(x) x
+      #endif
+    #endif
+
+  #endif // #ifdef SS_ANSI
+
+  #ifndef UNUSED
+    #define UNUSED(x) x
+  #endif
+
+#endif // #ifndef W32BASE_H
+
+// Standard headers needed
+
+#include <string>      // basic_string
+#include <algorithm>    // for_each, etc.
+#include <functional>    // for StdStringLessNoCase, et al
+#ifndef SS_NO_LOCALE
+  #include <locale>      // for various facets
+#endif
+
+// If this is a recent enough version of VC include comdef.h, so we can write
+// member functions to deal with COM types & compiler support classes e.g.
+// _bstr_t
+
+#if defined (_MSC_VER) && (_MSC_VER >= 1100)
+ #include <comdef.h>
+ #define SS_INC_COMDEF  // signal that we #included MS comdef.h file
+ #define STDSTRING_INC_COMDEF
+ #define SS_NOTHROW __declspec(nothrow)
+#else
+  #define SS_NOTHROW
+#endif
+
+#ifndef TRACE
+  #define TRACE_DEFINED_HERE
+  #define TRACE
+#endif
+
+// Microsoft defines PCSTR, PCWSTR, etc, but no PCTSTR.  I hate to use the
+// versions with the "L" in front of them because that's a leftover from Win 16
+// days, even though it evaluates to the same thing.  Therefore, Define a PCSTR
+// as an LPCTSTR.
+
+#if !defined(PCTSTR) && !defined(PCTSTR_DEFINED)
+  typedef const TCHAR*      PCTSTR;
+  #define PCTSTR_DEFINED
+#endif
+
+#if !defined(PCOLESTR) && !defined(PCOLESTR_DEFINED)
+  typedef const OLECHAR*      PCOLESTR;
+  #define PCOLESTR_DEFINED
+#endif
+
+#if !defined(POLESTR) && !defined(POLESTR_DEFINED)
+  typedef OLECHAR*        POLESTR;
+  #define POLESTR_DEFINED
+#endif
+
+#if !defined(PCUSTR) && !defined(PCUSTR_DEFINED)
+  typedef const unsigned char*  PCUSTR;
+  typedef unsigned char*      PUSTR;
+  #define PCUSTR_DEFINED
+#endif
+
+
+// SGI compiler 7.3 doesnt know these  types - oh and btw, remember to use
+// -LANG:std in the CXX Flags
+#if defined(__sgi)
+    typedef unsigned long           DWORD;
+    typedef void *                  LPCVOID;
+#endif
+
+
+// SS_USE_FACET macro and why we need it:
+//
+// Since I'm a good little Standard C++ programmer, I use locales.  Thus, I
+// need to make use of the use_facet<> template function here.   Unfortunately,
+// this need is complicated by the fact the MS' implementation of the Standard
+// C++ Library has a non-standard version of use_facet that takes more
+// arguments than the standard dictates.  Since I'm trying to write CStdString
+// to work with any version of the Standard library, this presents a problem.
+//
+// The upshot of this is that I can't do 'use_facet' directly.  The MS' docs
+// tell me that I have to use a macro, _USE() instead.  Since _USE obviously
+// won't be available in other implementations, this means that I have to write
+// my OWN macro -- SS_USE_FACET -- that evaluates either to _USE or to the
+// standard, use_facet.
+//
+// If you are having trouble with the SS_USE_FACET macro, in your implementation
+// of the Standard C++ Library, you can define your own version of SS_USE_FACET.
+
+#ifndef schMSG
+  #define schSTR(x)     #x
+  #define schSTR2(x)  schSTR(x)
+  #define schMSG(desc) message(__FILE__ "(" schSTR2(__LINE__) "):" #desc)
+#endif
+
+#ifndef SS_USE_FACET
+
+  // STLPort #defines a macro (__STL_NO_EXPLICIT_FUNCTION_TMPL_ARGS) for
+  // all MSVC builds, erroneously in my opinion.  It causes problems for
+  // my SS_ANSI builds.  In my code, I always comment out that line.  You'll
+  // find it in   \stlport\config\stl_msvc.h
+
+  #if defined(__SGI_STL_PORT) && (__SGI_STL_PORT >= 0x400 )
+
+    #if defined(__STL_NO_EXPLICIT_FUNCTION_TMPL_ARGS) && defined(_MSC_VER)
+      #ifdef SS_ANSI
+        #pragma schMSG(__STL_NO_EXPLICIT_FUNCTION_TMPL_ARGS defined!!)
+      #endif
+    #endif
+    #define SS_USE_FACET(loc, fac) std::use_facet<fac >(loc)
+
+  #elif defined(_MSC_VER )
+
+    #define SS_USE_FACET(loc, fac) std::_USE(loc, fac)
+
+  // ...and
+  #elif defined(_RWSTD_NO_TEMPLATE_ON_RETURN_TYPE)
+
+        #define SS_USE_FACET(loc, fac) std::use_facet(loc, (fac*)0)
+
+  #else
+
+    #define SS_USE_FACET(loc, fac) std::use_facet<fac >(loc)
+
+  #endif
+
+#endif
+
+// =============================================================================
+// UNICODE/MBCS conversion macros.  Made to work just like the MFC/ATL ones.
+// =============================================================================
+
+#include <wchar.h>      // Added to Std Library with Amendment #1.
+
+// First define the conversion helper functions.  We define these regardless of
+// any preprocessor macro settings since their names won't collide.
+
+// Not sure if we need all these headers.   I believe ANSI says we do.
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <wctype.h>
+#include <ctype.h>
+#include <stdlib.h>
+#ifndef va_start
+  #include <varargs.h>
+#endif
+
+
+#ifdef SS_NO_LOCALE
+
+  #if defined(_WIN32) || defined (_WIN32_WCE)
+
+    inline PWSTR StdCodeCvt(PWSTR pDstW, int nDst, PCSTR pSrcA, int nSrc,
+      UINT acp=CP_ACP)
+    {
+      ASSERT(0 != pSrcA);
+      ASSERT(0 != pDstW);
+      pDstW[0] = '\0';
+      MultiByteToWideChar(acp, 0, pSrcA, nSrc, pDstW, nDst);
+      return pDstW;
+    }
+    inline PWSTR StdCodeCvt(PWSTR pDstW, int nDst, PCUSTR pSrcA, int nSrc,
+      UINT acp=CP_ACP)
+    {
+      return StdCodeCvt(pDstW, nDst, (PCSTR)pSrcA, nSrc, acp);
+    }
+
+    inline PSTR StdCodeCvt(PSTR pDstA, int nDst, PCWSTR pSrcW, int nSrc,
+      UINT acp=CP_ACP)
+    {
+      ASSERT(0 != pDstA);
+      ASSERT(0 != pSrcW);
+      pDstA[0] = '\0';
+      WideCharToMultiByte(acp, 0, pSrcW, nSrc, pDstA, nDst, 0, 0);
+      return pDstA;
+    }
+    inline PUSTR StdCodeCvt(PUSTR pDstA, int nDst, PCWSTR pSrcW, int nSrc,
+      UINT acp=CP_ACP)
+    {
+      return (PUSTR)StdCodeCvt((PSTR)pDstA, nDst, pSrcW, nSrc, acp);
+    }
+  #else
+  #endif
+
+#else
+
+  // StdCodeCvt - made to look like Win32 functions WideCharToMultiByte
+  //        and MultiByteToWideChar but uses locales in SS_ANSI
+  //        builds.  There are a number of overloads.
+  //              First argument is the destination buffer.
+  //              Second argument is the source buffer
+  //#if defined (SS_ANSI) || !defined (SS_WIN32)
+
+  // 'SSCodeCvt' - shorthand name for the codecvt facet we use
+
+  typedef std::codecvt<wchar_t, char, mbstate_t> SSCodeCvt;
+
+  inline PWSTR StdCodeCvt(PWSTR pDstW, int nDst, PCSTR pSrcA, int nSrc,
+    const std::locale& loc=std::locale())
+  {
+    ASSERT(0 != pSrcA);
+    ASSERT(0 != pDstW);
+
+    pDstW[0]          = '\0';
+
+    if ( nSrc > 0 )
+    {
+      PCSTR pNextSrcA      = pSrcA;
+      PWSTR pNextDstW      = pDstW;
+      SSCodeCvt::result res  = SSCodeCvt::ok;
+      const SSCodeCvt& conv  = SS_USE_FACET(loc, SSCodeCvt);
+      SSCodeCvt::state_type st= { 0 };
+      res            = conv.in(st,
+                    pSrcA, pSrcA + nSrc, pNextSrcA,
+                    pDstW, pDstW + nDst, pNextDstW);
+#ifdef _LINUX
+#define ASSERT2(a) if (!(a)) {fprintf(stderr, "StdString: Assertion Failed on line %d\n", __LINE__);}
+#else
+#define ASSERT2 ASSERT
+#endif
+      ASSERT2(SSCodeCvt::ok == res);
+      ASSERT2(SSCodeCvt::error != res);
+      ASSERT2(pNextDstW >= pDstW);
+      ASSERT2(pNextSrcA >= pSrcA);
+#undef ASSERT2
+      // Null terminate the converted string
+
+      if ( pNextDstW - pDstW > nDst )
+        *(pDstW + nDst) = '\0';
+      else
+        *pNextDstW = '\0';
+    }
+    return pDstW;
+  }
+  inline PWSTR StdCodeCvt(PWSTR pDstW, int nDst, PCUSTR pSrcA, int nSrc,
+    const std::locale& loc=std::locale())
+  {
+    return StdCodeCvt(pDstW, nDst, (PCSTR)pSrcA, nSrc, loc);
+  }
+
+  inline PSTR StdCodeCvt(PSTR pDstA, int nDst, PCWSTR pSrcW, int nSrc,
+    const std::locale& loc=std::locale())
+  {
+    ASSERT(0 != pDstA);
+    ASSERT(0 != pSrcW);
+
+    pDstA[0]          = '\0';
+
+    if ( nSrc > 0 )
+    {
+      PSTR pNextDstA      = pDstA;
+      PCWSTR pNextSrcW    = pSrcW;
+      SSCodeCvt::result res  = SSCodeCvt::ok;
+      const SSCodeCvt& conv  = SS_USE_FACET(loc, SSCodeCvt);
+      SSCodeCvt::state_type st= { 0 };
+      res            = conv.out(st,
+                    pSrcW, pSrcW + nSrc, pNextSrcW,
+                    pDstA, pDstA + nDst, pNextDstA);
+#ifdef _LINUX
+#define ASSERT2(a) if (!(a)) {fprintf(stderr, "StdString: Assertion Failed on line %d\n", __LINE__);}
+#else
+#define ASSERT2 ASSERT
+#endif
+      ASSERT2(SSCodeCvt::error != res);
+      ASSERT2(SSCodeCvt::ok == res);  // strict, comment out for sanity
+      ASSERT2(pNextDstA >= pDstA);
+      ASSERT2(pNextSrcW >= pSrcW);
+#undef ASSERT2
+
+      // Null terminate the converted string
+
+      if ( pNextDstA - pDstA > nDst )
+        *(pDstA + nDst) = '\0';
+      else
+        *pNextDstA = '\0';
+    }
+    return pDstA;
+  }
+
+  inline PUSTR StdCodeCvt(PUSTR pDstA, int nDst, PCWSTR pSrcW, int nSrc,
+    const std::locale& loc=std::locale())
+  {
+    return (PUSTR)StdCodeCvt((PSTR)pDstA, nDst, pSrcW, nSrc, loc);
+  }
+
+#endif
+
+
+
+// Unicode/MBCS conversion macros are only available on implementations of
+// the "C" library that have the non-standard _alloca function.  As far as I
+// know that's only Microsoft's though I've heard that the function exists
+// elsewhere.
+
+#if defined(SS_ALLOCA) && !defined SS_NO_CONVERSION
+
+    #include <malloc.h>  // needed for _alloca
+
+    // Define our conversion macros to look exactly like Microsoft's to
+    // facilitate using this stuff both with and without MFC/ATL
+
+    #ifdef _CONVERSION_USES_THREAD_LOCALE
+
+      #ifndef _DEBUG
+        #define SSCVT int _cvt; _cvt; UINT _acp=GetACP(); \
+          _acp; PCWSTR _pw; _pw; PCSTR _pa; _pa
+      #else
+        #define SSCVT int _cvt = 0; _cvt; UINT _acp=GetACP();\
+           _acp; PCWSTR _pw=0; _pw; PCSTR _pa=0; _pa
+      #endif
+      #define SSA2W(pa) (\
+        ((_pa = pa) == 0) ? 0 : (\
+          _cvt = (sslen(_pa)),\
+          StdCodeCvt((PWSTR) _alloca((_cvt+1)*2), (_cvt+1)*2, \
+              _pa, _cvt, _acp)))
+      #define SSW2A(pw) (\
+        ((_pw = pw) == 0) ? 0 : (\
+          _cvt = sslen(_pw), \
+          StdCodeCvt((LPSTR) _alloca((_cvt+1)*2), (_cvt+1)*2, \
+          _pw, _cvt, _acp)))
+  #else
+
+      #ifndef _DEBUG
+        #define SSCVT int _cvt; _cvt; UINT _acp=CP_ACP; _acp;\
+           PCWSTR _pw; _pw; PCSTR _pa; _pa
+      #else
+        #define SSCVT int _cvt = 0; _cvt; UINT _acp=CP_ACP; \
+          _acp; PCWSTR _pw=0; _pw; PCSTR _pa=0; _pa
+      #endif
+      #define SSA2W(pa) (\
+        ((_pa = pa) == 0) ? 0 : (\
+          _cvt = (sslen(_pa)),\
+          StdCodeCvt((PWSTR) _alloca((_cvt+1)*2), (_cvt+1)*2, \
+          _pa, _cvt)))
+      #define SSW2A(pw) (\
+        ((_pw = pw) == 0) ? 0 : (\
+          _cvt = (sslen(_pw)),\
+          StdCodeCvt((LPSTR) _alloca((_cvt+1)*2), (_cvt+1)*2, \
+          _pw, _cvt)))
+    #endif
+
+    #define SSA2CW(pa) ((PCWSTR)SSA2W((pa)))
+    #define SSW2CA(pw) ((PCSTR)SSW2A((pw)))
+
+    #ifdef UNICODE
+      #define SST2A  SSW2A
+      #define SSA2T  SSA2W
+      #define SST2CA  SSW2CA
+      #define SSA2CT  SSA2CW
+    // (Did you get a compiler error here about not being able to convert
+    // PTSTR into PWSTR?  Then your _UNICODE and UNICODE flags are messed
+    // up.  Best bet: #define BOTH macros before including any MS headers.)
+      inline PWSTR  SST2W(PTSTR p)      { return p; }
+      inline PTSTR  SSW2T(PWSTR p)      { return p; }
+      inline PCWSTR  SST2CW(PCTSTR p)    { return p; }
+      inline PCTSTR  SSW2CT(PCWSTR p)    { return p; }
+    #else
+      #define SST2W  SSA2W
+      #define SSW2T  SSW2A
+      #define SST2CW  SSA2CW
+      #define SSW2CT  SSW2CA
+      inline PSTR    SST2A(PTSTR p)      { return p; }
+      inline PTSTR  SSA2T(PSTR p)      { return p; }
+      inline PCSTR  SST2CA(PCTSTR p)    { return p; }
+      inline PCTSTR  SSA2CT(PCSTR p)      { return p; }
+    #endif // #ifdef UNICODE
+
+    #if defined(UNICODE)
+    // in these cases the default (TCHAR) is the same as OLECHAR
+      inline PCOLESTR  SST2COLE(PCTSTR p)    { return p; }
+      inline PCTSTR  SSOLE2CT(PCOLESTR p)  { return p; }
+      inline POLESTR  SST2OLE(PTSTR p)    { return p; }
+      inline PTSTR  SSOLE2T(POLESTR p)    { return p; }
+    #elif defined(OLE2ANSI)
+    // in these cases the default (TCHAR) is the same as OLECHAR
+      inline PCOLESTR  SST2COLE(PCTSTR p)    { return p; }
+      inline PCTSTR  SSOLE2CT(PCOLESTR p)  { return p; }
+      inline POLESTR  SST2OLE(PTSTR p)    { return p; }
+      inline PTSTR  SSOLE2T(POLESTR p)    { return p; }
+    #else
+      //CharNextW doesn't work on Win95 so we use this
+      #define SST2COLE(pa)  SSA2CW((pa))
+      #define SST2OLE(pa)    SSA2W((pa))
+      #define SSOLE2CT(po)  SSW2CA((po))
+      #define SSOLE2T(po)    SSW2A((po))
+    #endif
+
+    #ifdef OLE2ANSI
+      #define SSW2OLE    SSW2A
+      #define SSOLE2W    SSA2W
+      #define SSW2COLE  SSW2CA
+      #define SSOLE2CW  SSA2CW
+      inline POLESTR    SSA2OLE(PSTR p)    { return p; }
+      inline PSTR      SSOLE2A(POLESTR p)  { return p; }
+      inline PCOLESTR    SSA2COLE(PCSTR p)  { return p; }
+      inline PCSTR    SSOLE2CA(PCOLESTR p){ return p; }
+    #else
+      #define SSA2OLE    SSA2W
+      #define SSOLE2A    SSW2A
+      #define SSA2COLE  SSA2CW
+      #define SSOLE2CA  SSW2CA
+      inline POLESTR    SSW2OLE(PWSTR p)  { return p; }
+      inline PWSTR    SSOLE2W(POLESTR p)  { return p; }
+      inline PCOLESTR    SSW2COLE(PCWSTR p)  { return p; }
+      inline PCWSTR    SSOLE2CW(PCOLESTR p){ return p; }
+    #endif
+
+    // Above we've defined macros that look like MS' but all have
+    // an 'SS' prefix.  Now we need the real macros.  We'll either
+    // get them from the macros above or from MFC/ATL.
+
+  #if defined (USES_CONVERSION)
+
+    #define _NO_STDCONVERSION  // just to be consistent
+
+  #else
+
+    #ifdef _MFC_VER
+
+      #include <afxconv.h>
+      #define _NO_STDCONVERSION // just to be consistent
+
+    #else
+
+      #define USES_CONVERSION SSCVT
+      #define A2CW      SSA2CW
+      #define W2CA      SSW2CA
+      #define T2A        SST2A
+      #define A2T        SSA2T
+      #define T2W        SST2W
+      #define W2T        SSW2T
+      #define T2CA      SST2CA
+      #define A2CT      SSA2CT
+      #define T2CW      SST2CW
+      #define W2CT      SSW2CT
+      #define ocslen      sslen
+      #define ocscpy      sscpy
+      #define T2COLE      SST2COLE
+      #define OLE2CT      SSOLE2CT
+      #define T2OLE      SST2COLE
+      #define OLE2T      SSOLE2CT
+      #define A2OLE      SSA2OLE
+      #define OLE2A      SSOLE2A
+      #define W2OLE      SSW2OLE
+      #define OLE2W      SSOLE2W
+      #define A2COLE      SSA2COLE
+      #define OLE2CA      SSOLE2CA
+      #define W2COLE      SSW2COLE
+      #define OLE2CW      SSOLE2CW
+
+    #endif // #ifdef _MFC_VER
+  #endif // #ifndef USES_CONVERSION
+#endif // #ifndef SS_NO_CONVERSION
+
+// Define ostring - generic name for std::basic_string<OLECHAR>
+
+#if !defined(ostring) && !defined(OSTRING_DEFINED)
+  typedef std::basic_string<OLECHAR> ostring;
+  #define OSTRING_DEFINED
+#endif
+
+// StdCodeCvt when there's no conversion to be done
+template <typename T>
+inline T* StdCodeCvt(T* pDst, int nDst, const T* pSrc, int nSrc)
+{
+  int nChars = SSMIN(nSrc, nDst);
+
+  if ( nChars > 0 )
+  {
+    pDst[0]        = '\0';
+    std::basic_string<T>::traits_type::copy(pDst, pSrc, nChars);
+//    std::char_traits<T>::copy(pDst, pSrc, nChars);
+    pDst[nChars]  = '\0';
+  }
+
+  return pDst;
+}
+inline PSTR StdCodeCvt(PSTR pDst, int nDst, PCUSTR pSrc, int nSrc)
+{
+  return StdCodeCvt(pDst, nDst, (PCSTR)pSrc, nSrc);
+}
+inline PUSTR StdCodeCvt(PUSTR pDst, int nDst, PCSTR pSrc, int nSrc)
+{
+  return (PUSTR)StdCodeCvt((PSTR)pDst, nDst, pSrc, nSrc);
+}
+
+// Define tstring -- generic name for std::basic_string<TCHAR>
+
+#if !defined(tstring) && !defined(TSTRING_DEFINED)
+  typedef std::basic_string<TCHAR> tstring;
+  #define TSTRING_DEFINED
+#endif
+
+// a very shorthand way of applying the fix for KB problem Q172398
+// (basic_string assignment bug)
+
+#if defined ( _MSC_VER ) && ( _MSC_VER < 1200 )
+  #define Q172398(x) (x).erase()
+#else
+  #define Q172398(x)
+#endif
+
+// =============================================================================
+// INLINE FUNCTIONS ON WHICH CSTDSTRING RELIES
+//
+// Usually for generic text mapping, we rely on preprocessor macro definitions
+// to map to string functions.  However the CStdStr<> template cannot use
+// macro-based generic text mappings because its character types do not get
+// resolved until template processing which comes AFTER macro processing.  In
+// other words, the preprocessor macro UNICODE is of little help to us in the
+// CStdStr template
+//
+// Therefore, to keep the CStdStr declaration simple, we have these inline
+// functions.  The template calls them often.  Since they are inline (and NOT
+// exported when this is built as a DLL), they will probably be resolved away
+// to nothing.
+//
+// Without these functions, the CStdStr<> template would probably have to broken
+// out into two, almost identical classes.  Either that or it would be a huge,
+// convoluted mess, with tons of "if" statements all over the place checking the
+// size of template parameter CT.
+// =============================================================================
+
+#ifdef SS_NO_LOCALE
+
+  // --------------------------------------------------------------------------
+  // Win32 GetStringTypeEx wrappers
+  // --------------------------------------------------------------------------
+  inline bool wsGetStringType(LCID lc, DWORD dwT, PCSTR pS, int nSize,
+    WORD* pWd)
+  {
+    return FALSE != GetStringTypeExA(lc, dwT, pS, nSize, pWd);
+  }
+  inline bool wsGetStringType(LCID lc, DWORD dwT, PCWSTR pS, int nSize,
+    WORD* pWd)
+  {
+    return FALSE != GetStringTypeExW(lc, dwT, pS, nSize, pWd);
+  }
+
+
+  template<typename CT>
+    inline bool ssisspace (CT t)
+  {
+    WORD toYourMother;
+    return  wsGetStringType(GetThreadLocale(), CT_CTYPE1, &t, 1, &toYourMother)
+      && 0 != (C1_BLANK & toYourMother);
+  }
+
+#endif
+
+// If they defined SS_NO_REFCOUNT, then we must convert all assignments
+
+#if defined (_MSC_VER) && (_MSC_VER < 1300)
+  #ifdef SS_NO_REFCOUNT
+    #define SSREF(x) (x).c_str()
+  #else
+    #define SSREF(x) (x)
+  #endif
+#else
+  #define SSREF(x) (x)
+#endif
+
+// -----------------------------------------------------------------------------
+// sslen: strlen/wcslen wrappers
+// -----------------------------------------------------------------------------
+template<typename CT> inline int sslen(const CT* pT)
+{
+  return 0 == pT ? 0 : (int)std::basic_string<CT>::traits_type::length(pT);
+//  return 0 == pT ? 0 : std::char_traits<CT>::length(pT);
+}
+inline SS_NOTHROW int sslen(const std::string& s)
+{
+  return static_cast<int>(s.length());
+}
+inline SS_NOTHROW int sslen(const std::wstring& s)
+{
+  return static_cast<int>(s.length());
+}
+
+// -----------------------------------------------------------------------------
+// sstolower/sstoupper -- convert characters to upper/lower case
+// -----------------------------------------------------------------------------
+
+#ifdef SS_NO_LOCALE
+  inline char sstoupper(char ch)    { return (char)::toupper(ch); }
+  inline wchar_t sstoupper(wchar_t ch){ return (wchar_t)::towupper(ch); }
+  inline char sstolower(char ch)    { return (char)::tolower(ch); }
+  inline wchar_t sstolower(wchar_t ch){ return (wchar_t)::tolower(ch); }
+#else
+  template<typename CT>
+  inline CT sstolower(const CT& t, const std::locale& loc = std::locale())
+  {
+    return std::tolower<CT>(t, loc);
+  }
+  template<typename CT>
+  inline CT sstoupper(const CT& t, const std::locale& loc = std::locale())
+  {
+    return std::toupper<CT>(t, loc);
+  }
+#endif
+
+// -----------------------------------------------------------------------------
+// ssasn: assignment functions -- assign "sSrc" to "sDst"
+// -----------------------------------------------------------------------------
+typedef std::string::size_type    SS_SIZETYPE; // just for shorthand, really
+typedef std::string::pointer    SS_PTRTYPE;
+typedef std::wstring::size_type    SW_SIZETYPE;
+typedef std::wstring::pointer    SW_PTRTYPE;
+
+
+template <typename T>
+inline void  ssasn(std::basic_string<T>& sDst, const std::basic_string<T>& sSrc)
+{
+  if ( sDst.c_str() != sSrc.c_str() )
+  {
+    sDst.erase();
+    sDst.assign(SSREF(sSrc));
+  }
+}
+template <typename T>
+inline void  ssasn(std::basic_string<T>& sDst, const T *pA)
+{
+  // Watch out for NULLs, as always.
+
+  if ( 0 == pA )
+  {
+    sDst.erase();
+  }
+
+  // If pA actually points to part of sDst, we must NOT erase(), but
+  // rather take a substring
+
+  else if ( pA >= sDst.c_str() && pA <= sDst.c_str() + sDst.size() )
+  {
+    sDst =sDst.substr(static_cast<typename std::basic_string<T>::size_type>(pA-sDst.c_str()));
+  }
+
+  // Otherwise (most cases) apply the assignment bug fix, if applicable
+  // and do the assignment
+
+  else
+  {
+    Q172398(sDst);
+    sDst.assign(pA);
+  }
+}
+inline void  ssasn(std::string& sDst, const std::wstring& sSrc)
+{
+  if ( sSrc.empty() )
+  {
+    sDst.erase();
+  }
+  else
+  {
+    int nDst  = static_cast<int>(sSrc.size());
+
+    // In MBCS builds, pad the buffer to account for the possibility of
+    // some 3 byte characters.  Not perfect but should get most cases.
+
+#ifdef SS_MBCS
+    // In MBCS builds, we don't know how long the destination string will be.
+    nDst  = static_cast<int>(static_cast<double>(nDst) * 1.3);
+    sDst.resize(nDst+1);
+    PCSTR szCvt = StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()), nDst,
+      sSrc.c_str(), static_cast<int>(sSrc.size()));
+    sDst.resize(sslen(szCvt));
+#else
+    sDst.resize(nDst+1);
+    StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()), nDst,
+      sSrc.c_str(), static_cast<int>(sSrc.size()));
+    sDst.resize(sSrc.size());
+#endif
+  }
+}
+inline void  ssasn(std::string& sDst, PCWSTR pW)
+{
+  int nSrc  = sslen(pW);
+  if ( nSrc > 0 )
+  {
+    int nSrc  = sslen(pW);
+    int nDst  = nSrc;
+
+    // In MBCS builds, pad the buffer to account for the possibility of
+    // some 3 byte characters.  Not perfect but should get most cases.
+
+#ifdef SS_MBCS
+    nDst  = static_cast<int>(static_cast<double>(nDst) * 1.3);
+    // In MBCS builds, we don't know how long the destination string will be.
+    sDst.resize(nDst + 1);
+    PCSTR szCvt = StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()), nDst,
+      pW, nSrc);
+    sDst.resize(sslen(szCvt));
+#else
+    sDst.resize(nDst + 1);
+    StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()), nDst, pW, nSrc);
+    sDst.resize(nDst);
+#endif
+  }
+  else
+  {
+    sDst.erase();
+  }
+}
+inline void ssasn(std::string& sDst, const int nNull)
+{
+  //UNUSED(nNull);
+  ASSERT(nNull==0);
+  sDst.assign("");
+}
+#undef StrSizeType
+inline void  ssasn(std::wstring& sDst, const std::string& sSrc)
+{
+  if ( sSrc.empty() )
+  {
+    sDst.erase();
+  }
+  else
+  {
+    int nSrc  = static_cast<int>(sSrc.size());
+    int nDst  = nSrc;
+
+    sDst.resize(nSrc+1);
+    PCWSTR szCvt = StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()), nDst,
+      sSrc.c_str(), nSrc);
+
+    sDst.resize(sslen(szCvt));
+  }
+}
+inline void  ssasn(std::wstring& sDst, PCSTR pA)
+{
+  int nSrc  = sslen(pA);
+
+  if ( 0 == nSrc )
+  {
+    sDst.erase();
+  }
+  else
+  {
+    int nDst  = nSrc;
+    sDst.resize(nDst+1);
+    PCWSTR szCvt = StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()), nDst, pA,
+      nSrc);
+
+    sDst.resize(sslen(szCvt));
+  }
+}
+inline void ssasn(std::wstring& sDst, const int nNull)
+{
+  //UNUSED(nNull);
+  ASSERT(nNull==0);
+  sDst.assign(L"");
+}
+
+// -----------------------------------------------------------------------------
+// ssadd: string object concatenation -- add second argument to first
+// -----------------------------------------------------------------------------
+inline void  ssadd(std::string& sDst, const std::wstring& sSrc)
+{
+  int nSrc  = static_cast<int>(sSrc.size());
+
+  if ( nSrc > 0 )
+  {
+    int nDst  = static_cast<int>(sDst.size());
+    int nAdd  = nSrc;
+
+    // In MBCS builds, pad the buffer to account for the possibility of
+    // some 3 byte characters.  Not perfect but should get most cases.
+
+#ifdef SS_MBCS
+    nAdd    = static_cast<int>(static_cast<double>(nAdd) * 1.3);
+    sDst.resize(nDst+nAdd+1);
+    PCSTR szCvt = StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()+nDst),
+      nAdd, sSrc.c_str(), nSrc);
+    sDst.resize(nDst + sslen(szCvt));
+#else
+    sDst.resize(nDst+nAdd+1);
+    StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()+nDst), nAdd, sSrc.c_str(), nSrc);
+    sDst.resize(nDst + nAdd);
+#endif
+  }
+}
+template <typename T>
+inline void  ssadd(typename std::basic_string<T>& sDst, const typename std::basic_string<T>& sSrc)
+{
+  sDst += sSrc;
+}
+inline void  ssadd(std::string& sDst, PCWSTR pW)
+{
+  int nSrc    = sslen(pW);
+  if ( nSrc > 0 )
+  {
+    int nDst  = static_cast<int>(sDst.size());
+    int nAdd  = nSrc;
+
+#ifdef SS_MBCS
+    nAdd  = static_cast<int>(static_cast<double>(nAdd) * 1.3);
+    sDst.resize(nDst + nAdd + 1);
+    PCSTR szCvt = StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()+nDst),
+      nAdd, pW, nSrc);
+    sDst.resize(nDst + sslen(szCvt));
+#else
+    sDst.resize(nDst + nAdd + 1);
+    StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()+nDst), nAdd, pW, nSrc);
+    sDst.resize(nDst + nSrc);
+#endif
+  }
+}
+template <typename T>
+inline void  ssadd(typename std::basic_string<T>& sDst, const T *pA)
+{
+  if ( pA )
+  {
+    // If the string being added is our internal string or a part of our
+    // internal string, then we must NOT do any reallocation without
+    // first copying that string to another object (since we're using a
+    // direct pointer)
+
+    if ( pA >= sDst.c_str() && pA <= sDst.c_str()+sDst.length())
+    {
+      if ( sDst.capacity() <= sDst.size()+sslen(pA) )
+        sDst.append(std::basic_string<T>(pA));
+      else
+        sDst.append(pA);
+    }
+    else
+    {
+      sDst.append(pA);
+    }
+  }
+}
+inline void  ssadd(std::wstring& sDst, const std::string& sSrc)
+{
+  if ( !sSrc.empty() )
+  {
+    int nSrc  = static_cast<int>(sSrc.size());
+    int nDst  = static_cast<int>(sDst.size());
+
+    sDst.resize(nDst + nSrc + 1);
+#ifdef SS_MBCS
+    PCWSTR szCvt = StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()+nDst),
+      nSrc, sSrc.c_str(), nSrc+1);
+    sDst.resize(nDst + sslen(szCvt));
+#else
+    StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()+nDst), nSrc, sSrc.c_str(), nSrc+1);
+    sDst.resize(nDst + nSrc);
+#endif
+  }
+}
+inline void  ssadd(std::wstring& sDst, PCSTR pA)
+{
+  int nSrc    = sslen(pA);
+
+  if ( nSrc > 0 )
+  {
+    int nDst  = static_cast<int>(sDst.size());
+
+    sDst.resize(nDst + nSrc + 1);
+#ifdef SS_MBCS
+    PCWSTR szCvt = StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()+nDst),
+      nSrc, pA, nSrc+1);
+    sDst.resize(nDst + sslen(szCvt));
+#else
+    StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()+nDst), nSrc, pA, nSrc+1);
+    sDst.resize(nDst + nSrc);
+#endif
+  }
+}
+
+// -----------------------------------------------------------------------------
+// sscmp: comparison (case sensitive, not affected by locale)
+// -----------------------------------------------------------------------------
+template<typename CT>
+inline int sscmp(const CT* pA1, const CT* pA2)
+{
+    CT f;
+    CT l;
+
+    do
+    {
+      f = *(pA1++);
+      l = *(pA2++);
+    } while ( (f) && (f == l) );
+
+    return (int)(f - l);
+}
+
+// -----------------------------------------------------------------------------
+// ssicmp: comparison (case INsensitive, not affected by locale)
+// -----------------------------------------------------------------------------
+template<typename CT>
+inline int ssicmp(const CT* pA1, const CT* pA2)
+{
+  // Using the "C" locale = "not affected by locale"
+
+  std::locale loc = std::locale::classic();
+    const std::ctype<CT>& ct = SS_USE_FACET(loc, std::ctype<CT>);
+    CT f;
+    CT l;
+
+    do
+    {
+      f = ct.tolower(*(pA1++));
+      l = ct.tolower(*(pA2++));
+    } while ( (f) && (f == l) );
+
+    return (int)(f - l);
+}
+
+// -----------------------------------------------------------------------------
+// ssupr/sslwr: Uppercase/Lowercase conversion functions
+// -----------------------------------------------------------------------------
+
+template<typename CT>
+inline void sslwr(CT* pT, size_t nLen, const std::locale& loc=std::locale())
+{
+  SS_USE_FACET(loc, std::ctype<CT>).tolower(pT, pT+nLen);
+}
+template<typename CT>
+inline void ssupr(CT* pT, size_t nLen, const std::locale& loc=std::locale())
+{
+  SS_USE_FACET(loc, std::ctype<CT>).toupper(pT, pT+nLen);
+}
+
+// -----------------------------------------------------------------------------
+// vsprintf/vswprintf or _vsnprintf/_vsnwprintf equivalents.  In standard
+// builds we can't use _vsnprintf/_vsnwsprintf because they're MS extensions.
+//
+// -----------------------------------------------------------------------------
+// Borland's headers put some ANSI "C" functions in the 'std' namespace.
+// Promote them to the global namespace so we can use them here.
+
+#if defined(__BORLANDC__)
+    using std::vsprintf;
+    using std::vswprintf;
+#endif
+
+  // GNU is supposed to have vsnprintf and vsnwprintf.  But only the newer
+  // distributions do.
+
+#if defined(__GNUC__)
+
+  inline int ssvsprintf(PSTR pA, size_t nCount, PCSTR pFmtA, va_list vl)
+  {
+    return vsnprintf(pA, nCount, pFmtA, vl);
+  }
+  inline int ssvsprintf(PWSTR pW, size_t nCount, PCWSTR pFmtW, va_list vl)
+  {
+    return vswprintf(pW, nCount, pFmtW, vl);
+  }
+
+  // Microsofties can use
+#elif defined(_MSC_VER) && !defined(SS_ANSI)
+
+  inline int  ssvsprintf(PSTR pA, size_t nCount, PCSTR pFmtA, va_list vl)
+  {
+    return _vsnprintf(pA, nCount, pFmtA, vl);
+  }
+  inline int  ssvsprintf(PWSTR pW, size_t nCount, PCWSTR pFmtW, va_list vl)
+  {
+    return _vsnwprintf(pW, nCount, pFmtW, vl);
+  }
+
+#elif defined (SS_DANGEROUS_FORMAT)  // ignore buffer size parameter if needed?
+
+  inline int ssvsprintf(PSTR pA, size_t /*nCount*/, PCSTR pFmtA, va_list vl)
+  {
+    return vsprintf(pA, pFmtA, vl);
+  }
+
+  inline int ssvsprintf(PWSTR pW, size_t nCount, PCWSTR pFmtW, va_list vl)
+  {
+    // JMO: Some distributions of the "C" have a version of vswprintf that
+        // takes 3 arguments (e.g. Microsoft, Borland, GNU).  Others have a
+        // version which takes 4 arguments (an extra "count" argument in the
+        // second position.  The best stab I can take at this so far is that if
+        // you are NOT running with MS, Borland, or GNU, then I'll assume you
+        // have the version that takes 4 arguments.
+        //
+        // I'm sure that these checks don't catch every platform correctly so if
+        // you get compiler errors on one of the lines immediately below, it's
+        // probably because your implemntation takes a different number of
+        // arguments.  You can comment out the offending line (and use the
+        // alternate version) or you can figure out what compiler flag to check
+        // and add that preprocessor check in.  Regardless, if you get an error
+        // on these lines, I'd sure like to hear from you about it.
+        //
+        // Thanks to Ronny Schulz for the SGI-specific checks here.
+
+//  #if !defined(__MWERKS__) && !defined(__SUNPRO_CC_COMPAT) && !defined(__SUNPRO_CC)
+    #if    !defined(_MSC_VER) \
+        && !defined (__BORLANDC__) \
+        && !defined(__GNUC__) \
+        && !defined(__sgi)
+
+        return vswprintf(pW, nCount, pFmtW, vl);
+
+    // suddenly with the current SGI 7.3 compiler there is no such function as
+    // vswprintf and the substitute needs explicit casts to compile
+
+    #elif defined(__sgi)
+
+        nCount;
+        return vsprintf( (char *)pW, (char *)pFmtW, vl);
+
+    #else
+
+        nCount;
+        return vswprintf(pW, pFmtW, vl);
+
+    #endif
+
+  }
+
+#endif
+
+  // GOT COMPILER PROBLEMS HERE?
+  // ---------------------------
+  // Does your compiler choke on one or more of the following 2 functions?  It
+  // probably means that you don't have have either vsnprintf or vsnwprintf in
+  // your version of the CRT.  This is understandable since neither is an ANSI
+  // "C" function.  However it still leaves you in a dilemma.  In order to make
+  // this code build, you're going to have to to use some non-length-checked
+  // formatting functions that every CRT has:  vsprintf and vswprintf.
+  //
+  // This is very dangerous.  With the proper erroneous (or malicious) code, it
+  // can lead to buffer overlows and crashing your PC.  Use at your own risk
+  // In order to use them, just #define SS_DANGEROUS_FORMAT at the top of
+  // this file.
+  //
+  // Even THEN you might not be all the way home due to some non-conforming
+  // distributions.  More on this in the comments below.
+
+  inline int  ssnprintf(PSTR pA, size_t nCount, PCSTR pFmtA, va_list vl)
+  {
+  #ifdef _MSC_VER
+      return _vsnprintf(pA, nCount, pFmtA, vl);
+  #else
+      return vsnprintf(pA, nCount, pFmtA, vl);
+  #endif
+  }
+  inline int  ssnprintf(PWSTR pW, size_t nCount, PCWSTR pFmtW, va_list vl)
+  {
+  #ifdef _MSC_VER
+      return _vsnwprintf(pW, nCount, pFmtW, vl);
+  #else
+      return vswprintf(pW, nCount, pFmtW, vl);
+  #endif
+  }
+
+
+
+
+// -----------------------------------------------------------------------------
+// ssload: Type safe, overloaded ::LoadString wrappers
+// There is no equivalent of these in non-Win32-specific builds.  However, I'm
+// thinking that with the message facet, there might eventually be one
+// -----------------------------------------------------------------------------
+#if defined (SS_WIN32) && !defined(SS_ANSI)
+  inline int ssload(HMODULE hInst, UINT uId, PSTR pBuf, int nMax)
+  {
+    return ::LoadStringA(hInst, uId, pBuf, nMax);
+  }
+  inline int ssload(HMODULE hInst, UINT uId, PWSTR pBuf, int nMax)
+  {
+    return ::LoadStringW(hInst, uId, pBuf, nMax);
+  }
+#if defined ( _MSC_VER ) && ( _MSC_VER >= 1500 )
+  inline int ssload(HMODULE hInst, UINT uId, uint16_t *pBuf, int nMax)
+  {
+    return 0;
+  }
+  inline int ssload(HMODULE hInst, UINT uId, uint32_t *pBuf, int nMax)
+  {
+    return 0;
+  }
+#endif
+#endif
+
+
+// -----------------------------------------------------------------------------
+// sscoll/ssicoll: Collation wrappers
+//    Note -- with MSVC I have reversed the arguments order here because the
+//    functions appear to return the opposite of what they should
+// -----------------------------------------------------------------------------
+#ifndef SS_NO_LOCALE
+template <typename CT>
+inline int sscoll(const CT* sz1, int nLen1, const CT* sz2, int nLen2)
+{
+  const std::collate<CT>& coll =
+    SS_USE_FACET(std::locale(), std::collate<CT>);
+
+  return coll.compare(sz2, sz2+nLen2, sz1, sz1+nLen1);
+}
+template <typename CT>
+inline int ssicoll(const CT* sz1, int nLen1, const CT* sz2, int nLen2)
+{
+  const std::locale loc;
+  const std::collate<CT>& coll = SS_USE_FACET(loc, std::collate<CT>);
+
+  // Some implementations seem to have trouble using the collate<>
+  // facet typedefs so we'll just default to basic_string and hope
+  // that's what the collate facet uses (which it generally should)
+
+//  std::collate<CT>::string_type s1(sz1);
+//  std::collate<CT>::string_type s2(sz2);
+  const std::basic_string<CT> sEmpty;
+    std::basic_string<CT> s1(sz1 ? sz1 : sEmpty.c_str());
+    std::basic_string<CT> s2(sz2 ? sz2 : sEmpty.c_str());
+
+  sslwr(const_cast<CT*>(s1.c_str()), nLen1, loc);
+  sslwr(const_cast<CT*>(s2.c_str()), nLen2, loc);
+  return coll.compare(s2.c_str(), s2.c_str()+nLen2,
+            s1.c_str(), s1.c_str()+nLen1);
+}
+#endif
+
+
+// -----------------------------------------------------------------------------
+// ssfmtmsg: FormatMessage equivalents.  Needed because I added a CString facade
+// Again -- no equivalent of these on non-Win32 builds but their might one day
+// be one if the message facet gets implemented
+// -----------------------------------------------------------------------------
+#if defined (SS_WIN32) && !defined(SS_ANSI)
+  inline DWORD ssfmtmsg(DWORD dwFlags, LPCVOID pSrc, DWORD dwMsgId,
+              DWORD dwLangId, PSTR pBuf, DWORD nSize,
+              va_list* vlArgs)
+  {
+    return FormatMessageA(dwFlags, pSrc, dwMsgId, dwLangId,
+                pBuf, nSize,vlArgs);
+  }
+  inline DWORD ssfmtmsg(DWORD dwFlags, LPCVOID pSrc, DWORD dwMsgId,
+              DWORD dwLangId, PWSTR pBuf, DWORD nSize,
+              va_list* vlArgs)
+  {
+    return FormatMessageW(dwFlags, pSrc, dwMsgId, dwLangId,
+                pBuf, nSize,vlArgs);
+  }
+#else
+#endif
+
+
+
+// FUNCTION: sscpy.  Copies up to 'nMax' characters from pSrc to pDst.
+// -----------------------------------------------------------------------------
+// FUNCTION:  sscpy
+//    inline int sscpy(PSTR pDst, PCSTR pSrc, int nMax=-1);
+//    inline int sscpy(PUSTR pDst,  PCSTR pSrc, int nMax=-1)
+//    inline int sscpy(PSTR pDst, PCWSTR pSrc, int nMax=-1);
+//    inline int sscpy(PWSTR pDst, PCWSTR pSrc, int nMax=-1);
+//    inline int sscpy(PWSTR pDst, PCSTR pSrc, int nMax=-1);
+//
+// DESCRIPTION:
+//    This function is very much (but not exactly) like strcpy.  These
+//    overloads simplify copying one C-style string into another by allowing
+//    the caller to specify two different types of strings if necessary.
+//
+//    The strings must NOT overlap
+//
+//    "Character" is expressed in terms of the destination string, not
+//    the source.  If no 'nMax' argument is supplied, then the number of
+//    characters copied will be sslen(pSrc).  A NULL terminator will
+//    also be added so pDst must actually be big enough to hold nMax+1
+//    characters.  The return value is the number of characters copied,
+//    not including the NULL terminator.
+//
+// PARAMETERS:
+//    pSrc - the string to be copied FROM.  May be a char based string, an
+//         MBCS string (in Win32 builds) or a wide string (wchar_t).
+//    pSrc - the string to be copied TO.  Also may be either MBCS or wide
+//    nMax - the maximum number of characters to be copied into szDest.  Note
+//         that this is expressed in whatever a "character" means to pDst.
+//         If pDst is a wchar_t type string than this will be the maximum
+//         number of wchar_ts that my be copied.  The pDst string must be
+//         large enough to hold least nMaxChars+1 characters.
+//         If the caller supplies no argument for nMax this is a signal to
+//         the routine to copy all the characters in pSrc, regardless of
+//         how long it is.
+//
+// RETURN VALUE: none
+// -----------------------------------------------------------------------------
+
+template<typename CT1, typename CT2>
+inline int sscpycvt(CT1* pDst, const CT2* pSrc, int nMax)
+{
+  // Note -- we assume pDst is big enough to hold pSrc.  If not, we're in
+  // big trouble.  No bounds checking.  Caveat emptor.
+
+  int nSrc = sslen(pSrc);
+
+  const CT1* szCvt = StdCodeCvt(pDst, nMax, pSrc, nSrc);
+
+  // If we're copying the same size characters, then all the "code convert"
+  // just did was basically memcpy so the #of characters copied is the same
+  // as the number requested.  I should probably specialize this function
+  // template to achieve this purpose as it is silly to do a runtime check
+  // of a fact known at compile time.  I'll get around to it.
+
+  return sslen(szCvt);
+}
+
+template<typename T>
+inline int sscpycvt(T* pDst, const T* pSrc, int nMax)
+{
+  int nCount = nMax;
+  for (; nCount > 0 && *pSrc; ++pSrc, ++pDst, --nCount)
+    std::basic_string<T>::traits_type::assign(*pDst, *pSrc);
+
+  *pDst = 0;
+  return nMax - nCount;
+}
+
+inline int sscpycvt(PWSTR pDst, PCSTR pSrc, int nMax)
+{
+  // Note -- we assume pDst is big enough to hold pSrc.  If not, we're in
+  // big trouble.  No bounds checking.  Caveat emptor.
+
+  const PWSTR szCvt = StdCodeCvt(pDst, nMax, pSrc, nMax);
+  return sslen(szCvt);
+}
+
+template<typename CT1, typename CT2>
+inline int sscpy(CT1* pDst, const CT2* pSrc, int nMax, int nLen)
+{
+  return sscpycvt(pDst, pSrc, SSMIN(nMax, nLen));
+}
+template<typename CT1, typename CT2>
+inline int sscpy(CT1* pDst, const CT2* pSrc, int nMax)
+{
+  return sscpycvt(pDst, pSrc, SSMIN(nMax, sslen(pSrc)));
+}
+template<typename CT1, typename CT2>
+inline int sscpy(CT1* pDst, const CT2* pSrc)
+{
+  return sscpycvt(pDst, pSrc, sslen(pSrc));
+}
+template<typename CT1, typename CT2>
+inline int sscpy(CT1* pDst, const std::basic_string<CT2>& sSrc, int nMax)
+{
+  return sscpycvt(pDst, sSrc.c_str(), SSMIN(nMax, (int)sSrc.length()));
+}
+template<typename CT1, typename CT2>
+inline int sscpy(CT1* pDst, const std::basic_string<CT2>& sSrc)
+{
+  return sscpycvt(pDst, sSrc.c_str(), (int)sSrc.length());
+}
+
+#ifdef SS_INC_COMDEF
+  template<typename CT1>
+  inline int sscpy(CT1* pDst, const _bstr_t& bs, int nMax)
+  {
+    return sscpycvt(pDst, static_cast<PCOLESTR>(bs),
+            SSMIN(nMax, static_cast<int>(bs.length())));
+  }
+  template<typename CT1>
+  inline int sscpy(CT1* pDst, const _bstr_t& bs)
+  {
+    return sscpy(pDst, bs, static_cast<int>(bs.length()));
+  }
+#endif
+
+
+// -----------------------------------------------------------------------------
+// Functional objects for changing case.  They also let you pass locales
+// -----------------------------------------------------------------------------
+
+#ifdef SS_NO_LOCALE
+  template<typename CT>
+  struct SSToUpper : public std::unary_function<CT, CT>
+  {
+    inline CT operator()(const CT& t) const
+    {
+      return sstoupper(t);
+    }
+  };
+  template<typename CT>
+  struct SSToLower : public std::unary_function<CT, CT>
+  {
+    inline CT operator()(const CT& t) const
+    {
+      return sstolower(t);
+    }
+  };
+#else
+  template<typename CT>
+  struct SSToUpper : public std::binary_function<CT, std::locale, CT>
+  {
+    inline CT operator()(const CT& t, const std::locale& loc) const
+    {
+      return sstoupper<CT>(t, loc);
+    }
+  };
+  template<typename CT>
+  struct SSToLower : public std::binary_function<CT, std::locale, CT>
+  {
+    inline CT operator()(const CT& t, const std::locale& loc) const
+    {
+      return sstolower<CT>(t, loc);
+    }
+  };
+#endif
+
+// This struct is used for TrimRight() and TrimLeft() function implementations.
+//template<typename CT>
+//struct NotSpace : public std::unary_function<CT, bool>
+//{
+//  const std::locale& loc;
+//  inline NotSpace(const std::locale& locArg) : loc(locArg) {}
+//  inline bool operator() (CT t) { return !std::isspace(t, loc); }
+//};
+template<typename CT>
+struct NotSpace : public std::unary_function<CT, bool>
+{
+  // DINKUMWARE BUG:
+  // Note -- using std::isspace in a COM DLL gives us access violations
+  // because it causes the dynamic addition of a function to be called
+  // when the library shuts down.  Unfortunately the list is maintained
+  // in DLL memory but the function is in static memory.  So the COM DLL
+  // goes away along with the function that was supposed to be called,
+  // and then later when the DLL CRT shuts down it unloads the list and
+  // tries to call the long-gone function.
+  // This is DinkumWare's implementation problem.  If you encounter this
+  // problem, you may replace the calls here with good old isspace() and
+  // iswspace() from the CRT unless they specify SS_ANSI
+
+#ifdef SS_NO_LOCALE
+
+  bool operator() (CT t) const { return !ssisspace(t); }
+
+#else
+  const std::locale loc;
+  NotSpace(const std::locale& locArg=std::locale()) : loc(locArg) {}
+  bool operator() (CT t) const { return !std::isspace(t, loc); }
+#endif
+};
+
+
+
+
+//      Now we can define the template (finally!)
+// =============================================================================
+// TEMPLATE: CStdStr
+//    template<typename CT> class CStdStr : public std::basic_string<CT>
+//
+// REMARKS:
+//    This template derives from basic_string<CT> and adds some MFC CString-
+//    like functionality
+//
+//    Basically, this is my attempt to make Standard C++ library strings as
+//    easy to use as the MFC CString class.
+//
+//    Note that although this is a template, it makes the assumption that the
+//    template argument (CT, the character type) is either char or wchar_t.
+// =============================================================================
+
+//#define CStdStr _SS  // avoid compiler warning 4786
+
+//    template<typename ARG> ARG& FmtArg(ARG& arg)  { return arg; }
+//    PCSTR  FmtArg(const std::string& arg)  { return arg.c_str(); }
+//    PCWSTR FmtArg(const std::wstring& arg) { return arg.c_str(); }
+
+template<typename ARG>
+struct FmtArg
+{
+    explicit FmtArg(const ARG& arg) : a_(arg) {}
+    const ARG& operator()() const { return a_; }
+    const ARG& a_;
+private:
+    FmtArg& operator=(const FmtArg&) { return *this; }
+};
+
+template<typename CT>
+class CStdStr : public std::basic_string<CT>
+{
+  // Typedefs for shorter names.  Using these names also appears to help
+  // us avoid some ambiguities that otherwise arise on some platforms
+
+  #define MYBASE std::basic_string<CT>         // my base class
+  //typedef typename std::basic_string<CT>    MYBASE;   // my base class
+  typedef CStdStr<CT>              MYTYPE;   // myself
+  typedef typename MYBASE::const_pointer    PCMYSTR; // PCSTR or PCWSTR
+  typedef typename MYBASE::pointer      PMYSTR;   // PSTR or PWSTR
+  typedef typename MYBASE::iterator      MYITER;  // my iterator type
+  typedef typename MYBASE::const_iterator    MYCITER; // you get the idea...
+  typedef typename MYBASE::reverse_iterator  MYRITER;
+  typedef typename MYBASE::size_type      MYSIZE;
+  typedef typename MYBASE::value_type      MYVAL;
+  typedef typename MYBASE::allocator_type    MYALLOC;
+
+public:
+  // shorthand conversion from PCTSTR to string resource ID
+  #define SSRES(pctstr)  LOWORD(reinterpret_cast<unsigned long>(pctstr))
+
+  bool TryLoad(const void* pT)
+  {
+    bool bLoaded = false;
+
+#if defined(SS_WIN32) && !defined(SS_ANSI)
+    if ( ( pT != NULL ) && SS_IS_INTRESOURCE(pT) )
+    {
+      UINT nId = LOWORD(reinterpret_cast<unsigned long>(pT));
+      if ( !LoadString(nId) )
+      {
+        TRACE(_T("Can't load string %u\n"), SSRES(pT));
+      }
+      bLoaded = true;
+    }
+#endif
+
+    return bLoaded;
+  }
+
+
+  // CStdStr inline constructors
+  CStdStr()
+  {
+  }
+
+  CStdStr(const MYTYPE& str) : MYBASE(SSREF(str))
+  {
+  }
+
+  CStdStr(const std::string& str)
+  {
+    ssasn(*this, SSREF(str));
+  }
+
+  CStdStr(const std::wstring& str)
+  {
+    ssasn(*this, SSREF(str));
+  }
+
+  CStdStr(PCMYSTR pT, MYSIZE n) : MYBASE(pT, n)
+  {
+  }
+
+#ifdef SS_UNSIGNED
+  CStdStr(PCUSTR pU)
+  {
+    *this = reinterpret_cast<PCSTR>(pU);
+  }
+#endif
+
+  CStdStr(PCSTR pA)
+  {
+  #ifdef SS_ANSI
+    *this = pA;
+  #else
+    if ( !TryLoad(pA) )
+      *this = pA;
+  #endif
+  }
+
+  CStdStr(PCWSTR pW)
+  {
+  #ifdef SS_ANSI
+    *this = pW;
+  #else
+    if ( !TryLoad(pW) )
+      *this = pW;
+  #endif
+  }
+
+  CStdStr(uint16_t* pW)
+  {
+  #ifdef SS_ANSI
+    *this = pW;
+  #else
+    if ( !TryLoad(pW) )
+      *this = pW;
+  #endif
+  }
+
+  CStdStr(uint32_t* pW)
+  {
+  #ifdef SS_ANSI
+    *this = pW;
+  #else
+    if ( !TryLoad(pW) )
+      *this = pW;
+  #endif
+  }
+
+  CStdStr(MYCITER first, MYCITER last)
+    : MYBASE(first, last)
+  {
+  }
+
+  CStdStr(MYSIZE nSize, MYVAL ch, const MYALLOC& al=MYALLOC())
+    : MYBASE(nSize, ch, al)
+  {
+  }
+
+  #ifdef SS_INC_COMDEF
+    CStdStr(const _bstr_t& bstr)
+    {
+      if ( bstr.length() > 0 )
+        this->append(static_cast<PCMYSTR>(bstr), bstr.length());
+    }
+  #endif
+
+  // CStdStr inline assignment operators -- the ssasn function now takes care
+  // of fixing  the MSVC assignment bug (see knowledge base article Q172398).
+  MYTYPE& operator=(const MYTYPE& str)
+  {
+    ssasn(*this, str);
+    return *this;
+  }
+
+  MYTYPE& operator=(const std::string& str)
+  {
+    ssasn(*this, str);
+    return *this;
+  }
+
+  MYTYPE& operator=(const std::wstring& str)
+  {
+    ssasn(*this, str);
+    return *this;
+  }
+
+  MYTYPE& operator=(PCSTR pA)
+  {
+    ssasn(*this, pA);
+    return *this;
+  }
+
+  MYTYPE& operator=(PCWSTR pW)
+  {
+    ssasn(*this, pW);
+    return *this;
+  }
+
+#ifdef SS_UNSIGNED
+  MYTYPE& operator=(PCUSTR pU)
+  {
+    ssasn(*this, reinterpret_cast<PCSTR>(pU));
+    return *this;
+  }
+#endif
+
+  MYTYPE& operator=(uint16_t* pA)
+  {
+    ssasn(*this, pA);
+    return *this;
+  }
+
+  MYTYPE& operator=(uint32_t* pA)
+  {
+    ssasn(*this, pA);
+    return *this;
+  }
+
+  MYTYPE& operator=(CT t)
+  {
+    Q172398(*this);
+    this->assign(1, t);
+    return *this;
+  }
+
+  #ifdef SS_INC_COMDEF
+    MYTYPE& operator=(const _bstr_t& bstr)
+    {
+      if ( bstr.length() > 0 )
+      {
+        this->assign(static_cast<PCMYSTR>(bstr), bstr.length());
+        return *this;
+      }
+      else
+      {
+        this->erase();
+        return *this;
+      }
+    }
+  #endif
+
+
+  // Overloads  also needed to fix the MSVC assignment bug (KB: Q172398)
+  //  *** Thanks to Pete The Plumber for catching this one ***
+  // They also are compiled if you have explicitly turned off refcounting
+  #if ( defined(_MSC_VER) && ( _MSC_VER < 1200 ) ) || defined(SS_NO_REFCOUNT)
+
+    MYTYPE& assign(const MYTYPE& str)
+    {
+      Q172398(*this);
+      sscpy(GetBuffer(str.size()+1), SSREF(str));
+      this->ReleaseBuffer(str.size());
+      return *this;
+    }
+
+    MYTYPE& assign(const MYTYPE& str, MYSIZE nStart, MYSIZE nChars)
+    {
+      // This overload of basic_string::assign is supposed to assign up to
+      // <nChars> or the NULL terminator, whichever comes first.  Since we
+      // are about to call a less forgiving overload (in which <nChars>
+      // must be a valid length), we must adjust the length here to a safe
+      // value.  Thanks to Ullrich Poll�hne for catching this bug
+
+      nChars    = SSMIN(nChars, str.length() - nStart);
+      MYTYPE strTemp(str.c_str()+nStart, nChars);
+      Q172398(*this);
+      this->assign(strTemp);
+      return *this;
+    }
+
+    MYTYPE& assign(const MYBASE& str)
+    {
+      ssasn(*this, str);
+      return *this;
+    }
+
+    MYTYPE& assign(const MYBASE& str, MYSIZE nStart, MYSIZE nChars)
+    {
+      // This overload of basic_string::assign is supposed to assign up to
+      // <nChars> or the NULL terminator, whichever comes first.  Since we
+      // are about to call a less forgiving overload (in which <nChars>
+      // must be a valid length), we must adjust the length here to a safe
+      // value. Thanks to Ullrich Poll�hne for catching this bug
+
+      nChars    = SSMIN(nChars, str.length() - nStart);
+
+      // Watch out for assignment to self
+
+      if ( this == &str )
+      {
+        MYTYPE strTemp(str.c_str() + nStart, nChars);
+        static_cast<MYBASE*>(this)->assign(strTemp);
+      }
+      else
+      {
+        Q172398(*this);
+        static_cast<MYBASE*>(this)->assign(str.c_str()+nStart, nChars);
+      }
+      return *this;
+    }
+
+    MYTYPE& assign(const CT* pC, MYSIZE nChars)
+    {
+      // Q172398 only fix -- erase before assigning, but not if we're
+      // assigning from our own buffer
+
+  #if defined ( _MSC_VER ) && ( _MSC_VER < 1200 )
+      if ( !this->empty() &&
+        ( pC < this->data() || pC > this->data() + this->capacity() ) )
+      {
+        this->erase();
+      }
+  #endif
+      Q172398(*this);
+      static_cast<MYBASE*>(this)->assign(pC, nChars);
+      return *this;
+    }
+
+    MYTYPE& assign(MYSIZE nChars, MYVAL val)
+    {
+      Q172398(*this);
+      static_cast<MYBASE*>(this)->assign(nChars, val);
+      return *this;
+    }
+
+    MYTYPE& assign(const CT* pT)
+    {
+      return this->assign(pT, MYBASE::traits_type::length(pT));
+    }
+
+    MYTYPE& assign(MYCITER iterFirst, MYCITER iterLast)
+    {
+  #if defined ( _MSC_VER ) && ( _MSC_VER < 1200 )
+      // Q172398 fix.  don't call erase() if we're assigning from ourself
+      if ( iterFirst < this->begin() ||
+                 iterFirst > this->begin() + this->size() )
+            {
+        this->erase()
+            }
+  #endif
+      this->replace(this->begin(), this->end(), iterFirst, iterLast);
+      return *this;
+    }
+  #endif
+
+
+  // -------------------------------------------------------------------------
+  // CStdStr inline concatenation.
+  // -------------------------------------------------------------------------
+  MYTYPE& operator+=(const MYTYPE& str)
+  {
+    ssadd(*this, str);
+    return *this;
+  }
+
+  MYTYPE& operator+=(const std::string& str)
+  {
+    ssadd(*this, str);
+    return *this;
+  }
+
+  MYTYPE& operator+=(const std::wstring& str)
+  {
+    ssadd(*this, str);
+    return *this;
+  }
+
+  MYTYPE& operator+=(PCSTR pA)
+  {
+    ssadd(*this, pA);
+    return *this;
+  }
+
+  MYTYPE& operator+=(PCWSTR pW)
+  {
+    ssadd(*this, pW);
+    return *this;
+  }
+
+  MYTYPE& operator+=(uint16_t* pW)
+  {
+    ssadd(*this, pW);
+    return *this;
+  }
+
+  MYTYPE& operator+=(uint32_t* pW)
+  {
+    ssadd(*this, pW);
+    return *this;
+  }
+
+  MYTYPE& operator+=(CT t)
+  {
+    this->append(1, t);
+    return *this;
+  }
+  #ifdef SS_INC_COMDEF  // if we have _bstr_t, define a += for it too.
+    MYTYPE& operator+=(const _bstr_t& bstr)
+    {
+      return this->operator+=(static_cast<PCMYSTR>(bstr));
+    }
+  #endif
+
+
+  // -------------------------------------------------------------------------
+  // Case changing functions
+  // -------------------------------------------------------------------------
+
+    MYTYPE& ToUpper(const std::locale& loc=std::locale())
+  {
+    // Note -- if there are any MBCS character sets in which the lowercase
+    // form a character takes up a different number of bytes than the
+    // uppercase form, this would probably not work...
+
+    std::transform(this->begin(),
+             this->end(),
+             this->begin(),
+#ifdef SS_NO_LOCALE
+             SSToUpper<CT>());
+#else
+             std::bind2nd(SSToUpper<CT>(), loc));
+#endif
+
+    // ...but if it were, this would probably work better.  Also, this way
+    // seems to be a bit faster when anything other then the "C" locale is
+    // used...
+
+//    if ( !empty() )
+//    {
+//      ssupr(this->GetBuf(), this->size(), loc);
+//      this->RelBuf();
+//    }
+
+    return *this;
+  }
+
+  MYTYPE& ToLower(const std::locale& loc=std::locale())
+  {
+    // Note -- if there are any MBCS character sets in which the lowercase
+    // form a character takes up a different number of bytes than the
+    // uppercase form, this would probably not work...
+
+    std::transform(this->begin(),
+             this->end(),
+             this->begin(),
+#ifdef SS_NO_LOCALE
+             SSToLower<CT>());
+#else
+             std::bind2nd(SSToLower<CT>(), loc));
+#endif
+
+    // ...but if it were, this would probably work better.  Also, this way
+    // seems to be a bit faster when anything other then the "C" locale is
+    // used...
+
+//    if ( !empty() )
+//    {
+//      sslwr(this->GetBuf(), this->size(), loc);
+//      this->RelBuf();
+//    }
+    return *this;
+  }
+
+
+  MYTYPE& Normalize()
+  {
+    return Trim().ToLower();
+  }
+
+
+  // -------------------------------------------------------------------------
+  // CStdStr -- Direct access to character buffer.  In the MS' implementation,
+  // the at() function that we use here also calls _Freeze() providing us some
+  // protection from multithreading problems associated with ref-counting.
+    // In VC 7 and later, of course, the ref-counting stuff is gone.
+  // -------------------------------------------------------------------------
+
+  CT* GetBuf(int nMinLen=-1)
+  {
+    if ( static_cast<int>(this->size()) < nMinLen )
+      this->resize(static_cast<MYSIZE>(nMinLen));
+
+    return this->empty() ? const_cast<CT*>(this->data()) : &(this->at(0));
+  }
+
+  CT* SetBuf(int nLen)
+  {
+    nLen = ( nLen > 0 ? nLen : 0 );
+    if ( this->capacity() < 1 && nLen == 0 )
+      this->resize(1);
+
+    this->resize(static_cast<MYSIZE>(nLen));
+    return const_cast<CT*>(this->data());
+  }
+  void RelBuf(int nNewLen=-1)
+  {
+    this->resize(static_cast<MYSIZE>(nNewLen > -1 ? nNewLen :
+                                                        sslen(this->c_str())));
+  }
+
+  void BufferRel()     { RelBuf(); }      // backwards compatability
+  CT*  Buffer()       { return GetBuf(); }  // backwards compatability
+  CT*  BufferSet(int nLen) { return SetBuf(nLen);}// backwards compatability
+
+  bool Equals(const CT* pT, bool bUseCase=false) const
+  {
+    return  0 == (bUseCase ? this->compare(pT) : ssicmp(this->c_str(), pT));
+  }
+
+  // -------------------------------------------------------------------------
+  // FUNCTION:  CStdStr::Load
+  // REMARKS:
+  //    Loads string from resource specified by nID
+  //
+  // PARAMETERS:
+  //    nID - resource Identifier.  Purely a Win32 thing in this case
+  //
+  // RETURN VALUE:
+  //    true if successful, false otherwise
+  // -------------------------------------------------------------------------
+
+#ifndef SS_ANSI
+
+  bool Load(UINT nId, HMODULE hModule=NULL)
+  {
+    bool bLoaded    = false;  // set to true of we succeed.
+
+  #ifdef _MFC_VER    // When in Rome (or MFC land)...
+
+    // If they gave a resource handle, use it.  Note - this is archaic
+    // and not really what I would recommend.  But then again, in MFC
+    // land, you ought to be using CString for resources anyway since
+    // it walks the resource chain for you.
+
+    HMODULE hModuleOld = NULL;
+
+    if ( NULL != hModule )
+    {
+      hModuleOld = AfxGetResourceHandle();
+      AfxSetResourceHandle(hModule);
+    }
+
+    // ...load the string
+
+    CString strRes;
+    bLoaded        = FALSE != strRes.LoadString(nId);
+
+    // ...and if we set the resource handle, restore it.
+
+    if ( NULL != hModuleOld )
+      AfxSetResourceHandle(hModule);
+
+    if ( bLoaded )
+      *this      = strRes;
+
+  #else // otherwise make our own hackneyed version of CString's Load
+
+    // Get the resource name and module handle
+
+    if ( NULL == hModule )
+      hModule      = GetResourceHandle();
+
+    PCTSTR szName    = MAKEINTRESOURCE((nId>>4)+1); // lifted
+    DWORD dwSize    = 0;
+
+    // No sense continuing if we can't find the resource
+
+    HRSRC hrsrc      = ::FindResource(hModule, szName, RT_STRING);
+
+    if ( NULL == hrsrc )
+    {
+      TRACE(_T("Cannot find resource %d: 0x%X"), nId, ::GetLastError());
+    }
+    else if ( 0 == (dwSize = ::SizeofResource(hModule, hrsrc) / sizeof(CT)))
+    {
+      TRACE(_T("Cant get size of resource %d 0x%X\n"),nId,GetLastError());
+    }
+    else
+    {
+      bLoaded      = 0 != ssload(hModule, nId, GetBuf(dwSize), dwSize);
+      ReleaseBuffer();
+    }
+
+  #endif  // #ifdef _MFC_VER
+
+    if ( !bLoaded )
+      TRACE(_T("String not loaded 0x%X\n"), ::GetLastError());
+
+    return bLoaded;
+  }
+
+#endif  // #ifdef SS_ANSI
+
+  // -------------------------------------------------------------------------
+  // FUNCTION:  CStdStr::Format
+  //    void _cdecl Formst(CStdStringA& PCSTR szFormat, ...)
+  //    void _cdecl Format(PCSTR szFormat);
+  //
+  // DESCRIPTION:
+  //    This function does sprintf/wsprintf style formatting on CStdStringA
+  //    objects.  It looks a lot like MFC's CString::Format.  Some people
+  //    might even call this identical.  Fortunately, these people are now
+  //    dead... heh heh.
+  //
+  // PARAMETERS:
+  //    nId - ID of string resource holding the format string
+  //    szFormat - a PCSTR holding the format specifiers
+  //    argList - a va_list holding the arguments for the format specifiers.
+  //
+  // RETURN VALUE:  None.
+  // -------------------------------------------------------------------------
+  // formatting (using wsprintf style formatting)
+
+    // If they want a Format() function that safely handles string objects
+    // without casting
+
+#ifdef SS_SAFE_FORMAT
+
+    // Question:  Joe, you wacky coder you, why do you have so many overloads
+    //      of the Format() function
+    // Answer:  One reason only - CString compatability.  In short, by making
+    //      the Format() function a template this way, I can do strong typing
+    //      and allow people to pass CStdString arguments as fillers for
+    //      "%s" format specifiers without crashing their program!  The downside
+    //      is that I need to overload on the number of arguments.   If you are
+    //      passing more arguments than I have listed below in any of my
+    //      overloads, just add another one.
+    //
+    //      Yes, yes, this is really ugly.  In essence what I am doing here is
+    //      protecting people from a bad (and incorrect) programming practice
+    //      that they should not be doing anyway.  I am protecting them from
+    //      themselves.  Why am I doing this?  Well, if you had any idea the
+    //      number of times I've been emailed by people about this
+    //      "incompatability" in my code, you wouldn't ask.
+
+  void Fmt(const CT* szFmt, ...)
+  {
+    va_list argList;
+    va_start(argList, szFmt);
+    FormatV(szFmt, argList);
+    va_end(argList);
+  }
+
+#ifndef SS_ANSI
+
+    void Format(UINT nId)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+            this->swap(strFmt);
+    }
+    template<class A1>
+    void Format(UINT nId, const A1& v)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+            Fmt(strFmt, FmtArg<A1>(v)());
+    }
+    template<class A1, class A2>
+    void Format(UINT nId, const A1& v1, const A2& v2)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+           Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)());
+    }
+    template<class A1, class A2, class A3>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(),FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(),FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+           Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)(), FmtArg<A10>(v10)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)(),FmtArg<A10>(v10)(),FmtArg<A11>(v11)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+                FmtArg<A12>(v12)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+                FmtArg<A12>(v12)(), FmtArg<A13>(v13)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13, class A14>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13, const A14& v14)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+                FmtArg<A12>(v12)(), FmtArg<A13>(v13)(),FmtArg<A14>(v14)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13, class A14, class A15>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13, const A14& v14, const A15& v15)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+                FmtArg<A12>(v12)(),FmtArg<A13>(v13)(),FmtArg<A14>(v14)(),
+                FmtArg<A15>(v15)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13, class A14, class A15, class A16>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13, const A14& v14, const A15& v15,
+                const A16& v16)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+                FmtArg<A12>(v12)(),FmtArg<A13>(v13)(),FmtArg<A14>(v14)(),
+                FmtArg<A15>(v15)(), FmtArg<A16>(v16)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13, class A14, class A15, class A16, class A17>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13, const A14& v14, const A15& v15,
+                const A16& v16, const A17& v17)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+                FmtArg<A12>(v12)(),FmtArg<A13>(v13)(),FmtArg<A14>(v14)(),
+                FmtArg<A15>(v15)(),FmtArg<A16>(v16)(),FmtArg<A17>(v17)());
+        }
+    }
+
+#endif // #ifndef SS_ANSI
+
+    // ...now the other overload of Format: the one that takes a string literal
+
+    void Format(const CT* szFmt)
+    {
+        *this = szFmt;
+    }
+    template<class A1>
+    void Format(const CT* szFmt, const A1& v)
+    {
+        Fmt(szFmt, FmtArg<A1>(v)());
+    }
+    template<class A1, class A2>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)());
+    }
+    template<class A1, class A2, class A3>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)());
+    }
+    template<class A1, class A2, class A3, class A4>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)(), FmtArg<A10>(v10)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)(),FmtArg<A10>(v10)(),FmtArg<A11>(v11)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+            FmtArg<A12>(v12)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+            FmtArg<A12>(v12)(), FmtArg<A13>(v13)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13, class A14>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13, const A14& v14)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+            FmtArg<A12>(v12)(), FmtArg<A13>(v13)(),FmtArg<A14>(v14)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13, class A14, class A15>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13, const A14& v14, const A15& v15)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+            FmtArg<A12>(v12)(),FmtArg<A13>(v13)(),FmtArg<A14>(v14)(),
+            FmtArg<A15>(v15)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13, class A14, class A15, class A16>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13, const A14& v14, const A15& v15,
+                const A16& v16)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+            FmtArg<A12>(v12)(),FmtArg<A13>(v13)(),FmtArg<A14>(v14)(),
+            FmtArg<A15>(v15)(), FmtArg<A16>(v16)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13, class A14, class A15, class A16, class A17>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13, const A14& v14, const A15& v15,
+                const A16& v16, const A17& v17)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+            FmtArg<A12>(v12)(),FmtArg<A13>(v13)(),FmtArg<A14>(v14)(),
+            FmtArg<A15>(v15)(),FmtArg<A16>(v16)(),FmtArg<A17>(v17)());
+    }
+
+#else  // #ifdef SS_SAFE_FORMAT
+
+
+#ifndef SS_ANSI
+
+  void Format(UINT nId, ...)
+  {
+    va_list argList;
+    va_start(argList, nId);
+
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+      FormatV(strFmt, argList);
+
+    va_end(argList);
+  }
+
+#endif  // #ifdef SS_ANSI
+
+  void Format(const CT* szFmt, ...)
+  {
+    va_list argList;
+    va_start(argList, szFmt);
+    FormatV(szFmt, argList);
+    va_end(argList);
+  }
+
+#endif // #ifdef SS_SAFE_FORMAT
+
+  void AppendFormat(const CT* szFmt, ...)
+  {
+    va_list argList;
+    va_start(argList, szFmt);
+    AppendFormatV(szFmt, argList);
+    va_end(argList);
+  }
+
+  #define MAX_FMT_TRIES    5   // #of times we try
+  #define FMT_BLOCK_SIZE    2048 // # of bytes to increment per try
+  #define BUFSIZE_1ST  256
+  #define BUFSIZE_2ND 512
+  #define STD_BUF_SIZE    1024
+
+  // an efficient way to add formatted characters to the string.  You may only
+  // add up to STD_BUF_SIZE characters at a time, though
+  void AppendFormatV(const CT* szFmt, va_list argList)
+  {
+    CT szBuf[STD_BUF_SIZE];
+    int nLen = ssnprintf(szBuf, STD_BUF_SIZE-1, szFmt, argList);
+
+    if ( 0 < nLen )
+      this->append(szBuf, nLen);
+  }
+
+  // -------------------------------------------------------------------------
+  // FUNCTION:  FormatV
+  //    void FormatV(PCSTR szFormat, va_list, argList);
+  //
+  // DESCRIPTION:
+  //    This function formats the string with sprintf style format-specs.
+  //    It makes a general guess at required buffer size and then tries
+  //    successively larger buffers until it finds one big enough or a
+  //    threshold (MAX_FMT_TRIES) is exceeded.
+  //
+  // PARAMETERS:
+  //    szFormat - a PCSTR holding the format of the output
+  //    argList - a Microsoft specific va_list for variable argument lists
+  //
+  // RETURN VALUE:
+  // -------------------------------------------------------------------------
+
+  // NOTE: Changed by JM to actually function under non-win32,
+  //       and to remove the upper limit on size.
+  void FormatV(const CT* szFormat, va_list argList)
+  {
+    // try and grab a sufficient buffersize
+    int nChars = FMT_BLOCK_SIZE;
+    va_list argCopy;
+
+    CT *p = reinterpret_cast<CT*>(malloc(sizeof(CT)*nChars));
+    if (!p) return;
+
+    while (1)
+    {
+      va_copy(argCopy, argList);
+
+      int nActual = ssvsprintf(p, nChars, szFormat, argCopy);
+      /* If that worked, return the string. */
+      if (nActual > -1 && nActual < nChars)
+      { /* make sure it's NULL terminated */
+        p[nActual] = '\0';
+        this->assign(p, nActual);
+        free(p);
+        va_end(argCopy);
+        return;
+      }
+      /* Else try again with more space. */
+      if (nActual > -1)        /* glibc 2.1 */
+        nChars = nActual + 1;  /* precisely what is needed */
+      else                     /* glibc 2.0 */
+        nChars *= 2;           /* twice the old size */
+
+      CT *np = reinterpret_cast<CT*>(realloc(p, sizeof(CT)*nChars));
+      if (np == NULL)
+      {
+        free(p);
+        va_end(argCopy);
+        return;   // failed :(
+      }
+      p = np;
+      va_end(argCopy);
+    }
+  }
+
+  // -------------------------------------------------------------------------
+  // CString Facade Functions:
+  //
+  // The following methods are intended to allow you to use this class as a
+  // near drop-in replacement for CString.
+  // -------------------------------------------------------------------------
+  #ifdef SS_WIN32
+    BSTR AllocSysString() const
+    {
+      ostring os;
+      ssasn(os, *this);
+      return ::SysAllocString(os.c_str());
+    }
+  #endif
+
+#ifndef SS_NO_LOCALE
+  int Collate(PCMYSTR szThat) const
+  {
+    return sscoll(this->c_str(), this->length(), szThat, sslen(szThat));
+  }
+
+  int CollateNoCase(PCMYSTR szThat) const
+  {
+    return ssicoll(this->c_str(), this->length(), szThat, sslen(szThat));
+  }
+#endif
+  int Compare(PCMYSTR szThat) const
+  {
+    return this->compare(szThat);
+  }
+
+  int CompareNoCase(PCMYSTR szThat)  const
+  {
+    return ssicmp(this->c_str(), szThat);
+  }
+
+  int Delete(int nIdx, int nCount=1)
+  {
+        if ( nIdx < 0 )
+      nIdx = 0;
+
+    if ( nIdx < this->GetLength() )
+      this->erase(static_cast<MYSIZE>(nIdx), static_cast<MYSIZE>(nCount));
+
+    return GetLength();
+  }
+
+  void Empty()
+  {
+    this->erase();
+  }
+
+  int Find(CT ch) const
+  {
+    MYSIZE nIdx  = this->find_first_of(ch);
+    return static_cast<int>(MYBASE::npos == nIdx  ? -1 : nIdx);
+  }
+
+  int Find(PCMYSTR szSub) const
+  {
+    MYSIZE nIdx  = this->find(szSub);
+    return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);
+  }
+
+  int Find(CT ch, int nStart) const
+  {
+    // CString::Find docs say add 1 to nStart when it's not zero
+    // CString::Find code doesn't do that however.  We'll stick
+    // with what the code does
+
+    MYSIZE nIdx  = this->find_first_of(ch, static_cast<MYSIZE>(nStart));
+    return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);
+  }
+
+  int Find(PCMYSTR szSub, int nStart) const
+  {
+    // CString::Find docs say add 1 to nStart when it's not zero
+    // CString::Find code doesn't do that however.  We'll stick
+    // with what the code does
+
+    MYSIZE nIdx  = this->find(szSub, static_cast<MYSIZE>(nStart));
+    return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);
+  }
+
+  int FindOneOf(PCMYSTR szCharSet) const
+  {
+    MYSIZE nIdx = this->find_first_of(szCharSet);
+    return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);
+  }
+
+#ifndef SS_ANSI
+  void FormatMessage(PCMYSTR szFormat, ...) throw(std::exception)
+  {
+    va_list argList;
+    va_start(argList, szFormat);
+    PMYSTR szTemp;
+    if ( ssfmtmsg(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER,
+             szFormat, 0, 0,
+             reinterpret_cast<PMYSTR>(&szTemp), 0, &argList) == 0 ||
+       szTemp == 0 )
+    {
+      throw std::runtime_error("out of memory");
+    }
+    *this = szTemp;
+    LocalFree(szTemp);
+    va_end(argList);
+  }
+
+  void FormatMessage(UINT nFormatId, ...) throw(std::exception)
+  {
+    MYTYPE sFormat;
+    VERIFY(sFormat.LoadString(nFormatId));
+    va_list argList;
+    va_start(argList, nFormatId);
+    PMYSTR szTemp;
+    if ( ssfmtmsg(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER,
+             sFormat, 0, 0,
+             reinterpret_cast<PMYSTR>(&szTemp), 0, &argList) == 0 ||
+      szTemp == 0)
+    {
+      throw std::runtime_error("out of memory");
+    }
+    *this = szTemp;
+    LocalFree(szTemp);
+    va_end(argList);
+  }
+#endif
+
+  // GetAllocLength -- an MSVC7 function but it costs us nothing to add it.
+
+  int GetAllocLength()
+  {
+    return static_cast<int>(this->capacity());
+  }
+
+  // -------------------------------------------------------------------------
+  // GetXXXX -- Direct access to character buffer
+  // -------------------------------------------------------------------------
+  CT GetAt(int nIdx) const
+  {
+    return this->at(static_cast<MYSIZE>(nIdx));
+  }
+
+  CT* GetBuffer(int nMinLen=-1)
+  {
+    return GetBuf(nMinLen);
+  }
+
+  CT* GetBufferSetLength(int nLen)
+  {
+    return BufferSet(nLen);
+  }
+
+  // GetLength() -- MFC docs say this is the # of BYTES but
+  // in truth it is the number of CHARACTERs (chars or wchar_ts)
+  int GetLength() const
+  {
+    return static_cast<int>(this->length());
+  }
+
+  int Insert(int nIdx, CT ch)
+  {
+    if ( static_cast<MYSIZE>(nIdx) > this->size()-1 )
+      this->append(1, ch);
+    else
+      this->insert(static_cast<MYSIZE>(nIdx), 1, ch);
+
+    return GetLength();
+  }
+  int Insert(int nIdx, PCMYSTR sz)
+  {
+    if ( static_cast<MYSIZE>(nIdx) >= this->size() )
+      this->append(sz, static_cast<MYSIZE>(sslen(sz)));
+    else
+      this->insert(static_cast<MYSIZE>(nIdx), sz);
+
+    return GetLength();
+  }
+
+  bool IsEmpty() const
+  {
+    return this->empty();
+  }
+
+  MYTYPE Left(int nCount) const
+  {
+        // Range check the count.
+
+    nCount = SSMAX(0, SSMIN(nCount, static_cast<int>(this->size())));
+    return this->substr(0, static_cast<MYSIZE>(nCount));
+  }
+
+#ifndef SS_ANSI
+  bool LoadString(UINT nId)
+  {
+    return this->Load(nId);
+  }
+#endif
+
+  void MakeLower()
+  {
+    ToLower();
+  }
+
+  void MakeReverse()
+  {
+    std::reverse(this->begin(), this->end());
+  }
+
+  void MakeUpper()
+  {
+    ToUpper();
+  }
+
+  MYTYPE Mid(int nFirst) const
+  {
+    return Mid(nFirst, this->GetLength()-nFirst);
+  }
+
+  MYTYPE Mid(int nFirst, int nCount) const
+  {
+    // CString does range checking here.  Since we're trying to emulate it,
+    // we must check too.
+
+    if ( nFirst < 0 )
+      nFirst = 0;
+    if ( nCount < 0 )
+      nCount = 0;
+
+    int nSize = static_cast<int>(this->size());
+
+    if ( nFirst + nCount > nSize )
+      nCount = nSize - nFirst;
+
+    if ( nFirst > nSize )
+      return MYTYPE();
+
+    ASSERT(nFirst >= 0);
+    ASSERT(nFirst + nCount <= nSize);
+
+    return this->substr(static_cast<MYSIZE>(nFirst),
+              static_cast<MYSIZE>(nCount));
+  }
+
+  void ReleaseBuffer(int nNewLen=-1)
+  {
+    RelBuf(nNewLen);
+  }
+
+  int Remove(CT ch)
+  {
+    MYSIZE nIdx    = 0;
+    int nRemoved  = 0;
+    while ( (nIdx=this->find_first_of(ch)) != MYBASE::npos )
+    {
+      this->erase(nIdx, 1);
+      nRemoved++;
+    }
+    return nRemoved;
+  }
+
+  int Replace(CT chOld, CT chNew)
+  {
+    int nReplaced  = 0;
+
+    for ( MYITER iter=this->begin(); iter != this->end(); iter++ )
+    {
+      if ( *iter == chOld )
+      {
+        *iter = chNew;
+        nReplaced++;
+      }
+    }
+
+    return nReplaced;
+  }
+
+  int Replace(PCMYSTR szOld, PCMYSTR szNew)
+  {
+    int nReplaced    = 0;
+    MYSIZE nIdx      = 0;
+    MYSIZE nOldLen    = sslen(szOld);
+
+    if ( 0 != nOldLen )
+    {
+      // If the replacement string is longer than the one it replaces, this
+      // string is going to have to grow in size,  Figure out how much
+      // and grow it all the way now, rather than incrementally
+
+      MYSIZE nNewLen    = sslen(szNew);
+      if ( nNewLen > nOldLen )
+      {
+        int nFound      = 0;
+        while ( nIdx < this->length() &&
+          (nIdx=this->find(szOld, nIdx)) != MYBASE::npos )
+        {
+          nFound++;
+          nIdx += nOldLen;
+        }
+        this->reserve(this->size() + nFound * (nNewLen - nOldLen));
+      }
+
+
+      static const CT ch  = CT(0);
+      PCMYSTR szRealNew  = szNew == 0 ? &ch : szNew;
+      nIdx        = 0;
+
+      while ( nIdx < this->length() &&
+        (nIdx=this->find(szOld, nIdx)) != MYBASE::npos )
+      {
+        this->replace(this->begin()+nIdx, this->begin()+nIdx+nOldLen,
+          szRealNew);
+
+        nReplaced++;
+        nIdx += nNewLen;
+      }
+    }
+
+    return nReplaced;
+  }
+
+  int ReverseFind(CT ch) const
+  {
+    MYSIZE nIdx  = this->find_last_of(ch);
+    return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);
+  }
+
+  // ReverseFind overload that's not in CString but might be useful
+  int ReverseFind(PCMYSTR szFind, MYSIZE pos=MYBASE::npos) const
+  {
+    //yuvalt - this does not compile with g++ since MYTTYPE() is different type
+    //MYSIZE nIdx  = this->rfind(0 == szFind ? MYTYPE() : szFind, pos);
+    MYSIZE nIdx  = this->rfind(0 == szFind ? "" : szFind, pos);
+    return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);
+  }
+
+  MYTYPE Right(int nCount) const
+  {
+        // Range check the count.
+
+    nCount = SSMAX(0, SSMIN(nCount, static_cast<int>(this->size())));
+    return this->substr(this->size()-static_cast<MYSIZE>(nCount));
+  }
+
+  void SetAt(int nIndex, CT ch)
+  {
+    ASSERT(this->size() > static_cast<MYSIZE>(nIndex));
+    this->at(static_cast<MYSIZE>(nIndex))    = ch;
+  }
+
+#ifndef SS_ANSI
+  BSTR SetSysString(BSTR* pbstr) const
+  {
+    ostring os;
+    ssasn(os, *this);
+    if ( !::SysReAllocStringLen(pbstr, os.c_str(), os.length()) )
+      throw std::runtime_error("out of memory");
+
+    ASSERT(*pbstr != 0);
+    return *pbstr;
+  }
+#endif
+
+  MYTYPE SpanExcluding(PCMYSTR szCharSet) const
+  {
+        MYSIZE pos = this->find_first_of(szCharSet);
+        return pos == MYBASE::npos ? *this : Left(pos);
+  }
+
+  MYTYPE SpanIncluding(PCMYSTR szCharSet) const
+  {
+        MYSIZE pos = this->find_first_not_of(szCharSet);
+        return pos == MYBASE::npos ? *this : Left(pos);
+  }
+
+#if defined SS_WIN32 && !defined(UNICODE) && !defined(SS_ANSI)
+
+  // CString's OemToAnsi and AnsiToOem functions are available only in
+  // Unicode builds.  However since we're a template we also need a
+  // runtime check of CT and a reinterpret_cast to account for the fact
+  // that CStdStringW gets instantiated even in non-Unicode builds.
+
+  void AnsiToOem()
+  {
+    if ( sizeof(CT) == sizeof(char) && !empty() )
+    {
+      ::CharToOem(reinterpret_cast<PCSTR>(this->c_str()),
+            reinterpret_cast<PSTR>(GetBuf()));
+    }
+    else
+    {
+      ASSERT(false);
+    }
+  }
+
+  void OemToAnsi()
+  {
+    if ( sizeof(CT) == sizeof(char) && !empty() )
+    {
+      ::OemToChar(reinterpret_cast<PCSTR>(this->c_str()),
+            reinterpret_cast<PSTR>(GetBuf()));
+    }
+    else
+    {
+      ASSERT(false);
+    }
+  }
+
+#endif
+
+
+  // -------------------------------------------------------------------------
+  // Trim and its variants
+  // -------------------------------------------------------------------------
+  MYTYPE& Trim()
+  {
+    return TrimLeft().TrimRight();
+  }
+
+  MYTYPE& TrimLeft()
+  {
+    this->erase(this->begin(),
+      std::find_if(this->begin(), this->end(), NotSpace<CT>()));
+
+    return *this;
+  }
+
+  MYTYPE&  TrimLeft(CT tTrim)
+  {
+    this->erase(0, this->find_first_not_of(tTrim));
+    return *this;
+  }
+
+  MYTYPE&  TrimLeft(PCMYSTR szTrimChars)
+  {
+    this->erase(0, this->find_first_not_of(szTrimChars));
+    return *this;
+  }
+
+  MYTYPE& TrimRight()
+  {
+    // NOTE:  When comparing reverse_iterators here (MYRITER), I avoid using
+    // operator!=.  This is because namespace rel_ops also has a template
+    // operator!= which conflicts with the global operator!= already defined
+    // for reverse_iterator in the header <utility>.
+    // Thanks to John James for alerting me to this.
+
+    MYRITER it = std::find_if(this->rbegin(), this->rend(), NotSpace<CT>());
+    if ( !(this->rend() == it) )
+      this->erase(this->rend() - it);
+
+    this->erase(!(it == this->rend()) ? this->find_last_of(*it) + 1 : 0);
+    return *this;
+  }
+
+  MYTYPE&  TrimRight(CT tTrim)
+  {
+    MYSIZE nIdx  = this->find_last_not_of(tTrim);
+    this->erase(MYBASE::npos == nIdx ? 0 : ++nIdx);
+    return *this;
+  }
+
+  MYTYPE&  TrimRight(PCMYSTR szTrimChars)
+  {
+    MYSIZE nIdx  = this->find_last_not_of(szTrimChars);
+    this->erase(MYBASE::npos == nIdx ? 0 : ++nIdx);
+    return *this;
+  }
+
+  void      FreeExtra()
+  {
+    MYTYPE mt;
+    this->swap(mt);
+    if ( !mt.empty() )
+      this->assign(mt.c_str(), mt.size());
+  }
+
+  // I have intentionally not implemented the following CString
+  // functions.   You cannot make them work without taking advantage
+  // of implementation specific behavior.  However if you absolutely
+  // MUST have them, uncomment out these lines for "sort-of-like"
+  // their behavior.  You're on your own.
+
+//  CT*        LockBuffer()  { return GetBuf(); }// won't really lock
+//  void      UnlockBuffer(); { }  // why have UnlockBuffer w/o LockBuffer?
+
+  // Array-indexing operators.  Required because we defined an implicit cast
+  // to operator const CT* (Thanks to Julian Selman for pointing this out)
+
+  CT& operator[](int nIdx)
+  {
+    return static_cast<MYBASE*>(this)->operator[](static_cast<MYSIZE>(nIdx));
+  }
+
+  const CT& operator[](int nIdx) const
+  {
+    return static_cast<const MYBASE*>(this)->operator[](static_cast<MYSIZE>(nIdx));
+  }
+
+  CT& operator[](unsigned int nIdx)
+  {
+    return static_cast<MYBASE*>(this)->operator[](static_cast<MYSIZE>(nIdx));
+  }
+
+  const CT& operator[](unsigned int nIdx) const
+  {
+    return static_cast<const MYBASE*>(this)->operator[](static_cast<MYSIZE>(nIdx));
+  }
+
+  CT& operator[](unsigned long nIdx)
+  {
+    return static_cast<MYBASE*>(this)->operator[](static_cast<MYSIZE>(nIdx));
+  }
+
+  const CT& operator[](unsigned long nIdx) const
+  {
+    return static_cast<const MYBASE*>(this)->operator[](static_cast<MYSIZE>(nIdx));
+  }
+
+#ifndef SS_NO_IMPLICIT_CAST
+  operator const CT*() const
+  {
+    return this->c_str();
+  }
+#endif
+
+  // IStream related functions.  Useful in IPersistStream implementations
+
+#ifdef SS_INC_COMDEF
+
+  // struct SSSHDR - useful for non Std C++ persistence schemes.
+  typedef struct SSSHDR
+  {
+    BYTE  byCtrl;
+    ULONG  nChars;
+  } SSSHDR;  // as in "Standard String Stream Header"
+
+  #define SSSO_UNICODE  0x01  // the string is a wide string
+  #define SSSO_COMPRESS  0x02  // the string is compressed
+
+  // -------------------------------------------------------------------------
+  // FUNCTION: StreamSize
+  // REMARKS:
+  //    Returns how many bytes it will take to StreamSave() this CStdString
+  //    object to an IStream.
+  // -------------------------------------------------------------------------
+  ULONG StreamSize() const
+  {
+    // Control header plus string
+    ASSERT(this->size()*sizeof(CT) < 0xffffffffUL - sizeof(SSSHDR));
+    return (this->size() * sizeof(CT)) + sizeof(SSSHDR);
+  }
+
+  // -------------------------------------------------------------------------
+  // FUNCTION: StreamSave
+  // REMARKS:
+  //    Saves this CStdString object to a COM IStream.
+  // -------------------------------------------------------------------------
+  HRESULT StreamSave(IStream* pStream) const
+  {
+    ASSERT(this->size()*sizeof(CT) < 0xffffffffUL - sizeof(SSSHDR));
+    HRESULT hr    = E_FAIL;
+    ASSERT(pStream != 0);
+    SSSHDR hdr;
+    hdr.byCtrl    = sizeof(CT) == 2 ? SSSO_UNICODE : 0;
+    hdr.nChars    = this->size();
+
+
+    if ( FAILED(hr=pStream->Write(&hdr, sizeof(SSSHDR), 0)) )
+    {
+      TRACE(_T("StreamSave: Cannot write control header, ERR=0x%X\n"),hr);
+    }
+    else if ( empty() )
+    {
+      ;    // nothing to write
+    }
+    else if ( FAILED(hr=pStream->Write(this->c_str(),
+      this->size()*sizeof(CT), 0)) )
+    {
+      TRACE(_T("StreamSave: Cannot write string to stream 0x%X\n"), hr);
+    }
+
+    return hr;
+  }
+
+
+  // -------------------------------------------------------------------------
+  // FUNCTION: StreamLoad
+  // REMARKS:
+  //    This method loads the object from an IStream.
+  // -------------------------------------------------------------------------
+  HRESULT StreamLoad(IStream* pStream)
+  {
+    ASSERT(pStream != 0);
+    SSSHDR hdr;
+    HRESULT hr      = E_FAIL;
+
+    if ( FAILED(hr=pStream->Read(&hdr, sizeof(SSSHDR), 0)) )
+    {
+      TRACE(_T("StreamLoad: Cant read control header, ERR=0x%X\n"), hr);
+    }
+    else if ( hdr.nChars > 0 )
+    {
+      ULONG nRead    = 0;
+      PMYSTR pMyBuf  = BufferSet(hdr.nChars);
+
+      // If our character size matches the character size of the string
+      // we're trying to read, then we can read it directly into our
+      // buffer. Otherwise, we have to read into an intermediate buffer
+      // and convert.
+
+      if ( (hdr.byCtrl & SSSO_UNICODE) != 0 )
+      {
+        ULONG nBytes  = hdr.nChars * sizeof(wchar_t);
+        if ( sizeof(CT) == sizeof(wchar_t) )
+        {
+          if ( FAILED(hr=pStream->Read(pMyBuf, nBytes, &nRead)) )
+            TRACE(_T("StreamLoad: Cannot read string: 0x%X\n"), hr);
+        }
+        else
+        {
+          PWSTR pBufW = reinterpret_cast<PWSTR>(_alloca((nBytes)+1));
+          if ( FAILED(hr=pStream->Read(pBufW, nBytes, &nRead)) )
+            TRACE(_T("StreamLoad: Cannot read string: 0x%X\n"), hr);
+          else
+            sscpy(pMyBuf, pBufW, hdr.nChars);
+        }
+      }
+      else
+      {
+        ULONG nBytes  = hdr.nChars * sizeof(char);
+        if ( sizeof(CT) == sizeof(char) )
+        {
+          if ( FAILED(hr=pStream->Read(pMyBuf, nBytes, &nRead)) )
+            TRACE(_T("StreamLoad: Cannot read string: 0x%X\n"), hr);
+        }
+        else
+        {
+          PSTR pBufA = reinterpret_cast<PSTR>(_alloca(nBytes));
+          if ( FAILED(hr=pStream->Read(pBufA, hdr.nChars, &nRead)) )
+            TRACE(_T("StreamLoad: Cannot read string: 0x%X\n"), hr);
+          else
+            sscpy(pMyBuf, pBufA, hdr.nChars);
+        }
+      }
+    }
+    else
+    {
+      this->erase();
+    }
+    return hr;
+  }
+#endif // #ifdef SS_INC_COMDEF
+
+#ifndef SS_ANSI
+
+  // SetResourceHandle/GetResourceHandle.  In MFC builds, these map directly
+  // to AfxSetResourceHandle and AfxGetResourceHandle.  In non-MFC builds they
+  // point to a single static HINST so that those who call the member
+  // functions that take resource IDs can provide an alternate HINST of a DLL
+  // to search.  This is not exactly the list of HMODULES that MFC provides
+  // but it's better than nothing.
+
+  #ifdef _MFC_VER
+    static void SetResourceHandle(HMODULE hNew)
+    {
+      AfxSetResourceHandle(hNew);
+    }
+    static HMODULE GetResourceHandle()
+    {
+      return AfxGetResourceHandle();
+    }
+  #else
+    static void SetResourceHandle(HMODULE hNew)
+    {
+      SSResourceHandle() = hNew;
+    }
+    static HMODULE GetResourceHandle()
+    {
+      return SSResourceHandle();
+    }
+  #endif
+
+#endif
+};
+
+// -----------------------------------------------------------------------------
+// MSVC USERS: HOW TO EXPORT CSTDSTRING FROM A DLL
+//
+// If you are using MS Visual C++ and you want to export CStdStringA and
+// CStdStringW from a DLL, then all you need to
+//
+//    1.  make sure that all components link to the same DLL version
+//      of the CRT (not the static one).
+//    2.  Uncomment the 3 lines of code below
+//    3.  #define 2 macros per the instructions in MS KnowledgeBase
+//      article Q168958.  The macros are:
+//
+//    MACRO    DEFINTION WHEN EXPORTING    DEFINITION WHEN IMPORTING
+//    -----    ------------------------    -------------------------
+//    SSDLLEXP  (nothing, just #define it)    extern
+//    SSDLLSPEC  __declspec(dllexport)      __declspec(dllimport)
+//
+//    Note that these macros must be available to ALL clients who want to
+//    link to the DLL and use the class.  If they
+//
+// A word of advice: Don't bother.
+//
+// Really, it is not necessary to export CStdString functions from a DLL.  I
+// never do.  In my projects, I do generally link to the DLL version of the
+// Standard C++ Library, but I do NOT attempt to export CStdString functions.
+// I simply include the header where it is needed and allow for the code
+// redundancy.
+//
+// That redundancy is a lot less than you think.  This class does most of its
+// work via the Standard C++ Library, particularly the base_class basic_string<>
+// member functions.  Most of the functions here are small enough to be inlined
+// anyway.  Besides, you'll find that in actual practice you use less than 1/2
+// of the code here, even in big projects and different modules will use as
+// little as 10% of it.  That means a lot less functions actually get linked
+// your binaries.  If you export this code from a DLL, it ALL gets linked in.
+//
+// I've compared the size of the binaries from exporting vs NOT exporting.  Take
+// my word for it -- exporting this code is not worth the hassle.
+//
+// -----------------------------------------------------------------------------
+//#pragma warning(disable:4231) // non-standard extension ("extern template")
+//  SSDLLEXP template class SSDLLSPEC CStdStr<char>;
+//  SSDLLEXP template class SSDLLSPEC CStdStr<wchar_t>;
+
+
+// =============================================================================
+//            END OF CStdStr INLINE FUNCTION DEFINITIONS
+// =============================================================================
+
+//  Now typedef our class names based upon this humongous template
+
+typedef CStdStr<char>    CStdStringA;  // a better std::string
+typedef CStdStr<wchar_t>  CStdStringW;  // a better std::wstring
+typedef CStdStr<uint16_t>  CStdString16;  // a 16bit char string
+typedef CStdStr<uint32_t>  CStdString32;  // a 32bit char string
+typedef CStdStr<OLECHAR>  CStdStringO;  // almost always CStdStringW
+
+// -----------------------------------------------------------------------------
+// CStdStr addition functions defined as inline
+// -----------------------------------------------------------------------------
+
+
+inline CStdStringA operator+(const CStdStringA& s1, const CStdStringA& s2)
+{
+  CStdStringA sRet(SSREF(s1));
+  sRet.append(s2);
+  return sRet;
+}
+inline CStdStringA operator+(const CStdStringA& s1, CStdStringA::value_type t)
+{
+  CStdStringA sRet(SSREF(s1));
+  sRet.append(1, t);
+  return sRet;
+}
+inline CStdStringA operator+(const CStdStringA& s1, PCSTR pA)
+{
+  CStdStringA sRet(SSREF(s1));
+  sRet.append(pA);
+  return sRet;
+}
+inline CStdStringA operator+(PCSTR pA, const CStdStringA& sA)
+{
+  CStdStringA sRet;
+  CStdStringA::size_type nObjSize = sA.size();
+  CStdStringA::size_type nLitSize =
+    static_cast<CStdStringA::size_type>(sslen(pA));
+
+  sRet.reserve(nLitSize + nObjSize);
+  sRet.assign(pA);
+  sRet.append(sA);
+  return sRet;
+}
+
+
+inline CStdStringA operator+(const CStdStringA& s1, const CStdStringW& s2)
+{
+  return s1 + CStdStringA(s2);
+}
+inline CStdStringW operator+(const CStdStringW& s1, const CStdStringW& s2)
+{
+  CStdStringW sRet(SSREF(s1));
+  sRet.append(s2);
+  return sRet;
+}
+inline CStdStringA operator+(const CStdStringA& s1, PCWSTR pW)
+{
+  return s1 + CStdStringA(pW);
+}
+
+#ifdef UNICODE
+  inline CStdStringW operator+(PCWSTR pW, const CStdStringA& sA)
+  {
+    return CStdStringW(pW) + CStdStringW(SSREF(sA));
+  }
+  inline CStdStringW operator+(PCSTR pA, const CStdStringW& sW)
+  {
+    return CStdStringW(pA) + sW;
+  }
+#else
+  inline CStdStringA operator+(PCWSTR pW, const CStdStringA& sA)
+  {
+    return CStdStringA(pW) + sA;
+  }
+  inline CStdStringA operator+(PCSTR pA, const CStdStringW& sW)
+  {
+    return pA + CStdStringA(sW);
+  }
+#endif
+
+// ...Now the wide string versions.
+inline CStdStringW operator+(const CStdStringW& s1, CStdStringW::value_type t)
+{
+  CStdStringW sRet(SSREF(s1));
+  sRet.append(1, t);
+  return sRet;
+}
+inline CStdStringW operator+(const CStdStringW& s1, PCWSTR pW)
+{
+  CStdStringW sRet(SSREF(s1));
+  sRet.append(pW);
+  return sRet;
+}
+inline CStdStringW operator+(PCWSTR pW, const CStdStringW& sW)
+{
+  CStdStringW sRet;
+  CStdStringW::size_type nObjSize = sW.size();
+  CStdStringA::size_type nLitSize =
+    static_cast<CStdStringW::size_type>(sslen(pW));
+
+  sRet.reserve(nLitSize + nObjSize);
+  sRet.assign(pW);
+  sRet.append(sW);
+  return sRet;
+}
+
+inline CStdStringW operator+(const CStdStringW& s1, const CStdStringA& s2)
+{
+  return s1 + CStdStringW(s2);
+}
+inline CStdStringW operator+(const CStdStringW& s1, PCSTR pA)
+{
+  return s1 + CStdStringW(pA);
+}
+
+
+// New-style format function is a template
+
+#ifdef SS_SAFE_FORMAT
+
+template<>
+struct FmtArg<CStdStringA>
+{
+    explicit FmtArg(const CStdStringA& arg) : a_(arg) {}
+    PCSTR operator()() const { return a_.c_str(); }
+    const CStdStringA& a_;
+private:
+    FmtArg<CStdStringA>& operator=(const FmtArg<CStdStringA>&) { return *this; }
+};
+template<>
+struct FmtArg<CStdStringW>
+{
+    explicit FmtArg(const CStdStringW& arg) : a_(arg) {}
+    PCWSTR operator()() const { return a_.c_str(); }
+    const CStdStringW& a_;
+private:
+    FmtArg<CStdStringW>& operator=(const FmtArg<CStdStringW>&) { return *this; }
+};
+
+template<>
+struct FmtArg<std::string>
+{
+    explicit FmtArg(const std::string& arg) : a_(arg) {}
+    PCSTR operator()() const { return a_.c_str(); }
+    const std::string& a_;
+private:
+    FmtArg<std::string>& operator=(const FmtArg<std::string>&) { return *this; }
+};
+template<>
+struct FmtArg<std::wstring>
+{
+    explicit FmtArg(const std::wstring& arg) : a_(arg) {}
+    PCWSTR operator()() const { return a_.c_str(); }
+    const std::wstring& a_;
+private:
+    FmtArg<std::wstring>& operator=(const FmtArg<std::wstring>&) {return *this;}
+};
+#endif // #ifdef SS_SAFEFORMAT
+
+#ifndef SS_ANSI
+  // SSResourceHandle: our MFC-like resource handle
+  inline HMODULE& SSResourceHandle()
+  {
+    static HMODULE hModuleSS  = GetModuleHandle(0);
+    return hModuleSS;
+  }
+#endif
+
+
+// In MFC builds, define some global serialization operators
+// Special operators that allow us to serialize CStdStrings to CArchives.
+// Note that we use an intermediate CString object in order to ensure that
+// we use the exact same format.
+
+#ifdef _MFC_VER
+  inline CArchive& AFXAPI operator<<(CArchive& ar, const CStdStringA& strA)
+  {
+    CString strTemp  = strA;
+    return ar << strTemp;
+  }
+  inline CArchive& AFXAPI operator<<(CArchive& ar, const CStdStringW& strW)
+  {
+    CString strTemp  = strW;
+    return ar << strTemp;
+  }
+
+  inline CArchive& AFXAPI operator>>(CArchive& ar, CStdStringA& strA)
+  {
+    CString strTemp;
+    ar >> strTemp;
+    strA = strTemp;
+    return ar;
+  }
+  inline CArchive& AFXAPI operator>>(CArchive& ar, CStdStringW& strW)
+  {
+    CString strTemp;
+    ar >> strTemp;
+    strW = strTemp;
+    return ar;
+  }
+#endif  // #ifdef _MFC_VER -- (i.e. is this MFC?)
+
+
+
+// -----------------------------------------------------------------------------
+// GLOBAL FUNCTION:  WUFormat
+//    CStdStringA WUFormat(UINT nId, ...);
+//    CStdStringA WUFormat(PCSTR szFormat, ...);
+//
+// REMARKS:
+//    This function allows the caller for format and return a CStdStringA
+//    object with a single line of code.
+// -----------------------------------------------------------------------------
+#ifdef SS_ANSI
+#else
+  inline CStdStringA WUFormatA(UINT nId, ...)
+  {
+    va_list argList;
+    va_start(argList, nId);
+
+    CStdStringA strFmt;
+    CStdStringA strOut;
+    if ( strFmt.Load(nId) )
+      strOut.FormatV(strFmt, argList);
+
+    va_end(argList);
+    return strOut;
+  }
+  inline CStdStringA WUFormatA(PCSTR szFormat, ...)
+  {
+    va_list argList;
+    va_start(argList, szFormat);
+    CStdStringA strOut;
+    strOut.FormatV(szFormat, argList);
+    va_end(argList);
+    return strOut;
+  }
+  inline CStdStringW WUFormatW(UINT nId, ...)
+  {
+    va_list argList;
+    va_start(argList, nId);
+
+    CStdStringW strFmt;
+    CStdStringW strOut;
+    if ( strFmt.Load(nId) )
+      strOut.FormatV(strFmt, argList);
+
+    va_end(argList);
+    return strOut;
+  }
+  inline CStdStringW WUFormatW(PCWSTR szwFormat, ...)
+  {
+    va_list argList;
+    va_start(argList, szwFormat);
+    CStdStringW strOut;
+    strOut.FormatV(szwFormat, argList);
+    va_end(argList);
+    return strOut;
+  }
+#endif // #ifdef SS_ANSI
+
+
+
+#if defined(SS_WIN32) && !defined (SS_ANSI)
+  // -------------------------------------------------------------------------
+  // FUNCTION: WUSysMessage
+  //   CStdStringA WUSysMessageA(DWORD dwError, DWORD dwLangId=SS_DEFLANGID);
+  //   CStdStringW WUSysMessageW(DWORD dwError, DWORD dwLangId=SS_DEFLANGID);
+  //
+  // DESCRIPTION:
+  //   This function simplifies the process of obtaining a string equivalent
+  //   of a system error code returned from GetLastError().  You simply
+  //   supply the value returned by GetLastError() to this function and the
+  //   corresponding system string is returned in the form of a CStdStringA.
+  //
+  // PARAMETERS:
+  //   dwError - a DWORD value representing the error code to be translated
+  //   dwLangId - the language id to use.  defaults to english.
+  //
+  // RETURN VALUE:
+  //   a CStdStringA equivalent of the error code.  Currently, this function
+  //   only returns either English of the system default language strings.
+  // -------------------------------------------------------------------------
+  #define SS_DEFLANGID MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT)
+  inline CStdStringA WUSysMessageA(DWORD dwError, DWORD dwLangId=SS_DEFLANGID)
+  {
+    CHAR szBuf[512];
+
+    if ( 0 != ::FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwError,
+                   dwLangId, szBuf, 511, NULL) )
+      return WUFormatA("%s (0x%X)", szBuf, dwError);
+    else
+       return WUFormatA("Unknown error (0x%X)", dwError);
+  }
+  inline CStdStringW WUSysMessageW(DWORD dwError, DWORD dwLangId=SS_DEFLANGID)
+  {
+    WCHAR szBuf[512];
+
+    if ( 0 != ::FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwError,
+                   dwLangId, szBuf, 511, NULL) )
+      return WUFormatW(L"%s (0x%X)", szBuf, dwError);
+    else
+       return WUFormatW(L"Unknown error (0x%X)", dwError);
+  }
+#endif
+
+// Define TCHAR based friendly names for some of these functions
+
+#ifdef UNICODE
+  //#define CStdString        CStdStringW
+  typedef CStdStringW        CStdString;
+  #define WUSysMessage      WUSysMessageW
+  #define WUFormat        WUFormatW
+#else
+  //#define CStdString        CStdStringA
+  typedef CStdStringA        CStdString;
+  #define WUSysMessage      WUSysMessageA
+  #define WUFormat        WUFormatA
+#endif
+
+// ...and some shorter names for the space-efficient
+
+#define WUSysMsg          WUSysMessage
+#define WUSysMsgA          WUSysMessageA
+#define WUSysMsgW          WUSysMessageW
+#define WUFmtA            WUFormatA
+#define  WUFmtW            WUFormatW
+#define WUFmt            WUFormat
+#define WULastErrMsg()        WUSysMessage(::GetLastError())
+#define WULastErrMsgA()        WUSysMessageA(::GetLastError())
+#define WULastErrMsgW()        WUSysMessageW(::GetLastError())
+
+
+// -----------------------------------------------------------------------------
+// FUNCTIONAL COMPARATORS:
+// REMARKS:
+//    These structs are derived from the std::binary_function template.  They
+//    give us functional classes (which may be used in Standard C++ Library
+//    collections and algorithms) that perform case-insensitive comparisons of
+//    CStdString objects.  This is useful for maps in which the key may be the
+//     proper string but in the wrong case.
+// -----------------------------------------------------------------------------
+#define StdStringLessNoCaseW    SSLNCW  // avoid VC compiler warning 4786
+#define StdStringEqualsNoCaseW    SSENCW
+#define StdStringLessNoCaseA    SSLNCA
+#define StdStringEqualsNoCaseA    SSENCA
+
+#ifdef UNICODE
+  #define StdStringLessNoCase    SSLNCW
+  #define StdStringEqualsNoCase  SSENCW
+#else
+  #define StdStringLessNoCase    SSLNCA
+  #define StdStringEqualsNoCase  SSENCA
+#endif
+
+struct StdStringLessNoCaseW
+  : std::binary_function<CStdStringW, CStdStringW, bool>
+{
+  inline
+  bool operator()(const CStdStringW& sLeft, const CStdStringW& sRight) const
+  { return ssicmp(sLeft.c_str(), sRight.c_str()) < 0; }
+};
+struct StdStringEqualsNoCaseW
+  : std::binary_function<CStdStringW, CStdStringW, bool>
+{
+  inline
+  bool operator()(const CStdStringW& sLeft, const CStdStringW& sRight) const
+  { return ssicmp(sLeft.c_str(), sRight.c_str()) == 0; }
+};
+struct StdStringLessNoCaseA
+  : std::binary_function<CStdStringA, CStdStringA, bool>
+{
+  inline
+  bool operator()(const CStdStringA& sLeft, const CStdStringA& sRight) const
+  { return ssicmp(sLeft.c_str(), sRight.c_str()) < 0; }
+};
+struct StdStringEqualsNoCaseA
+  : std::binary_function<CStdStringA, CStdStringA, bool>
+{
+  inline
+  bool operator()(const CStdStringA& sLeft, const CStdStringA& sRight) const
+  { return ssicmp(sLeft.c_str(), sRight.c_str()) == 0; }
+};
+
+// If we had to define our own version of TRACE above, get rid of it now
+
+#ifdef TRACE_DEFINED_HERE
+  #undef TRACE
+  #undef TRACE_DEFINED_HERE
+#endif
+
+
+// These std::swap specializations come courtesy of Mike Crusader.
+
+//namespace std
+//{
+//  inline void swap(CStdStringA& s1, CStdStringA& s2) throw()
+//  {
+//    s1.swap(s2);
+//  }
+//  template<>
+//  inline void swap(CStdStringW& s1, CStdStringW& s2) throw()
+//  {
+//    s1.swap(s2);
+//  }
+//}
+
+// Turn back on any Borland warnings we turned off.
+
+#ifdef __BORLANDC__
+    #pragma option pop  // Turn back on inline function warnings
+//  #pragma warn +inl   // Turn back on inline function warnings
+#endif
+
+#endif  // #ifndef STDSTRING_H
diff --git a/xbmc/pvrclients/MediaPortal/channels.cpp b/xbmc/pvrclients/MediaPortal/channels.cpp
new file mode 100644 (file)
index 0000000..58e2df3
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <vector>
+#include "channels.h"
+#include "utils.h"
+#include <stdlib.h>
+#include <string.h>
+
+cChannel::cChannel(const PVR_CHANNEL *Channel)
+{
+
+}
+
+cChannel::cChannel()
+{
+  name = "";
+  uid = 0;
+  external_id = 0;
+}
+
+cChannel::~cChannel()
+{
+}
+
+bool cChannel::Parse(const std::string& data)
+{
+  vector<string> fields;
+
+  Tokenize(data, fields, "|");
+
+  if (fields.size() >= 4)
+  {
+    //Expected format:
+    //ListTVChannels, ListRadioChannels
+    //0 = channel uid
+    //1 = channel external id/number
+    //2 = channel name
+    //3 = isencrypted ("0"/"1")
+
+    uid = atoi(fields[0].c_str());
+    external_id = atoi(fields[1].c_str());
+    name = fields[2];
+    encrypted = (strncmp(fields[3].c_str(), "1", 1) == 0);
+    return true;
+  } else {
+    return false;
+  }
+}
diff --git a/xbmc/pvrclients/MediaPortal/channels.h b/xbmc/pvrclients/MediaPortal/channels.h
new file mode 100644 (file)
index 0000000..812b4ca
--- /dev/null
@@ -0,0 +1,49 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef __CHANNELS_H
+#define __CHANNELS_H
+
+#include "libXBMC_pvr.h"
+#include <string>
+
+class cChannel
+{
+private:
+  std::string name;
+  int uid;
+  int external_id;
+  bool encrypted;
+
+public:
+  cChannel(const PVR_CHANNEL *Channel);
+  cChannel();
+  virtual ~cChannel();
+
+  bool Parse(const std::string& data);
+  const char *Name(void) const { return name.c_str(); }
+  int UID(void) const { return uid; }
+  int ExternalID(void) const { return external_id; }
+  bool Encrypted(void) const { return encrypted; }
+};
+
+#endif //__TIMERS_H
diff --git a/xbmc/pvrclients/MediaPortal/client.cpp b/xbmc/pvrclients/MediaPortal/client.cpp
new file mode 100644 (file)
index 0000000..3799c9e
--- /dev/null
@@ -0,0 +1,672 @@
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, 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 "pvrclient-mediaportal.h"
+#include "utils.h"
+
+using namespace std;
+
+cPVRClientMediaPortal *g_client = NULL;
+bool m_bCreated         = false;
+ADDON_STATUS curStatus  = STATUS_UNKNOWN;
+int g_clientID          = -1;
+
+/* User adjustable settings are saved here.
+ * Default values are defined inside client.h
+ * and exported to the other source files.
+ */
+std::string m_sHostname     = DEFAULT_HOST;
+int m_iPort                 = DEFAULT_PORT;
+bool m_bOnlyFTA             = DEFAULT_FTA_ONLY;
+bool m_bRadioEnabled        = DEFAULT_RADIO;
+bool m_bCharsetConv         = DEFAULT_CHARCONV;
+int m_iConnectTimeout       = DEFAULT_TIMEOUT;
+bool m_bNoBadChannels       = DEFAULT_BADCHANNELS;
+bool m_bHandleMessages      = DEFAULT_HANDLE_MSG;
+std::string g_szUserPath    = "";
+std::string g_szClientPath  = "";
+std::string g_sTVGroup      = "";
+std::string g_sRadioGroup   = "";
+bool m_bResolveRTSPHostname = DEFAULT_RESOLVE_RTSP_HOSTNAME;
+bool m_bReadGenre           = DEFAULT_READ_GENRE;
+int m_iSleepOnRTSPurl       = DEFAULT_SLEEP_RTSP_URL;
+
+cHelper_libXBMC_addon *XBMC = NULL;
+cHelper_libXBMC_pvr   *PVR  = NULL;
+
+extern "C" {
+
+/***********************************************************
+ * Standard AddOn related public library functions
+ ***********************************************************/
+
+//-- Create -------------------------------------------------------------------
+// Called after loading of the dll, all steps to become Client functional
+// must be performed here.
+//-----------------------------------------------------------------------------
+ADDON_STATUS Create(void* hdl, void* props)
+{
+  if (!hdl || !props)
+    return STATUS_UNKNOWN;
+
+  PVR_PROPS* pvrprops = (PVR_PROPS*)props;
+
+  XBMC = new cHelper_libXBMC_addon;
+  if (!XBMC->RegisterMe(hdl))
+    return STATUS_UNKNOWN;
+
+  PVR = new cHelper_libXBMC_pvr;
+  if (!PVR->RegisterMe(hdl))
+    return STATUS_UNKNOWN;
+
+  XBMC->Log(LOG_DEBUG, "Creating MediaPortal PVR-Client");
+
+  curStatus      = STATUS_UNKNOWN;
+  g_client       = new cPVRClientMediaPortal();
+  g_clientID     = pvrprops->clientID;
+  g_szUserPath   = pvrprops->userpath;
+  g_szClientPath = pvrprops->clientpath;
+
+  /* Read setting "host" from settings.xml */
+  char buffer[1024];
+
+  if (XBMC->GetSetting("host", &buffer))
+    m_sHostname = buffer;
+  else
+  {
+    /* If setting is unknown fallback to defaults */
+    XBMC->Log(LOG_ERROR, "Couldn't get 'host' setting, falling back to '127.0.0.1' as default");
+    m_sHostname = DEFAULT_HOST;
+  }
+
+  /* Read setting "port" from settings.xml */
+  if (!XBMC->GetSetting("port", &m_iPort))
+  {
+    /* If setting is unknown fallback to defaults */
+    XBMC->Log(LOG_ERROR, "Couldn't get 'port' setting, falling back to '9596' as default");
+    m_iPort = DEFAULT_PORT;
+  }
+
+  /* Read setting "ftaonly" from settings.xml */
+  if (!XBMC->GetSetting("ftaonly", &m_bOnlyFTA))
+  {
+    /* If setting is unknown fallback to defaults */
+    XBMC->Log(LOG_ERROR, "Couldn't get 'ftaonly' setting, falling back to 'false' as default");
+    m_bOnlyFTA = DEFAULT_FTA_ONLY;
+  }
+
+  /* Read setting "useradio" from settings.xml */
+  if (!XBMC->GetSetting("useradio", &m_bRadioEnabled))
+  {
+    /* If setting is unknown fallback to defaults */
+    XBMC->Log(LOG_ERROR, "Couldn't get 'useradio' setting, falling back to 'true' as default");
+    m_bRadioEnabled = DEFAULT_RADIO;
+  }
+
+  /* Read setting "convertchar" from settings.xml */
+  if (!XBMC->GetSetting("convertchar", &m_bCharsetConv))
+  {
+    /* If setting is unknown fallback to defaults */
+    XBMC->Log(LOG_ERROR, "Couldn't get 'convertchar' setting, falling back to 'false' as default");
+    m_bCharsetConv = DEFAULT_CHARCONV;
+  }
+
+  /* Read setting "timeout" from settings.xml */
+  if (!XBMC->GetSetting("timeout", &m_iConnectTimeout))
+  {
+    /* If setting is unknown fallback to defaults */
+    XBMC->Log(LOG_ERROR, "Couldn't get 'timeout' setting, falling back to %i seconds as default", DEFAULT_TIMEOUT);
+    m_iConnectTimeout = DEFAULT_TIMEOUT;
+  }
+
+  if (!XBMC->GetSetting("tvgroup", &buffer))
+  {
+    /* If setting is unknown fallback to defaults */
+    XBMC->Log(LOG_ERROR, "Couldn't get 'tvgroup' setting, falling back to '' as default");
+  } else {
+    g_sTVGroup = buffer;
+  }
+
+  if (!XBMC->GetSetting("radiogroup", &buffer))
+  {
+    /* If setting is unknown fallback to defaults */
+    XBMC->Log(LOG_ERROR, "Couldn't get 'tvgroup' setting, falling back to '' as default");
+  } else {
+    g_sRadioGroup = buffer;
+  }
+
+  /* Read setting "resolvertsphostname" from settings.xml */
+  if (!XBMC->GetSetting("resolvertsphostname", &m_bResolveRTSPHostname))
+  {
+    /* If setting is unknown fallback to defaults */
+    XBMC->Log(LOG_ERROR, "Couldn't get 'resolvertsphostname' setting, falling back to 'true' as default");
+    m_bRadioEnabled = DEFAULT_RESOLVE_RTSP_HOSTNAME;
+  }
+
+  /* Read setting "readgenre" from settings.xml */
+  if (!XBMC->GetSetting("readgenre", &m_bReadGenre))
+  {
+    /* If setting is unknown fallback to defaults */
+    XBMC->Log(LOG_ERROR, "Couldn't get 'resolvertsphostname' setting, falling back to 'true' as default");
+    m_bReadGenre = DEFAULT_READ_GENRE;
+  }
+
+  /* Read setting "sleeponrtspurl" from settings.xml */
+  if (!XBMC->GetSetting("sleeponrtspurl", &m_iSleepOnRTSPurl))
+  {
+    /* If setting is unknown fallback to defaults */
+    XBMC->Log(LOG_ERROR, "Couldn't get 'sleeponrtspurl' setting, falling back to %i seconds as default", DEFAULT_SLEEP_RTSP_URL);
+    m_iSleepOnRTSPurl = DEFAULT_SLEEP_RTSP_URL;
+  }
+
+  /* Create connection to MediaPortal XBMC TV client */
+  if (!g_client->Connect())
+  {
+    curStatus = STATUS_LOST_CONNECTION;
+  }
+  else
+  {
+    curStatus = STATUS_OK;
+  }
+
+  m_bCreated = true;
+
+  return curStatus;
+}
+
+//-- Destroy ------------------------------------------------------------------
+// Used during destruction of the client, all steps to do clean and safe Create
+// again must be done.
+//-----------------------------------------------------------------------------
+void Destroy()
+{
+  if (m_bCreated)
+  {
+    g_client->Disconnect();
+    delete_null(g_client);
+
+    m_bCreated = false;
+  }
+
+  if (PVR)
+  {
+    delete_null(PVR);
+  }
+  if (XBMC)
+  {
+    delete_null(XBMC);
+  }
+
+
+  curStatus = STATUS_UNKNOWN;
+}
+
+//-- GetStatus ----------------------------------------------------------------
+// Report the current Add-On Status to XBMC
+//-----------------------------------------------------------------------------
+ADDON_STATUS GetStatus()
+{
+  return curStatus;
+}
+
+//-- HasSettings --------------------------------------------------------------
+// Report "true", yes this AddOn have settings
+//-----------------------------------------------------------------------------
+bool HasSettings()
+{
+  return true;
+}
+
+unsigned int GetSettings(StructSetting ***sSet)
+{
+  return 0;
+}
+
+//-- SetSetting ---------------------------------------------------------------
+// Called everytime a setting is changed by the user and to inform AddOn about
+// new setting and to do required stuff to apply it.
+//-----------------------------------------------------------------------------
+ADDON_STATUS SetSetting(const char *settingName, const void *settingValue)
+{
+  string str = settingName;
+  if (str == "host")
+  {
+    string tmp_sHostname;
+    XBMC->Log(LOG_INFO, "Changed Setting 'host' from %s to %s", m_sHostname.c_str(), (const char*) settingValue);
+    tmp_sHostname = m_sHostname;
+    m_sHostname = (const char*) settingValue;
+    if (tmp_sHostname != m_sHostname)
+      return STATUS_NEED_RESTART;
+  }
+  else if (str == "port")
+  {
+    XBMC->Log(LOG_INFO, "Changed Setting 'port' from %u to %u", m_iPort, *(int*) settingValue);
+    if (m_iPort != *(int*) settingValue)
+    {
+      m_iPort = *(int*) settingValue;
+      return STATUS_NEED_RESTART;
+    }
+  }
+  else if (str == "ftaonly")
+  {
+    XBMC->Log(LOG_INFO, "Changed setting 'ftaonly' from %u to %u", m_bOnlyFTA, *(bool*) settingValue);
+    m_bOnlyFTA = *(bool*) settingValue;
+  }
+  else if (str == "useradio")
+  {
+    XBMC->Log(LOG_INFO, "Changed setting 'useradio' from %u to %u", m_bRadioEnabled, *(bool*) settingValue);
+    m_bRadioEnabled = *(bool*) settingValue;
+  }
+  else if (str == "convertchar")
+  {
+    XBMC->Log(LOG_INFO, "Changed setting 'convertchar' from %u to %u", m_bCharsetConv, *(bool*) settingValue);
+    m_bCharsetConv = *(bool*) settingValue;
+  }
+  else if (str == "timeout")
+  {
+    XBMC->Log(LOG_INFO, "Changed setting 'timeout' from %u to %u", m_iConnectTimeout, *(int*) settingValue);
+    m_iConnectTimeout = *(int*) settingValue;
+  }
+  else if (str == "tvgroup")
+  {
+    XBMC->Log(LOG_INFO, "Changed setting 'tvgroup' from %s to %s", g_sTVGroup.c_str(), (const char*) settingValue);
+    g_sTVGroup = (const char*) settingValue;
+  }
+  else if (str == "radiogroup")
+  {
+    XBMC->Log(LOG_INFO, "Changed setting 'radiogroup' from %s to %s", g_sTVGroup.c_str(), (const char*) settingValue);
+    g_sTVGroup = (const char*) settingValue;
+  }
+  else if (str == "resolvertsphostname")
+  {
+    XBMC->Log(LOG_INFO, "Changed setting 'resolvertsphostname' from %u to %u", m_bResolveRTSPHostname, *(bool*) settingValue);
+    m_bResolveRTSPHostname = *(bool*) settingValue;
+  }
+  else if (str == "readgenre")
+  {
+    XBMC->Log(LOG_INFO, "Changed setting 'readgenre' from %u to %u",m_bReadGenre, *(bool*) settingValue);
+    m_bReadGenre = *(bool*) settingValue;
+  }
+  else if (str == "sleeponrtspurl")
+  {
+    XBMC->Log(LOG_INFO, "Changed setting 'sleeponrtspurl' from %u to %u", m_iSleepOnRTSPurl, *(int*) settingValue);
+    m_iSleepOnRTSPurl = *(int*) settingValue;
+  }
+
+  return STATUS_OK;
+}
+
+//-- Remove ------------------------------------------------------------------
+// Used during destruction of the client, all steps to do clean and safe Create
+// again must be done.
+//-----------------------------------------------------------------------------
+void Remove()
+{
+  if (m_bCreated)
+  {
+    g_client->Disconnect();
+
+    delete g_client;
+    g_client = NULL;
+
+    m_bCreated = false;
+  }
+  curStatus = STATUS_UNKNOWN;
+}
+
+void Stop()
+{
+}
+
+void FreeSettings()
+{
+
+}
+
+/***********************************************************
+ * PVR Client AddOn specific public library functions
+ ***********************************************************/
+
+//-- GetProperties ------------------------------------------------------------
+// Tell XBMC our requirements
+//-----------------------------------------------------------------------------
+PVR_ERROR GetProperties(PVR_SERVERPROPS* props)
+{
+  XBMC->Log(LOG_DEBUG, "->GetProperties()");
+
+  props->SupportChannelLogo        = false;
+  props->SupportTimeShift          = false;
+  props->SupportEPG                = true;
+  props->SupportRecordings         = true;
+  props->SupportTimers             = true;
+  props->SupportTV                 = true;
+  props->SupportRadio              = m_bRadioEnabled;
+  props->SupportChannelSettings    = true;
+  props->SupportDirector           = false;
+  props->SupportBouquets           = false;
+  props->HandleInputStream         = true;
+  props->HandleDemuxing            = false;
+  props->SupportChannelScan        = false;
+
+  return PVR_ERROR_NO_ERROR;
+}
+
+PVR_ERROR GetStreamProperties(PVR_STREAMPROPS* props)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+//-- GetBackendName -----------------------------------------------------------
+// Return the Name of the Backend
+//-----------------------------------------------------------------------------
+const char * GetBackendName()
+{
+  return g_client->GetBackendName();
+}
+
+//-- GetBackendVersion --------------------------------------------------------
+// Return the Version of the Backend as String
+//-----------------------------------------------------------------------------
+const char * GetBackendVersion()
+{
+  return g_client->GetBackendVersion();
+}
+
+//-- GetConnectionString ------------------------------------------------------
+// Return a String with connection info, if available
+//-----------------------------------------------------------------------------
+const char * GetConnectionString()
+{
+  return g_client->GetConnectionString();
+}
+
+//-- GetDriveSpace ------------------------------------------------------------
+// Return the Total and Free Drive space on the PVR Backend
+//-----------------------------------------------------------------------------
+PVR_ERROR GetDriveSpace(long long *total, long long *used)
+{
+  return g_client->GetDriveSpace(total, used);
+}
+
+PVR_ERROR GetBackendTime(time_t *localTime, int *gmtOffset)
+{
+  return g_client->GetMPTVTime(localTime, gmtOffset);
+}
+
+PVR_ERROR DialogChannelScan()
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+PVR_ERROR MenuHook(const PVR_MENUHOOK &menuhook)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+
+/*******************************************/
+/** PVR EPG Functions                     **/
+
+PVR_ERROR RequestEPGForChannel(PVRHANDLE handle, const PVR_CHANNEL &channel, time_t start, time_t end)
+{
+  return g_client->RequestEPGForChannel(channel, handle, start, end);
+}
+
+
+/*******************************************/
+/** PVR Bouquets Functions                **/
+
+int GetNumBouquets()
+{
+  return 0;
+}
+
+PVR_ERROR RequestBouquetsList(PVRHANDLE handle, int radio)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+
+/*******************************************/
+/** PVR Channel Functions                 **/
+
+int GetNumChannels()
+{
+  return g_client->GetNumChannels();
+}
+
+PVR_ERROR RequestChannelList(PVRHANDLE handle, int radio)
+{
+  return g_client->RequestChannelList(handle, radio);
+}
+
+PVR_ERROR DeleteChannel(unsigned int number)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+PVR_ERROR RenameChannel(unsigned int number, const char *newname)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+PVR_ERROR MoveChannel(unsigned int number, unsigned int newnumber)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+PVR_ERROR DialogChannelSettings(const PVR_CHANNEL &channelinfo)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+PVR_ERROR DialogAddChannel(const PVR_CHANNEL &channelinfo)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+
+/*******************************************/
+/** PVR Recording Functions               **/
+
+int GetNumRecordings(void)
+{
+  return g_client->GetNumRecordings();
+}
+
+PVR_ERROR RequestRecordingsList(PVRHANDLE handle)
+{
+  return g_client->RequestRecordingsList(handle);
+}
+
+PVR_ERROR DeleteRecording(const PVR_RECORDINGINFO &recinfo)
+{
+  return g_client->DeleteRecording(recinfo);
+}
+
+PVR_ERROR RenameRecording(const PVR_RECORDINGINFO &recinfo, const char *newname)
+{
+  return g_client->RenameRecording(recinfo, newname);
+}
+
+
+/*******************************************/
+/** PVR Recording cut marks Functions     **/
+
+bool HaveCutmarks()
+{
+  return false;
+}
+
+PVR_ERROR RequestCutMarksList(PVRHANDLE handle)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+PVR_ERROR AddCutMark(const PVR_CUT_MARK &cutmark)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+PVR_ERROR DeleteCutMark(const PVR_CUT_MARK &cutmark)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+PVR_ERROR StartCut()
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+/*******************************************/
+/** PVR Timer Functions                   **/
+
+int GetNumTimers(void)
+{
+  return g_client->GetNumTimers();
+}
+
+PVR_ERROR RequestTimerList(PVRHANDLE handle)
+{
+  return g_client->RequestTimerList(handle);
+}
+
+PVR_ERROR AddTimer(const PVR_TIMERINFO &timerinfo)
+{
+  return g_client->AddTimer(timerinfo);
+}
+
+PVR_ERROR DeleteTimer(const PVR_TIMERINFO &timerinfo, bool force)
+{
+  return g_client->DeleteTimer(timerinfo, force);
+}
+
+PVR_ERROR RenameTimer(const PVR_TIMERINFO &timerinfo, const char *newname)
+{
+  return g_client->RenameTimer(timerinfo, newname);
+}
+
+PVR_ERROR UpdateTimer(const PVR_TIMERINFO &timerinfo)
+{
+  return g_client->UpdateTimer(timerinfo);
+}
+
+
+/*******************************************/
+/** PVR Live Stream Functions             **/
+
+bool OpenLiveStream(const PVR_CHANNEL &channelinfo)
+{
+  return g_client->OpenLiveStream(channelinfo);
+}
+
+void CloseLiveStream()
+{
+  return g_client->CloseLiveStream();
+}
+
+int ReadLiveStream(unsigned char* buf, int buf_size)
+{
+  return g_client->ReadLiveStream(buf, buf_size);
+}
+
+long long SeekLiveStream(long long pos, int whence)
+{
+  return -1;
+}
+
+long long PositionLiveStream(void)
+{
+  return -1;
+}
+
+long long LengthLiveStream(void)
+{
+  return -1;
+}
+
+int GetCurrentClientChannel()
+{
+  return g_client->GetCurrentClientChannel();
+}
+
+bool SwitchChannel(const PVR_CHANNEL &channelinfo)
+{
+  return g_client->SwitchChannel(channelinfo);
+}
+
+PVR_ERROR SignalQuality(PVR_SIGNALQUALITY &qualityinfo)
+{
+  return g_client->SignalQuality(qualityinfo);
+}
+
+/*******************************************/
+/** PVR Recording Stream Functions        **/
+
+bool OpenRecordedStream(const PVR_RECORDINGINFO &recinfo)
+{
+  return g_client->OpenRecordedStream(recinfo);
+}
+
+void CloseRecordedStream(void)
+{
+  return g_client->CloseRecordedStream();
+}
+
+int ReadRecordedStream(unsigned char* buf, int buf_size)
+{
+  return g_client->ReadRecordedStream(buf, buf_size);
+}
+
+long long SeekRecordedStream(long long pos, int whence)
+{
+  return g_client->SeekRecordedStream(pos, whence);
+}
+
+long long PositionRecordedStream(void)
+{
+  return -1;
+}
+
+long long LengthRecordedStream(void)
+{
+  return g_client->LengthRecordedStream();
+}
+
+// MG: added for Mediaportal
+const char * GetLiveStreamURL(const PVR_CHANNEL &channelinfo)
+{
+  return g_client->GetLiveStreamURL(channelinfo);
+}
+
+/** UNUSED API FUNCTIONS */
+DemuxPacket* DemuxRead(){return NULL;}
+void DemuxAbort(){}
+void DemuxReset(){}
+void DemuxFlush(){}
+bool SwapLiveTVSecondaryStream() { return false; }
+bool OpenSecondaryStream(const PVR_CHANNEL &channelinfo) { return false; }
+void CloseSecondaryStream() {}
+int ReadSecondaryStream(unsigned char* buf, int buf_size) { return 0; }
+
+} //extern "C"
diff --git a/xbmc/pvrclients/MediaPortal/client.h b/xbmc/pvrclients/MediaPortal/client.h
new file mode 100644 (file)
index 0000000..49d3b4c
--- /dev/null
@@ -0,0 +1,65 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef CLIENT_H
+#define CLIENT_H
+
+//#include <string>
+#include "StdString.h"
+#include "pvrclient-mediaportal.h"
+#include "libXBMC_addon.h"
+#include "libXBMC_pvr.h"
+
+#define DEFAULT_HOST                  "127.0.0.1"
+#define DEFAULT_PORT                  9596
+#define DEFAULT_FTA_ONLY              false
+#define DEFAULT_RADIO                 true
+#define DEFAULT_CHARCONV              false
+#define DEFAULT_TIMEOUT               3
+#define DEFAULT_BADCHANNELS           true
+#define DEFAULT_HANDLE_MSG            false
+#define DEFAULT_RESOLVE_RTSP_HOSTNAME true
+#define DEFAULT_READ_GENRE            false
+#define DEFAULT_SLEEP_RTSP_URL        0
+
+extern bool         m_bCreated;
+extern std::string  m_sHostname;
+extern int          m_iPort;
+extern bool         m_bOnlyFTA;
+extern bool         m_bRadioEnabled;
+extern bool         m_bCharsetConv;
+extern int          m_iConnectTimeout;
+extern bool         m_bNoBadChannels;
+extern bool         m_bHandleMessages;
+extern int          g_clientID;
+extern std::string  g_szUserPath;
+extern std::string  g_szClientPath;
+extern std::string  g_sTVGroup;
+extern std::string  g_sRadioGroup;
+extern bool         m_bResolveRTSPHostname;
+extern bool         m_bReadGenre;
+extern int          m_iSleepOnRTSPurl;
+
+extern cHelper_libXBMC_addon *XBMC;
+extern cHelper_libXBMC_pvr   *PVR;
+
+#endif /* CLIENT_H */
diff --git a/xbmc/pvrclients/MediaPortal/epg.cpp b/xbmc/pvrclients/MediaPortal/epg.cpp
new file mode 100644 (file)
index 0000000..456d19b
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <vector>
+#include <stdio.h>
+
+using namespace std;
+
+#include "epg.h"
+#include "utils.h"
+#include "client.h"
+
+//Copied from PVREpg.h:
+//subtypes derived from English strings.xml and CPVREpgInfoTag::ConvertGenreIdToString
+//TODO: Finish me... This list is not yet complete
+#define EVCONTENTMASK_MOVIEDRAMA                     0x10
+//Subtypes MOVIE/DRAMA
+#define DETECTIVE_THRILLER                           0x01
+#define ADVENTURE_WESTERN_WAR                        0x02
+#define SF_FANTASY_HORROR                            0x03
+#define COMEDY                                       0x04
+#define SOAP_MELODRAMA_FOLKLORIC                     0x05
+#define ROMANCE                                      0x06
+#define SERIOUS_CLASSICAL_RELIGIOUS_HISTORICAL_DRAMA 0x07
+#define ADULTMOVIE_DRAMA                             0x08
+
+#define EVCONTENTMASK_NEWSCURRENTAFFAIRS             0x20
+//subtypes:
+#define NEWS_WEATHER_REPORT                          0x01
+#define NEWS_MAGAZINE                                0x02
+#define DOCUMENTARY                                  0x03
+#define DISCUSSION_INTERVIEW_DEBATE                  0x04
+
+#define EVCONTENTMASK_SHOW                           0x30
+//subtypes:
+#define GAMESHOW_QUIZ_CONTEST                        0x01
+#define VARIETY_SHOW                                 0x02
+#define TALK_SHOW                                    0x03
+
+#define EVCONTENTMASK_SPORTS                         0x40
+
+#define EVCONTENTMASK_CHILDRENYOUTH                  0x50
+//subtypes
+#define PRESCHOOL_CHILD_PROGRAM                      0x01
+#define ENTERTAINMENT_6TO14                          0x02
+#define ENTERTAINMENT_10TO16                         0x03
+#define INFO_EDUC_SCHOOL_PROGRAM                     0x04
+#define CARTOONS_PUPPETS                             0x05
+
+#define EVCONTENTMASK_MUSICBALLETDANCE               0x60
+#define EVCONTENTMASK_ARTSCULTURE                    0x70
+//subtypes
+#define PERFORMING_ARTS                              0x01
+#define FINE_ARTS                                    0x02
+#define RELIGION                                     0x03
+#define POP_CULTURE_TRAD_ARTS                        0x04
+#define LITERATURE                                   0x05
+#define FILM_CINEMA                                  0x06
+#define EXP_FILM_VIDEO                               0x07
+#define BROADCASTING_PRESS                           0x08
+#define NEW_MEDIA                                    0x09
+#define ARTS_CULTURE_MAGAZINES                       0x10
+#define FASHION                                      0x11
+
+#define EVCONTENTMASK_SOCIALPOLITICALECONOMICS       0x80
+//subtype
+#define MAGAZINES_REPORTS_DOCUMENTARY                0x01
+#define ECONOMICS_SOCIAL_ADVISORY                    0x02
+#define REMARKABLE_PEOPLE                            0x03
+
+#define EVCONTENTMASK_EDUCATIONALSCIENCE             0x90
+//subtypes
+#define NATURE_ANIMALS_ENVIRONMENT                   0x01
+#define TECHNOLOGY_NATURAL_SCIENCES                  0x02
+#define MEDICINE_PHYSIOLOGY_PSYCHOLOGY               0x03
+#define FOREIGN_COUNTRIES_EXPEDITIONS                0x04
+#define SOCIAL_SPIRITUAL_SCIENCES                    0x05
+#define FURTHER_EDUCATION                            0x06
+#define LANGUAGES                                    0x07
+
+#define EVCONTENTMASK_LEISUREHOBBIES                 0xA0
+#define EVCONTENTMASK_SPECIAL                        0xB0
+#define EVCONTENTMASK_USERDEFINED                    0xF0
+
+cEpg::cEpg()
+{
+  m_uid             = 0;
+  m_StartTime       = 0;
+  m_EndTime         = 0;
+  m_Duration        = 0;
+  m_genre_type      = 0;
+  m_genre_subtype   = 0;
+  m_UTCdiff = GetUTCdifftime();
+}
+
+cEpg::~cEpg()
+{
+}
+
+void cEpg::Reset()
+{
+  m_genre.clear();
+  m_title.clear();
+  m_shortText.clear();
+  m_description.clear();
+
+  m_StartTime       = 0;
+  m_EndTime         = 0;
+  m_Duration        = 0;
+  m_genre_type      = 0;
+  m_genre_subtype   = 0;
+  m_uid             = 0;
+}
+
+bool cEpg::ParseLine(string& data)
+{
+  struct tm timeinfo;
+  int year, month ,day;
+  int hour, minute, second;
+  int count;
+
+  try
+  {
+    vector<string> epgfields;
+
+    Tokenize(data, epgfields, "|");
+
+    if( epgfields.size() == 5 )
+    {
+      XBMC->Log(LOG_DEBUG, "%s: %s", epgfields[0].c_str(), epgfields[2].c_str());
+      // field 0 = start date + time
+      // field 1 = end   date + time
+      // field 2 = title
+      // field 3 = description
+      // field 4 = genre string
+
+      count = sscanf(epgfields[0].c_str(), "%d-%d-%d %d:%d:%d", &year, &month, &day, &hour, &minute, &second);
+
+      if( count != 6)
+        return false;
+
+      timeinfo.tm_hour = hour;
+      timeinfo.tm_min = minute;
+      timeinfo.tm_sec = second;
+      timeinfo.tm_year = year - 1900;
+      timeinfo.tm_mon = month - 1;
+      timeinfo.tm_mday = day;
+      // Make the other fields empty:
+      timeinfo.tm_isdst = 0;
+      timeinfo.tm_wday = 0;
+      timeinfo.tm_yday = 0;
+
+      m_StartTime = mktime (&timeinfo);// + m_UTCdiff; //m_StartTime should be localtime, MP TV returns UTC
+
+      if(m_StartTime < 0)
+      {
+        XBMC->Log(LOG_ERROR, "cEpg::ParseLine: Unable to convert start time '%s' into date+time", epgfields[0].c_str());
+        return false;
+      }
+
+      count = sscanf(epgfields[1].c_str(), "%d-%d-%d %d:%d:%d", &year, &month, &day, &hour, &minute, &second);
+
+      if(count != 6)
+        return false;
+
+      timeinfo.tm_hour = hour;
+      timeinfo.tm_min = minute;
+      timeinfo.tm_sec = second;
+      timeinfo.tm_year = year - 1900;
+      timeinfo.tm_mon = month - 1;
+      timeinfo.tm_mday = day;
+      // Make the other fields empty:
+      timeinfo.tm_isdst = 0;
+      timeinfo.tm_wday = 0;
+      timeinfo.tm_yday = 0;
+
+      m_EndTime = mktime (&timeinfo);// + m_UTCdiff; //m_EndTime should be localtime, MP TV returns UTC
+
+      if( m_EndTime < 0)
+      {
+        XBMC->Log(LOG_ERROR, "cEpg::ParseLine: Unable to convert end time '%s' into date+time", epgfields[0].c_str());
+        return false;
+      }
+
+      m_Duration  = m_EndTime - m_StartTime;
+      m_uid       = 0;
+
+      m_title = epgfields[2];
+      m_description = epgfields[3];
+      m_shortText = epgfields[2];
+      SetGenre(epgfields[4], 0, 0);
+
+      return true;
+    }
+  }
+  catch(std::exception &e)
+  {
+    XBMC->Log(LOG_ERROR, "Exception '%s' during parse EPG data string.", e.what());
+  }
+
+  return false;
+}
+
+void cEpg::SetGenre(string& Genre, int genreType, int genreSubType)
+{
+  //TODO: The xmltv plugin may return genre strings in local language
+  //      The only way to solve this at the XMBC side is to transfer the
+  //      genre string to XBMC or to let this plugin (or the TVServerXBMC
+  //      plugin) translate it into XBMC compatible (numbered) genre types
+  m_genre = Genre;
+  m_genre_subtype = 0;
+
+  if(m_bReadGenre && m_genre.length() > 0) {
+
+    if(m_genre.compare("news/current affairs (general)") == 0) {
+      m_genre_type = EVCONTENTMASK_NEWSCURRENTAFFAIRS;
+    } else if (m_genre.compare("magazines/reports/documentary") == 0) {
+      m_genre_type = EVCONTENTMASK_SOCIALPOLITICALECONOMICS;
+      m_genre_subtype = MAGAZINES_REPORTS_DOCUMENTARY;
+    } else if (m_genre.compare("sports (general)") == 0) {
+      m_genre_type = EVCONTENTMASK_SPORTS;
+    } else if (m_genre.compare("arts/culture (without music, general)") == 0) {
+      m_genre_type = EVCONTENTMASK_ARTSCULTURE;
+    } else if (m_genre.compare("childrens's/youth program (general)") == 0) {
+      m_genre_type = EVCONTENTMASK_CHILDRENYOUTH;
+    } else if (m_genre.compare("show/game show (general)") == 0) {
+      m_genre_type = EVCONTENTMASK_SHOW;
+    } else if (m_genre.compare("detective/thriller") == 0) {
+      m_genre_type = EVCONTENTMASK_MOVIEDRAMA;
+      m_genre_subtype = DETECTIVE_THRILLER;
+    } else if (m_genre.compare("religion") == 0) {
+      m_genre_type = EVCONTENTMASK_ARTSCULTURE;
+      m_genre_subtype = RELIGION;
+    } else if (m_genre.compare("documentary") == 0) {
+      m_genre_type = EVCONTENTMASK_NEWSCURRENTAFFAIRS;
+      m_genre_subtype = DOCUMENTARY;
+    } else if (m_genre.compare("education/science/factual topics (general)") == 0) {
+      m_genre_type = EVCONTENTMASK_EDUCATIONALSCIENCE;
+    } else if (m_genre.compare("comedy") == 0) {
+      m_genre_type = EVCONTENTMASK_MOVIEDRAMA;
+      m_genre_subtype = COMEDY;
+    } else if (m_genre.compare("soap/melodram/folkloric") == 0) {
+      m_genre_type = EVCONTENTMASK_MOVIEDRAMA;
+      m_genre_subtype = SOAP_MELODRAMA_FOLKLORIC;
+    } else if (m_genre.compare("cartoon/puppets") == 0) {
+      m_genre_type = EVCONTENTMASK_CHILDRENYOUTH;
+      m_genre_subtype = CARTOONS_PUPPETS;
+    } else if (m_genre.compare("movie/drama (general)") == 0) {
+      m_genre_type = EVCONTENTMASK_MOVIEDRAMA;
+    } else if (m_genre.compare("nature/animals/environment") == 0) {
+      m_genre_type = EVCONTENTMASK_EDUCATIONALSCIENCE;
+      m_genre_subtype = NATURE_ANIMALS_ENVIRONMENT;
+    } else if (m_genre.compare("adult movie/drama") == 0) {
+      m_genre_type = EVCONTENTMASK_MOVIEDRAMA;
+      m_genre_subtype = ADULTMOVIE_DRAMA;
+    } else if (m_genre.compare("music/ballet/dance (general)") == 0) {
+      m_genre_type = EVCONTENTMASK_MUSICBALLETDANCE;
+    } else {
+      //XBMC->Log(LOG_DEBUG, "epg::setgenre: TODO mapping of MPTV's '%s' genre.", Genre.c_str());
+      m_genre_type     = genreType;
+      m_genre_subtype  = genreSubType;
+    }
+  } else {
+    m_genre_type = 0;
+  }
+}
diff --git a/xbmc/pvrclients/MediaPortal/epg.h b/xbmc/pvrclients/MediaPortal/epg.h
new file mode 100644 (file)
index 0000000..7ce9afe
--- /dev/null
@@ -0,0 +1,75 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef __EPG_H
+#define __EPG_H
+
+#include <stdlib.h>
+#include "libXBMC_addon.h"
+#include "libXBMC_pvr.h"
+
+class cEpg
+{
+private:
+  unsigned int m_uid;
+  string m_title;
+  string m_shortText;
+  string m_description;
+  //string m_aux;
+  time_t m_StartTime;
+  time_t m_EndTime;
+  int m_Duration;
+  string m_genre;
+  int m_genre_type;
+  int m_genre_subtype;
+  //time_t m_vps;              // Video Programming Service timestamp (VPS, aka "Programme Identification Label", PIL)
+  time_t m_UTCdiff;
+
+public:
+  cEpg();
+  virtual ~cEpg();
+  void Reset();
+
+  bool ParseLine(string& data);
+  //bool ParseEntryLine(const char *s);
+  //const char *Aux(void) const { return m_aux; }
+  int UniqueId(void) const { return m_uid; }
+  time_t StartTime(void) const { return m_StartTime; }
+  time_t EndTime(void) const { return m_EndTime; }
+  time_t Duration(void) const { return m_Duration; }
+  //time_t Vps(void) const { return m_vps; }
+  //void SetVps(time_t Vps);
+  const char *Title(void) const { return m_title.c_str(); }
+  const char *ShortText(void) const { return m_shortText.c_str(); }
+  const char *Description(void) const { return m_description.c_str(); }
+  const char *Genre(void) const { return m_genre.c_str(); }
+  int GenreType(void) const { return m_genre_type; }
+  int GenreSubType(void) const { return m_genre_subtype; }
+  //void SetTitle(const char *Title);
+  //void SetShortText(const char *ShortText);
+  //void SetDescription(const char *Description);
+  void SetGenre(string& Genre, int genreType, int genreSubType);
+
+
+};
+
+#endif //__EPG_H
diff --git a/xbmc/pvrclients/MediaPortal/linux/pvrclient-mediaportal_os_posix.h b/xbmc/pvrclients/MediaPortal/linux/pvrclient-mediaportal_os_posix.h
new file mode 100644 (file)
index 0000000..61b8d63
--- /dev/null
@@ -0,0 +1,81 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef PVRCLIENT_MEDIAPORTAL_OS_POSIX_H
+#define PVRCLIENT_MEDIAPORTAL_OS_POSIX_H
+
+#define _FILE_OFFSET_BITS 64
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/time.h>
+#include <sys/timeb.h>
+#include <sys/wait.h>
+#include <sys/signal.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <pthread.h>
+
+typedef int bool_t;
+typedef int SOCKET;
+
+//#define closesocket(a) close(a)
+//#define SOCKET_ERROR   (-1)
+//#define INVALID_SOCKET (-1)
+
+//#define __cdecl
+//#define __declspec(x)
+
+#define SD_BOTH SHUT_RDWR
+
+#define LIBTYPE
+#define sock_getlasterror errno
+#define sock_getlasterror_socktimeout (errno == EAGAIN)
+#define console_vprintf vprintf
+#define console_printf printf
+#define THREAD_FUNC_PREFIX void *
+
+static inline uint64_t getcurrenttime(void)
+{
+       struct timeval t;
+       gettimeofday(&t, NULL);
+       return ((uint64_t)t.tv_sec * 1000) + (t.tv_usec / 1000);
+}
+
+static inline int setsocktimeout(int s, int level, int optname, uint64_t timeout)
+{
+       struct timeval t;
+       t.tv_sec = timeout / 1000;
+       t.tv_usec = (timeout % 1000) * 1000;
+       return setsockopt(s, level, optname, (char *)&t, sizeof(t));
+}
+
+#endif
diff --git a/xbmc/pvrclients/MediaPortal/project/VS2008Express/XBMC_MPTV.sln b/xbmc/pvrclients/MediaPortal/project/VS2008Express/XBMC_MPTV.sln
new file mode 100644 (file)
index 0000000..7660876
--- /dev/null
@@ -0,0 +1,19 @@
+Microsoft Visual Studio Solution File, Format Version 10.00\r
+# Visual Studio 2008\r
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pvrclient_mptv", "XBMC_MPTV.vcproj", "{74C9642E-1988-48DC-8404-11717C355378}"\r
+EndProject\r
+Global\r
+       GlobalSection(SolutionConfigurationPlatforms) = preSolution\r
+               Debug|Win32 = Debug|Win32\r
+               Release|Win32 = Release|Win32\r
+       EndGlobalSection\r
+       GlobalSection(ProjectConfigurationPlatforms) = postSolution\r
+               {74C9642E-1988-48DC-8404-11717C355378}.Debug|Win32.ActiveCfg = Debug|Win32\r
+               {74C9642E-1988-48DC-8404-11717C355378}.Debug|Win32.Build.0 = Debug|Win32\r
+               {74C9642E-1988-48DC-8404-11717C355378}.Release|Win32.ActiveCfg = Release|Win32\r
+               {74C9642E-1988-48DC-8404-11717C355378}.Release|Win32.Build.0 = Release|Win32\r
+       EndGlobalSection\r
+       GlobalSection(SolutionProperties) = preSolution\r
+               HideSolutionNode = FALSE\r
+       EndGlobalSection\r
+EndGlobal\r
diff --git a/xbmc/pvrclients/MediaPortal/project/VS2008Express/XBMC_MPTV.vcproj b/xbmc/pvrclients/MediaPortal/project/VS2008Express/XBMC_MPTV.vcproj
new file mode 100644 (file)
index 0000000..a9fc74b
--- /dev/null
@@ -0,0 +1,294 @@
+<?xml version="1.0" encoding="Windows-1252"?>\r
+<VisualStudioProject\r
+       ProjectType="Visual C++"\r
+       Version="9,00"\r
+       Name="pvrclient_mptv"\r
+       ProjectGUID="{74C9642E-1988-48DC-8404-11717C355378}"\r
+       RootNamespace="XBMC_MPTV"\r
+       Keyword="Win32Proj"\r
+       TargetFrameworkVersion="131072"\r
+       >\r
+       <Platforms>\r
+               <Platform\r
+                       Name="Win32"\r
+               />\r
+       </Platforms>\r
+       <ToolFiles>\r
+       </ToolFiles>\r
+       <Configurations>\r
+               <Configuration\r
+                       Name="Debug|Win32"\r
+                       OutputDirectory="Debug"\r
+                       IntermediateDirectory="Debug"\r
+                       ConfigurationType="2"\r
+                       InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"\r
+                       CharacterSet="2"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               Optimization="0"\r
+                               AdditionalIncludeDirectories="..\..\..\..\..\addons\library.xbmc.addon;..\..\..\..\..\addons\library.xbmc.pvr;..\..\..\..\addons\include;."\r
+                               PreprocessorDefinitions="_WIN32;_DEBUG;_WINDOWS;MPTV_EXPORTS;_WIN32PC;_WINSOCKAPI_;_USE_32BIT_TIME_T"\r
+                               MinimalRebuild="true"\r
+                               BasicRuntimeChecks="3"\r
+                               RuntimeLibrary="1"\r
+                               UsePrecompiledHeader="0"\r
+                               WarningLevel="3"\r
+                               Detect64BitPortabilityProblems="false"\r
+                               DebugInformationFormat="4"\r
+                               DisableSpecificWarnings="4996;"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               IgnoreImportLibrary="false"\r
+                               AdditionalDependencies="ws2_32.lib"\r
+                               OutputFile="../../../../../addons/pvr/MediaPortal/XBMC_MPTV_win32.pvr"\r
+                               LinkIncremental="2"\r
+                               IgnoreDefaultLibraryNames=""\r
+                               GenerateDebugInformation="true"\r
+                               ProgramDatabaseFile="$(OutDir)/XBMC_MPTV.pdb"\r
+                               SubSystem="2"\r
+                               RandomizedBaseAddress="1"\r
+                               DataExecutionPrevention="0"\r
+                               ImportLibrary="$(OutDir)/XBMC_MPTV.lib"\r
+                               TargetMachine="1"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManifestTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCAppVerifierTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                               CommandLine=""\r
+                               ExcludedFromBuild="false"\r
+                       />\r
+               </Configuration>\r
+               <Configuration\r
+                       Name="Release|Win32"\r
+                       OutputDirectory="Release"\r
+                       IntermediateDirectory="Release"\r
+                       ConfigurationType="2"\r
+                       InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"\r
+                       CharacterSet="2"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               Optimization="0"\r
+                               AdditionalIncludeDirectories="..\..\..\..\..\addons\library.xbmc.addon;..\..\..\..\..\addons\library.xbmc.pvr;..\..\..\..\addons\include;."\r
+                               PreprocessorDefinitions="_WIN32;_WINDOWS;MPTV_EXPORTS;_WIN32PC;_WINSOCKAPI_;_USE_32BIT_TIME_T"\r
+                               MinimalRebuild="true"\r
+                               BasicRuntimeChecks="0"\r
+                               RuntimeLibrary="0"\r
+                               UsePrecompiledHeader="0"\r
+                               WarningLevel="0"\r
+                               Detect64BitPortabilityProblems="false"\r
+                               DebugInformationFormat="3"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               AdditionalDependencies="ws2_32.lib"\r
+                               OutputFile="../../../../../addons/pvr/MediaPortal/XBMC_MPTV_win32.pvr"\r
+                               LinkIncremental="2"\r
+                               IgnoreDefaultLibraryNames=""\r
+                               GenerateDebugInformation="false"\r
+                               ProgramDatabaseFile="$(OutDir)/XBMC_MPTV.pdb"\r
+                               SubSystem="2"\r
+                               OptimizeReferences="0"\r
+                               EnableCOMDATFolding="0"\r
+                               RandomizedBaseAddress="1"\r
+                               DataExecutionPrevention="0"\r
+                               ImportLibrary="$(OutDir)/XBMC_MPTV.lib"\r
+                               TargetMachine="1"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManifestTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCAppVerifierTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                       />\r
+               </Configuration>\r
+       </Configurations>\r
+       <References>\r
+       </References>\r
+       <Files>\r
+               <Filter\r
+                       Name="Source Files"\r
+                       Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"\r
+                       UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"\r
+                       >\r
+                       <File\r
+                               RelativePath="..\..\channels.cpp"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\client.cpp"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\..\..\addons\library.xbmc.addon\dlfcn-win32.cpp"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\epg.cpp"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\pvrclient-mediaportal.cpp"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\recordings.cpp"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\Socket.cpp"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\timers.cpp"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\utils.cpp"\r
+                               >\r
+                       </File>\r
+               </Filter>\r
+               <Filter\r
+                       Name="Header Files"\r
+                       Filter="h;hpp;hxx;hm;inl;inc;xsd"\r
+                       UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"\r
+                       >\r
+                       <File\r
+                               RelativePath="..\..\channels.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\client.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\..\..\addons\library.xbmc.addon\dlfcn-win32.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\epg.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\pvrclient-mediaportal.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\pvrclient-mediaportal_os.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\windows\pvrclient-mediaportal_os_windows.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\recordings.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\Socket.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\StdString.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\timers.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\utils.h"\r
+                               >\r
+                       </File>\r
+               </Filter>\r
+               <File\r
+                       RelativePath="..\..\README"\r
+                       >\r
+               </File>\r
+       </Files>\r
+       <Globals>\r
+       </Globals>\r
+</VisualStudioProject>\r
diff --git a/xbmc/pvrclients/MediaPortal/project/VS2010Express/XBMC_MPTV.vcxproj b/xbmc/pvrclients/MediaPortal/project/VS2010Express/XBMC_MPTV.vcxproj
new file mode 100644 (file)
index 0000000..c9732c5
--- /dev/null
@@ -0,0 +1,149 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectName>pvrclient_mptv</ProjectName>
+    <ProjectGuid>{74C9642E-1988-48DC-8404-11717C355378}</ProjectGuid>
+    <RootNamespace>XBMC_MPTV</RootNamespace>
+    <Keyword>Win32Proj</Keyword>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup>
+    <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\..\..\..\addons\pvr.team-mediaportal.tvserver\</OutDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Debug\</IntDir>
+    <IgnoreImportLibrary Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</IgnoreImportLibrary>
+    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental>
+    <PostBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PostBuildEventUseInBuild>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\..\..\..\addons\pvr.team-mediaportal.tvserver\</OutDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Release\</IntDir>
+    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</LinkIncremental>
+    <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">XBMC_MPTV_win32</TargetName>
+    <TargetExt Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.pvr</TargetExt>
+    <TargetName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">XBMC_MPTV_win32</TargetName>
+    <TargetExt Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.pvr</TargetExt>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <Optimization>Disabled</Optimization>
+      <AdditionalIncludeDirectories>..\..\..\..\..\addons\library.xbmc.addon;..\..\..\..\..\addons\library.xbmc.pvr;..\..\..\..\addons\include;.;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>_WIN32;_DEBUG;_WINDOWS;MPTV_EXPORTS;_WIN32PC;_WINSOCKAPI_;_USE_32BIT_TIME_T;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <MinimalRebuild>true</MinimalRebuild>
+      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+      <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+    </ClCompile>
+    <Link>
+      <AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <OutputFile>..\..\..\..\..\addons\pvr.team-mediaportal.tvserver\XBMC_MPTV_win32.pvr</OutputFile>
+      <IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <ProgramDatabaseFile>$(OutDir)XBMC_MPTV.pdb</ProgramDatabaseFile>
+      <SubSystem>Windows</SubSystem>
+      <RandomizedBaseAddress>false</RandomizedBaseAddress>
+      <DataExecutionPrevention>
+      </DataExecutionPrevention>
+      <ImportLibrary>$(OutDir)XBMC_MPTV.lib</ImportLibrary>
+      <TargetMachine>MachineX86</TargetMachine>
+    </Link>
+    <PostBuildEvent>
+      <Command>
+      </Command>
+    </PostBuildEvent>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <Optimization>Disabled</Optimization>
+      <AdditionalIncludeDirectories>..\..\..\..\..\addons\library.xbmc.addon;..\..\..\..\..\addons\library.xbmc.pvr;..\..\..\..\addons\include;.;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>_WIN32;_WINDOWS;MPTV_EXPORTS;_WIN32PC;_WINSOCKAPI_;_USE_32BIT_TIME_T;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <MinimalRebuild>true</MinimalRebuild>
+      <BasicRuntimeChecks>Default</BasicRuntimeChecks>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>TurnOffAllWarnings</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+    </ClCompile>
+    <Link>
+      <AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <OutputFile>..\..\..\..\..\addons\pvr.team-mediaportal.tvserver\XBMC_MPTV_win32.pvr</OutputFile>
+      <IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
+      <GenerateDebugInformation>false</GenerateDebugInformation>
+      <ProgramDatabaseFile>$(OutDir)XBMC_MPTV.pdb</ProgramDatabaseFile>
+      <SubSystem>Windows</SubSystem>
+      <OptimizeReferences>
+      </OptimizeReferences>
+      <EnableCOMDATFolding>
+      </EnableCOMDATFolding>
+      <RandomizedBaseAddress>false</RandomizedBaseAddress>
+      <DataExecutionPrevention>
+      </DataExecutionPrevention>
+      <ImportLibrary>$(OutDir)XBMC_MPTV.lib</ImportLibrary>
+      <TargetMachine>MachineX86</TargetMachine>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\channels.cpp" />
+    <ClCompile Include="..\..\client.cpp" />
+    <ClCompile Include="..\..\..\..\..\addons\library.xbmc.addon\dlfcn-win32.cpp" />
+    <ClCompile Include="..\..\epg.cpp" />
+    <ClCompile Include="..\..\pvrclient-mediaportal.cpp" />
+    <ClCompile Include="..\..\recordings.cpp" />
+    <ClCompile Include="..\..\Socket.cpp" />
+    <ClCompile Include="..\..\timers.cpp" />
+    <ClCompile Include="..\..\utils.cpp" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\channels.h" />
+    <ClInclude Include="..\..\client.h" />
+    <ClInclude Include="..\..\..\..\..\addons\library.xbmc.addon\dlfcn-win32.h" />
+    <ClInclude Include="..\..\epg.h" />
+    <ClInclude Include="..\..\pvrclient-mediaportal.h" />
+    <ClInclude Include="..\..\pvrclient-mediaportal_os.h" />
+    <ClInclude Include="..\..\windows\pvrclient-mediaportal_os_windows.h" />
+    <ClInclude Include="..\..\recordings.h" />
+    <ClInclude Include="..\..\Socket.h" />
+    <ClInclude Include="..\..\StdString.h" />
+    <ClInclude Include="..\..\timers.h" />
+    <ClInclude Include="..\..\utils.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="..\..\README" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/xbmc/pvrclients/MediaPortal/project/VS2010Express/XBMC_MPTV.vcxproj.filters b/xbmc/pvrclients/MediaPortal/project/VS2010Express/XBMC_MPTV.vcxproj.filters
new file mode 100644 (file)
index 0000000..023d312
--- /dev/null
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\channels.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\client.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\..\..\addons\library.xbmc.addon\dlfcn-win32.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\epg.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\pvrclient-mediaportal.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\recordings.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\Socket.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\timers.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\utils.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\channels.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\client.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\..\..\..\addons\library.xbmc.addon\dlfcn-win32.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\epg.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\pvrclient-mediaportal.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\pvrclient-mediaportal_os.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\windows\pvrclient-mediaportal_os_windows.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\recordings.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\Socket.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\StdString.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\timers.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\utils.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="..\..\README" />
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/xbmc/pvrclients/MediaPortal/pvrclient-mediaportal.cpp b/xbmc/pvrclients/MediaPortal/pvrclient-mediaportal.cpp
new file mode 100644 (file)
index 0000000..917c72b
--- /dev/null
@@ -0,0 +1,1014 @@
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "client.h"
+#include "timers.h"
+#include "channels.h"
+#include "recordings.h"
+#include "epg.h"
+#include "utils.h"
+#include "pvrclient-mediaportal.h"
+#include <ctime>
+
+#define SEEK_POSSIBLE 0x10 // flag used to check if protocol allows seeks
+
+using namespace std;
+
+/************************************************************/
+/** Class interface */
+
+cPVRClientMediaPortal::cPVRClientMediaPortal()
+{
+  m_iCurrentChannel   = 1;
+  m_tcpclient         = new Socket(af_inet, pf_inet, sock_stream, tcp);
+  m_bConnected        = false;
+  m_bStop             = true;
+  m_bTimeShiftStarted = false;
+  m_BackendUTCoffset  = 0;
+  m_BackendTime       = 0;
+  m_bStop             = true;
+  m_bConnected        = false;
+}
+
+cPVRClientMediaPortal::~cPVRClientMediaPortal()
+{
+  XBMC->Log(LOG_DEBUG, "->~cPVRClientMediaPortal()");
+  Disconnect();
+  delete m_tcpclient;
+}
+
+string cPVRClientMediaPortal::SendCommand(string command)
+{
+  int code;
+  vector<string> lines;
+
+  if ( !m_tcpclient->send(command) )
+  {
+    if ( !m_tcpclient->is_valid() ) {
+      // Connection lost, try to reconnect
+      if ( Connect() ) {
+        // Resend the command
+        if (!m_tcpclient->send(command))
+        {
+          XBMC->Log(LOG_ERROR, "SendCommand('%s') failed.", command.c_str());
+          return "";
+        }
+      }
+    }
+  }
+
+  string response;
+  if ( !m_tcpclient->ReadResponse(code, lines) )
+  {
+    XBMC->Log(LOG_ERROR, "SendCommand - Failed with code: %d (%s)", code, lines[lines.size()-1].c_str());
+  } //else
+  //{
+  //  XBMC->Log(LOG_DEBUG, "cPVRClientMediaPortal::SendCommand('%s') response: %s", command.c_str(), lines[lines.size()-1].c_str());
+  //}
+  return lines[lines.size()-1].c_str();
+}
+
+bool cPVRClientMediaPortal::SendCommand2(string command, int& code, vector<string>& lines)
+{
+  if ( !m_tcpclient->send(command) )
+  {
+    if ( !m_tcpclient->is_valid() )
+    {
+      // Connection lost, try to reconnect
+      if ( Connect() )
+      {
+        // Resend the command
+        if (!m_tcpclient->send(command))
+        {
+          XBMC->Log(LOG_ERROR, "SendCommand2('%s') failed.", command.c_str());
+          return false;
+        }
+      }
+    }
+  }
+
+  if (!m_tcpclient->ReadResponse(code, lines))
+  {
+    XBMC->Log(LOG_ERROR, "SendCommand - Failed with code: %d (%s)", code, lines[lines.size()-1].c_str());
+    return false;
+  } else {
+    string result = lines[lines.size()-1];
+    lines.clear();
+    //XBMC->Log(LOG_DEBUG, "cPVRClientMediaPortal::SendCommand('%s') response: %s", command.c_str(), result.c_str());
+
+    Tokenize(result, lines, ",");
+
+    return true;
+  }
+}
+
+bool cPVRClientMediaPortal::Connect()
+{
+  string result;
+
+  /* Open Connection to MediaPortal Backend TV Server via the XBMC TV Server plugin*/
+  XBMC->Log(LOG_DEBUG, "Connect() - Connecting to %s:%i", m_sHostname.c_str(), m_iPort);
+
+  if (!m_tcpclient->create())
+  {
+    XBMC->Log(LOG_ERROR, "Connect() - Could not connect create socket");
+    return false;
+  }
+
+  if (!m_tcpclient->connect(m_sHostname, m_iPort))
+  {
+    XBMC->Log(LOG_ERROR, "Connect() - Could not connect to MPTV backend");
+    return false;
+  }
+
+  m_tcpclient->set_non_blocking(1);
+
+  result = SendCommand("PVRclientXBMC:0-1\n");
+
+  if(result.find("Unexpected protocol") != std::string::npos)
+  {
+    XBMC->Log(LOG_DEBUG, "TVServer does not accept protocol: PVRclientXBMC:0-1");
+    return false;
+  } else {
+    vector<string> fields;
+    int major = 0, minor = 0, revision = 0 , build = 0;
+    int count = 0;
+
+    // Check the version of the TVServerXBMC plugin:
+    Tokenize(result, fields, "|");
+    if(fields.size() == 2)
+    {
+      // Ok, this TVServerXBMC version answers with a version string
+      count = sscanf(fields[1].c_str(), "%d.%d.%d.%d", &major, &minor, &revision, &build);
+      if( count < 4 )
+      {
+        XBMC->Log(LOG_ERROR, "Connect() - Could not parse the TVServerXBMC version string '%s'", fields[1].c_str());
+      }
+      // Check for the minimal requirement: 1.0.7.x
+      if( major < 1 || minor < 0 || revision < 7 )
+      {
+        XBMC->Log(LOG_ERROR, "Warning: Your TVServerXBMC version '%s' is too old. Please upgrade to 1.0.7.0 or higher!", fields[1].c_str());
+      }
+    } else {
+      XBMC->Log(LOG_ERROR, "Warning: Your TVServerXBMC version is too old. Please upgrade.");
+    }
+  }
+
+  char buffer[512];
+  snprintf(buffer, 512, "%s:%i", m_sHostname.c_str(), m_iPort);
+  m_ConnectionString = buffer;
+
+  m_bConnected = true;
+  return true;
+}
+
+void cPVRClientMediaPortal::Disconnect()
+{
+  string result;
+
+  XBMC->Log(LOG_DEBUG, "->Disconnect()");
+
+  if (m_tcpclient->is_valid() && m_bTimeShiftStarted)
+  {
+    result = SendCommand("IsTimeshifting:\n");
+
+    if (result.find("True") != std::string::npos )
+    {
+      result = SendCommand("StopTimeshift:\n");
+    }
+  }
+
+  result = SendCommand("CloseConnection:\n");
+
+  m_bStop = true;
+
+  m_tcpclient->close();
+
+  m_bConnected = false;
+}
+
+/* IsUp()
+ * \brief   Check whether we still have a connection with the TVServer. If not, try
+ *          to reconnect
+ * \return  True when a connection is available, False when even a reconnect failed
+ */
+bool cPVRClientMediaPortal::IsUp()
+{
+  if(!m_tcpclient->is_valid())
+  {
+    if(!Connect()) {
+      return false;
+    }
+  }
+  return true;
+}
+
+void* cPVRClientMediaPortal::Process(void*)
+{
+  XBMC->Log(LOG_DEBUG, "->Process() Not yet implemented");
+  return NULL;
+}
+
+
+/************************************************************/
+/** General handling */
+
+const char* cPVRClientMediaPortal::GetBackendName()
+{
+  if (!m_tcpclient->is_valid())
+    return m_sHostname.c_str();
+
+  XBMC->Log(LOG_DEBUG, "->GetBackendName()");
+
+  if(m_BackendName.length() == 0)
+  {
+    m_BackendName = SendCommand("GetBackendName:\n");
+  }
+
+  return m_BackendName.c_str();
+}
+
+const char* cPVRClientMediaPortal::GetBackendVersion()
+{
+  if (!IsUp())
+    return "0.0";
+
+  XBMC->Log(LOG_DEBUG, "->GetBackendVersion()");
+
+  if(m_BackendVersion.length() == 0)
+  {
+    m_BackendVersion = SendCommand("GetVersion:\n");
+  }
+
+  return m_BackendVersion.c_str();
+}
+
+const char* cPVRClientMediaPortal::GetConnectionString()
+{
+  XBMC->Log(LOG_DEBUG, "->GetConnectionString()");
+
+  return m_ConnectionString.c_str();
+}
+
+PVR_ERROR cPVRClientMediaPortal::GetDriveSpace(long long *total, long long *used)
+{
+  XBMC->Log(LOG_DEBUG, "->GetDriveSpace(): Todo implement me...");
+
+  *total = 0;
+  *used = 0;
+
+  return PVR_ERROR_NO_ERROR;
+}
+
+PVR_ERROR cPVRClientMediaPortal::GetMPTVTime(time_t *localTime, int *gmtOffset)
+{
+  string result;
+  vector<string> fields;
+  int year = 0, month = 0, day = 0;
+  int hour = 0, minute = 0, second = 0;
+  int count = 0;
+  struct tm timeinfo;
+
+  //XBMC->Log(LOG_DEBUG, "->GetMPTVTime");
+
+  if (!IsUp())
+    return PVR_ERROR_SERVER_ERROR;
+
+  result = SendCommand("GetTime:\n");
+
+  Tokenize(result, fields, "|");
+
+  if(fields.size() == 3)
+  {
+    //From CPVREpg::CPVREpg(): Expected PVREpg GMT offset is in seconds
+    m_BackendUTCoffset = ((atoi(fields[1].c_str()) * 60) + atoi(fields[2].c_str())) * 60;
+
+    count = sscanf(fields[0].c_str(), "%d-%d-%d %d:%d:%d", &year, &month, &day, &hour, &minute, &second);
+
+    if(count == 6)
+    {
+      //timeinfo = *localtime ( &rawtime );
+      timeinfo.tm_hour = hour;
+      timeinfo.tm_min = minute;
+      timeinfo.tm_sec = second;
+      timeinfo.tm_year = year - 1900;
+      timeinfo.tm_mon = month - 1;
+      timeinfo.tm_mday = day;
+      // Make the other fields empty:
+      timeinfo.tm_isdst = 0;
+      timeinfo.tm_wday = 0;
+      timeinfo.tm_yday = 0;
+
+      m_BackendTime = mktime(&timeinfo);
+
+      if(m_BackendTime < 0)
+      {
+        XBMC->Log(LOG_DEBUG, "GetMPTVTime: Unable to convert string '%s' into date+time", fields[0].c_str());
+        return PVR_ERROR_SERVER_ERROR;
+      }
+
+      XBMC->Log(LOG_DEBUG, "GetMPTVTime: %s, offset: %i seconds", asctime(gmtime(&m_BackendTime)), m_BackendUTCoffset );
+
+      *localTime = m_BackendTime;
+      *gmtOffset = m_BackendUTCoffset;
+      return PVR_ERROR_NO_ERROR;
+    }
+    else
+    {
+      return PVR_ERROR_SERVER_ERROR;
+    }
+  } else
+    return PVR_ERROR_SERVER_ERROR;
+}
+
+/************************************************************/
+/** EPG handling */
+
+PVR_ERROR cPVRClientMediaPortal::RequestEPGForChannel(const PVR_CHANNEL &channel, PVRHANDLE handle, time_t start, time_t end)
+{
+  vector<string> lines;
+  char           command[256];
+  string         result;
+  cEpg           epg;
+  PVR_PROGINFO   broadcast;
+
+  XBMC->Log(LOG_DEBUG, "->RequestEPGForChannel(%i)", channel.number);
+
+  if (!IsUp())
+    return PVR_ERROR_SERVER_ERROR;
+
+  snprintf(command, 256, "GetEPG:%i\n", channel.number);
+
+  result = SendCommand(command);
+
+  if(result.compare(0,5, "ERROR") != 0)
+  {
+    Tokenize(result, lines, ",");
+
+    for (vector<string>::iterator it = lines.begin(); it < lines.end(); it++)
+    {
+      string& data(*it);
+
+      //CStdString str_result = data;
+      //
+      //if (m_bCharsetConv)
+      //  XBMC_unknown_to_utf8(str_result);
+
+      if( data.length() > 0) {
+        uri::decode(data);
+
+        bool isEnd = epg.ParseLine(data);
+
+        if (isEnd && epg.StartTime() != 0)
+        {
+          broadcast.channum         = channel.number;
+          broadcast.uid             = epg.UniqueId();
+          broadcast.title           = epg.Title();
+          broadcast.subtitle        = epg.ShortText();
+          broadcast.description     = epg.Description();
+          broadcast.starttime       = epg.StartTime();
+          broadcast.endtime         = epg.EndTime();
+          broadcast.genre_type      = epg.GenreType();
+          broadcast.genre_sub_type  = epg.GenreSubType();
+          broadcast.parental_rating = 0;
+          PVR->TransferEpgEntry(handle, &broadcast);
+        }
+        epg.Reset();
+      }
+    }
+  } else {
+    XBMC->Log(LOG_DEBUG, "RequestEPGForChannel(%i) %s", channel.number, result.c_str());
+  }
+
+  return PVR_ERROR_NO_ERROR;
+}
+
+
+/************************************************************/
+/** Channel handling */
+
+int cPVRClientMediaPortal::GetNumChannels()
+{
+  string result;
+  //CStdString      command;
+
+  if (!IsUp())
+    return -1;
+
+  //command.Format("GetChannelCount:%s\n", g_sTVGroup.c_str());
+  // Get the total channel count (radio+tv)
+  // It is only used to check whether XBMC should request the channel list
+  result = SendCommand("GetChannelCount:\n");
+
+  return atol(result.c_str());
+}
+
+PVR_ERROR cPVRClientMediaPortal::RequestChannelList(PVRHANDLE handle, int radio)
+{
+  vector<string>  lines;
+  CStdString      command;
+  int             code;
+
+  if (!IsUp())
+    return PVR_ERROR_SERVER_ERROR;
+
+  if(radio)
+  {
+    XBMC->Log(LOG_DEBUG, "RequestChannelList for Radio group:%s", g_sRadioGroup.c_str());
+    command.Format("ListRadioChannels:%s\n", g_sRadioGroup.c_str());
+  } else {
+    XBMC->Log(LOG_DEBUG, "RequestChannelList for TV group:%s", g_sTVGroup.c_str());
+    command.Format("ListTVChannels:%s\n", g_sTVGroup.c_str());
+  }
+  SendCommand2(command.c_str(), code, lines);
+
+  for (vector<string>::iterator it = lines.begin(); it < lines.end(); it++)
+  {
+    string& data(*it);
+
+    if (data.length() == 0) {
+      break;
+    }
+
+    uri::decode(data);
+    //if(radio) {
+    //  XBMC->Log(LOG_DEBUG, "Radio channel: %s", data.c_str() );
+    //} else {
+    //  XBMC->Log(LOG_DEBUG, "TV channel: %s", data.c_str() );
+    //}
+
+    cChannel channel;
+    if( channel.Parse(data) )
+    {
+      PVR_CHANNEL tag;
+      tag.uid = channel.UID();
+      tag.number = channel.UID(); //channel.ExternalID();
+      tag.name = channel.Name();
+      tag.callsign = "";
+      tag.iconpath = "";
+      tag.encryption = channel.Encrypted();
+      tag.radio = (radio > 0 ? true : false) ; //TODO:(channel.Vpid() == 0) && (channel.Apid(0) != 0) ? true : false;
+      tag.hide = false;
+      tag.recording = false;
+      tag.bouquet = 0;
+      tag.multifeed = false;
+      tag.input_format = "";
+
+      if(radio)
+        tag.stream_url = "pvr://stream/radio/%i.ts"; //stream.c_str();
+      else
+        tag.stream_url = "pvr://stream/tv/%i.ts"; //stream.c_str();
+
+      PVR->TransferChannelEntry(handle, &tag);
+    }
+  }
+
+  //pthread_mutex_unlock(&m_critSection);
+  return PVR_ERROR_NO_ERROR;
+}
+
+/************************************************************/
+/** Record handling **/
+
+int cPVRClientMediaPortal::GetNumRecordings(void)
+{
+  string            result;
+
+  if (!IsUp())
+    return PVR_ERROR_SERVER_ERROR;
+
+  result = SendCommand("GetRecordingCount:\n");
+
+  return atol(result.c_str());
+}
+
+PVR_ERROR cPVRClientMediaPortal::RequestRecordingsList(PVRHANDLE handle)
+{
+  vector<string>  lines;
+  string          result;
+
+  if (!IsUp())
+    return PVR_ERROR_SERVER_ERROR;
+
+  result = SendCommand("ListRecordings:\n");
+
+  Tokenize(result, lines, ",");
+
+  for (vector<string>::iterator it = lines.begin(); it != lines.end(); it++)
+  {
+    string& data(*it);
+    uri::decode(data);
+
+    XBMC->Log(LOG_DEBUG, "RECORDING: %s", data.c_str() );
+
+    ///* Convert to UTF8 string format */
+    //if (m_bCharsetConv)
+    //  XBMC_unknown_to_utf8(str_result);
+
+    cRecording recording;
+    if (recording.ParseLine(data))
+    {
+      PVR_RECORDINGINFO tag;
+      tag.index           = recording.Index();
+      tag.channel_name    = recording.ChannelName();
+      tag.lifetime        = MAXLIFETIME; //TODO: recording.Lifetime();
+      tag.priority        = 0; //TODO? recording.Priority();
+      tag.recording_time  = recording.StartTime();
+      tag.duration        = (int) recording.Duration();
+      tag.description     = recording.Description();
+      tag.stream_url      = recording.Stream();
+      tag.title           = recording.Title();
+      tag.subtitle        = tag.title;
+      tag.directory       = "";
+
+      PVR->TransferRecordingEntry(handle, &tag);
+    }
+  }
+
+  return PVR_ERROR_NO_ERROR;
+}
+
+PVR_ERROR cPVRClientMediaPortal::DeleteRecording(const PVR_RECORDINGINFO &recinfo)
+{
+  char            command[256];
+  string          result;
+
+  if (!IsUp())
+    return PVR_ERROR_SERVER_ERROR;
+
+  snprintf(command, 256, "DeleteRecordedTV:%i\n", recinfo.index);
+
+  result = SendCommand(command);
+
+  if(result.find("True") ==  string::npos)
+  {
+    return PVR_ERROR_NOT_DELETED;
+  }
+
+  return PVR_ERROR_NO_ERROR;
+}
+
+PVR_ERROR cPVRClientMediaPortal::RenameRecording(const PVR_RECORDINGINFO &recinfo, const char *newname)
+{
+  char           command[512];
+  string         result;
+
+  if (!IsUp())
+    return PVR_ERROR_SERVER_ERROR;
+
+  snprintf(command, 512, "UpdateRecording:%i,%s\n",
+    recinfo.index,
+    newname);
+
+  result = SendCommand(command);
+
+  if(result.find("True") == string::npos)
+  {
+    XBMC->Log(LOG_DEBUG, "RenameRecording(%i) to %s [failed]", recinfo.index, newname);
+    return PVR_ERROR_NOT_DELETED;
+  }
+  XBMC->Log(LOG_DEBUG, "RenameRecording(%i) to %s [done]", recinfo.index, newname);
+
+  return PVR_ERROR_NO_ERROR;
+}
+
+
+/************************************************************/
+/** Timer handling */
+
+int cPVRClientMediaPortal::GetNumTimers(void)
+{
+  string            result;
+
+  if (!IsUp())
+    return PVR_ERROR_SERVER_ERROR;
+
+  result = SendCommand("GetScheduleCount:\n");
+
+  return atol(result.c_str());
+}
+
+PVR_ERROR cPVRClientMediaPortal::RequestTimerList(PVRHANDLE handle)
+{
+  vector<string>  lines;
+  string          result;
+
+  if (!IsUp())
+    return PVR_ERROR_SERVER_ERROR;
+
+  result = SendCommand("ListSchedules:\n");
+
+  Tokenize(result, lines, ",");
+
+  for (vector<string>::iterator it = lines.begin(); it != lines.end(); it++)
+  {
+    string& data(*it);
+    uri::decode(data);
+
+    XBMC->Log(LOG_DEBUG, "SCHEDULED: %s", data.c_str() );
+
+    cTimer timer;
+    timer.ParseLine(data.c_str());
+
+    //TODO: finish me...
+    PVR_TIMERINFO tag;
+    tag.index       = timer.Index();
+    tag.active      = true; //false; //timer.HasFlags(tfActive);
+    tag.channelNum  = timer.Channel();
+    tag.firstday    = 0; //timer.FirstDay();
+    tag.starttime   = timer.StartTime();
+    tag.endtime     = timer.StopTime();
+    tag.recording   = 0; //timer.HasFlags(tfRecording) || timer.HasFlags(tfInstant);
+    tag.title       = timer.Title();
+    tag.directory   = timer.Dir();
+    tag.priority    = timer.Priority();
+    tag.lifetime    = 0; //timer.Lifetime();
+    tag.repeat      = false; //timer.WeekDays() == 0 ? false : true;
+    tag.repeatflags = 0;//timer.WeekDays();
+
+    PVR->TransferTimerEntry(handle, &tag);
+  }
+
+  return PVR_ERROR_NO_ERROR;
+}
+
+PVR_ERROR cPVRClientMediaPortal::GetTimerInfo(unsigned int timernumber, PVR_TIMERINFO &tag)
+{
+  string         result;
+  char           command[256];
+
+  XBMC->Log(LOG_DEBUG, "->GetTimerInfo(%i)", timernumber);
+
+  if (!IsUp())
+    return PVR_ERROR_SERVER_ERROR;
+
+  snprintf(command,256, "GetTimerInfo:%i\n", timernumber);
+
+  result = SendCommand(command);
+
+  cTimer timer;
+  timer.ParseLine(result.c_str());
+  //TODO: finish me...
+  tag.index = timer.Index();
+  tag.active = true; //false; //timer.HasFlags(tfActive);
+  tag.channelNum = timer.Channel();
+  tag.firstday = 0; //timer.FirstDay();
+  tag.starttime = timer.StartTime();
+  tag.endtime = timer.StopTime();
+  tag.recording = 0; //timer.HasFlags(tfRecording) || timer.HasFlags(tfInstant);
+  tag.title = timer.Title();
+  tag.priority = timer.Priority();
+  tag.lifetime = 0; //timer.Lifetime();
+  tag.repeat = false; //timer.WeekDays() == 0 ? false : true;
+  tag.repeatflags = 0;//timer.WeekDays();
+
+  return PVR_ERROR_NO_ERROR;
+}
+
+PVR_ERROR cPVRClientMediaPortal::AddTimer(const PVR_TIMERINFO &timerinfo)
+{
+  char           command[1024];
+  string         result;
+
+#ifdef _TIME32_T_DEFINED
+  XBMC->Log(LOG_DEBUG, "->AddTimer Channel: %i, starttime: %i endtime: %i program: %s", timerinfo.channelNum, timerinfo.starttime, timerinfo.endtime, timerinfo.title);
+#else
+  XBMC->Log(LOG_DEBUG, "->AddTimer Channel: %i, 64 bit times not yet supported!", timerinfo.channelNum);
+#endif
+
+  struct tm starttime;
+  struct tm endtime;
+
+  if (!IsUp())
+    return PVR_ERROR_SERVER_ERROR;
+
+  starttime = *gmtime( &timerinfo.starttime );
+  XBMC->Log(LOG_DEBUG, "Start time %s", asctime(&starttime));
+
+  endtime = *gmtime( &timerinfo.endtime );
+  XBMC->Log(LOG_DEBUG, "End time %s", asctime(&endtime));
+
+
+  CStdString title = timerinfo.title;
+  title.Replace("|","");  //Remove commas from title field
+
+  snprintf(command, 1024, "AddSchedule:%i|%s|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i\n",
+          timerinfo.channelNum,                                              //Channel number
+          title.c_str(),                                                     //Program title
+          starttime.tm_year + 1900, starttime.tm_mon + 1, starttime.tm_mday, //Start date
+          starttime.tm_hour, starttime.tm_min, starttime.tm_sec,             //Start time
+          endtime.tm_year + 1900, endtime.tm_mon + 1, endtime.tm_mday,       //End date
+          endtime.tm_hour, endtime.tm_min, endtime.tm_sec);                  //End time
+
+  if (timerinfo.index == -1)
+  {
+    result = SendCommand(command);
+
+    if(result.find("True") ==  string::npos)
+    {
+      XBMC->Log(LOG_DEBUG, "AddTimer for channel: %i [failed]", timerinfo.channelNum);
+      return PVR_ERROR_NOT_SAVED;
+    }
+    XBMC->Log(LOG_DEBUG, "AddTimer for channel: %i [done]", timerinfo.channelNum);
+  }
+  else
+  {
+    // Modified timer
+    XBMC->Log(LOG_DEBUG, "AddTimer Modify timer for channel: %i; Not yet supported!");
+    return PVR_ERROR_NOT_SAVED;
+  }
+
+  return PVR_ERROR_NO_ERROR;
+}
+
+PVR_ERROR cPVRClientMediaPortal::DeleteTimer(const PVR_TIMERINFO &timerinfo, bool force)
+{
+  char           command[256];
+  string         result;
+
+  if (!IsUp())
+    return PVR_ERROR_SERVER_ERROR;
+
+  snprintf(command, 256, "DeleteSchedule:%i\n",timerinfo.index);
+
+  if (timerinfo.index == -1)
+  {
+    XBMC->Log(LOG_DEBUG, "DeleteTimer: schedule index = -1", timerinfo.index);
+    return PVR_ERROR_NOT_DELETED;
+  } else {
+    XBMC->Log(LOG_DEBUG, "DeleteTimer: About to delete MediaPortal schedule index=%i", timerinfo.index);
+    result = SendCommand(command);
+
+    if(result.find("True") ==  string::npos)
+    {
+      XBMC->Log(LOG_DEBUG, "DeleteTimer %i [failed]", timerinfo.index);
+      return PVR_ERROR_NOT_DELETED;
+    }
+    XBMC->Log(LOG_DEBUG, "DeleteTimer %i [done]", timerinfo.index);
+
+  }
+
+  //  return PVR_ERROR_SERVER_ERROR;
+  //  return PVR_ERROR_NOT_SYNC;
+  //    return PVR_ERROR_RECORDING_RUNNING;
+  //    return PVR_ERROR_NOT_DELETED;
+
+  return PVR_ERROR_NO_ERROR;
+}
+
+PVR_ERROR cPVRClientMediaPortal::RenameTimer(const PVR_TIMERINFO &timerinfo, const char *newname)
+{
+  XBMC->Log(LOG_DEBUG, "RenameTimer %i for channel: %i; Not yet supported!", timerinfo.index, timerinfo.channelNum);
+  if (!IsUp())
+    return PVR_ERROR_SERVER_ERROR;
+
+  PVR_TIMERINFO timerinfo1;
+  PVR_ERROR ret = GetTimerInfo(timerinfo.index, timerinfo1);
+  if (ret != PVR_ERROR_NO_ERROR)
+    return ret;
+
+  timerinfo1.title = newname;
+  return UpdateTimer(timerinfo1);
+}
+
+PVR_ERROR cPVRClientMediaPortal::UpdateTimer(const PVR_TIMERINFO &timerinfo)
+{
+  char           command[1024];
+  string         result;
+
+  struct tm starttime;
+  struct tm endtime;
+
+  //TODO: timerinfo.file bevat troep. Nakijken. => gefixed
+  //TODO: bij opname Journaal 18:00-18:15 => 17:58-19:25 ??? Hmmz, dit klopt niet toch? => gefixed
+
+#ifdef _TIME32_T_DEFINED
+  XBMC->Log(LOG_DEBUG, "->Updateimer Index: %i Channel: %i, starttime: %i endtime: %i program: %s", timerinfo.index, timerinfo.channelNum, timerinfo.starttime, timerinfo.endtime, timerinfo.title);
+#else
+  XBMC->Log(LOG_DEBUG, "->UpdateTimer Channel: %i, 64 bit times not yet supported!", timerinfo.channelNum);
+#endif
+
+  if (!IsUp())
+    return PVR_ERROR_SERVER_ERROR;
+
+  starttime = *gmtime( &timerinfo.starttime );
+  XBMC->Log(LOG_DEBUG, "Start time %s", asctime(&starttime));
+
+  endtime = *gmtime( &timerinfo.endtime );
+  XBMC->Log(LOG_DEBUG, "End time %s", asctime(&endtime));
+
+  CStdString title = timerinfo.title;
+  title.Replace(",","");  //Remove commas from title field
+
+  snprintf(command, 1024, "UpdateSchedule:%i|%i|%i|%s|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i\n",
+          timerinfo.index,                                                   //Schedule index
+          timerinfo.active,                                                  //Active
+          timerinfo.channelNum,                                              //Channel number
+          title.c_str(),                                                     //Program title
+          starttime.tm_year + 1900, starttime.tm_mon + 1, starttime.tm_mday, //Start date
+          starttime.tm_hour, starttime.tm_min, starttime.tm_sec,             //Start time
+          endtime.tm_year + 1900, endtime.tm_mon + 1, endtime.tm_mday,       //End date
+          endtime.tm_hour, endtime.tm_min, endtime.tm_sec);                  //End time
+
+  result = SendCommand(command);
+
+  if(result.find("True") ==  string::npos)
+  {
+    XBMC->Log(LOG_DEBUG, "AddTimer for channel: %i [failed]", timerinfo.channelNum);
+    return PVR_ERROR_NOT_SAVED;
+  }
+  XBMC->Log(LOG_DEBUG, "AddTimer for channel: %i [done]", timerinfo.channelNum);
+
+  return PVR_ERROR_NO_ERROR;
+}
+
+
+/************************************************************/
+/** Live stream handling */
+
+// The MediaPortal TV Server uses rtsp streams which XBMC can handle directly
+// so we don't need to open the streams in this pvr addon.
+// However, we still need to request the stream URL for the channel we want
+// to watch as it is not known on beforehand.
+// Most of the times it is the same URL for each selected channel. Only the
+// stream itself changes. Example URL: rtsp://tvserverhost/stream2.0
+// The number 2.0 may change when the tvserver is streaming multiple tv channels
+// at the same time.
+bool cPVRClientMediaPortal::OpenLiveStream(const PVR_CHANNEL &channelinfo)
+{
+  unsigned int channel = channelinfo.number;
+
+  XBMC->Log(LOG_DEBUG, "->OpenLiveStream(%i): Not supported for this PVR addon.", channel);
+
+  return false;
+}
+
+void cPVRClientMediaPortal::CloseLiveStream()
+{
+  string result;
+
+  if (!IsUp())
+     return;
+
+  if (m_bTimeShiftStarted)
+  {
+    result = SendCommand("StopTimeshift:\n");
+    XBMC->Log(LOG_INFO, "CloseLiveStream: %s", result.c_str());
+    m_bTimeShiftStarted = false;
+  } else {
+    XBMC->Log(LOG_DEBUG, "CloseLiveStream: Nothing to do.");
+
+  }
+}
+
+int cPVRClientMediaPortal::ReadLiveStream(unsigned char* buf, int buf_size)
+{
+    return 0;
+}
+
+int cPVRClientMediaPortal::GetCurrentClientChannel()
+{
+  XBMC->Log(LOG_DEBUG, "->GetCurrentClientChannel");
+  return m_iCurrentChannel;
+}
+
+bool cPVRClientMediaPortal::SwitchChannel(const PVR_CHANNEL &channelinfo)
+{
+
+  XBMC->Log(LOG_DEBUG, "->SwitchChannel(%i)", channelinfo.number);
+  string rtsp_url = GetLiveStreamURL(channelinfo);
+
+  if(rtsp_url.length() > 0)
+  {
+    m_bTimeShiftStarted = false; //debug test
+    return true;
+  }
+  else
+    return false;
+}
+
+PVR_ERROR cPVRClientMediaPortal::SignalQuality(PVR_SIGNALQUALITY &qualityinfo)
+{
+  //XBMC->Log(LOG_DEBUG, "->SignalQuality(): Not yet supported.");
+
+  return PVR_ERROR_NO_ERROR;
+}
+
+
+/************************************************************/
+/** Record stream handling */
+// MediaPortal recordings are also rtsp streams. Main difference here with
+// respect to the live tv streams is that the URLs for the recordings
+// can be requested on beforehand (done in the TVserverXBMC plugin).
+// These URLs are stored in the field PVR_RECORDINGINFO.stream_url
+bool cPVRClientMediaPortal::OpenRecordedStream(const PVR_RECORDINGINFO &recinfo)
+{
+  XBMC->Log(LOG_DEBUG, "->OpenRecordedStream(index=%i)", recinfo.index);
+  if (!IsUp())
+     return false;
+
+  return true;
+}
+
+void cPVRClientMediaPortal::CloseRecordedStream(void)
+{
+  return;
+}
+
+int cPVRClientMediaPortal::ReadRecordedStream(unsigned char* buf, int buf_size)
+{
+  return 0;
+}
+
+long long cPVRClientMediaPortal::SeekRecordedStream(long long pos, int whence)
+{
+  return 0;
+}
+
+long long cPVRClientMediaPortal::LengthRecordedStream(void)
+{
+  return 0;
+}
+
+/*
+ * \brief Request the stream URL for live tv/live radio.
+ * The MediaPortal TV Server will try to open the requested channel for
+ * time-shifting and when successful it will start an rtsp:// stream for this
+ * channel and return the URL for this stream.
+ */
+const char* cPVRClientMediaPortal::GetLiveStreamURL(const PVR_CHANNEL &channelinfo)
+{
+  unsigned int channel = channelinfo.number;
+
+  string result;
+  char   command[256] = "";
+
+  XBMC->Log(LOG_DEBUG, "->GetLiveStreamURL(%i)", channel);
+  if (!IsUp())
+  {
+    return false;
+  }
+
+  // Closing existing timeshift streams will be done in the MediaPortal TV
+  // Server plugin, so we can request the new channel stream directly without
+  // stopping the existing stream
+
+  if(m_bResolveRTSPHostname == false)
+  {
+    // RTSP URL may contain a hostname, XBMC will do the IP resolve
+    snprintf(command, 256, "TimeshiftChannel:%i|False\n", channel);
+  }
+  else
+  {
+    // RTSP URL will always contain an IP address, TVServerXBMC will
+    // do the IP resolve
+    snprintf(command, 256, "TimeshiftChannel:%i\n", channel);
+  }
+  result = SendCommand(command);
+
+  if (result.find("ERROR") != std::string::npos || result.length() == 0)
+  {
+    XBMC->Log(LOG_ERROR, "Could not stream channel %i. %s", channel, result.c_str());
+    return "";
+  }
+  else
+  {
+    if (m_iSleepOnRTSPurl > 0)
+    {
+      XBMC->Log(LOG_INFO, "Sleeping %i ms before opening stream: %s", m_iSleepOnRTSPurl, result.c_str());
+      usleep(m_iSleepOnRTSPurl * 1000);
+    }
+
+    XBMC->Log(LOG_INFO, "Channel stream URL: %s", result.c_str());
+    m_iCurrentChannel = channel;
+    m_ConnectionString = result;
+
+    // Check the returned stream URL. When the URL is an rtsp stream, we need
+    // to close it again after watching to stop the timeshift.
+    // A radio web stream (added to the TV Server) will return the web stream
+    // URL without starting a timeshift.
+    if(result.compare(0,4, "rtsp") == 0)
+    {
+      m_bTimeShiftStarted = true;
+    }
+    return m_ConnectionString.c_str();
+  }
+}
diff --git a/xbmc/pvrclients/MediaPortal/pvrclient-mediaportal.h b/xbmc/pvrclients/MediaPortal/pvrclient-mediaportal.h
new file mode 100644 (file)
index 0000000..395f1b9
--- /dev/null
@@ -0,0 +1,139 @@
+#pragma once
+
+#ifndef __PVRCLIENT_MEDIAPORTAL_H__
+#define __PVRCLIENT_MEDIAPORTAL_H__
+
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+/*
+* for DESCRIPTION see 'PVRClient-MediaPortal.cpp'
+*/
+
+#include "pvrclient-mediaportal_os.h"
+
+#include <vector>
+
+/* Master defines for client control */
+#ifndef _WINSOCKAPI_
+#define _WINSOCKAPI_ //Needed here to prevent inclusion of <winsock.h> via the header below
+#endif
+#include "../../addons/include/xbmc_pvr_types.h"
+
+/* Local includes */
+#include "Socket.h"
+
+class cPVRClientMediaPortal
+{
+public:
+  /* Class interface */
+  cPVRClientMediaPortal();
+  ~cPVRClientMediaPortal();
+
+  /* VTP Listening Thread */
+  static void* Process(void*);
+
+  /* Server handling */
+  bool Connect();
+  void Disconnect();
+  bool IsUp();
+
+  /* General handling */
+  const char* GetBackendName();
+  const char* GetBackendVersion();
+  const char* GetConnectionString();
+  PVR_ERROR GetDriveSpace(long long *total, long long *used);
+  PVR_ERROR GetMPTVTime(time_t *localTime, int *gmtOffset);
+
+  /* EPG handling */
+  PVR_ERROR RequestEPGForChannel(const PVR_CHANNEL &channel, PVRHANDLE handle, time_t start = NULL, time_t end = NULL);
+
+  /* Channel handling */
+  int GetNumChannels(void);
+  PVR_ERROR RequestChannelList(PVRHANDLE handle, int radio = 0);
+
+  /* Record handling **/
+  int GetNumRecordings(void);
+  PVR_ERROR RequestRecordingsList(PVRHANDLE handle);
+  PVR_ERROR DeleteRecording(const PVR_RECORDINGINFO &recinfo);
+  PVR_ERROR RenameRecording(const PVR_RECORDINGINFO &recinfo, const char *newname);
+
+  /* Timer handling */
+  int GetNumTimers(void);
+  PVR_ERROR RequestTimerList(PVRHANDLE handle);
+  PVR_ERROR GetTimerInfo(unsigned int timernumber, PVR_TIMERINFO &tag);
+  PVR_ERROR AddTimer(const PVR_TIMERINFO &timerinfo);
+  PVR_ERROR DeleteTimer(const PVR_TIMERINFO &timerinfo, bool force = false);
+  PVR_ERROR RenameTimer(const PVR_TIMERINFO &timerinfo, const char *newname);
+  PVR_ERROR UpdateTimer(const PVR_TIMERINFO &timerinfo);
+
+  /* Live stream handling */
+  bool OpenLiveStream(const PVR_CHANNEL &channelinfo);
+  void CloseLiveStream();
+  int ReadLiveStream(unsigned char* buf, int buf_size);
+  int GetCurrentClientChannel();
+  bool SwitchChannel(const PVR_CHANNEL &channelinfo);
+  PVR_ERROR SignalQuality(PVR_SIGNALQUALITY &qualityinfo);
+
+  /* Record stream handling */
+  bool OpenRecordedStream(const PVR_RECORDINGINFO &recinfo);
+  void CloseRecordedStream(void);
+  int ReadRecordedStream(unsigned char* buf, int buf_size);
+  long long SeekRecordedStream(long long pos, int whence=SEEK_SET);
+  long long LengthRecordedStream(void);
+
+  //MG: Added for MediaPortal streaming
+  const char* GetLiveStreamURL(const PVR_CHANNEL &channelinfo);
+
+protected:
+  Socket           *m_tcpclient;
+
+private:
+  bool GetChannel(unsigned int number, PVR_CHANNEL &channeldata);
+  /* MediaPortal to XBMC Callback functions; Not yet supported */
+  //static void* CallbackRcvThread(void* arg);
+  //bool VDRToXBMCCommand(char *Cmd);
+  //bool CallBackMODT(const char *Option);
+  //bool CallBackDELT(const char *Option);
+  //bool CallBackADDT(const char *Option);
+  //bool CallBackSMSG(const char *Option);
+  //bool CallBackIMSG(const char *Option);
+  //bool CallBackWMSG(const char *Option);
+  //bool CallBackEMSG(const char *Option);
+
+  int                     m_iCurrentChannel;
+  bool                    m_bConnected;
+  bool                    m_bStop;
+  bool                    m_bTimeShiftStarted;
+  std::string             m_ConnectionString;
+  std::string             m_BackendName;
+  std::string             m_BackendVersion;
+  time_t                  m_BackendUTCoffset;
+  time_t                  m_BackendTime;
+
+  void Close();
+
+  //MG: Added for TVServer communication:
+  std::string SendCommand(std::string command);
+  bool SendCommand2(std::string command, int& code, std::vector<std::string>& lines);
+};
+
+#endif // __PVRCLIENT_MEDIAPORTAL_H__
diff --git a/xbmc/pvrclients/MediaPortal/pvrclient-mediaportal_os.h b/xbmc/pvrclients/MediaPortal/pvrclient-mediaportal_os.h
new file mode 100644 (file)
index 0000000..2f41472
--- /dev/null
@@ -0,0 +1,47 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef PVRCLIENT_MEDIAPORTAL_OS_H
+#define PVRCLIENT_MEDIAPORTAL_OS_H
+
+#if defined(_WIN32) || defined(_WIN64)
+#define __WINDOWS__
+#endif
+
+#if defined(__WINDOWS__)
+#ifndef _WINSOCKAPI_
+#define _WINSOCKAPI_
+#endif
+#include "windows/pvrclient-mediaportal_os_windows.h"
+#else
+#include "linux/pvrclient-mediaportal_os_posix.h"
+#endif
+
+#if !defined(TRUE)
+#define TRUE 1
+#endif
+
+#if !defined(FALSE)
+#define FALSE 0
+#endif
+
+#endif
diff --git a/xbmc/pvrclients/MediaPortal/recordings.cpp b/xbmc/pvrclients/MediaPortal/recordings.cpp
new file mode 100644 (file)
index 0000000..b87ae7a
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <vector>
+#include <stdio.h>
+
+using namespace std;
+
+#include "recordings.h"
+#include "utils.h"
+
+cRecording::cRecording()
+{
+  m_StartTime       = 0;
+  m_Duration        = 0;
+  m_Index           = -1;
+  m_UTCdiff = GetUTCdifftime();
+}
+
+cRecording::cRecording(const PVR_RECORDINGINFO *Recording)
+{
+
+}
+
+cRecording::~cRecording()
+{
+}
+
+bool cRecording::ParseLine(const std::string& data)
+{
+  time_t endtime;
+  struct tm timeinfo;
+  int year, month ,day;
+  int hour, minute, second;
+  int count;
+
+  vector<string> fields;
+
+  Tokenize(data, fields, "|");
+
+  if( fields.size() == 9 )
+  {
+    //[0] index / mediaportal recording id
+    //[1] start time
+    //[2] end time
+    //[3] channel name
+    //[4] title
+    //[5] description
+    //[6] stream_url
+    //[7] filename (we can bypass rtsp streaming when XBMC and the TV server are on the same machine)
+    //[8] lifetime (mediaportal keep until?)
+
+    m_Index = atoi(fields[0].c_str());
+
+    count = sscanf(fields[1].c_str(), "%d-%d-%d %d:%d:%d", &year, &month, &day, &hour, &minute, &second);
+
+    if (count != 6)
+      return false;
+
+    timeinfo.tm_hour = hour;
+    timeinfo.tm_min = minute;
+    timeinfo.tm_sec = second;
+    timeinfo.tm_year = year - 1900;
+    timeinfo.tm_mon = month - 1;
+    timeinfo.tm_mday = day;
+    // Make the other fields empty:
+    timeinfo.tm_isdst = 0;
+    timeinfo.tm_wday = 0;
+    timeinfo.tm_yday = 0;
+
+    m_StartTime = mktime (&timeinfo) + m_UTCdiff; //Start time in localtime
+
+    if (m_StartTime < 0)
+      return false;
+
+    count = sscanf(fields[2].c_str(), "%d-%d-%d %d:%d:%d", &year, &month, &day, &hour, &minute, &second);
+
+    if (count != 6)
+      return false;
+
+    timeinfo.tm_hour = hour;
+    timeinfo.tm_min = minute;
+    timeinfo.tm_sec = second;
+    timeinfo.tm_year = year - 1900;
+    timeinfo.tm_mon = month - 1;
+    timeinfo.tm_mday = day;
+    // Make the other fields empty:
+    timeinfo.tm_isdst = 0;
+    timeinfo.tm_wday = 0;
+    timeinfo.tm_yday = 0;
+
+    endtime = mktime (&timeinfo) + m_UTCdiff; //Start time in localtime
+
+    if (endtime < 0)
+      return false;
+
+    m_Duration = endtime - m_StartTime;
+
+    m_channelName = fields[3];
+    m_title = fields[4];
+    m_description = fields[5];
+    m_stream = fields[6];
+    m_fileName = fields[7];
+    m_lifetime = fields[8];
+
+    return true;
+  }
+  else
+  {
+    return false;
+  }
+}
diff --git a/xbmc/pvrclients/MediaPortal/recordings.h b/xbmc/pvrclients/MediaPortal/recordings.h
new file mode 100644 (file)
index 0000000..8c76fbb
--- /dev/null
@@ -0,0 +1,68 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef __RECORDINGS_H
+#define __RECORDINGS_H
+
+#include <stdlib.h>
+#include "libXBMC_addon.h"
+#include "libXBMC_pvr.h"
+
+using namespace std;
+
+#define DEFAULTFRAMESPERSECOND 25.0
+#define MAXPRIORITY 99
+#define MAXLIFETIME 99
+
+class cRecording
+{
+private:
+  int m_Index;
+  string m_channelName;
+  string m_fileName;
+  string m_stream;
+  string m_lifetime;
+  time_t m_StartTime;
+  int m_Duration;
+  string m_title;             // Title of this event
+  string m_shortText;         // Short description of this event (typically the episode name in case of a series)
+  string m_description;       // Description of this event
+  //time_t m_vps;               // Video Programming Service timestamp (VPS, aka "Programme Identification Label", PIL)
+  time_t m_UTCdiff;
+
+public:
+  cRecording(const PVR_RECORDINGINFO *Recording);
+  cRecording();
+  virtual ~cRecording();
+
+  bool ParseLine(const std::string& data);
+  const char *ChannelName(void) const { return m_channelName.c_str(); }
+  int Index(void) const { return m_Index; }
+  time_t StartTime(void) const { return m_StartTime; }
+  time_t Duration(void) const { return m_Duration; }
+  const char *Title(void) const { return m_title.c_str(); }
+  const char *Description(void) const { return m_description.c_str(); }
+  const char *FileName(void) const { return m_fileName.c_str(); }
+  const char *Stream(void) const { return m_stream.c_str(); }
+};
+
+#endif //__RECORDINGS_H
diff --git a/xbmc/pvrclients/MediaPortal/timers.cpp b/xbmc/pvrclients/MediaPortal/timers.cpp
new file mode 100644 (file)
index 0000000..0ee8691
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <vector>
+#include <stdio.h>
+
+using namespace std;
+
+#include "timers.h"
+#include "utils.h"
+/*
+#define SECSINDAY  86400
+*/
+
+cTimer::cTimer()
+{
+  m_starttime   = 0;
+  m_stoptime    = 0;
+  m_index       = 0;
+  m_UTCdiff     = GetUTCdifftime();
+}
+
+cTimer::~cTimer()
+{
+}
+
+time_t cTimer::StartTime(void) const
+{
+  return m_starttime;
+}
+
+time_t cTimer::StopTime(void) const
+{
+  return m_stoptime;
+}
+
+bool cTimer::ParseLine(const char *s)
+{
+  struct tm timeinfo;
+  int year, month ,day;
+  int hour, minute, second;
+  int count;
+
+  vector<string> schedulefields;
+  string data = s;
+  uri::decode(data);
+
+  Tokenize(data, schedulefields, "|");
+
+  if(schedulefields.size() >= 7)
+  {
+    // field 0 = index
+    // field 1 = start date + time
+    // field 2 = end   date + time
+    // field 3 = channel nr
+    // field 4 = channel name
+    // field 5 = program name
+    // field 6 = repeat info
+    // field 7 = priority
+
+    m_index = atoi(schedulefields[0].c_str());
+
+    count = sscanf(schedulefields[1].c_str(), "%d-%d-%d %d:%d:%d", &year, &month, &day, &hour, &minute, &second);
+
+    if(count != 6)
+      return false;
+
+    //timeinfo = *localtime ( &rawtime );
+    timeinfo.tm_hour = hour;
+    timeinfo.tm_min = minute;
+    timeinfo.tm_sec = second;
+    timeinfo.tm_year = year - 1900;
+    timeinfo.tm_mon = month - 1;
+    timeinfo.tm_mday = day;
+    // Make the other fields empty:
+    timeinfo.tm_isdst = 0;
+    timeinfo.tm_wday = 0;
+    timeinfo.tm_yday = 0;
+
+    m_starttime = mktime (&timeinfo) + m_UTCdiff; //m_StartTime should be localtime, MP TV returns UTC
+
+    if( m_starttime < 0)
+      return false;
+
+    count = sscanf(schedulefields[2].c_str(), "%d-%d-%d %d:%d:%d", &year, &month, &day, &hour, &minute, &second);
+
+    if( count != 6)
+      return false;
+
+    //timeinfo2 = *localtime ( &rawtime );
+    timeinfo.tm_hour = hour;
+    timeinfo.tm_min = minute;
+    timeinfo.tm_sec = second;
+    timeinfo.tm_year = year - 1900;
+    timeinfo.tm_mon = month - 1;
+    timeinfo.tm_mday = day;
+    // Make the other fields empty:
+    timeinfo.tm_isdst = 0;
+    timeinfo.tm_wday = 0;
+    timeinfo.tm_yday = 0;
+
+    m_stoptime = mktime (&timeinfo) + m_UTCdiff; //m_EndTime should be localtime, MP TV returns UTC
+
+    if( m_stoptime < 0)
+      return false;
+
+    m_channel = atoi(schedulefields[3].c_str());
+    m_title = schedulefields[5];
+    m_priority = atoi(schedulefields[7].c_str());
+
+    return true;
+  }
+  return false;
+}
diff --git a/xbmc/pvrclients/MediaPortal/timers.h b/xbmc/pvrclients/MediaPortal/timers.h
new file mode 100644 (file)
index 0000000..22ce65f
--- /dev/null
@@ -0,0 +1,64 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef __TIMERS_H
+#define __TIMERS_H
+
+#include "libXBMC_pvr.h"
+#include <stdlib.h>
+#include <string>
+
+/*
+enum eTimerFlags { tfNone      = 0x0000,
+                   tfActive    = 0x0001,
+                   tfInstant   = 0x0002,
+                   tfVps       = 0x0004,
+                   tfRecording = 0x0008,
+                   tfAll       = 0xFFFF,
+                 };
+*/
+class cTimer
+{
+private:
+  time_t m_starttime, m_stoptime;
+  int m_priority;
+  int m_channel;
+  string m_title;
+  string m_directory;
+  int m_index;
+  time_t m_UTCdiff;
+
+public:
+  cTimer();
+  virtual ~cTimer();
+
+  int Index(void) const { return m_index; }
+  unsigned int Channel(void) const { return m_channel; }
+  int Priority(void) const { return m_priority; }
+  const char* Title(void) const { return m_title.c_str(); }
+  const char* Dir(void) const { return m_directory.c_str(); }
+  time_t StartTime(void) const;
+  time_t StopTime(void) const;
+  bool ParseLine(const char *s);
+};
+
+#endif //__TIMERS_H
diff --git a/xbmc/pvrclients/MediaPortal/utils.cpp b/xbmc/pvrclients/MediaPortal/utils.cpp
new file mode 100644 (file)
index 0000000..b65eaa4
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "utils.h"
+
+using namespace std;
+
+namespace uri {
+  const char ENCODE_BEGIN_CHAR = '%';
+  const traits SCHEME_TRAITS = {
+      0, 0, ':',
+      {
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CVA2,CINV,CVA2,CVA2,CINV,
+          CVA2,CVA2,CVA2,CVA2,CVA2,CVA2,CVA2,CVA2, CVA2,CVA2,CEND,CINV,CINV,CINV,CINV,CINV,
+          CINV,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,
+          CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CINV,CINV,CINV,CINV,CINV,
+          CINV,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,
+          CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CINV,CINV,CINV,CINV,CINV, // 127 7F
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+      }
+  };
+  const traits AUTHORITY_TRAITS = {
+      "//", 0, 0,
+      {
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CEND,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, // 127 7F
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+      }
+  };
+  const traits PATH_TRAITS = {
+      0, 0, 0,
+      {   // '/' is invalid
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CVAL,CINV,CINV,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CINV,
+          CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CINV,CVAL,CINV,CINV,
+          CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,
+          CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CINV,CINV,CINV,CINV,CVAL,
+          CINV,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,
+          CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CINV,CINV,CINV,CVAL,CINV, // 127 7F
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+      }
+  };
+  const traits QUERY_TRAITS = {
+      0, '?', 0,
+      {   // '=' and '&' are invalid
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CVAL,CINV,CINV,CVAL,CVAL,CINV,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,
+          CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CINV,CINV,CINV,CVAL,
+          CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,
+          CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CINV,CINV,CINV,CINV,CVAL,
+          CINV,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,
+          CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CINV,CINV,CINV,CVAL,CINV, // 127 7F
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+      }
+  };
+  const traits FRAGMENT_TRAITS = {
+      0, '#', 0,
+      {
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CVAL,CINV,CINV,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,
+          CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CINV,CVAL,CINV,CVAL,
+          CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,
+          CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CINV,CINV,CINV,CINV,CVAL,
+          CINV,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,
+          CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL,CVAL, CVAL,CVAL,CVAL,CINV,CINV,CINV,CVAL,CINV, // 127 7F
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+          CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV, CINV,CINV,CINV,CINV,CINV,CINV,CINV,CINV,
+      }
+  };
+
+  bool parse_hex(const std::string& s, size_t pos, char& chr) {
+    if (s.size() < pos + 2)
+        return false;
+    unsigned int v;
+    unsigned int c = (unsigned int)s[pos];
+    if ('0' <= c && c <= '9')
+        v = (c - '0') << 4;
+    else if ('A' <= c && c <= 'F')
+        v = (10 + (c - 'A')) << 4;
+    else if ('a' <= c && c <= 'f')
+        v = (10 + (c - 'a')) << 4;
+    else
+        return false;
+    c = (unsigned int)s[pos + 1];
+    if ('0' <= c && c <= '9')
+        v += c - '0';
+    else if ('A' <= c && c <= 'F')
+        v += 10 + (c - 'A');
+    else if ('a' <= c && c <= 'f')
+        v += 10 + (c - 'a');
+    else
+        return false;
+    chr = (char)v; // Set output.
+    return true;
+  }
+
+  void append_hex(char v, std::string& s) {
+    unsigned int c = (unsigned char)v & 0xF0;
+    c >>= 4;
+    s.insert(s.end(), (char)((9 < c) ? (c - 10) + 'A' : c + '0'));
+    c = v & 0x0F;
+    s.insert(s.end(), (char)((9 < c) ? (c - 10) + 'A' : c + '0'));
+  }
+
+  std::string encode(const traits& ts, const std::string& comp) {
+      std::string::const_iterator f = comp.begin();
+      std::string::const_iterator anchor = f;
+      std::string s;
+      for (; f != comp.end();) {
+          char c = *f;
+          if (ts.char_class[(unsigned char)c] < CVAL || c == ENCODE_BEGIN_CHAR) { // Must encode.
+              s.append(anchor, f); // Catch up to this char.
+              s.append(1, ENCODE_BEGIN_CHAR);
+              append_hex(c, s); // Convert.
+              anchor = ++f;
+          }
+          else
+              ++f;
+      }
+      return (anchor == comp.begin()) ? comp : s.append(f, comp.end());
+  }
+
+  bool decode(std::string& s) {
+      size_t pos = s.find(ENCODE_BEGIN_CHAR);
+      if (pos == std::string::npos) // Handle the "99%" case fast.
+          return true;
+      std::string v;
+      for (size_t i = 0;;) {
+          if (pos == std::string::npos) {
+              v.append(s, i, s.size() - i); // Append up to end.
+              break;
+          }
+          v.append(s, i, pos - i); // Append up to char.
+          i = pos + 3; // Skip all 3 chars.
+          char c;
+          if (!parse_hex(s, pos + 1, c)) // Convert hex.
+              return false;
+          v.insert(v.end(), c); // Append converted hex.
+          pos = s.find(ENCODE_BEGIN_CHAR, i); // Find next
+      }
+      s = v;
+      return true;
+  }
+}
+
+void Tokenize(const string& str, vector<string>& tokens, const string& delimiters = " ")
+{
+  // Skip delimiters at beginning.
+  //string::size_type lastPos = str.find_first_not_of(delimiters, 0);
+  // Don't skip delimiters at beginning.
+  string::size_type start_pos = 0;
+  // Find first "non-delimiter".
+  string::size_type delim_pos = 0;
+
+  while (string::npos != delim_pos)
+  {
+    delim_pos = str.find_first_of(delimiters, start_pos);
+    // Found a token, add it to the vector.
+    tokens.push_back(str.substr(start_pos, delim_pos - start_pos));
+    start_pos = delim_pos + 1;
+
+    // Find next "non-delimiter"
+  }
+}
+
+time_t GetUTCdifftime(void)
+{
+  // Determine time difference between UTC and localtime
+  time_t rawtime;
+  struct tm* timeinfo;
+  time_t local;
+  time_t gm;
+
+  time( &rawtime ); // this is already localtime???
+  timeinfo = localtime ( &rawtime );
+  local = mktime(timeinfo);
+  timeinfo = gmtime ( &rawtime );
+  gm = mktime(timeinfo);
+
+  return(local - gm);
+}
diff --git a/xbmc/pvrclients/MediaPortal/utils.h b/xbmc/pvrclients/MediaPortal/utils.h
new file mode 100644 (file)
index 0000000..e563f68
--- /dev/null
@@ -0,0 +1,65 @@
+#ifndef __UTILS_H__
+#define __UTILS_H__
+
+#include <string>
+#include <vector>
+#include <ctime>
+
+using namespace std;
+
+#define delete_null(ptr) (delete (ptr), ptr = NULL)
+
+/**
+ * String tokenize
+ * Split string using the given delimiter into a vector of substrings
+ */
+void Tokenize(const string& str, vector<string>& tokens, const string& delimiters);
+
+/**
+ * GetUTCdifftime
+ * \return time_t value with the difference between local time and UTC in seconds
+ */
+time_t GetUTCdifftime(void);
+
+namespace uri {
+  /// Char class.
+  enum char_class_e {
+    CINV = -2, ///< invalid
+    CEND = -1, ///< end delimitor
+    CVAL = 0, ///< valid any position
+    CVA2 = 1, ///< valid anywhere but 1st position
+  };
+  /// Traits used for parsing and encoding components.
+  struct traits {
+    const char* begin_cstring; ///< begin cstring (or 0 if none)
+    const char begin_char; ///< begin char (or 0 if none)
+    const char end_char; ///< end char (or 0 if none)
+    char char_class[256]; ///< map of char to class
+  };
+
+  /// Encode the URI (sub) component. Note that this should be used on the
+  /// subcomponents before appending to subdelimiter chars, if any.
+  ///
+  /// From the RFC: URI producing applications should percent-encode data octets
+  /// that correspond to characters in the reserved set unless these characters
+  /// are specifically allowed by the URI scheme to represent data in that
+  /// component.  If a reserved character is found in a URI component and
+  /// no delimiting role is known for that character, then it must be
+  /// interpreted as representing the data octet corresponding to that
+  /// character's encoding in US-ASCII.
+  /// @see http://tools.ietf.org/html/rfc3986
+  /// @see decode
+  std::string encode(const traits& ts, const std::string& comp);
+  /// Decode the pct-encoded (hex) sequences, if any, return success.
+  /// Does not change string on error.
+  /// @see http://tools.ietf.org/html/rfc3986#section-2.1
+  /// @see encode
+  bool decode(std::string& s);
+  extern const char ENCODE_BEGIN_CHAR; ///< encode begin char ('\%')
+  extern const traits SCHEME_TRAITS; ///< scheme traits
+  extern const traits AUTHORITY_TRAITS; ///< authority traits
+  extern const traits PATH_TRAITS; ///< path traits
+  extern const traits QUERY_TRAITS; ///< query traits
+  extern const traits FRAGMENT_TRAITS; ///< fragment traits
+}
+#endif //__UTILS_H__
diff --git a/xbmc/pvrclients/MediaPortal/windows/pvrclient-mediaportal_os_windows.h b/xbmc/pvrclients/MediaPortal/windows/pvrclient-mediaportal_os_windows.h
new file mode 100644 (file)
index 0000000..b7899ee
--- /dev/null
@@ -0,0 +1,87 @@
+#pragma once\r
+/*\r
+ *      Copyright (C) 2005-2010 Team XBMC\r
+ *      http://www.xbmc.org\r
+ *\r
+ *  This Program is free software; you can redistribute it and/or modify\r
+ *  it under the terms of the GNU General Public License as published by\r
+ *  the Free Software Foundation; either version 2, or (at your option)\r
+ *  any later version.\r
+ *\r
+ *  This Program is distributed in the hope that it will be useful,\r
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ *  GNU General Public License for more details.\r
+ *\r
+ *  You should have received a copy of the GNU General Public License\r
+ *  along with XBMC; see the file COPYING.  If not, write to\r
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.\r
+ *  http://www.gnu.org/copyleft/gpl.html\r
+ *\r
+ */\r
+\r
+\r
+#ifndef PVRCLIENT_MEDIAPORTAL_OS_WIN_H\r
+#define PVRCLIENT_MEDIAPORTAL_OS_WIN_H\r
+\r
+#ifndef _WINSOCKAPI_\r
+#define _WINSOCKAPI_\r
+#endif\r
+#include <winsock2.h>\r
+#include <stdlib.h>\r
+#include <stdio.h>\r
+#include <stdarg.h>\r
+#include <signal.h>\r
+#include <time.h>\r
+#include <sys/types.h>\r
+#include <sys/timeb.h>\r
+\r
+#if defined(DLL_IMPORT)\r
+#define LIBTYPE __declspec( dllexport )\r
+#elif  defined(DLL_EXPORT)\r
+#define LIBTYPE __declspec( dllimport )\r
+#else\r
+#define LIBTYPE\r
+#endif\r
+\r
+//#define SD_BOTH 2 // See winsock2.h\r
+\r
+typedef int bool_t;\r
+typedef signed __int8 int8_t;\r
+typedef signed __int16 int16_t;\r
+typedef signed __int32 int32_t;\r
+typedef signed __int64 int64_t;\r
+typedef unsigned __int8 uint8_t;\r
+typedef unsigned __int16 uint16_t;\r
+typedef unsigned __int32 uint32_t;\r
+typedef unsigned __int64 uint64_t;\r
+typedef HANDLE pthread_t;\r
+typedef HANDLE pthread_mutex_t;\r
+typedef unsigned __int32 uint;\r
+\r
+#ifndef va_copy\r
+#define va_copy(x, y) x = y\r
+#endif\r
+\r
+#define snprintf _snprintf\r
+\r
+static inline void pthread_mutex_init(pthread_mutex_t *mutex, void *attr)\r
+{\r
+       *mutex = CreateMutex(NULL, FALSE, NULL);\r
+}\r
+\r
+static inline void pthread_mutex_lock(pthread_mutex_t *mutex)\r
+{\r
+       WaitForSingleObject(*mutex, INFINITE);\r
+}\r
+\r
+static inline void pthread_mutex_unlock(pthread_mutex_t *mutex)\r
+{\r
+       ReleaseMutex(*mutex);\r
+}\r
+\r
+static inline void usleep(unsigned long usec)\r
+{\r
+  Sleep(usec/1000);\r
+}\r
+#endif\r
diff --git a/xbmc/pvrclients/mythtv/Makefile.in b/xbmc/pvrclients/mythtv/Makefile.in
new file mode 100644 (file)
index 0000000..4de2729
--- /dev/null
@@ -0,0 +1,25 @@
+#
+# Makefile for the XBMC MythTV PVR AddOn
+#
+# See the README for copyright information and
+# how to reach the author.
+#
+
+LIBS   = -ldl
+LIBDIR = ../../../addons/pvr.hts
+LIB    = ../../../addons/pvr.hts/XBMC_Tvheadend.pvr
+
+SRCS=client.cpp \
+       MythXml.cpp
+
+include ../Makefile.include
+
+clean:
+       -rm -f $(OBJS) $(LIB) *~
+       $(MAKE) -C libmythxml clean
+
+INCLUDES += -I../../linux
+
+$(LIB): $(OBJS)
+       $(MAKE) -C libmythxml
+       $(CXX) $(CXXFLAGS) $(LDFLAGS) -shared -g $(OBJS) $(LIBS) $(LIBDIRS) $(SILIB) -o $(LIB)
diff --git a/xbmc/pvrclients/mythtv/MythXml.cpp b/xbmc/pvrclients/mythtv/MythXml.cpp
new file mode 100644 (file)
index 0000000..4642764
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * MythXml.cpp
+ *
+ *  Created on: Oct 7, 2010
+ *      Author: tafypz
+ */
+
+#include "MythXml.h"
+
+#include "FileSystem/FileCurl.h"
+#include "utils/log.h"
+
+#include "libmythxml/GetNumChannelsParameters.h"
+#include "libmythxml/GetNumChannelsResult.h"
+#include "libmythxml/GetNumChannelsCommand.h"
+#include "libmythxml/GetChannelListCommand.h"
+#include "libmythxml/GetChannelListParameters.h"
+#include "libmythxml/GetChannelListResult.h"
+#include "libmythxml/GetProgramGuideParameters.h"
+#include "libmythxml/GetProgramGuideResult.h"
+#include "libmythxml/GetProgramGuideCommand.h"
+
+
+using namespace XFILE;
+
+MythXml::MythXml() {
+       hostname_ = "";
+       port_ = -1;
+       pin_ = -1;
+       timeout_ = -1;
+}
+
+MythXml::~MythXml() {
+}
+
+void MythXml::init(){
+}
+
+void MythXml::cleanup(){
+}
+
+bool MythXml::open(CStdString hostname, int port, CStdString user, CStdString pass, int pin, long timeout){
+       hostname_ = hostname;
+       port_ = port;
+       timeout_ = timeout;
+       pin_ = pin;
+       CStdString strUrl;
+       strUrl.Format("http://%s:%i/Myth/GetConnectionInfo?Pin=%i", hostname.c_str(), port, pin);
+       CStdString strXML;
+
+       CFileCurl http;
+
+       http.SetTimeout(timeout);
+       if(!http.Get(strUrl, strXML)){
+               CLog::Log(LOGDEBUG, "MythXml - Could not open connection to mythtv backend.");
+               http.Cancel();
+               return false;
+       }
+       http.Cancel();
+       return true;
+}
+
+int MythXml::getNumChannels(){
+       if(!checkConnection())
+               return 0;
+       GetNumChannelsCommand cmd;
+       GetNumChannelsParameters params;
+       GetNumChannelsResult result;
+       cmd.execute(hostname_, port_, params, result, timeout_);
+       return result.getNumberOfChannels();
+}
+
+PVR_ERROR MythXml::requestChannelList(PVRHANDLE handle, int radio){
+  if(!checkConnection())
+               return PVR_ERROR_SERVER_ERROR;
+       GetChannelListCommand cmd;
+       GetChannelListParameters params;
+       GetChannelListResult result;
+       cmd.execute(hostname_, port_, params, result, timeout_);
+  
+       if(!result.isSuccess())
+         return PVR_ERROR_UNKOWN;
+       
+       const vector<SChannel>& channellist = result.getChannels();
+       vector<SChannel>::const_iterator it;
+       PVR_CHANNEL tag;
+       for( it = channellist.begin(); it != channellist.end(); ++it){
+         const SChannel& channel = *it; 
+         memset(&tag, 0 , sizeof(tag));
+         tag.uid           = channel.id;
+         tag.number        = channel.id;
+         tag.name          = channel.name.c_str();
+         tag.callsign      = channel.callsign.c_str();;
+         tag.radio         = false;
+         tag.input_format  = "";
+         tag.stream_url    = "";
+         tag.bouquet       = 0;
+
+         PVR->TransferChannelEntry(handle, &tag);
+       }
+       return PVR_ERROR_NO_ERROR;
+}
+
+PVR_ERROR MythXml::requestEPGForChannel(PVRHANDLE handle, const PVR_CHANNEL &channel, time_t start, time_t end){
+  if(!checkConnection())
+               return PVR_ERROR_SERVER_ERROR;
+       GetProgramGuideCommand cmd;
+       GetProgramGuideParameters params(channel.uid, CDateTime(start), CDateTime(end), true);
+       GetProgramGuideResult result;
+       
+       cmd.execute(hostname_, port_, params, result, timeout_);
+  
+       if(!result.isSuccess())
+         return PVR_ERROR_UNKOWN;
+       
+       PVR_PROGINFO guideItem;
+       const vector<SEpg>& epgInfo = result.getEpg();
+       vector<SEpg>::const_iterator it;
+       for( it = epgInfo.begin(); it != epgInfo.end(); ++it)
+       {
+         const SEpg& epg = *it;
+         time_t itemStart;
+         time_t itemEnd;
+         epg.start_time.GetAsTime(itemStart);
+         epg.end_time.GetAsTime(itemEnd);
+         
+         guideItem.channum         = epg.chan_num;
+         guideItem.uid             = epg.id;
+         guideItem.title           = epg.title;
+         guideItem.subtitle        = epg.subtitle;
+         guideItem.description     = epg.description;
+         guideItem.genre_type      = epg.genre_type;
+         guideItem.genre_sub_type  = epg.genre_subtype;
+         guideItem.parental_rating = epg.parental_rating;
+         guideItem.starttime       = itemStart;
+         guideItem.endtime         = itemEnd;
+         PVR->TransferEpgEntry(handle, &guideItem);
+       }
+       return PVR_ERROR_NO_ERROR;
+}
+
+bool MythXml::checkConnection(){
+       return true;
+}
diff --git a/xbmc/pvrclients/mythtv/MythXml.h b/xbmc/pvrclients/mythtv/MythXml.h
new file mode 100644 (file)
index 0000000..f41d6e5
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * MythXml.h
+ *
+ *  Created on: Oct 7, 2010
+ *      Author: tafypz
+ */
+
+#ifndef XBMC_PVRCLIENTS_MYTHTV_MYTHXML_H_
+#define XBMC_PVRCLIENTS_MYTHTV_MYTHXML_H_
+
+#include "client.h"
+
+/*! \class MythXml
+       \brief Acts as the glue between the PVR Addon world and the mythXML world.
+ */
+class MythXml {
+public:
+       MythXml();
+       virtual ~MythXml();
+       void init();
+       void cleanup();
+       bool open(CStdString hostname, int port, CStdString user, CStdString pass, int pin, long timeout);
+       int getNumChannels();
+       PVR_ERROR requestChannelList(PVRHANDLE handle, int radio);
+       PVR_ERROR requestEPGForChannel(PVRHANDLE handle, const PVR_CHANNEL &channel, time_t start, time_t end);
+private:
+       bool checkConnection();
+       CStdString hostname_;
+       int port_;
+       int timeout_;
+       int pin_;
+};
+
+#endif /* XBMC_PVRCLIENTS_MYTHTV_MYTHXML_H_ */
diff --git a/xbmc/pvrclients/mythtv/StdString.h b/xbmc/pvrclients/mythtv/StdString.h
new file mode 100644 (file)
index 0000000..b6f8203
--- /dev/null
@@ -0,0 +1,4333 @@
+#pragma once
+#include <string>
+#include <stdint.h>
+#if !defined(_LINUX)
+#include <windows.h>
+#include "pvrclient-mythtv_os.h"
+#endif
+
+// =============================================================================
+//  FILE:  StdString.h
+//  AUTHOR:  Joe O'Leary (with outside help noted in comments)
+//
+//    If you find any bugs in this code, please let me know:
+//
+//        jmoleary@earthlink.net
+//        http://www.joeo.net/stdstring.htm (a bit outdated)
+//
+//      The latest version of this code should always be available at the
+//      following link:
+//
+//              http://www.joeo.net/code/StdString.zip (Dec 6, 2003)
+//
+//
+//  REMARKS:
+//    This header file declares the CStdStr template.  This template derives
+//    the Standard C++ Library basic_string<> template and add to it the
+//    the following conveniences:
+//      - The full MFC CString set of functions (including implicit cast)
+//      - writing to/reading from COM IStream interfaces
+//      - Functional objects for use in STL algorithms
+//
+//    From this template, we intstantiate two classes:  CStdStringA and
+//    CStdStringW.  The name "CStdString" is just a #define of one of these,
+//    based upone the UNICODE macro setting
+//
+//    This header also declares our own version of the MFC/ATL UNICODE-MBCS
+//    conversion macros.  Our version looks exactly like the Microsoft's to
+//    facilitate portability.
+//
+//  NOTE:
+//    If you you use this in an MFC or ATL build, you should include either
+//    afx.h or atlbase.h first, as appropriate.
+//
+//  PEOPLE WHO HAVE CONTRIBUTED TO THIS CLASS:
+//
+//    Several people have helped me iron out problems and othewise improve
+//    this class.  OK, this is a long list but in my own defense, this code
+//    has undergone two major rewrites.  Many of the improvements became
+//    necessary after I rewrote the code as a template.  Others helped me
+//    improve the CString facade.
+//
+//    Anyway, these people are (in chronological order):
+//
+//      - Pete the Plumber (???)
+//      - Julian Selman
+//      - Chris (of Melbsys)
+//      - Dave Plummer
+//      - John C Sipos
+//      - Chris Sells
+//      - Nigel Nunn
+//      - Fan Xia
+//      - Matthew Williams
+//      - Carl Engman
+//      - Mark Zeren
+//      - Craig Watson
+//      - Rich Zuris
+//      - Karim Ratib
+//      - Chris Conti
+//      - Baptiste Lepilleur
+//      - Greg Pickles
+//      - Jim Cline
+//      - Jeff Kohn
+//      - Todd Heckel
+//      - Ullrich Poll�hne
+//      - Joe Vitaterna
+//      - Joe Woodbury
+//      - Aaron (no last name)
+//      - Joldakowski (???)
+//      - Scott Hathaway
+//      - Eric Nitzche
+//      - Pablo Presedo
+//      - Farrokh Nejadlotfi
+//      - Jason Mills
+//      - Igor Kholodov
+//      - Mike Crusader
+//      - John James
+//      - Wang Haifeng
+//      - Tim Dowty
+//          - Arnt Witteveen
+//          - Glen Maynard
+//          - Paul DeMarco
+//          - Bagira (full name?)
+//          - Ronny Schulz
+//          - Jakko Van Hunen
+//      - Charles Godwin
+//      - Henk Demper
+//      - Greg Marr
+//      - Bill Carducci
+//      - Brian Groose
+//      - MKingman
+//      - Don Beusee
+//
+//  REVISION HISTORY
+//
+//    2005-JAN-10 - Thanks to Don Beusee for pointing out the danger in mapping
+//          length-checked formatting functions to non-length-checked
+//          CRT equivalents.  Also thanks to him for motivating me to
+//          optimize my implementation of Replace()
+//
+//    2004-APR-22 - A big, big thank you to "MKingman" (whoever you are) for
+//          finally spotting a silly little error in StdCodeCvt that
+//          has been causing me (and users of CStdString) problems for
+//          years in some relatively rare conversions.  I had reversed
+//          two length arguments.
+//
+//    2003-NOV-24 - Thanks to a bunch of people for helping me clean up many
+//          compiler warnings (and yes, even a couple of actual compiler
+//          errors).  These include Henk Demper for figuring out how
+//          to make the Intellisense work on with CStdString on VC6,
+//          something I was never able to do.  Greg Marr pointed out
+//          a compiler warning about an unreferenced symbol and a
+//          problem with my version of Load in MFC builds.  Bill
+//          Carducci took a lot of time with me to help me figure out
+//          why some implementations of the Standard C++ Library were
+//          returning error codes for apparently successful conversions
+//          between ASCII and UNICODE.  Finally thanks to Brian Groose
+//          for helping me fix compiler signed unsigned warnings in
+//          several functions.
+//
+//    2003-JUL-10 - Thanks to Charles Godwin for making me realize my 'FmtArg'
+//          fixes had inadvertently broken the DLL-export code (which is
+//                  normally commented out.  I had to move it up higher.  Also
+//          this helped me catch a bug in ssicoll that would prevent
+//                  compilation, otherwise.
+//
+//    2003-MAR-14 - Thanks to Jakko Van Hunen for pointing out a copy-and-paste
+//                  bug in one of the overloads of FmtArg.
+//
+//    2003-MAR-10 - Thanks to Ronny Schulz for (twice!) sending me some changes
+//                  to help CStdString build on SGI and for pointing out an
+//                  error in placement of my preprocessor macros for ssfmtmsg.
+//
+//    2002-NOV-26 - Thanks to Bagira for pointing out that my implementation of
+//                  SpanExcluding was not properly handling the case in which
+//                  the string did NOT contain any of the given characters
+//
+//    2002-OCT-21 - Many thanks to Paul DeMarco who was invaluable in helping me
+//                  get this code working with Borland's free compiler as well
+//                  as the Dev-C++ compiler (available free at SourceForge).
+//
+//    2002-SEP-13 - Thanks to Glen Maynard who helped me get rid of some loud
+//                  but harmless warnings that were showing up on g++.  Glen
+//                  also pointed out that some pre-declarations of FmtArg<>
+//                  specializations were unnecessary (and no good on G++)
+//
+//    2002-JUN-26 - Thanks to Arnt Witteveen for pointing out that I was using
+//                  static_cast<> in a place in which I should have been using
+//                  reinterpret_cast<> (the ctor for unsigned char strings).
+//                  That's what happens when I don't unit-test properly!
+//                  Arnt also noticed that CString was silently correcting the
+//                  'nCount' argument to Left() and Right() where CStdString was
+//                  not (and crashing if it was bad).  That is also now fixed!
+//
+//    2002-FEB-25 - Thanks to Tim Dowty for pointing out (and giving me the fix
+//          for) a conversion problem with non-ASCII MBCS characters.
+//          CStdString is now used in my favorite commercial MP3 player!
+//
+//    2001-DEC-06 - Thanks to Wang Haifeng for spotting a problem in one of the
+//          assignment operators (for _bstr_t) that would cause compiler
+//          errors when refcounting protection was turned off.
+//
+//    2001-NOV-27 - Remove calls to operator!= which involve reverse_iterators
+//          due to a conflict with the rel_ops operator!=.  Thanks to
+//          John James for pointing this out.
+//
+//    2001-OCT-29 - Added a minor range checking fix for the Mid function to
+//          make it as forgiving as CString's version is.  Thanks to
+//          Igor Kholodov for noticing this.
+//          - Added a specialization of std::swap for CStdString.  Thanks
+//          to Mike Crusader for suggesting this!  It's commented out
+//          because you're not supposed to inject your own code into the
+//          'std' namespace.  But if you don't care about that, it's
+//          there if you want it
+//          - Thanks to Jason Mills for catching a case where CString was
+//          more forgiving in the Delete() function than I was.
+//
+//    2001-JUN-06 - I was violating the Standard name lookup rules stated
+//          in [14.6.2(3)].  None of the compilers I've tried so
+//          far apparently caught this but HP-UX aCC 3.30 did.  The
+//          fix was to add 'this->' prefixes in many places.
+//          Thanks to Farrokh Nejadlotfi for this!
+//
+//    2001-APR-27 - StreamLoad was calculating the number of BYTES in one
+//          case, not characters.  Thanks to Pablo Presedo for this.
+//
+//    2001-FEB-23 - Replace() had a bug which caused infinite loops if the
+//          source string was empty.  Fixed thanks to Eric Nitzsche.
+//
+//    2001-FEB-23 - Scott Hathaway was a huge help in providing me with the
+//          ability to build CStdString on Sun Unix systems.  He
+//          sent me detailed build reports about what works and what
+//          does not.  If CStdString compiles on your Unix box, you
+//          can thank Scott for it.
+//
+//    2000-DEC-29 - Joldakowski noticed one overload of Insert failed to do a
+//          range check as CString's does.  Now fixed -- thanks!
+//
+//    2000-NOV-07 - Aaron pointed out that I was calling static member
+//          functions of char_traits via a temporary.  This was not
+//          technically wrong, but it was unnecessary and caused
+//          problems for poor old buggy VC5.  Thanks Aaron!
+//
+//    2000-JUL-11 - Joe Woodbury noted that the CString::Find docs don't match
+//          what the CString::Find code really ends up doing.   I was
+//          trying to match the docs.  Now I match the CString code
+//          - Joe also caught me truncating strings for GetBuffer() calls
+//          when the supplied length was less than the current length.
+//
+//    2000-MAY-25 - Better support for STLPORT's Standard library distribution
+//          - Got rid of the NSP macro - it interfered with Koenig lookup
+//          - Thanks to Joe Woodbury for catching a TrimLeft() bug that
+//          I introduced in January.  Empty strings were not getting
+//          trimmed
+//
+//    2000-APR-17 - Thanks to Joe Vitaterna for pointing out that ReverseFind
+//          is supposed to be a const function.
+//
+//    2000-MAR-07 - Thanks to Ullrich Poll�hne for catching a range bug in one
+//          of the overloads of assign.
+//
+//    2000-FEB-01 - You can now use CStdString on the Mac with CodeWarrior!
+//          Thanks to Todd Heckel for helping out with this.
+//
+//    2000-JAN-23 - Thanks to Jim Cline for pointing out how I could make the
+//          Trim() function more efficient.
+//          - Thanks to Jeff Kohn for prompting me to find and fix a typo
+//          in one of the addition operators that takes _bstr_t.
+//          - Got rid of the .CPP file -  you only need StdString.h now!
+//
+//    1999-DEC-22 - Thanks to Greg Pickles for helping me identify a problem
+//          with my implementation of CStdString::FormatV in which
+//          resulting string might not be properly NULL terminated.
+//
+//    1999-DEC-06 - Chris Conti pointed yet another basic_string<> assignment
+//          bug that MS has not fixed.  CStdString did nothing to fix
+//          it either but it does now!  The bug was: create a string
+//          longer than 31 characters, get a pointer to it (via c_str())
+//          and then assign that pointer to the original string object.
+//          The resulting string would be empty.  Not with CStdString!
+//
+//    1999-OCT-06 - BufferSet was erasing the string even when it was merely
+//          supposed to shrink it.  Fixed.  Thanks to Chris Conti.
+//          - Some of the Q172398 fixes were not checking for assignment-
+//          to-self.  Fixed.  Thanks to Baptiste Lepilleur.
+//
+//    1999-AUG-20 - Improved Load() function to be more efficient by using
+//          SizeOfResource().  Thanks to Rich Zuris for this.
+//          - Corrected resource ID constructor, again thanks to Rich.
+//          - Fixed a bug that occurred with UNICODE characters above
+//          the first 255 ANSI ones.  Thanks to Craig Watson.
+//          - Added missing overloads of TrimLeft() and TrimRight().
+//          Thanks to Karim Ratib for pointing them out
+//
+//    1999-JUL-21 - Made all calls to GetBuf() with no args check length first.
+//
+//    1999-JUL-10 - Improved MFC/ATL independence of conversion macros
+//          - Added SS_NO_REFCOUNT macro to allow you to disable any
+//          reference-counting your basic_string<> impl. may do.
+//          - Improved ReleaseBuffer() to be as forgiving as CString.
+//          Thanks for Fan Xia for helping me find this and to
+//          Matthew Williams for pointing it out directly.
+//
+//    1999-JUL-06 - Thanks to Nigel Nunn for catching a very sneaky bug in
+//          ToLower/ToUpper.  They should call GetBuf() instead of
+//          data() in order to ensure the changed string buffer is not
+//          reference-counted (in those implementations that refcount).
+//
+//    1999-JUL-01 - Added a true CString facade.  Now you can use CStdString as
+//          a drop-in replacement for CString.  If you find this useful,
+//          you can thank Chris Sells for finally convincing me to give
+//          in and implement it.
+//          - Changed operators << and >> (for MFC CArchive) to serialize
+//          EXACTLY as CString's do.  So now you can send a CString out
+//          to a CArchive and later read it in as a CStdString.   I have
+//          no idea why you would want to do this but you can.
+//
+//    1999-JUN-21 - Changed the CStdString class into the CStdStr template.
+//          - Fixed FormatV() to correctly decrement the loop counter.
+//          This was harmless bug but a bug nevertheless.  Thanks to
+//          Chris (of Melbsys) for pointing it out
+//          - Changed Format() to try a normal stack-based array before
+//          using to _alloca().
+//          - Updated the text conversion macros to properly use code
+//          pages and to fit in better in MFC/ATL builds.  In other
+//          words, I copied Microsoft's conversion stuff again.
+//          - Added equivalents of CString::GetBuffer, GetBufferSetLength
+//          - new sscpy() replacement of CStdString::CopyString()
+//          - a Trim() function that combines TrimRight() and TrimLeft().
+//
+//    1999-MAR-13 - Corrected the "NotSpace" functional object to use _istpace()
+//          instead of _isspace()   Thanks to Dave Plummer for this.
+//
+//    1999-FEB-26 - Removed errant line (left over from testing) that #defined
+//          _MFC_VER.  Thanks to John C Sipos for noticing this.
+//
+//    1999-FEB-03 - Fixed a bug in a rarely-used overload of operator+() that
+//          caused infinite recursion and stack overflow
+//          - Added member functions to simplify the process of
+//          persisting CStdStrings to/from DCOM IStream interfaces
+//          - Added functional objects (e.g. StdStringLessNoCase) that
+//          allow CStdStrings to be used as keys STL map objects with
+//          case-insensitive comparison
+//          - Added array indexing operators (i.e. operator[]).  I
+//          originally assumed that these were unnecessary and would be
+//          inherited from basic_string.  However, without them, Visual
+//          C++ complains about ambiguous overloads when you try to use
+//          them.  Thanks to Julian Selman to pointing this out.
+//
+//    1998-FEB-?? - Added overloads of assign() function to completely account
+//          for Q172398 bug.  Thanks to "Pete the Plumber" for this
+//
+//    1998-FEB-?? - Initial submission
+//
+// COPYRIGHT:
+//    2002 Joseph M. O'Leary.  This code is 100% free.  Use it anywhere you
+//      want.  Rewrite it, restructure it, whatever.  If you can write software
+//      that makes money off of it, good for you.  I kinda like capitalism.
+//      Please don't blame me if it causes your $30 billion dollar satellite
+//      explode in orbit.  If you redistribute it in any form, I'd appreciate it
+//      if you would leave this notice here.
+// =============================================================================
+
+// Avoid multiple inclusion
+
+#ifndef STDSTRING_H
+#define STDSTRING_H
+
+// When using VC, turn off browser references
+// Turn off unavoidable compiler warnings
+
+#if defined(_MSC_VER) && (_MSC_VER > 1100)
+  #pragma component(browser, off, references, "CStdString")
+  #pragma warning (disable : 4290) // C++ Exception Specification ignored
+  #pragma warning (disable : 4127) // Conditional expression is constant
+  #pragma warning (disable : 4097) // typedef name used as synonym for class name
+#endif
+
+// Borland warnings to turn off
+
+#ifdef __BORLANDC__
+    #pragma option push -w-inl
+//  #pragma warn -inl   // Turn off inline function warnings
+#endif
+
+// SS_IS_INTRESOURCE
+// -----------------
+//    A copy of IS_INTRESOURCE from VC7.  Because old VC6 version of winuser.h
+//    doesn't have this.
+
+#define SS_IS_INTRESOURCE(_r) (false)
+
+#if !defined (SS_ANSI) && defined(_MSC_VER)
+  #undef SS_IS_INTRESOURCE
+  #if defined(_WIN64)
+    #define SS_IS_INTRESOURCE(_r) (((unsigned __int64)(_r) >> 16) == 0)
+  #else
+    #define SS_IS_INTRESOURCE(_r) (((unsigned long)(_r) >> 16) == 0)
+  #endif
+#endif
+
+
+// MACRO: SS_UNSIGNED
+// ------------------
+//      This macro causes the addition of a constructor and assignment operator
+//      which take unsigned characters.  CString has such functions and in order
+//      to provide maximum CString-compatability, this code needs them as well.
+//      In practice you will likely never need these functions...
+
+//#define SS_UNSIGNED
+
+#ifdef SS_ALLOW_UNSIGNED_CHARS
+  #define SS_UNSIGNED
+#endif
+
+// MACRO: SS_SAFE_FORMAT
+// ---------------------
+//      This macro provides limited compatability with a questionable CString
+//      "feature".  You can define it in order to avoid a common problem that
+//      people encounter when switching from CString to CStdString.
+//
+//      To illustrate the problem -- With CString, you can do this:
+//
+//          CString sName("Joe");
+//          CString sTmp;
+//          sTmp.Format("My name is %s", sName);                    // WORKS!
+//
+//      However if you were to try this with CStdString, your program would
+//      crash.
+//
+//          CStdString sName("Joe");
+//          CStdString sTmp;
+//          sTmp.Format("My name is %s", sName);                    // CRASHES!
+//
+//      You must explicitly call c_str() or cast the object to the proper type
+//
+//          sTmp.Format("My name is %s", sName.c_str());            // WORKS!
+//          sTmp.Format("My name is %s", static_cast<PCSTR>(sName));// WORKS!
+//          sTmp.Format("My name is %s", (PCSTR)sName);        // WORKS!
+//
+//      This is because it is illegal to pass anything but a POD type as a
+//      variadic argument to a variadic function (i.e. as one of the "..."
+//      arguments).  The type const char* is a POD type.  The type CStdString
+//      is not.  Of course, neither is the type CString, but CString lets you do
+//      it anyway due to the way they laid out the class in binary.  I have no
+//      control over this in CStdString since I derive from whatever
+//      implementation of basic_string is available.
+//
+//      However if you have legacy code (which does this) that you want to take
+//      out of the MFC world and you don't want to rewrite all your calls to
+//      Format(), then you can define this flag and it will no longer crash.
+//
+//      Note however that this ONLY works for Format(), not sprintf, fprintf,
+//      etc.  If you pass a CStdString object to one of those functions, your
+//      program will crash.  Not much I can do to get around this, short of
+//      writing substitutes for those functions as well.
+
+#define SS_SAFE_FORMAT  // use new template style Format() function
+
+
+// MACRO: SS_NO_IMPLICIT_CAST
+// --------------------------
+//      Some people don't like the implicit cast to const char* (or rather to
+//      const CT*) that CStdString (and MFC's CString) provide.  That was the
+//      whole reason I created this class in the first place, but hey, whatever
+//      bakes your cake.  Just #define this macro to get rid of the the implicit
+//      cast.
+
+//#define SS_NO_IMPLICIT_CAST // gets rid of operator const CT*()
+
+
+// MACRO: SS_NO_REFCOUNT
+// ---------------------
+//    turns off reference counting at the assignment level.  Only needed
+//    for the version of basic_string<> that comes with Visual C++ versions
+//    6.0 or earlier, and only then in some heavily multithreaded scenarios.
+//    Uncomment it if you feel you need it.
+
+//#define SS_NO_REFCOUNT
+
+// MACRO: SS_WIN32
+// ---------------
+//      When this flag is set, we are building code for the Win32 platform and
+//      may use Win32 specific functions (such as LoadString).  This gives us
+//      a couple of nice extras for the code.
+//
+//      Obviously, Microsoft's is not the only compiler available for Win32 out
+//      there.  So I can't just check to see if _MSC_VER is defined to detect
+//      if I'm building on Win32.  So for now, if you use MS Visual C++ or
+//      Borland's compiler, I turn this on.  Otherwise you may turn it on
+//      yourself, if you prefer
+
+#if defined(_MSC_VER) || defined(__BORLANDC__) || defined(_WIN32)
+ #define SS_WIN32
+#endif
+
+// MACRO: SS_ANSI
+// --------------
+//      When this macro is defined, the code attempts only to use ANSI/ISO
+//      standard library functions to do it's work.  It will NOT attempt to use
+//      any Win32 of Visual C++ specific functions -- even if they are
+//      available.  You may define this flag yourself to prevent any Win32
+//      of VC++ specific functions from being called.
+
+// If we're not on Win32, we MUST use an ANSI build
+
+#ifndef SS_WIN32
+    #if !defined(SS_NO_ANSI)
+        #define SS_ANSI
+    #endif
+#endif
+
+// MACRO: SS_ALLOCA
+// ----------------
+//      Some implementations of the Standard C Library have a non-standard
+//      function known as alloca().  This functions allows one to allocate a
+//      variable amount of memory on the stack.  It is needed to implement
+//      the ASCII/MBCS conversion macros.
+//
+//      I wanted to find some way to determine automatically if alloca() is
+//    available on this platform via compiler flags but that is asking for
+//    trouble.  The crude test presented here will likely need fixing on
+//    other platforms.  Therefore I'll leave it up to you to fiddle with
+//    this test to determine if it exists.  Just make sure SS_ALLOCA is or
+//    is not defined as appropriate and you control this feature.
+
+#if defined(_MSC_VER) && !defined(SS_ANSI)
+  #define SS_ALLOCA
+#endif
+
+
+// MACRO: SS_MBCS
+// --------------
+//    Setting this macro means you are using MBCS characters.  In MSVC builds,
+//    this macro gets set automatically by detection of the preprocessor flag
+//    _MBCS.  For other platforms you may set it manually if you wish.  The
+//    only effect it currently has is to cause the allocation of more space
+//    for wchar_t --> char conversions.
+//    Note that MBCS does not mean UNICODE.
+//
+//  #define SS_MBCS
+//
+
+#ifdef _MBCS
+  #define SS_MBCS
+#endif
+
+
+// MACRO SS_NO_LOCALE
+// ------------------
+// If your implementation of the Standard C++ Library lacks the <locale> header,
+// you can #define this macro to make your code build properly.  Note that this
+// is some of my newest code and frankly I'm not very sure of it, though it does
+// pass my unit tests.
+
+// #define SS_NO_LOCALE
+
+
+// Compiler Error regarding _UNICODE and UNICODE
+// -----------------------------------------------
+// Microsoft header files are screwy.  Sometimes they depend on a preprocessor
+// flag named "_UNICODE".  Other times they check "UNICODE" (note the lack of
+// leading underscore in the second version".  In several places, they silently
+// "synchronize" these two flags this by defining one of the other was defined.
+// In older version of this header, I used to try to do the same thing.
+//
+// However experience has taught me that this is a bad idea.  You get weird
+// compiler errors that seem to indicate things like LPWSTR and LPTSTR not being
+// equivalent in UNICODE builds, stuff like that (when they MUST be in a proper
+// UNICODE  build).  You end up scratching your head and saying, "But that HAS
+// to compile!".
+//
+// So what should you do if you get this error?
+//
+// Make sure that both macros (_UNICODE and UNICODE) are defined before this
+// file is included.  You can do that by either
+//
+//    a) defining both yourself before any files get included
+//    b) including the proper MS headers in the proper order
+//    c) including this file before any other file, uncommenting
+//       the #defines below, and commenting out the #errors
+//
+//  Personally I recommend solution a) but it's your call.
+
+#ifdef _MSC_VER
+  #if defined (_UNICODE) && !defined (UNICODE)
+    #error UNICODE defined  but not UNICODE
+  //  #define UNICODE  // no longer silently fix this
+  #endif
+  #if defined (UNICODE) && !defined (_UNICODE)
+    #error Warning, UNICODE defined  but not _UNICODE
+  //  #define _UNICODE  // no longer silently fix this
+  #endif
+#endif
+
+
+// -----------------------------------------------------------------------------
+// MIN and MAX.  The Standard C++ template versions go by so many names (at
+// at least in the MS implementation) that you never know what's available
+// -----------------------------------------------------------------------------
+template<class Type>
+inline const Type& SSMIN(const Type& arg1, const Type& arg2)
+{
+  return arg2 < arg1 ? arg2 : arg1;
+}
+template<class Type>
+inline const Type& SSMAX(const Type& arg1, const Type& arg2)
+{
+  return arg2 > arg1 ? arg2 : arg1;
+}
+
+// If they have not #included W32Base.h (part of my W32 utility library) then
+// we need to define some stuff.  Otherwise, this is all defined there.
+
+#if !defined(W32BASE_H)
+
+  // If they want us to use only standard C++ stuff (no Win32 stuff)
+
+  #ifdef SS_ANSI
+
+    // On Win32 we have TCHAR.H so just include it.  This is NOT violating
+        // the spirit of SS_ANSI as we are not calling any Win32 functions here.
+
+    #ifdef SS_WIN32
+
+      #include <TCHAR.H>
+      #include <WTYPES.H>
+      #ifndef STRICT
+        #define STRICT
+      #endif
+
+        // ... but on non-Win32 platforms, we must #define the types we need.
+
+    #else
+
+      typedef const char*    PCSTR;
+      typedef char*      PSTR;
+      typedef const wchar_t*  PCWSTR;
+      typedef wchar_t*    PWSTR;
+      #ifdef UNICODE
+        typedef wchar_t    TCHAR;
+      #else
+        typedef char    TCHAR;
+      #endif
+      typedef wchar_t      OLECHAR;
+
+    #endif  // #ifndef _WIN32
+
+
+    // Make sure ASSERT and verify are defined using only ANSI stuff
+
+    #ifndef ASSERT
+      #include <assert.h>
+      #define ASSERT(f) assert((f))
+    #endif
+    #ifndef VERIFY
+      #ifdef _DEBUG
+        #define VERIFY(x) ASSERT((x))
+      #else
+        #define VERIFY(x) x
+      #endif
+    #endif
+
+  #else // ...else SS_ANSI is NOT defined
+
+    #include <TCHAR.H>
+    #include <WTYPES.H>
+    #ifndef STRICT
+      #define STRICT
+    #endif
+
+    // Make sure ASSERT and verify are defined
+
+    #ifndef ASSERT
+      #include <crtdbg.h>
+      #define ASSERT(f) _ASSERTE((f))
+    #endif
+    #ifndef VERIFY
+      #ifdef _DEBUG
+        #define VERIFY(x) ASSERT((x))
+      #else
+        #define VERIFY(x) x
+      #endif
+    #endif
+
+  #endif // #ifdef SS_ANSI
+
+  #ifndef UNUSED
+    #define UNUSED(x) x
+  #endif
+
+#endif // #ifndef W32BASE_H
+
+// Standard headers needed
+
+#include <string>      // basic_string
+#include <algorithm>    // for_each, etc.
+#include <functional>    // for StdStringLessNoCase, et al
+#ifndef SS_NO_LOCALE
+  #include <locale>      // for various facets
+#endif
+
+// If this is a recent enough version of VC include comdef.h, so we can write
+// member functions to deal with COM types & compiler support classes e.g.
+// _bstr_t
+
+#if defined (_MSC_VER) && (_MSC_VER >= 1100)
+ #include <comdef.h>
+ #define SS_INC_COMDEF  // signal that we #included MS comdef.h file
+ #define STDSTRING_INC_COMDEF
+ #define SS_NOTHROW __declspec(nothrow)
+#else
+  #define SS_NOTHROW
+#endif
+
+#ifndef TRACE
+  #define TRACE_DEFINED_HERE
+  #define TRACE
+#endif
+
+// Microsoft defines PCSTR, PCWSTR, etc, but no PCTSTR.  I hate to use the
+// versions with the "L" in front of them because that's a leftover from Win 16
+// days, even though it evaluates to the same thing.  Therefore, Define a PCSTR
+// as an LPCTSTR.
+
+#if !defined(PCTSTR) && !defined(PCTSTR_DEFINED)
+  typedef const TCHAR*      PCTSTR;
+  #define PCTSTR_DEFINED
+#endif
+
+#if !defined(PCOLESTR) && !defined(PCOLESTR_DEFINED)
+  typedef const OLECHAR*      PCOLESTR;
+  #define PCOLESTR_DEFINED
+#endif
+
+#if !defined(POLESTR) && !defined(POLESTR_DEFINED)
+  typedef OLECHAR*        POLESTR;
+  #define POLESTR_DEFINED
+#endif
+
+#if !defined(PCUSTR) && !defined(PCUSTR_DEFINED)
+  typedef const unsigned char*  PCUSTR;
+  typedef unsigned char*      PUSTR;
+  #define PCUSTR_DEFINED
+#endif
+
+
+// SGI compiler 7.3 doesnt know these  types - oh and btw, remember to use
+// -LANG:std in the CXX Flags
+#if defined(__sgi)
+    typedef unsigned long           DWORD;
+    typedef void *                  LPCVOID;
+#endif
+
+
+// SS_USE_FACET macro and why we need it:
+//
+// Since I'm a good little Standard C++ programmer, I use locales.  Thus, I
+// need to make use of the use_facet<> template function here.   Unfortunately,
+// this need is complicated by the fact the MS' implementation of the Standard
+// C++ Library has a non-standard version of use_facet that takes more
+// arguments than the standard dictates.  Since I'm trying to write CStdString
+// to work with any version of the Standard library, this presents a problem.
+//
+// The upshot of this is that I can't do 'use_facet' directly.  The MS' docs
+// tell me that I have to use a macro, _USE() instead.  Since _USE obviously
+// won't be available in other implementations, this means that I have to write
+// my OWN macro -- SS_USE_FACET -- that evaluates either to _USE or to the
+// standard, use_facet.
+//
+// If you are having trouble with the SS_USE_FACET macro, in your implementation
+// of the Standard C++ Library, you can define your own version of SS_USE_FACET.
+
+#ifndef schMSG
+  #define schSTR(x)     #x
+  #define schSTR2(x)  schSTR(x)
+  #define schMSG(desc) message(__FILE__ "(" schSTR2(__LINE__) "):" #desc)
+#endif
+
+#ifndef SS_USE_FACET
+
+  // STLPort #defines a macro (__STL_NO_EXPLICIT_FUNCTION_TMPL_ARGS) for
+  // all MSVC builds, erroneously in my opinion.  It causes problems for
+  // my SS_ANSI builds.  In my code, I always comment out that line.  You'll
+  // find it in   \stlport\config\stl_msvc.h
+
+  #if defined(__SGI_STL_PORT) && (__SGI_STL_PORT >= 0x400 )
+
+    #if defined(__STL_NO_EXPLICIT_FUNCTION_TMPL_ARGS) && defined(_MSC_VER)
+      #ifdef SS_ANSI
+        #pragma schMSG(__STL_NO_EXPLICIT_FUNCTION_TMPL_ARGS defined!!)
+      #endif
+    #endif
+    #define SS_USE_FACET(loc, fac) std::use_facet<fac >(loc)
+
+  #elif defined(_MSC_VER )
+
+    #define SS_USE_FACET(loc, fac) std::_USE(loc, fac)
+
+  // ...and
+  #elif defined(_RWSTD_NO_TEMPLATE_ON_RETURN_TYPE)
+
+        #define SS_USE_FACET(loc, fac) std::use_facet(loc, (fac*)0)
+
+  #else
+
+    #define SS_USE_FACET(loc, fac) std::use_facet<fac >(loc)
+
+  #endif
+
+#endif
+
+// =============================================================================
+// UNICODE/MBCS conversion macros.  Made to work just like the MFC/ATL ones.
+// =============================================================================
+
+#include <wchar.h>      // Added to Std Library with Amendment #1.
+
+// First define the conversion helper functions.  We define these regardless of
+// any preprocessor macro settings since their names won't collide.
+
+// Not sure if we need all these headers.   I believe ANSI says we do.
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <wctype.h>
+#include <ctype.h>
+#include <stdlib.h>
+#ifndef va_start
+  #include <varargs.h>
+#endif
+
+
+#ifdef SS_NO_LOCALE
+
+  #if defined(_WIN32) || defined (_WIN32_WCE)
+
+    inline PWSTR StdCodeCvt(PWSTR pDstW, int nDst, PCSTR pSrcA, int nSrc,
+      UINT acp=CP_ACP)
+    {
+      ASSERT(0 != pSrcA);
+      ASSERT(0 != pDstW);
+      pDstW[0] = '\0';
+      MultiByteToWideChar(acp, 0, pSrcA, nSrc, pDstW, nDst);
+      return pDstW;
+    }
+    inline PWSTR StdCodeCvt(PWSTR pDstW, int nDst, PCUSTR pSrcA, int nSrc,
+      UINT acp=CP_ACP)
+    {
+      return StdCodeCvt(pDstW, nDst, (PCSTR)pSrcA, nSrc, acp);
+    }
+
+    inline PSTR StdCodeCvt(PSTR pDstA, int nDst, PCWSTR pSrcW, int nSrc,
+      UINT acp=CP_ACP)
+    {
+      ASSERT(0 != pDstA);
+      ASSERT(0 != pSrcW);
+      pDstA[0] = '\0';
+      WideCharToMultiByte(acp, 0, pSrcW, nSrc, pDstA, nDst, 0, 0);
+      return pDstA;
+    }
+    inline PUSTR StdCodeCvt(PUSTR pDstA, int nDst, PCWSTR pSrcW, int nSrc,
+      UINT acp=CP_ACP)
+    {
+      return (PUSTR)StdCodeCvt((PSTR)pDstA, nDst, pSrcW, nSrc, acp);
+    }
+  #else
+  #endif
+
+#else
+
+  // StdCodeCvt - made to look like Win32 functions WideCharToMultiByte
+  //        and MultiByteToWideChar but uses locales in SS_ANSI
+  //        builds.  There are a number of overloads.
+  //              First argument is the destination buffer.
+  //              Second argument is the source buffer
+  //#if defined (SS_ANSI) || !defined (SS_WIN32)
+
+  // 'SSCodeCvt' - shorthand name for the codecvt facet we use
+
+  typedef std::codecvt<wchar_t, char, mbstate_t> SSCodeCvt;
+
+  inline PWSTR StdCodeCvt(PWSTR pDstW, int nDst, PCSTR pSrcA, int nSrc,
+    const std::locale& loc=std::locale())
+  {
+    ASSERT(0 != pSrcA);
+    ASSERT(0 != pDstW);
+
+    pDstW[0]          = '\0';
+
+    if ( nSrc > 0 )
+    {
+      PCSTR pNextSrcA      = pSrcA;
+      PWSTR pNextDstW      = pDstW;
+      SSCodeCvt::result res  = SSCodeCvt::ok;
+      const SSCodeCvt& conv  = SS_USE_FACET(loc, SSCodeCvt);
+      SSCodeCvt::state_type st= { 0 };
+      res            = conv.in(st,
+                    pSrcA, pSrcA + nSrc, pNextSrcA,
+                    pDstW, pDstW + nDst, pNextDstW);
+#ifdef _LINUX
+#define ASSERT2(a) if (!(a)) {fprintf(stderr, "StdString: Assertion Failed on line %d\n", __LINE__);}
+#else
+#define ASSERT2 ASSERT
+#endif
+      ASSERT2(SSCodeCvt::ok == res);
+      ASSERT2(SSCodeCvt::error != res);
+      ASSERT2(pNextDstW >= pDstW);
+      ASSERT2(pNextSrcA >= pSrcA);
+#undef ASSERT2
+      // Null terminate the converted string
+
+      if ( pNextDstW - pDstW > nDst )
+        *(pDstW + nDst) = '\0';
+      else
+        *pNextDstW = '\0';
+    }
+    return pDstW;
+  }
+  inline PWSTR StdCodeCvt(PWSTR pDstW, int nDst, PCUSTR pSrcA, int nSrc,
+    const std::locale& loc=std::locale())
+  {
+    return StdCodeCvt(pDstW, nDst, (PCSTR)pSrcA, nSrc, loc);
+  }
+
+  inline PSTR StdCodeCvt(PSTR pDstA, int nDst, PCWSTR pSrcW, int nSrc,
+    const std::locale& loc=std::locale())
+  {
+    ASSERT(0 != pDstA);
+    ASSERT(0 != pSrcW);
+
+    pDstA[0]          = '\0';
+
+    if ( nSrc > 0 )
+    {
+      PSTR pNextDstA      = pDstA;
+      PCWSTR pNextSrcW    = pSrcW;
+      SSCodeCvt::result res  = SSCodeCvt::ok;
+      const SSCodeCvt& conv  = SS_USE_FACET(loc, SSCodeCvt);
+      SSCodeCvt::state_type st= { 0 };
+      res            = conv.out(st,
+                    pSrcW, pSrcW + nSrc, pNextSrcW,
+                    pDstA, pDstA + nDst, pNextDstA);
+#ifdef _LINUX
+#define ASSERT2(a) if (!(a)) {fprintf(stderr, "StdString: Assertion Failed on line %d\n", __LINE__);}
+#else
+#define ASSERT2 ASSERT
+#endif
+      ASSERT2(SSCodeCvt::error != res);
+      ASSERT2(SSCodeCvt::ok == res);  // strict, comment out for sanity
+      ASSERT2(pNextDstA >= pDstA);
+      ASSERT2(pNextSrcW >= pSrcW);
+#undef ASSERT2
+
+      // Null terminate the converted string
+
+      if ( pNextDstA - pDstA > nDst )
+        *(pDstA + nDst) = '\0';
+      else
+        *pNextDstA = '\0';
+    }
+    return pDstA;
+  }
+
+  inline PUSTR StdCodeCvt(PUSTR pDstA, int nDst, PCWSTR pSrcW, int nSrc,
+    const std::locale& loc=std::locale())
+  {
+    return (PUSTR)StdCodeCvt((PSTR)pDstA, nDst, pSrcW, nSrc, loc);
+  }
+
+#endif
+
+
+
+// Unicode/MBCS conversion macros are only available on implementations of
+// the "C" library that have the non-standard _alloca function.  As far as I
+// know that's only Microsoft's though I've heard that the function exists
+// elsewhere.
+
+#if defined(SS_ALLOCA) && !defined SS_NO_CONVERSION
+
+    #include <malloc.h>  // needed for _alloca
+
+    // Define our conversion macros to look exactly like Microsoft's to
+    // facilitate using this stuff both with and without MFC/ATL
+
+    #ifdef _CONVERSION_USES_THREAD_LOCALE
+
+      #ifndef _DEBUG
+        #define SSCVT int _cvt; _cvt; UINT _acp=GetACP(); \
+          _acp; PCWSTR _pw; _pw; PCSTR _pa; _pa
+      #else
+        #define SSCVT int _cvt = 0; _cvt; UINT _acp=GetACP();\
+           _acp; PCWSTR _pw=0; _pw; PCSTR _pa=0; _pa
+      #endif
+      #define SSA2W(pa) (\
+        ((_pa = pa) == 0) ? 0 : (\
+          _cvt = (sslen(_pa)),\
+          StdCodeCvt((PWSTR) _alloca((_cvt+1)*2), (_cvt+1)*2, \
+              _pa, _cvt, _acp)))
+      #define SSW2A(pw) (\
+        ((_pw = pw) == 0) ? 0 : (\
+          _cvt = sslen(_pw), \
+          StdCodeCvt((LPSTR) _alloca((_cvt+1)*2), (_cvt+1)*2, \
+          _pw, _cvt, _acp)))
+  #else
+
+      #ifndef _DEBUG
+        #define SSCVT int _cvt; _cvt; UINT _acp=CP_ACP; _acp;\
+           PCWSTR _pw; _pw; PCSTR _pa; _pa
+      #else
+        #define SSCVT int _cvt = 0; _cvt; UINT _acp=CP_ACP; \
+          _acp; PCWSTR _pw=0; _pw; PCSTR _pa=0; _pa
+      #endif
+      #define SSA2W(pa) (\
+        ((_pa = pa) == 0) ? 0 : (\
+          _cvt = (sslen(_pa)),\
+          StdCodeCvt((PWSTR) _alloca((_cvt+1)*2), (_cvt+1)*2, \
+          _pa, _cvt)))
+      #define SSW2A(pw) (\
+        ((_pw = pw) == 0) ? 0 : (\
+          _cvt = (sslen(_pw)),\
+          StdCodeCvt((LPSTR) _alloca((_cvt+1)*2), (_cvt+1)*2, \
+          _pw, _cvt)))
+    #endif
+
+    #define SSA2CW(pa) ((PCWSTR)SSA2W((pa)))
+    #define SSW2CA(pw) ((PCSTR)SSW2A((pw)))
+
+    #ifdef UNICODE
+      #define SST2A  SSW2A
+      #define SSA2T  SSA2W
+      #define SST2CA  SSW2CA
+      #define SSA2CT  SSA2CW
+    // (Did you get a compiler error here about not being able to convert
+    // PTSTR into PWSTR?  Then your _UNICODE and UNICODE flags are messed
+    // up.  Best bet: #define BOTH macros before including any MS headers.)
+      inline PWSTR  SST2W(PTSTR p)      { return p; }
+      inline PTSTR  SSW2T(PWSTR p)      { return p; }
+      inline PCWSTR  SST2CW(PCTSTR p)    { return p; }
+      inline PCTSTR  SSW2CT(PCWSTR p)    { return p; }
+    #else
+      #define SST2W  SSA2W
+      #define SSW2T  SSW2A
+      #define SST2CW  SSA2CW
+      #define SSW2CT  SSW2CA
+      inline PSTR    SST2A(PTSTR p)      { return p; }
+      inline PTSTR  SSA2T(PSTR p)      { return p; }
+      inline PCSTR  SST2CA(PCTSTR p)    { return p; }
+      inline PCTSTR  SSA2CT(PCSTR p)      { return p; }
+    #endif // #ifdef UNICODE
+
+    #if defined(UNICODE)
+    // in these cases the default (TCHAR) is the same as OLECHAR
+      inline PCOLESTR  SST2COLE(PCTSTR p)    { return p; }
+      inline PCTSTR  SSOLE2CT(PCOLESTR p)  { return p; }
+      inline POLESTR  SST2OLE(PTSTR p)    { return p; }
+      inline PTSTR  SSOLE2T(POLESTR p)    { return p; }
+    #elif defined(OLE2ANSI)
+    // in these cases the default (TCHAR) is the same as OLECHAR
+      inline PCOLESTR  SST2COLE(PCTSTR p)    { return p; }
+      inline PCTSTR  SSOLE2CT(PCOLESTR p)  { return p; }
+      inline POLESTR  SST2OLE(PTSTR p)    { return p; }
+      inline PTSTR  SSOLE2T(POLESTR p)    { return p; }
+    #else
+      //CharNextW doesn't work on Win95 so we use this
+      #define SST2COLE(pa)  SSA2CW((pa))
+      #define SST2OLE(pa)    SSA2W((pa))
+      #define SSOLE2CT(po)  SSW2CA((po))
+      #define SSOLE2T(po)    SSW2A((po))
+    #endif
+
+    #ifdef OLE2ANSI
+      #define SSW2OLE    SSW2A
+      #define SSOLE2W    SSA2W
+      #define SSW2COLE  SSW2CA
+      #define SSOLE2CW  SSA2CW
+      inline POLESTR    SSA2OLE(PSTR p)    { return p; }
+      inline PSTR      SSOLE2A(POLESTR p)  { return p; }
+      inline PCOLESTR    SSA2COLE(PCSTR p)  { return p; }
+      inline PCSTR    SSOLE2CA(PCOLESTR p){ return p; }
+    #else
+      #define SSA2OLE    SSA2W
+      #define SSOLE2A    SSW2A
+      #define SSA2COLE  SSA2CW
+      #define SSOLE2CA  SSW2CA
+      inline POLESTR    SSW2OLE(PWSTR p)  { return p; }
+      inline PWSTR    SSOLE2W(POLESTR p)  { return p; }
+      inline PCOLESTR    SSW2COLE(PCWSTR p)  { return p; }
+      inline PCWSTR    SSOLE2CW(PCOLESTR p){ return p; }
+    #endif
+
+    // Above we've defined macros that look like MS' but all have
+    // an 'SS' prefix.  Now we need the real macros.  We'll either
+    // get them from the macros above or from MFC/ATL.
+
+  #if defined (USES_CONVERSION)
+
+    #define _NO_STDCONVERSION  // just to be consistent
+
+  #else
+
+    #ifdef _MFC_VER
+
+      #include <afxconv.h>
+      #define _NO_STDCONVERSION // just to be consistent
+
+    #else
+
+      #define USES_CONVERSION SSCVT
+      #define A2CW      SSA2CW
+      #define W2CA      SSW2CA
+      #define T2A        SST2A
+      #define A2T        SSA2T
+      #define T2W        SST2W
+      #define W2T        SSW2T
+      #define T2CA      SST2CA
+      #define A2CT      SSA2CT
+      #define T2CW      SST2CW
+      #define W2CT      SSW2CT
+      #define ocslen      sslen
+      #define ocscpy      sscpy
+      #define T2COLE      SST2COLE
+      #define OLE2CT      SSOLE2CT
+      #define T2OLE      SST2COLE
+      #define OLE2T      SSOLE2CT
+      #define A2OLE      SSA2OLE
+      #define OLE2A      SSOLE2A
+      #define W2OLE      SSW2OLE
+      #define OLE2W      SSOLE2W
+      #define A2COLE      SSA2COLE
+      #define OLE2CA      SSOLE2CA
+      #define W2COLE      SSW2COLE
+      #define OLE2CW      SSOLE2CW
+
+    #endif // #ifdef _MFC_VER
+  #endif // #ifndef USES_CONVERSION
+#endif // #ifndef SS_NO_CONVERSION
+
+// Define ostring - generic name for std::basic_string<OLECHAR>
+
+#if !defined(ostring) && !defined(OSTRING_DEFINED)
+  typedef std::basic_string<OLECHAR> ostring;
+  #define OSTRING_DEFINED
+#endif
+
+// StdCodeCvt when there's no conversion to be done
+template <typename T>
+inline T* StdCodeCvt(T* pDst, int nDst, const T* pSrc, int nSrc)
+{
+  int nChars = SSMIN(nSrc, nDst);
+
+  if ( nChars > 0 )
+  {
+    pDst[0]        = '\0';
+    std::basic_string<T>::traits_type::copy(pDst, pSrc, nChars);
+//    std::char_traits<T>::copy(pDst, pSrc, nChars);
+    pDst[nChars]  = '\0';
+  }
+
+  return pDst;
+}
+inline PSTR StdCodeCvt(PSTR pDst, int nDst, PCUSTR pSrc, int nSrc)
+{
+  return StdCodeCvt(pDst, nDst, (PCSTR)pSrc, nSrc);
+}
+inline PUSTR StdCodeCvt(PUSTR pDst, int nDst, PCSTR pSrc, int nSrc)
+{
+  return (PUSTR)StdCodeCvt((PSTR)pDst, nDst, pSrc, nSrc);
+}
+
+// Define tstring -- generic name for std::basic_string<TCHAR>
+
+#if !defined(tstring) && !defined(TSTRING_DEFINED)
+  typedef std::basic_string<TCHAR> tstring;
+  #define TSTRING_DEFINED
+#endif
+
+// a very shorthand way of applying the fix for KB problem Q172398
+// (basic_string assignment bug)
+
+#if defined ( _MSC_VER ) && ( _MSC_VER < 1200 )
+  #define Q172398(x) (x).erase()
+#else
+  #define Q172398(x)
+#endif
+
+// =============================================================================
+// INLINE FUNCTIONS ON WHICH CSTDSTRING RELIES
+//
+// Usually for generic text mapping, we rely on preprocessor macro definitions
+// to map to string functions.  However the CStdStr<> template cannot use
+// macro-based generic text mappings because its character types do not get
+// resolved until template processing which comes AFTER macro processing.  In
+// other words, the preprocessor macro UNICODE is of little help to us in the
+// CStdStr template
+//
+// Therefore, to keep the CStdStr declaration simple, we have these inline
+// functions.  The template calls them often.  Since they are inline (and NOT
+// exported when this is built as a DLL), they will probably be resolved away
+// to nothing.
+//
+// Without these functions, the CStdStr<> template would probably have to broken
+// out into two, almost identical classes.  Either that or it would be a huge,
+// convoluted mess, with tons of "if" statements all over the place checking the
+// size of template parameter CT.
+// =============================================================================
+
+#ifdef SS_NO_LOCALE
+
+  // --------------------------------------------------------------------------
+  // Win32 GetStringTypeEx wrappers
+  // --------------------------------------------------------------------------
+  inline bool wsGetStringType(LCID lc, DWORD dwT, PCSTR pS, int nSize,
+    WORD* pWd)
+  {
+    return FALSE != GetStringTypeExA(lc, dwT, pS, nSize, pWd);
+  }
+  inline bool wsGetStringType(LCID lc, DWORD dwT, PCWSTR pS, int nSize,
+    WORD* pWd)
+  {
+    return FALSE != GetStringTypeExW(lc, dwT, pS, nSize, pWd);
+  }
+
+
+  template<typename CT>
+    inline bool ssisspace (CT t)
+  {
+    WORD toYourMother;
+    return  wsGetStringType(GetThreadLocale(), CT_CTYPE1, &t, 1, &toYourMother)
+      && 0 != (C1_BLANK & toYourMother);
+  }
+
+#endif
+
+// If they defined SS_NO_REFCOUNT, then we must convert all assignments
+
+#if defined (_MSC_VER) && (_MSC_VER < 1300)
+  #ifdef SS_NO_REFCOUNT
+    #define SSREF(x) (x).c_str()
+  #else
+    #define SSREF(x) (x)
+  #endif
+#else
+  #define SSREF(x) (x)
+#endif
+
+// -----------------------------------------------------------------------------
+// sslen: strlen/wcslen wrappers
+// -----------------------------------------------------------------------------
+template<typename CT> inline int sslen(const CT* pT)
+{
+  return 0 == pT ? 0 : (int)std::basic_string<CT>::traits_type::length(pT);
+//  return 0 == pT ? 0 : std::char_traits<CT>::length(pT);
+}
+inline SS_NOTHROW int sslen(const std::string& s)
+{
+  return static_cast<int>(s.length());
+}
+inline SS_NOTHROW int sslen(const std::wstring& s)
+{
+  return static_cast<int>(s.length());
+}
+
+// -----------------------------------------------------------------------------
+// sstolower/sstoupper -- convert characters to upper/lower case
+// -----------------------------------------------------------------------------
+
+#ifdef SS_NO_LOCALE
+  inline char sstoupper(char ch)    { return (char)::toupper(ch); }
+  inline wchar_t sstoupper(wchar_t ch){ return (wchar_t)::towupper(ch); }
+  inline char sstolower(char ch)    { return (char)::tolower(ch); }
+  inline wchar_t sstolower(wchar_t ch){ return (wchar_t)::tolower(ch); }
+#else
+  template<typename CT>
+  inline CT sstolower(const CT& t, const std::locale& loc = std::locale())
+  {
+    return std::tolower<CT>(t, loc);
+  }
+  template<typename CT>
+  inline CT sstoupper(const CT& t, const std::locale& loc = std::locale())
+  {
+    return std::toupper<CT>(t, loc);
+  }
+#endif
+
+// -----------------------------------------------------------------------------
+// ssasn: assignment functions -- assign "sSrc" to "sDst"
+// -----------------------------------------------------------------------------
+typedef std::string::size_type    SS_SIZETYPE; // just for shorthand, really
+typedef std::string::pointer    SS_PTRTYPE;
+typedef std::wstring::size_type    SW_SIZETYPE;
+typedef std::wstring::pointer    SW_PTRTYPE;
+
+
+template <typename T>
+inline void  ssasn(std::basic_string<T>& sDst, const std::basic_string<T>& sSrc)
+{
+  if ( sDst.c_str() != sSrc.c_str() )
+  {
+    sDst.erase();
+    sDst.assign(SSREF(sSrc));
+  }
+}
+template <typename T>
+inline void  ssasn(std::basic_string<T>& sDst, const T *pA)
+{
+  // Watch out for NULLs, as always.
+
+  if ( 0 == pA )
+  {
+    sDst.erase();
+  }
+
+  // If pA actually points to part of sDst, we must NOT erase(), but
+  // rather take a substring
+
+  else if ( pA >= sDst.c_str() && pA <= sDst.c_str() + sDst.size() )
+  {
+    sDst =sDst.substr(static_cast<typename std::basic_string<T>::size_type>(pA-sDst.c_str()));
+  }
+
+  // Otherwise (most cases) apply the assignment bug fix, if applicable
+  // and do the assignment
+
+  else
+  {
+    Q172398(sDst);
+    sDst.assign(pA);
+  }
+}
+inline void  ssasn(std::string& sDst, const std::wstring& sSrc)
+{
+  if ( sSrc.empty() )
+  {
+    sDst.erase();
+  }
+  else
+  {
+    int nDst  = static_cast<int>(sSrc.size());
+
+    // In MBCS builds, pad the buffer to account for the possibility of
+    // some 3 byte characters.  Not perfect but should get most cases.
+
+#ifdef SS_MBCS
+    // In MBCS builds, we don't know how long the destination string will be.
+    nDst  = static_cast<int>(static_cast<double>(nDst) * 1.3);
+    sDst.resize(nDst+1);
+    PCSTR szCvt = StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()), nDst,
+      sSrc.c_str(), static_cast<int>(sSrc.size()));
+    sDst.resize(sslen(szCvt));
+#else
+    sDst.resize(nDst+1);
+    StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()), nDst,
+      sSrc.c_str(), static_cast<int>(sSrc.size()));
+    sDst.resize(sSrc.size());
+#endif
+  }
+}
+inline void  ssasn(std::string& sDst, PCWSTR pW)
+{
+  int nSrc  = sslen(pW);
+  if ( nSrc > 0 )
+  {
+    int nSrc  = sslen(pW);
+    int nDst  = nSrc;
+
+    // In MBCS builds, pad the buffer to account for the possibility of
+    // some 3 byte characters.  Not perfect but should get most cases.
+
+#ifdef SS_MBCS
+    nDst  = static_cast<int>(static_cast<double>(nDst) * 1.3);
+    // In MBCS builds, we don't know how long the destination string will be.
+    sDst.resize(nDst + 1);
+    PCSTR szCvt = StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()), nDst,
+      pW, nSrc);
+    sDst.resize(sslen(szCvt));
+#else
+    sDst.resize(nDst + 1);
+    StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()), nDst, pW, nSrc);
+    sDst.resize(nDst);
+#endif
+  }
+  else
+  {
+    sDst.erase();
+  }
+}
+inline void ssasn(std::string& sDst, const int nNull)
+{
+  //UNUSED(nNull);
+  ASSERT(nNull==0);
+  sDst.assign("");
+}
+#undef StrSizeType
+inline void  ssasn(std::wstring& sDst, const std::string& sSrc)
+{
+  if ( sSrc.empty() )
+  {
+    sDst.erase();
+  }
+  else
+  {
+    int nSrc  = static_cast<int>(sSrc.size());
+    int nDst  = nSrc;
+
+    sDst.resize(nSrc+1);
+    PCWSTR szCvt = StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()), nDst,
+      sSrc.c_str(), nSrc);
+
+    sDst.resize(sslen(szCvt));
+  }
+}
+inline void  ssasn(std::wstring& sDst, PCSTR pA)
+{
+  int nSrc  = sslen(pA);
+
+  if ( 0 == nSrc )
+  {
+    sDst.erase();
+  }
+  else
+  {
+    int nDst  = nSrc;
+    sDst.resize(nDst+1);
+    PCWSTR szCvt = StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()), nDst, pA,
+      nSrc);
+
+    sDst.resize(sslen(szCvt));
+  }
+}
+inline void ssasn(std::wstring& sDst, const int nNull)
+{
+  //UNUSED(nNull);
+  ASSERT(nNull==0);
+  sDst.assign(L"");
+}
+
+// -----------------------------------------------------------------------------
+// ssadd: string object concatenation -- add second argument to first
+// -----------------------------------------------------------------------------
+inline void  ssadd(std::string& sDst, const std::wstring& sSrc)
+{
+  int nSrc  = static_cast<int>(sSrc.size());
+
+  if ( nSrc > 0 )
+  {
+    int nDst  = static_cast<int>(sDst.size());
+    int nAdd  = nSrc;
+
+    // In MBCS builds, pad the buffer to account for the possibility of
+    // some 3 byte characters.  Not perfect but should get most cases.
+
+#ifdef SS_MBCS
+    nAdd    = static_cast<int>(static_cast<double>(nAdd) * 1.3);
+    sDst.resize(nDst+nAdd+1);
+    PCSTR szCvt = StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()+nDst),
+      nAdd, sSrc.c_str(), nSrc);
+    sDst.resize(nDst + sslen(szCvt));
+#else
+    sDst.resize(nDst+nAdd+1);
+    StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()+nDst), nAdd, sSrc.c_str(), nSrc);
+    sDst.resize(nDst + nAdd);
+#endif
+  }
+}
+template <typename T>
+inline void  ssadd(typename std::basic_string<T>& sDst, const typename std::basic_string<T>& sSrc)
+{
+  sDst += sSrc;
+}
+inline void  ssadd(std::string& sDst, PCWSTR pW)
+{
+  int nSrc    = sslen(pW);
+  if ( nSrc > 0 )
+  {
+    int nDst  = static_cast<int>(sDst.size());
+    int nAdd  = nSrc;
+
+#ifdef SS_MBCS
+    nAdd  = static_cast<int>(static_cast<double>(nAdd) * 1.3);
+    sDst.resize(nDst + nAdd + 1);
+    PCSTR szCvt = StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()+nDst),
+      nAdd, pW, nSrc);
+    sDst.resize(nDst + sslen(szCvt));
+#else
+    sDst.resize(nDst + nAdd + 1);
+    StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()+nDst), nAdd, pW, nSrc);
+    sDst.resize(nDst + nSrc);
+#endif
+  }
+}
+template <typename T>
+inline void  ssadd(typename std::basic_string<T>& sDst, const T *pA)
+{
+  if ( pA )
+  {
+    // If the string being added is our internal string or a part of our
+    // internal string, then we must NOT do any reallocation without
+    // first copying that string to another object (since we're using a
+    // direct pointer)
+
+    if ( pA >= sDst.c_str() && pA <= sDst.c_str()+sDst.length())
+    {
+      if ( sDst.capacity() <= sDst.size()+sslen(pA) )
+        sDst.append(std::basic_string<T>(pA));
+      else
+        sDst.append(pA);
+    }
+    else
+    {
+      sDst.append(pA);
+    }
+  }
+}
+inline void  ssadd(std::wstring& sDst, const std::string& sSrc)
+{
+  if ( !sSrc.empty() )
+  {
+    int nSrc  = static_cast<int>(sSrc.size());
+    int nDst  = static_cast<int>(sDst.size());
+
+    sDst.resize(nDst + nSrc + 1);
+#ifdef SS_MBCS
+    PCWSTR szCvt = StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()+nDst),
+      nSrc, sSrc.c_str(), nSrc+1);
+    sDst.resize(nDst + sslen(szCvt));
+#else
+    StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()+nDst), nSrc, sSrc.c_str(), nSrc+1);
+    sDst.resize(nDst + nSrc);
+#endif
+  }
+}
+inline void  ssadd(std::wstring& sDst, PCSTR pA)
+{
+  int nSrc    = sslen(pA);
+
+  if ( nSrc > 0 )
+  {
+    int nDst  = static_cast<int>(sDst.size());
+
+    sDst.resize(nDst + nSrc + 1);
+#ifdef SS_MBCS
+    PCWSTR szCvt = StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()+nDst),
+      nSrc, pA, nSrc+1);
+    sDst.resize(nDst + sslen(szCvt));
+#else
+    StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()+nDst), nSrc, pA, nSrc+1);
+    sDst.resize(nDst + nSrc);
+#endif
+  }
+}
+
+// -----------------------------------------------------------------------------
+// sscmp: comparison (case sensitive, not affected by locale)
+// -----------------------------------------------------------------------------
+template<typename CT>
+inline int sscmp(const CT* pA1, const CT* pA2)
+{
+    CT f;
+    CT l;
+
+    do
+    {
+      f = *(pA1++);
+      l = *(pA2++);
+    } while ( (f) && (f == l) );
+
+    return (int)(f - l);
+}
+
+// -----------------------------------------------------------------------------
+// ssicmp: comparison (case INsensitive, not affected by locale)
+// -----------------------------------------------------------------------------
+template<typename CT>
+inline int ssicmp(const CT* pA1, const CT* pA2)
+{
+  // Using the "C" locale = "not affected by locale"
+
+  std::locale loc = std::locale::classic();
+    const std::ctype<CT>& ct = SS_USE_FACET(loc, std::ctype<CT>);
+    CT f;
+    CT l;
+
+    do
+    {
+      f = ct.tolower(*(pA1++));
+      l = ct.tolower(*(pA2++));
+    } while ( (f) && (f == l) );
+
+    return (int)(f - l);
+}
+
+// -----------------------------------------------------------------------------
+// ssupr/sslwr: Uppercase/Lowercase conversion functions
+// -----------------------------------------------------------------------------
+
+template<typename CT>
+inline void sslwr(CT* pT, size_t nLen, const std::locale& loc=std::locale())
+{
+  SS_USE_FACET(loc, std::ctype<CT>).tolower(pT, pT+nLen);
+}
+template<typename CT>
+inline void ssupr(CT* pT, size_t nLen, const std::locale& loc=std::locale())
+{
+  SS_USE_FACET(loc, std::ctype<CT>).toupper(pT, pT+nLen);
+}
+
+// -----------------------------------------------------------------------------
+// vsprintf/vswprintf or _vsnprintf/_vsnwprintf equivalents.  In standard
+// builds we can't use _vsnprintf/_vsnwsprintf because they're MS extensions.
+//
+// -----------------------------------------------------------------------------
+// Borland's headers put some ANSI "C" functions in the 'std' namespace.
+// Promote them to the global namespace so we can use them here.
+
+#if defined(__BORLANDC__)
+    using std::vsprintf;
+    using std::vswprintf;
+#endif
+
+  // GNU is supposed to have vsnprintf and vsnwprintf.  But only the newer
+  // distributions do.
+
+#if defined(__GNUC__)
+
+  inline int ssvsprintf(PSTR pA, size_t nCount, PCSTR pFmtA, va_list vl)
+  {
+    return vsnprintf(pA, nCount, pFmtA, vl);
+  }
+  inline int ssvsprintf(PWSTR pW, size_t nCount, PCWSTR pFmtW, va_list vl)
+  {
+    return vswprintf(pW, nCount, pFmtW, vl);
+  }
+
+  // Microsofties can use
+#elif defined(_MSC_VER) && !defined(SS_ANSI)
+
+  inline int  ssvsprintf(PSTR pA, size_t nCount, PCSTR pFmtA, va_list vl)
+  {
+    return _vsnprintf(pA, nCount, pFmtA, vl);
+  }
+  inline int  ssvsprintf(PWSTR pW, size_t nCount, PCWSTR pFmtW, va_list vl)
+  {
+    return _vsnwprintf(pW, nCount, pFmtW, vl);
+  }
+
+#elif defined (SS_DANGEROUS_FORMAT)  // ignore buffer size parameter if needed?
+
+  inline int ssvsprintf(PSTR pA, size_t /*nCount*/, PCSTR pFmtA, va_list vl)
+  {
+    return vsprintf(pA, pFmtA, vl);
+  }
+
+  inline int ssvsprintf(PWSTR pW, size_t nCount, PCWSTR pFmtW, va_list vl)
+  {
+    // JMO: Some distributions of the "C" have a version of vswprintf that
+        // takes 3 arguments (e.g. Microsoft, Borland, GNU).  Others have a
+        // version which takes 4 arguments (an extra "count" argument in the
+        // second position.  The best stab I can take at this so far is that if
+        // you are NOT running with MS, Borland, or GNU, then I'll assume you
+        // have the version that takes 4 arguments.
+        //
+        // I'm sure that these checks don't catch every platform correctly so if
+        // you get compiler errors on one of the lines immediately below, it's
+        // probably because your implemntation takes a different number of
+        // arguments.  You can comment out the offending line (and use the
+        // alternate version) or you can figure out what compiler flag to check
+        // and add that preprocessor check in.  Regardless, if you get an error
+        // on these lines, I'd sure like to hear from you about it.
+        //
+        // Thanks to Ronny Schulz for the SGI-specific checks here.
+
+//  #if !defined(__MWERKS__) && !defined(__SUNPRO_CC_COMPAT) && !defined(__SUNPRO_CC)
+    #if    !defined(_MSC_VER) \
+        && !defined (__BORLANDC__) \
+        && !defined(__GNUC__) \
+        && !defined(__sgi)
+
+        return vswprintf(pW, nCount, pFmtW, vl);
+
+    // suddenly with the current SGI 7.3 compiler there is no such function as
+    // vswprintf and the substitute needs explicit casts to compile
+
+    #elif defined(__sgi)
+
+        nCount;
+        return vsprintf( (char *)pW, (char *)pFmtW, vl);
+
+    #else
+
+        nCount;
+        return vswprintf(pW, pFmtW, vl);
+
+    #endif
+
+  }
+
+#endif
+
+  // GOT COMPILER PROBLEMS HERE?
+  // ---------------------------
+  // Does your compiler choke on one or more of the following 2 functions?  It
+  // probably means that you don't have have either vsnprintf or vsnwprintf in
+  // your version of the CRT.  This is understandable since neither is an ANSI
+  // "C" function.  However it still leaves you in a dilemma.  In order to make
+  // this code build, you're going to have to to use some non-length-checked
+  // formatting functions that every CRT has:  vsprintf and vswprintf.
+  //
+  // This is very dangerous.  With the proper erroneous (or malicious) code, it
+  // can lead to buffer overlows and crashing your PC.  Use at your own risk
+  // In order to use them, just #define SS_DANGEROUS_FORMAT at the top of
+  // this file.
+  //
+  // Even THEN you might not be all the way home due to some non-conforming
+  // distributions.  More on this in the comments below.
+
+  inline int  ssnprintf(PSTR pA, size_t nCount, PCSTR pFmtA, va_list vl)
+  {
+  #ifdef _MSC_VER
+      return _vsnprintf(pA, nCount, pFmtA, vl);
+  #else
+      return vsnprintf(pA, nCount, pFmtA, vl);
+  #endif
+  }
+  inline int  ssnprintf(PWSTR pW, size_t nCount, PCWSTR pFmtW, va_list vl)
+  {
+  #ifdef _MSC_VER
+      return _vsnwprintf(pW, nCount, pFmtW, vl);
+  #else
+      return vswprintf(pW, nCount, pFmtW, vl);
+  #endif
+  }
+
+
+
+
+// -----------------------------------------------------------------------------
+// ssload: Type safe, overloaded ::LoadString wrappers
+// There is no equivalent of these in non-Win32-specific builds.  However, I'm
+// thinking that with the message facet, there might eventually be one
+// -----------------------------------------------------------------------------
+#if defined (SS_WIN32) && !defined(SS_ANSI)
+  inline int ssload(HMODULE hInst, UINT uId, PSTR pBuf, int nMax)
+  {
+    return ::LoadStringA(hInst, uId, pBuf, nMax);
+  }
+  inline int ssload(HMODULE hInst, UINT uId, PWSTR pBuf, int nMax)
+  {
+    return ::LoadStringW(hInst, uId, pBuf, nMax);
+  }
+#if defined ( _MSC_VER ) && ( _MSC_VER >= 1500 )
+  inline int ssload(HMODULE hInst, UINT uId, uint16_t *pBuf, int nMax)
+  {
+    return 0;
+  }
+  inline int ssload(HMODULE hInst, UINT uId, uint32_t *pBuf, int nMax)
+  {
+    return 0;
+  }
+#endif
+#endif
+
+
+// -----------------------------------------------------------------------------
+// sscoll/ssicoll: Collation wrappers
+//    Note -- with MSVC I have reversed the arguments order here because the
+//    functions appear to return the opposite of what they should
+// -----------------------------------------------------------------------------
+#ifndef SS_NO_LOCALE
+template <typename CT>
+inline int sscoll(const CT* sz1, int nLen1, const CT* sz2, int nLen2)
+{
+  const std::collate<CT>& coll =
+    SS_USE_FACET(std::locale(), std::collate<CT>);
+
+  return coll.compare(sz2, sz2+nLen2, sz1, sz1+nLen1);
+}
+template <typename CT>
+inline int ssicoll(const CT* sz1, int nLen1, const CT* sz2, int nLen2)
+{
+  const std::locale loc;
+  const std::collate<CT>& coll = SS_USE_FACET(loc, std::collate<CT>);
+
+  // Some implementations seem to have trouble using the collate<>
+  // facet typedefs so we'll just default to basic_string and hope
+  // that's what the collate facet uses (which it generally should)
+
+//  std::collate<CT>::string_type s1(sz1);
+//  std::collate<CT>::string_type s2(sz2);
+  const std::basic_string<CT> sEmpty;
+    std::basic_string<CT> s1(sz1 ? sz1 : sEmpty.c_str());
+    std::basic_string<CT> s2(sz2 ? sz2 : sEmpty.c_str());
+
+  sslwr(const_cast<CT*>(s1.c_str()), nLen1, loc);
+  sslwr(const_cast<CT*>(s2.c_str()), nLen2, loc);
+  return coll.compare(s2.c_str(), s2.c_str()+nLen2,
+            s1.c_str(), s1.c_str()+nLen1);
+}
+#endif
+
+
+// -----------------------------------------------------------------------------
+// ssfmtmsg: FormatMessage equivalents.  Needed because I added a CString facade
+// Again -- no equivalent of these on non-Win32 builds but their might one day
+// be one if the message facet gets implemented
+// -----------------------------------------------------------------------------
+#if defined (SS_WIN32) && !defined(SS_ANSI)
+  inline DWORD ssfmtmsg(DWORD dwFlags, LPCVOID pSrc, DWORD dwMsgId,
+              DWORD dwLangId, PSTR pBuf, DWORD nSize,
+              va_list* vlArgs)
+  {
+    return FormatMessageA(dwFlags, pSrc, dwMsgId, dwLangId,
+                pBuf, nSize,vlArgs);
+  }
+  inline DWORD ssfmtmsg(DWORD dwFlags, LPCVOID pSrc, DWORD dwMsgId,
+              DWORD dwLangId, PWSTR pBuf, DWORD nSize,
+              va_list* vlArgs)
+  {
+    return FormatMessageW(dwFlags, pSrc, dwMsgId, dwLangId,
+                pBuf, nSize,vlArgs);
+  }
+#else
+#endif
+
+
+
+// FUNCTION: sscpy.  Copies up to 'nMax' characters from pSrc to pDst.
+// -----------------------------------------------------------------------------
+// FUNCTION:  sscpy
+//    inline int sscpy(PSTR pDst, PCSTR pSrc, int nMax=-1);
+//    inline int sscpy(PUSTR pDst,  PCSTR pSrc, int nMax=-1)
+//    inline int sscpy(PSTR pDst, PCWSTR pSrc, int nMax=-1);
+//    inline int sscpy(PWSTR pDst, PCWSTR pSrc, int nMax=-1);
+//    inline int sscpy(PWSTR pDst, PCSTR pSrc, int nMax=-1);
+//
+// DESCRIPTION:
+//    This function is very much (but not exactly) like strcpy.  These
+//    overloads simplify copying one C-style string into another by allowing
+//    the caller to specify two different types of strings if necessary.
+//
+//    The strings must NOT overlap
+//
+//    "Character" is expressed in terms of the destination string, not
+//    the source.  If no 'nMax' argument is supplied, then the number of
+//    characters copied will be sslen(pSrc).  A NULL terminator will
+//    also be added so pDst must actually be big enough to hold nMax+1
+//    characters.  The return value is the number of characters copied,
+//    not including the NULL terminator.
+//
+// PARAMETERS:
+//    pSrc - the string to be copied FROM.  May be a char based string, an
+//         MBCS string (in Win32 builds) or a wide string (wchar_t).
+//    pSrc - the string to be copied TO.  Also may be either MBCS or wide
+//    nMax - the maximum number of characters to be copied into szDest.  Note
+//         that this is expressed in whatever a "character" means to pDst.
+//         If pDst is a wchar_t type string than this will be the maximum
+//         number of wchar_ts that my be copied.  The pDst string must be
+//         large enough to hold least nMaxChars+1 characters.
+//         If the caller supplies no argument for nMax this is a signal to
+//         the routine to copy all the characters in pSrc, regardless of
+//         how long it is.
+//
+// RETURN VALUE: none
+// -----------------------------------------------------------------------------
+
+template<typename CT1, typename CT2>
+inline int sscpycvt(CT1* pDst, const CT2* pSrc, int nMax)
+{
+  // Note -- we assume pDst is big enough to hold pSrc.  If not, we're in
+  // big trouble.  No bounds checking.  Caveat emptor.
+
+  int nSrc = sslen(pSrc);
+
+  const CT1* szCvt = StdCodeCvt(pDst, nMax, pSrc, nSrc);
+
+  // If we're copying the same size characters, then all the "code convert"
+  // just did was basically memcpy so the #of characters copied is the same
+  // as the number requested.  I should probably specialize this function
+  // template to achieve this purpose as it is silly to do a runtime check
+  // of a fact known at compile time.  I'll get around to it.
+
+  return sslen(szCvt);
+}
+
+template<typename T>
+inline int sscpycvt(T* pDst, const T* pSrc, int nMax)
+{
+  int nCount = nMax;
+  for (; nCount > 0 && *pSrc; ++pSrc, ++pDst, --nCount)
+    std::basic_string<T>::traits_type::assign(*pDst, *pSrc);
+
+  *pDst = 0;
+  return nMax - nCount;
+}
+
+inline int sscpycvt(PWSTR pDst, PCSTR pSrc, int nMax)
+{
+  // Note -- we assume pDst is big enough to hold pSrc.  If not, we're in
+  // big trouble.  No bounds checking.  Caveat emptor.
+
+  const PWSTR szCvt = StdCodeCvt(pDst, nMax, pSrc, nMax);
+  return sslen(szCvt);
+}
+
+template<typename CT1, typename CT2>
+inline int sscpy(CT1* pDst, const CT2* pSrc, int nMax, int nLen)
+{
+  return sscpycvt(pDst, pSrc, SSMIN(nMax, nLen));
+}
+template<typename CT1, typename CT2>
+inline int sscpy(CT1* pDst, const CT2* pSrc, int nMax)
+{
+  return sscpycvt(pDst, pSrc, SSMIN(nMax, sslen(pSrc)));
+}
+template<typename CT1, typename CT2>
+inline int sscpy(CT1* pDst, const CT2* pSrc)
+{
+  return sscpycvt(pDst, pSrc, sslen(pSrc));
+}
+template<typename CT1, typename CT2>
+inline int sscpy(CT1* pDst, const std::basic_string<CT2>& sSrc, int nMax)
+{
+  return sscpycvt(pDst, sSrc.c_str(), SSMIN(nMax, (int)sSrc.length()));
+}
+template<typename CT1, typename CT2>
+inline int sscpy(CT1* pDst, const std::basic_string<CT2>& sSrc)
+{
+  return sscpycvt(pDst, sSrc.c_str(), (int)sSrc.length());
+}
+
+#ifdef SS_INC_COMDEF
+  template<typename CT1>
+  inline int sscpy(CT1* pDst, const _bstr_t& bs, int nMax)
+  {
+    return sscpycvt(pDst, static_cast<PCOLESTR>(bs),
+            SSMIN(nMax, static_cast<int>(bs.length())));
+  }
+  template<typename CT1>
+  inline int sscpy(CT1* pDst, const _bstr_t& bs)
+  {
+    return sscpy(pDst, bs, static_cast<int>(bs.length()));
+  }
+#endif
+
+
+// -----------------------------------------------------------------------------
+// Functional objects for changing case.  They also let you pass locales
+// -----------------------------------------------------------------------------
+
+#ifdef SS_NO_LOCALE
+  template<typename CT>
+  struct SSToUpper : public std::unary_function<CT, CT>
+  {
+    inline CT operator()(const CT& t) const
+    {
+      return sstoupper(t);
+    }
+  };
+  template<typename CT>
+  struct SSToLower : public std::unary_function<CT, CT>
+  {
+    inline CT operator()(const CT& t) const
+    {
+      return sstolower(t);
+    }
+  };
+#else
+  template<typename CT>
+  struct SSToUpper : public std::binary_function<CT, std::locale, CT>
+  {
+    inline CT operator()(const CT& t, const std::locale& loc) const
+    {
+      return sstoupper<CT>(t, loc);
+    }
+  };
+  template<typename CT>
+  struct SSToLower : public std::binary_function<CT, std::locale, CT>
+  {
+    inline CT operator()(const CT& t, const std::locale& loc) const
+    {
+      return sstolower<CT>(t, loc);
+    }
+  };
+#endif
+
+// This struct is used for TrimRight() and TrimLeft() function implementations.
+//template<typename CT>
+//struct NotSpace : public std::unary_function<CT, bool>
+//{
+//  const std::locale& loc;
+//  inline NotSpace(const std::locale& locArg) : loc(locArg) {}
+//  inline bool operator() (CT t) { return !std::isspace(t, loc); }
+//};
+template<typename CT>
+struct NotSpace : public std::unary_function<CT, bool>
+{
+  // DINKUMWARE BUG:
+  // Note -- using std::isspace in a COM DLL gives us access violations
+  // because it causes the dynamic addition of a function to be called
+  // when the library shuts down.  Unfortunately the list is maintained
+  // in DLL memory but the function is in static memory.  So the COM DLL
+  // goes away along with the function that was supposed to be called,
+  // and then later when the DLL CRT shuts down it unloads the list and
+  // tries to call the long-gone function.
+  // This is DinkumWare's implementation problem.  If you encounter this
+  // problem, you may replace the calls here with good old isspace() and
+  // iswspace() from the CRT unless they specify SS_ANSI
+
+#ifdef SS_NO_LOCALE
+
+  bool operator() (CT t) const { return !ssisspace(t); }
+
+#else
+  const std::locale loc;
+  NotSpace(const std::locale& locArg=std::locale()) : loc(locArg) {}
+  bool operator() (CT t) const { return !std::isspace(t, loc); }
+#endif
+};
+
+
+
+
+//      Now we can define the template (finally!)
+// =============================================================================
+// TEMPLATE: CStdStr
+//    template<typename CT> class CStdStr : public std::basic_string<CT>
+//
+// REMARKS:
+//    This template derives from basic_string<CT> and adds some MFC CString-
+//    like functionality
+//
+//    Basically, this is my attempt to make Standard C++ library strings as
+//    easy to use as the MFC CString class.
+//
+//    Note that although this is a template, it makes the assumption that the
+//    template argument (CT, the character type) is either char or wchar_t.
+// =============================================================================
+
+//#define CStdStr _SS  // avoid compiler warning 4786
+
+//    template<typename ARG> ARG& FmtArg(ARG& arg)  { return arg; }
+//    PCSTR  FmtArg(const std::string& arg)  { return arg.c_str(); }
+//    PCWSTR FmtArg(const std::wstring& arg) { return arg.c_str(); }
+
+template<typename ARG>
+struct FmtArg
+{
+    explicit FmtArg(const ARG& arg) : a_(arg) {}
+    const ARG& operator()() const { return a_; }
+    const ARG& a_;
+private:
+    FmtArg& operator=(const FmtArg&) { return *this; }
+};
+
+template<typename CT>
+class CStdStr : public std::basic_string<CT>
+{
+  // Typedefs for shorter names.  Using these names also appears to help
+  // us avoid some ambiguities that otherwise arise on some platforms
+
+  #define MYBASE std::basic_string<CT>         // my base class
+  //typedef typename std::basic_string<CT>    MYBASE;   // my base class
+  typedef CStdStr<CT>              MYTYPE;   // myself
+  typedef typename MYBASE::const_pointer    PCMYSTR; // PCSTR or PCWSTR
+  typedef typename MYBASE::pointer      PMYSTR;   // PSTR or PWSTR
+  typedef typename MYBASE::iterator      MYITER;  // my iterator type
+  typedef typename MYBASE::const_iterator    MYCITER; // you get the idea...
+  typedef typename MYBASE::reverse_iterator  MYRITER;
+  typedef typename MYBASE::size_type      MYSIZE;
+  typedef typename MYBASE::value_type      MYVAL;
+  typedef typename MYBASE::allocator_type    MYALLOC;
+
+public:
+  // shorthand conversion from PCTSTR to string resource ID
+  #define SSRES(pctstr)  LOWORD(reinterpret_cast<unsigned long>(pctstr))
+
+  bool TryLoad(const void* pT)
+  {
+    bool bLoaded = false;
+
+#if defined(SS_WIN32) && !defined(SS_ANSI)
+    if ( ( pT != NULL ) && SS_IS_INTRESOURCE(pT) )
+    {
+      UINT nId = LOWORD(reinterpret_cast<unsigned long>(pT));
+      if ( !LoadString(nId) )
+      {
+        TRACE(_T("Can't load string %u\n"), SSRES(pT));
+      }
+      bLoaded = true;
+    }
+#endif
+
+    return bLoaded;
+  }
+
+
+  // CStdStr inline constructors
+  CStdStr()
+  {
+  }
+
+  CStdStr(const MYTYPE& str) : MYBASE(SSREF(str))
+  {
+  }
+
+  CStdStr(const std::string& str)
+  {
+    ssasn(*this, SSREF(str));
+  }
+
+  CStdStr(const std::wstring& str)
+  {
+    ssasn(*this, SSREF(str));
+  }
+
+  CStdStr(PCMYSTR pT, MYSIZE n) : MYBASE(pT, n)
+  {
+  }
+
+#ifdef SS_UNSIGNED
+  CStdStr(PCUSTR pU)
+  {
+    *this = reinterpret_cast<PCSTR>(pU);
+  }
+#endif
+
+  CStdStr(PCSTR pA)
+  {
+  #ifdef SS_ANSI
+    *this = pA;
+  #else
+    if ( !TryLoad(pA) )
+      *this = pA;
+  #endif
+  }
+
+  CStdStr(PCWSTR pW)
+  {
+  #ifdef SS_ANSI
+    *this = pW;
+  #else
+    if ( !TryLoad(pW) )
+      *this = pW;
+  #endif
+  }
+
+  CStdStr(uint16_t* pW)
+  {
+  #ifdef SS_ANSI
+    *this = pW;
+  #else
+    if ( !TryLoad(pW) )
+      *this = pW;
+  #endif
+  }
+
+  CStdStr(uint32_t* pW)
+  {
+  #ifdef SS_ANSI
+    *this = pW;
+  #else
+    if ( !TryLoad(pW) )
+      *this = pW;
+  #endif
+  }
+
+  CStdStr(MYCITER first, MYCITER last)
+    : MYBASE(first, last)
+  {
+  }
+
+  CStdStr(MYSIZE nSize, MYVAL ch, const MYALLOC& al=MYALLOC())
+    : MYBASE(nSize, ch, al)
+  {
+  }
+
+  #ifdef SS_INC_COMDEF
+    CStdStr(const _bstr_t& bstr)
+    {
+      if ( bstr.length() > 0 )
+        this->append(static_cast<PCMYSTR>(bstr), bstr.length());
+    }
+  #endif
+
+  // CStdStr inline assignment operators -- the ssasn function now takes care
+  // of fixing  the MSVC assignment bug (see knowledge base article Q172398).
+  MYTYPE& operator=(const MYTYPE& str)
+  {
+    ssasn(*this, str);
+    return *this;
+  }
+
+  MYTYPE& operator=(const std::string& str)
+  {
+    ssasn(*this, str);
+    return *this;
+  }
+
+  MYTYPE& operator=(const std::wstring& str)
+  {
+    ssasn(*this, str);
+    return *this;
+  }
+
+  MYTYPE& operator=(PCSTR pA)
+  {
+    ssasn(*this, pA);
+    return *this;
+  }
+
+  MYTYPE& operator=(PCWSTR pW)
+  {
+    ssasn(*this, pW);
+    return *this;
+  }
+
+#ifdef SS_UNSIGNED
+  MYTYPE& operator=(PCUSTR pU)
+  {
+    ssasn(*this, reinterpret_cast<PCSTR>(pU));
+    return *this;
+  }
+#endif
+
+  MYTYPE& operator=(uint16_t* pA)
+  {
+    ssasn(*this, pA);
+    return *this;
+  }
+
+  MYTYPE& operator=(uint32_t* pA)
+  {
+    ssasn(*this, pA);
+    return *this;
+  }
+
+  MYTYPE& operator=(CT t)
+  {
+    Q172398(*this);
+    this->assign(1, t);
+    return *this;
+  }
+
+  #ifdef SS_INC_COMDEF
+    MYTYPE& operator=(const _bstr_t& bstr)
+    {
+      if ( bstr.length() > 0 )
+      {
+        this->assign(static_cast<PCMYSTR>(bstr), bstr.length());
+        return *this;
+      }
+      else
+      {
+        this->erase();
+        return *this;
+      }
+    }
+  #endif
+
+
+  // Overloads  also needed to fix the MSVC assignment bug (KB: Q172398)
+  //  *** Thanks to Pete The Plumber for catching this one ***
+  // They also are compiled if you have explicitly turned off refcounting
+  #if ( defined(_MSC_VER) && ( _MSC_VER < 1200 ) ) || defined(SS_NO_REFCOUNT)
+
+    MYTYPE& assign(const MYTYPE& str)
+    {
+      Q172398(*this);
+      sscpy(GetBuffer(str.size()+1), SSREF(str));
+      this->ReleaseBuffer(str.size());
+      return *this;
+    }
+
+    MYTYPE& assign(const MYTYPE& str, MYSIZE nStart, MYSIZE nChars)
+    {
+      // This overload of basic_string::assign is supposed to assign up to
+      // <nChars> or the NULL terminator, whichever comes first.  Since we
+      // are about to call a less forgiving overload (in which <nChars>
+      // must be a valid length), we must adjust the length here to a safe
+      // value.  Thanks to Ullrich Poll�hne for catching this bug
+
+      nChars    = SSMIN(nChars, str.length() - nStart);
+      MYTYPE strTemp(str.c_str()+nStart, nChars);
+      Q172398(*this);
+      this->assign(strTemp);
+      return *this;
+    }
+
+    MYTYPE& assign(const MYBASE& str)
+    {
+      ssasn(*this, str);
+      return *this;
+    }
+
+    MYTYPE& assign(const MYBASE& str, MYSIZE nStart, MYSIZE nChars)
+    {
+      // This overload of basic_string::assign is supposed to assign up to
+      // <nChars> or the NULL terminator, whichever comes first.  Since we
+      // are about to call a less forgiving overload (in which <nChars>
+      // must be a valid length), we must adjust the length here to a safe
+      // value. Thanks to Ullrich Poll�hne for catching this bug
+
+      nChars    = SSMIN(nChars, str.length() - nStart);
+
+      // Watch out for assignment to self
+
+      if ( this == &str )
+      {
+        MYTYPE strTemp(str.c_str() + nStart, nChars);
+        static_cast<MYBASE*>(this)->assign(strTemp);
+      }
+      else
+      {
+        Q172398(*this);
+        static_cast<MYBASE*>(this)->assign(str.c_str()+nStart, nChars);
+      }
+      return *this;
+    }
+
+    MYTYPE& assign(const CT* pC, MYSIZE nChars)
+    {
+      // Q172398 only fix -- erase before assigning, but not if we're
+      // assigning from our own buffer
+
+  #if defined ( _MSC_VER ) && ( _MSC_VER < 1200 )
+      if ( !this->empty() &&
+        ( pC < this->data() || pC > this->data() + this->capacity() ) )
+      {
+        this->erase();
+      }
+  #endif
+      Q172398(*this);
+      static_cast<MYBASE*>(this)->assign(pC, nChars);
+      return *this;
+    }
+
+    MYTYPE& assign(MYSIZE nChars, MYVAL val)
+    {
+      Q172398(*this);
+      static_cast<MYBASE*>(this)->assign(nChars, val);
+      return *this;
+    }
+
+    MYTYPE& assign(const CT* pT)
+    {
+      return this->assign(pT, MYBASE::traits_type::length(pT));
+    }
+
+    MYTYPE& assign(MYCITER iterFirst, MYCITER iterLast)
+    {
+  #if defined ( _MSC_VER ) && ( _MSC_VER < 1200 )
+      // Q172398 fix.  don't call erase() if we're assigning from ourself
+      if ( iterFirst < this->begin() ||
+                 iterFirst > this->begin() + this->size() )
+            {
+        this->erase()
+            }
+  #endif
+      this->replace(this->begin(), this->end(), iterFirst, iterLast);
+      return *this;
+    }
+  #endif
+
+
+  // -------------------------------------------------------------------------
+  // CStdStr inline concatenation.
+  // -------------------------------------------------------------------------
+  MYTYPE& operator+=(const MYTYPE& str)
+  {
+    ssadd(*this, str);
+    return *this;
+  }
+
+  MYTYPE& operator+=(const std::string& str)
+  {
+    ssadd(*this, str);
+    return *this;
+  }
+
+  MYTYPE& operator+=(const std::wstring& str)
+  {
+    ssadd(*this, str);
+    return *this;
+  }
+
+  MYTYPE& operator+=(PCSTR pA)
+  {
+    ssadd(*this, pA);
+    return *this;
+  }
+
+  MYTYPE& operator+=(PCWSTR pW)
+  {
+    ssadd(*this, pW);
+    return *this;
+  }
+
+  MYTYPE& operator+=(uint16_t* pW)
+  {
+    ssadd(*this, pW);
+    return *this;
+  }
+
+  MYTYPE& operator+=(uint32_t* pW)
+  {
+    ssadd(*this, pW);
+    return *this;
+  }
+
+  MYTYPE& operator+=(CT t)
+  {
+    this->append(1, t);
+    return *this;
+  }
+  #ifdef SS_INC_COMDEF  // if we have _bstr_t, define a += for it too.
+    MYTYPE& operator+=(const _bstr_t& bstr)
+    {
+      return this->operator+=(static_cast<PCMYSTR>(bstr));
+    }
+  #endif
+
+
+  // -------------------------------------------------------------------------
+  // Case changing functions
+  // -------------------------------------------------------------------------
+
+    MYTYPE& ToUpper(const std::locale& loc=std::locale())
+  {
+    // Note -- if there are any MBCS character sets in which the lowercase
+    // form a character takes up a different number of bytes than the
+    // uppercase form, this would probably not work...
+
+    std::transform(this->begin(),
+             this->end(),
+             this->begin(),
+#ifdef SS_NO_LOCALE
+             SSToUpper<CT>());
+#else
+             std::bind2nd(SSToUpper<CT>(), loc));
+#endif
+
+    // ...but if it were, this would probably work better.  Also, this way
+    // seems to be a bit faster when anything other then the "C" locale is
+    // used...
+
+//    if ( !empty() )
+//    {
+//      ssupr(this->GetBuf(), this->size(), loc);
+//      this->RelBuf();
+//    }
+
+    return *this;
+  }
+
+  MYTYPE& ToLower(const std::locale& loc=std::locale())
+  {
+    // Note -- if there are any MBCS character sets in which the lowercase
+    // form a character takes up a different number of bytes than the
+    // uppercase form, this would probably not work...
+
+    std::transform(this->begin(),
+             this->end(),
+             this->begin(),
+#ifdef SS_NO_LOCALE
+             SSToLower<CT>());
+#else
+             std::bind2nd(SSToLower<CT>(), loc));
+#endif
+
+    // ...but if it were, this would probably work better.  Also, this way
+    // seems to be a bit faster when anything other then the "C" locale is
+    // used...
+
+//    if ( !empty() )
+//    {
+//      sslwr(this->GetBuf(), this->size(), loc);
+//      this->RelBuf();
+//    }
+    return *this;
+  }
+
+
+  MYTYPE& Normalize()
+  {
+    return Trim().ToLower();
+  }
+
+
+  // -------------------------------------------------------------------------
+  // CStdStr -- Direct access to character buffer.  In the MS' implementation,
+  // the at() function that we use here also calls _Freeze() providing us some
+  // protection from multithreading problems associated with ref-counting.
+    // In VC 7 and later, of course, the ref-counting stuff is gone.
+  // -------------------------------------------------------------------------
+
+  CT* GetBuf(int nMinLen=-1)
+  {
+    if ( static_cast<int>(this->size()) < nMinLen )
+      this->resize(static_cast<MYSIZE>(nMinLen));
+
+    return this->empty() ? const_cast<CT*>(this->data()) : &(this->at(0));
+  }
+
+  CT* SetBuf(int nLen)
+  {
+    nLen = ( nLen > 0 ? nLen : 0 );
+    if ( this->capacity() < 1 && nLen == 0 )
+      this->resize(1);
+
+    this->resize(static_cast<MYSIZE>(nLen));
+    return const_cast<CT*>(this->data());
+  }
+  void RelBuf(int nNewLen=-1)
+  {
+    this->resize(static_cast<MYSIZE>(nNewLen > -1 ? nNewLen :
+                                                        sslen(this->c_str())));
+  }
+
+  void BufferRel()     { RelBuf(); }      // backwards compatability
+  CT*  Buffer()       { return GetBuf(); }  // backwards compatability
+  CT*  BufferSet(int nLen) { return SetBuf(nLen);}// backwards compatability
+
+  bool Equals(const CT* pT, bool bUseCase=false) const
+  {
+    return  0 == (bUseCase ? this->compare(pT) : ssicmp(this->c_str(), pT));
+  }
+
+  // -------------------------------------------------------------------------
+  // FUNCTION:  CStdStr::Load
+  // REMARKS:
+  //    Loads string from resource specified by nID
+  //
+  // PARAMETERS:
+  //    nID - resource Identifier.  Purely a Win32 thing in this case
+  //
+  // RETURN VALUE:
+  //    true if successful, false otherwise
+  // -------------------------------------------------------------------------
+
+#ifndef SS_ANSI
+
+  bool Load(UINT nId, HMODULE hModule=NULL)
+  {
+    bool bLoaded    = false;  // set to true of we succeed.
+
+  #ifdef _MFC_VER    // When in Rome (or MFC land)...
+
+    // If they gave a resource handle, use it.  Note - this is archaic
+    // and not really what I would recommend.  But then again, in MFC
+    // land, you ought to be using CString for resources anyway since
+    // it walks the resource chain for you.
+
+    HMODULE hModuleOld = NULL;
+
+    if ( NULL != hModule )
+    {
+      hModuleOld = AfxGetResourceHandle();
+      AfxSetResourceHandle(hModule);
+    }
+
+    // ...load the string
+
+    CString strRes;
+    bLoaded        = FALSE != strRes.LoadString(nId);
+
+    // ...and if we set the resource handle, restore it.
+
+    if ( NULL != hModuleOld )
+      AfxSetResourceHandle(hModule);
+
+    if ( bLoaded )
+      *this      = strRes;
+
+  #else // otherwise make our own hackneyed version of CString's Load
+
+    // Get the resource name and module handle
+
+    if ( NULL == hModule )
+      hModule      = GetResourceHandle();
+
+    PCTSTR szName    = MAKEINTRESOURCE((nId>>4)+1); // lifted
+    DWORD dwSize    = 0;
+
+    // No sense continuing if we can't find the resource
+
+    HRSRC hrsrc      = ::FindResource(hModule, szName, RT_STRING);
+
+    if ( NULL == hrsrc )
+    {
+      TRACE(_T("Cannot find resource %d: 0x%X"), nId, ::GetLastError());
+    }
+    else if ( 0 == (dwSize = ::SizeofResource(hModule, hrsrc) / sizeof(CT)))
+    {
+      TRACE(_T("Cant get size of resource %d 0x%X\n"),nId,GetLastError());
+    }
+    else
+    {
+      bLoaded      = 0 != ssload(hModule, nId, GetBuf(dwSize), dwSize);
+      ReleaseBuffer();
+    }
+
+  #endif  // #ifdef _MFC_VER
+
+    if ( !bLoaded )
+      TRACE(_T("String not loaded 0x%X\n"), ::GetLastError());
+
+    return bLoaded;
+  }
+
+#endif  // #ifdef SS_ANSI
+
+  // -------------------------------------------------------------------------
+  // FUNCTION:  CStdStr::Format
+  //    void _cdecl Formst(CStdStringA& PCSTR szFormat, ...)
+  //    void _cdecl Format(PCSTR szFormat);
+  //
+  // DESCRIPTION:
+  //    This function does sprintf/wsprintf style formatting on CStdStringA
+  //    objects.  It looks a lot like MFC's CString::Format.  Some people
+  //    might even call this identical.  Fortunately, these people are now
+  //    dead... heh heh.
+  //
+  // PARAMETERS:
+  //    nId - ID of string resource holding the format string
+  //    szFormat - a PCSTR holding the format specifiers
+  //    argList - a va_list holding the arguments for the format specifiers.
+  //
+  // RETURN VALUE:  None.
+  // -------------------------------------------------------------------------
+  // formatting (using wsprintf style formatting)
+
+    // If they want a Format() function that safely handles string objects
+    // without casting
+
+#ifdef SS_SAFE_FORMAT
+
+    // Question:  Joe, you wacky coder you, why do you have so many overloads
+    //      of the Format() function
+    // Answer:  One reason only - CString compatability.  In short, by making
+    //      the Format() function a template this way, I can do strong typing
+    //      and allow people to pass CStdString arguments as fillers for
+    //      "%s" format specifiers without crashing their program!  The downside
+    //      is that I need to overload on the number of arguments.   If you are
+    //      passing more arguments than I have listed below in any of my
+    //      overloads, just add another one.
+    //
+    //      Yes, yes, this is really ugly.  In essence what I am doing here is
+    //      protecting people from a bad (and incorrect) programming practice
+    //      that they should not be doing anyway.  I am protecting them from
+    //      themselves.  Why am I doing this?  Well, if you had any idea the
+    //      number of times I've been emailed by people about this
+    //      "incompatability" in my code, you wouldn't ask.
+
+  void Fmt(const CT* szFmt, ...)
+  {
+    va_list argList;
+    va_start(argList, szFmt);
+    FormatV(szFmt, argList);
+    va_end(argList);
+  }
+
+#ifndef SS_ANSI
+
+    void Format(UINT nId)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+            this->swap(strFmt);
+    }
+    template<class A1>
+    void Format(UINT nId, const A1& v)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+            Fmt(strFmt, FmtArg<A1>(v)());
+    }
+    template<class A1, class A2>
+    void Format(UINT nId, const A1& v1, const A2& v2)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+           Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)());
+    }
+    template<class A1, class A2, class A3>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(),FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(),FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+           Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)(), FmtArg<A10>(v10)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)(),FmtArg<A10>(v10)(),FmtArg<A11>(v11)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+                FmtArg<A12>(v12)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+                FmtArg<A12>(v12)(), FmtArg<A13>(v13)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13, class A14>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13, const A14& v14)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+                FmtArg<A12>(v12)(), FmtArg<A13>(v13)(),FmtArg<A14>(v14)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13, class A14, class A15>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13, const A14& v14, const A15& v15)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+                FmtArg<A12>(v12)(),FmtArg<A13>(v13)(),FmtArg<A14>(v14)(),
+                FmtArg<A15>(v15)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13, class A14, class A15, class A16>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13, const A14& v14, const A15& v15,
+                const A16& v16)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+                FmtArg<A12>(v12)(),FmtArg<A13>(v13)(),FmtArg<A14>(v14)(),
+                FmtArg<A15>(v15)(), FmtArg<A16>(v16)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13, class A14, class A15, class A16, class A17>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13, const A14& v14, const A15& v15,
+                const A16& v16, const A17& v17)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+                FmtArg<A12>(v12)(),FmtArg<A13>(v13)(),FmtArg<A14>(v14)(),
+                FmtArg<A15>(v15)(),FmtArg<A16>(v16)(),FmtArg<A17>(v17)());
+        }
+    }
+
+#endif // #ifndef SS_ANSI
+
+    // ...now the other overload of Format: the one that takes a string literal
+
+    void Format(const CT* szFmt)
+    {
+        *this = szFmt;
+    }
+    template<class A1>
+    void Format(const CT* szFmt, const A1& v)
+    {
+        Fmt(szFmt, FmtArg<A1>(v)());
+    }
+    template<class A1, class A2>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)());
+    }
+    template<class A1, class A2, class A3>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)());
+    }
+    template<class A1, class A2, class A3, class A4>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)(), FmtArg<A10>(v10)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)(),FmtArg<A10>(v10)(),FmtArg<A11>(v11)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+            FmtArg<A12>(v12)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+            FmtArg<A12>(v12)(), FmtArg<A13>(v13)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13, class A14>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13, const A14& v14)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+            FmtArg<A12>(v12)(), FmtArg<A13>(v13)(),FmtArg<A14>(v14)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13, class A14, class A15>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13, const A14& v14, const A15& v15)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+            FmtArg<A12>(v12)(),FmtArg<A13>(v13)(),FmtArg<A14>(v14)(),
+            FmtArg<A15>(v15)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13, class A14, class A15, class A16>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13, const A14& v14, const A15& v15,
+                const A16& v16)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+            FmtArg<A12>(v12)(),FmtArg<A13>(v13)(),FmtArg<A14>(v14)(),
+            FmtArg<A15>(v15)(), FmtArg<A16>(v16)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13, class A14, class A15, class A16, class A17>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13, const A14& v14, const A15& v15,
+                const A16& v16, const A17& v17)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+            FmtArg<A12>(v12)(),FmtArg<A13>(v13)(),FmtArg<A14>(v14)(),
+            FmtArg<A15>(v15)(),FmtArg<A16>(v16)(),FmtArg<A17>(v17)());
+    }
+
+#else  // #ifdef SS_SAFE_FORMAT
+
+
+#ifndef SS_ANSI
+
+  void Format(UINT nId, ...)
+  {
+    va_list argList;
+    va_start(argList, nId);
+
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+      FormatV(strFmt, argList);
+
+    va_end(argList);
+  }
+
+#endif  // #ifdef SS_ANSI
+
+  void Format(const CT* szFmt, ...)
+  {
+    va_list argList;
+    va_start(argList, szFmt);
+    FormatV(szFmt, argList);
+    va_end(argList);
+  }
+
+#endif // #ifdef SS_SAFE_FORMAT
+
+  void AppendFormat(const CT* szFmt, ...)
+  {
+    va_list argList;
+    va_start(argList, szFmt);
+    AppendFormatV(szFmt, argList);
+    va_end(argList);
+  }
+
+  #define MAX_FMT_TRIES    5   // #of times we try
+  #define FMT_BLOCK_SIZE    2048 // # of bytes to increment per try
+  #define BUFSIZE_1ST  256
+  #define BUFSIZE_2ND 512
+  #define STD_BUF_SIZE    1024
+
+  // an efficient way to add formatted characters to the string.  You may only
+  // add up to STD_BUF_SIZE characters at a time, though
+  void AppendFormatV(const CT* szFmt, va_list argList)
+  {
+    CT szBuf[STD_BUF_SIZE];
+    int nLen = ssnprintf(szBuf, STD_BUF_SIZE-1, szFmt, argList);
+
+    if ( 0 < nLen )
+      this->append(szBuf, nLen);
+  }
+
+  // -------------------------------------------------------------------------
+  // FUNCTION:  FormatV
+  //    void FormatV(PCSTR szFormat, va_list, argList);
+  //
+  // DESCRIPTION:
+  //    This function formats the string with sprintf style format-specs.
+  //    It makes a general guess at required buffer size and then tries
+  //    successively larger buffers until it finds one big enough or a
+  //    threshold (MAX_FMT_TRIES) is exceeded.
+  //
+  // PARAMETERS:
+  //    szFormat - a PCSTR holding the format of the output
+  //    argList - a Microsoft specific va_list for variable argument lists
+  //
+  // RETURN VALUE:
+  // -------------------------------------------------------------------------
+
+  // NOTE: Changed by JM to actually function under non-win32,
+  //       and to remove the upper limit on size.
+  void FormatV(const CT* szFormat, va_list argList)
+  {
+    // try and grab a sufficient buffersize
+    int nChars = FMT_BLOCK_SIZE;
+    va_list argCopy;
+
+    CT *p = reinterpret_cast<CT*>(malloc(sizeof(CT)*nChars));
+    if (!p) return;
+
+    while (1)
+    {
+      va_copy(argCopy, argList);
+
+      int nActual = ssvsprintf(p, nChars, szFormat, argCopy);
+      /* If that worked, return the string. */
+      if (nActual > -1 && nActual < nChars)
+      { /* make sure it's NULL terminated */
+        p[nActual] = '\0';
+        this->assign(p, nActual);
+        free(p);
+        va_end(argCopy);
+        return;
+      }
+      /* Else try again with more space. */
+      if (nActual > -1)        /* glibc 2.1 */
+        nChars = nActual + 1;  /* precisely what is needed */
+      else                     /* glibc 2.0 */
+        nChars *= 2;           /* twice the old size */
+
+      CT *np = reinterpret_cast<CT*>(realloc(p, sizeof(CT)*nChars));
+      if (np == NULL)
+      {
+        free(p);
+        va_end(argCopy);
+        return;   // failed :(
+      }
+      p = np;
+      va_end(argCopy);
+    }
+  }
+
+  // -------------------------------------------------------------------------
+  // CString Facade Functions:
+  //
+  // The following methods are intended to allow you to use this class as a
+  // near drop-in replacement for CString.
+  // -------------------------------------------------------------------------
+  #ifdef SS_WIN32
+    BSTR AllocSysString() const
+    {
+      ostring os;
+      ssasn(os, *this);
+      return ::SysAllocString(os.c_str());
+    }
+  #endif
+
+#ifndef SS_NO_LOCALE
+  int Collate(PCMYSTR szThat) const
+  {
+    return sscoll(this->c_str(), this->length(), szThat, sslen(szThat));
+  }
+
+  int CollateNoCase(PCMYSTR szThat) const
+  {
+    return ssicoll(this->c_str(), this->length(), szThat, sslen(szThat));
+  }
+#endif
+  int Compare(PCMYSTR szThat) const
+  {
+    return this->compare(szThat);
+  }
+
+  int CompareNoCase(PCMYSTR szThat)  const
+  {
+    return ssicmp(this->c_str(), szThat);
+  }
+
+  int Delete(int nIdx, int nCount=1)
+  {
+        if ( nIdx < 0 )
+      nIdx = 0;
+
+    if ( nIdx < this->GetLength() )
+      this->erase(static_cast<MYSIZE>(nIdx), static_cast<MYSIZE>(nCount));
+
+    return GetLength();
+  }
+
+  void Empty()
+  {
+    this->erase();
+  }
+
+  int Find(CT ch) const
+  {
+    MYSIZE nIdx  = this->find_first_of(ch);
+    return static_cast<int>(MYBASE::npos == nIdx  ? -1 : nIdx);
+  }
+
+  int Find(PCMYSTR szSub) const
+  {
+    MYSIZE nIdx  = this->find(szSub);
+    return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);
+  }
+
+  int Find(CT ch, int nStart) const
+  {
+    // CString::Find docs say add 1 to nStart when it's not zero
+    // CString::Find code doesn't do that however.  We'll stick
+    // with what the code does
+
+    MYSIZE nIdx  = this->find_first_of(ch, static_cast<MYSIZE>(nStart));
+    return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);
+  }
+
+  int Find(PCMYSTR szSub, int nStart) const
+  {
+    // CString::Find docs say add 1 to nStart when it's not zero
+    // CString::Find code doesn't do that however.  We'll stick
+    // with what the code does
+
+    MYSIZE nIdx  = this->find(szSub, static_cast<MYSIZE>(nStart));
+    return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);
+  }
+
+  int FindOneOf(PCMYSTR szCharSet) const
+  {
+    MYSIZE nIdx = this->find_first_of(szCharSet);
+    return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);
+  }
+
+#ifndef SS_ANSI
+  void FormatMessage(PCMYSTR szFormat, ...) throw(std::exception)
+  {
+    va_list argList;
+    va_start(argList, szFormat);
+    PMYSTR szTemp;
+    if ( ssfmtmsg(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER,
+             szFormat, 0, 0,
+             reinterpret_cast<PMYSTR>(&szTemp), 0, &argList) == 0 ||
+       szTemp == 0 )
+    {
+      throw std::runtime_error("out of memory");
+    }
+    *this = szTemp;
+    LocalFree(szTemp);
+    va_end(argList);
+  }
+
+  void FormatMessage(UINT nFormatId, ...) throw(std::exception)
+  {
+    MYTYPE sFormat;
+    VERIFY(sFormat.LoadString(nFormatId));
+    va_list argList;
+    va_start(argList, nFormatId);
+    PMYSTR szTemp;
+    if ( ssfmtmsg(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER,
+             sFormat, 0, 0,
+             reinterpret_cast<PMYSTR>(&szTemp), 0, &argList) == 0 ||
+      szTemp == 0)
+    {
+      throw std::runtime_error("out of memory");
+    }
+    *this = szTemp;
+    LocalFree(szTemp);
+    va_end(argList);
+  }
+#endif
+
+  // GetAllocLength -- an MSVC7 function but it costs us nothing to add it.
+
+  int GetAllocLength()
+  {
+    return static_cast<int>(this->capacity());
+  }
+
+  // -------------------------------------------------------------------------
+  // GetXXXX -- Direct access to character buffer
+  // -------------------------------------------------------------------------
+  CT GetAt(int nIdx) const
+  {
+    return this->at(static_cast<MYSIZE>(nIdx));
+  }
+
+  CT* GetBuffer(int nMinLen=-1)
+  {
+    return GetBuf(nMinLen);
+  }
+
+  CT* GetBufferSetLength(int nLen)
+  {
+    return BufferSet(nLen);
+  }
+
+  // GetLength() -- MFC docs say this is the # of BYTES but
+  // in truth it is the number of CHARACTERs (chars or wchar_ts)
+  int GetLength() const
+  {
+    return static_cast<int>(this->length());
+  }
+
+  int Insert(int nIdx, CT ch)
+  {
+    if ( static_cast<MYSIZE>(nIdx) > this->size()-1 )
+      this->append(1, ch);
+    else
+      this->insert(static_cast<MYSIZE>(nIdx), 1, ch);
+
+    return GetLength();
+  }
+  int Insert(int nIdx, PCMYSTR sz)
+  {
+    if ( static_cast<MYSIZE>(nIdx) >= this->size() )
+      this->append(sz, static_cast<MYSIZE>(sslen(sz)));
+    else
+      this->insert(static_cast<MYSIZE>(nIdx), sz);
+
+    return GetLength();
+  }
+
+  bool IsEmpty() const
+  {
+    return this->empty();
+  }
+
+  MYTYPE Left(int nCount) const
+  {
+        // Range check the count.
+
+    nCount = SSMAX(0, SSMIN(nCount, static_cast<int>(this->size())));
+    return this->substr(0, static_cast<MYSIZE>(nCount));
+  }
+
+#ifndef SS_ANSI
+  bool LoadString(UINT nId)
+  {
+    return this->Load(nId);
+  }
+#endif
+
+  void MakeLower()
+  {
+    ToLower();
+  }
+
+  void MakeReverse()
+  {
+    std::reverse(this->begin(), this->end());
+  }
+
+  void MakeUpper()
+  {
+    ToUpper();
+  }
+
+  MYTYPE Mid(int nFirst) const
+  {
+    return Mid(nFirst, this->GetLength()-nFirst);
+  }
+
+  MYTYPE Mid(int nFirst, int nCount) const
+  {
+    // CString does range checking here.  Since we're trying to emulate it,
+    // we must check too.
+
+    if ( nFirst < 0 )
+      nFirst = 0;
+    if ( nCount < 0 )
+      nCount = 0;
+
+    int nSize = static_cast<int>(this->size());
+
+    if ( nFirst + nCount > nSize )
+      nCount = nSize - nFirst;
+
+    if ( nFirst > nSize )
+      return MYTYPE();
+
+    ASSERT(nFirst >= 0);
+    ASSERT(nFirst + nCount <= nSize);
+
+    return this->substr(static_cast<MYSIZE>(nFirst),
+              static_cast<MYSIZE>(nCount));
+  }
+
+  void ReleaseBuffer(int nNewLen=-1)
+  {
+    RelBuf(nNewLen);
+  }
+
+  int Remove(CT ch)
+  {
+    MYSIZE nIdx    = 0;
+    int nRemoved  = 0;
+    while ( (nIdx=this->find_first_of(ch)) != MYBASE::npos )
+    {
+      this->erase(nIdx, 1);
+      nRemoved++;
+    }
+    return nRemoved;
+  }
+
+  int Replace(CT chOld, CT chNew)
+  {
+    int nReplaced  = 0;
+
+    for ( MYITER iter=this->begin(); iter != this->end(); iter++ )
+    {
+      if ( *iter == chOld )
+      {
+        *iter = chNew;
+        nReplaced++;
+      }
+    }
+
+    return nReplaced;
+  }
+
+  int Replace(PCMYSTR szOld, PCMYSTR szNew)
+  {
+    int nReplaced    = 0;
+    MYSIZE nIdx      = 0;
+    MYSIZE nOldLen    = sslen(szOld);
+
+    if ( 0 != nOldLen )
+    {
+      // If the replacement string is longer than the one it replaces, this
+      // string is going to have to grow in size,  Figure out how much
+      // and grow it all the way now, rather than incrementally
+
+      MYSIZE nNewLen    = sslen(szNew);
+      if ( nNewLen > nOldLen )
+      {
+        int nFound      = 0;
+        while ( nIdx < this->length() &&
+          (nIdx=this->find(szOld, nIdx)) != MYBASE::npos )
+        {
+          nFound++;
+          nIdx += nOldLen;
+        }
+        this->reserve(this->size() + nFound * (nNewLen - nOldLen));
+      }
+
+
+      static const CT ch  = CT(0);
+      PCMYSTR szRealNew  = szNew == 0 ? &ch : szNew;
+      nIdx        = 0;
+
+      while ( nIdx < this->length() &&
+        (nIdx=this->find(szOld, nIdx)) != MYBASE::npos )
+      {
+        this->replace(this->begin()+nIdx, this->begin()+nIdx+nOldLen,
+          szRealNew);
+
+        nReplaced++;
+        nIdx += nNewLen;
+      }
+    }
+
+    return nReplaced;
+  }
+
+  int ReverseFind(CT ch) const
+  {
+    MYSIZE nIdx  = this->find_last_of(ch);
+    return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);
+  }
+
+  // ReverseFind overload that's not in CString but might be useful
+  int ReverseFind(PCMYSTR szFind, MYSIZE pos=MYBASE::npos) const
+  {
+    //yuvalt - this does not compile with g++ since MYTTYPE() is different type
+    //MYSIZE nIdx  = this->rfind(0 == szFind ? MYTYPE() : szFind, pos);
+    MYSIZE nIdx  = this->rfind(0 == szFind ? "" : szFind, pos);
+    return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);
+  }
+
+  MYTYPE Right(int nCount) const
+  {
+        // Range check the count.
+
+    nCount = SSMAX(0, SSMIN(nCount, static_cast<int>(this->size())));
+    return this->substr(this->size()-static_cast<MYSIZE>(nCount));
+  }
+
+  void SetAt(int nIndex, CT ch)
+  {
+    ASSERT(this->size() > static_cast<MYSIZE>(nIndex));
+    this->at(static_cast<MYSIZE>(nIndex))    = ch;
+  }
+
+#ifndef SS_ANSI
+  BSTR SetSysString(BSTR* pbstr) const
+  {
+    ostring os;
+    ssasn(os, *this);
+    if ( !::SysReAllocStringLen(pbstr, os.c_str(), os.length()) )
+      throw std::runtime_error("out of memory");
+
+    ASSERT(*pbstr != 0);
+    return *pbstr;
+  }
+#endif
+
+  MYTYPE SpanExcluding(PCMYSTR szCharSet) const
+  {
+        MYSIZE pos = this->find_first_of(szCharSet);
+        return pos == MYBASE::npos ? *this : Left(pos);
+  }
+
+  MYTYPE SpanIncluding(PCMYSTR szCharSet) const
+  {
+        MYSIZE pos = this->find_first_not_of(szCharSet);
+        return pos == MYBASE::npos ? *this : Left(pos);
+  }
+
+#if defined SS_WIN32 && !defined(UNICODE) && !defined(SS_ANSI)
+
+  // CString's OemToAnsi and AnsiToOem functions are available only in
+  // Unicode builds.  However since we're a template we also need a
+  // runtime check of CT and a reinterpret_cast to account for the fact
+  // that CStdStringW gets instantiated even in non-Unicode builds.
+
+  void AnsiToOem()
+  {
+    if ( sizeof(CT) == sizeof(char) && !empty() )
+    {
+      ::CharToOem(reinterpret_cast<PCSTR>(this->c_str()),
+            reinterpret_cast<PSTR>(GetBuf()));
+    }
+    else
+    {
+      ASSERT(false);
+    }
+  }
+
+  void OemToAnsi()
+  {
+    if ( sizeof(CT) == sizeof(char) && !empty() )
+    {
+      ::OemToChar(reinterpret_cast<PCSTR>(this->c_str()),
+            reinterpret_cast<PSTR>(GetBuf()));
+    }
+    else
+    {
+      ASSERT(false);
+    }
+  }
+
+#endif
+
+
+  // -------------------------------------------------------------------------
+  // Trim and its variants
+  // -------------------------------------------------------------------------
+  MYTYPE& Trim()
+  {
+    return TrimLeft().TrimRight();
+  }
+
+  MYTYPE& TrimLeft()
+  {
+    this->erase(this->begin(),
+      std::find_if(this->begin(), this->end(), NotSpace<CT>()));
+
+    return *this;
+  }
+
+  MYTYPE&  TrimLeft(CT tTrim)
+  {
+    this->erase(0, this->find_first_not_of(tTrim));
+    return *this;
+  }
+
+  MYTYPE&  TrimLeft(PCMYSTR szTrimChars)
+  {
+    this->erase(0, this->find_first_not_of(szTrimChars));
+    return *this;
+  }
+
+  MYTYPE& TrimRight()
+  {
+    // NOTE:  When comparing reverse_iterators here (MYRITER), I avoid using
+    // operator!=.  This is because namespace rel_ops also has a template
+    // operator!= which conflicts with the global operator!= already defined
+    // for reverse_iterator in the header <utility>.
+    // Thanks to John James for alerting me to this.
+
+    MYRITER it = std::find_if(this->rbegin(), this->rend(), NotSpace<CT>());
+    if ( !(this->rend() == it) )
+      this->erase(this->rend() - it);
+
+    this->erase(!(it == this->rend()) ? this->find_last_of(*it) + 1 : 0);
+    return *this;
+  }
+
+  MYTYPE&  TrimRight(CT tTrim)
+  {
+    MYSIZE nIdx  = this->find_last_not_of(tTrim);
+    this->erase(MYBASE::npos == nIdx ? 0 : ++nIdx);
+    return *this;
+  }
+
+  MYTYPE&  TrimRight(PCMYSTR szTrimChars)
+  {
+    MYSIZE nIdx  = this->find_last_not_of(szTrimChars);
+    this->erase(MYBASE::npos == nIdx ? 0 : ++nIdx);
+    return *this;
+  }
+
+  void      FreeExtra()
+  {
+    MYTYPE mt;
+    this->swap(mt);
+    if ( !mt.empty() )
+      this->assign(mt.c_str(), mt.size());
+  }
+
+  // I have intentionally not implemented the following CString
+  // functions.   You cannot make them work without taking advantage
+  // of implementation specific behavior.  However if you absolutely
+  // MUST have them, uncomment out these lines for "sort-of-like"
+  // their behavior.  You're on your own.
+
+//  CT*        LockBuffer()  { return GetBuf(); }// won't really lock
+//  void      UnlockBuffer(); { }  // why have UnlockBuffer w/o LockBuffer?
+
+  // Array-indexing operators.  Required because we defined an implicit cast
+  // to operator const CT* (Thanks to Julian Selman for pointing this out)
+
+  CT& operator[](int nIdx)
+  {
+    return static_cast<MYBASE*>(this)->operator[](static_cast<MYSIZE>(nIdx));
+  }
+
+  const CT& operator[](int nIdx) const
+  {
+    return static_cast<const MYBASE*>(this)->operator[](static_cast<MYSIZE>(nIdx));
+  }
+
+  CT& operator[](unsigned int nIdx)
+  {
+    return static_cast<MYBASE*>(this)->operator[](static_cast<MYSIZE>(nIdx));
+  }
+
+  const CT& operator[](unsigned int nIdx) const
+  {
+    return static_cast<const MYBASE*>(this)->operator[](static_cast<MYSIZE>(nIdx));
+  }
+
+  CT& operator[](unsigned long nIdx)
+  {
+    return static_cast<MYBASE*>(this)->operator[](static_cast<MYSIZE>(nIdx));
+  }
+
+  const CT& operator[](unsigned long nIdx) const
+  {
+    return static_cast<const MYBASE*>(this)->operator[](static_cast<MYSIZE>(nIdx));
+  }
+
+#ifndef SS_NO_IMPLICIT_CAST
+  operator const CT*() const
+  {
+    return this->c_str();
+  }
+#endif
+
+  // IStream related functions.  Useful in IPersistStream implementations
+
+#ifdef SS_INC_COMDEF
+
+  // struct SSSHDR - useful for non Std C++ persistence schemes.
+  typedef struct SSSHDR
+  {
+    BYTE  byCtrl;
+    ULONG  nChars;
+  } SSSHDR;  // as in "Standard String Stream Header"
+
+  #define SSSO_UNICODE  0x01  // the string is a wide string
+  #define SSSO_COMPRESS  0x02  // the string is compressed
+
+  // -------------------------------------------------------------------------
+  // FUNCTION: StreamSize
+  // REMARKS:
+  //    Returns how many bytes it will take to StreamSave() this CStdString
+  //    object to an IStream.
+  // -------------------------------------------------------------------------
+  ULONG StreamSize() const
+  {
+    // Control header plus string
+    ASSERT(this->size()*sizeof(CT) < 0xffffffffUL - sizeof(SSSHDR));
+    return (this->size() * sizeof(CT)) + sizeof(SSSHDR);
+  }
+
+  // -------------------------------------------------------------------------
+  // FUNCTION: StreamSave
+  // REMARKS:
+  //    Saves this CStdString object to a COM IStream.
+  // -------------------------------------------------------------------------
+  HRESULT StreamSave(IStream* pStream) const
+  {
+    ASSERT(this->size()*sizeof(CT) < 0xffffffffUL - sizeof(SSSHDR));
+    HRESULT hr    = E_FAIL;
+    ASSERT(pStream != 0);
+    SSSHDR hdr;
+    hdr.byCtrl    = sizeof(CT) == 2 ? SSSO_UNICODE : 0;
+    hdr.nChars    = this->size();
+
+
+    if ( FAILED(hr=pStream->Write(&hdr, sizeof(SSSHDR), 0)) )
+    {
+      TRACE(_T("StreamSave: Cannot write control header, ERR=0x%X\n"),hr);
+    }
+    else if ( empty() )
+    {
+      ;    // nothing to write
+    }
+    else if ( FAILED(hr=pStream->Write(this->c_str(),
+      this->size()*sizeof(CT), 0)) )
+    {
+      TRACE(_T("StreamSave: Cannot write string to stream 0x%X\n"), hr);
+    }
+
+    return hr;
+  }
+
+
+  // -------------------------------------------------------------------------
+  // FUNCTION: StreamLoad
+  // REMARKS:
+  //    This method loads the object from an IStream.
+  // -------------------------------------------------------------------------
+  HRESULT StreamLoad(IStream* pStream)
+  {
+    ASSERT(pStream != 0);
+    SSSHDR hdr;
+    HRESULT hr      = E_FAIL;
+
+    if ( FAILED(hr=pStream->Read(&hdr, sizeof(SSSHDR), 0)) )
+    {
+      TRACE(_T("StreamLoad: Cant read control header, ERR=0x%X\n"), hr);
+    }
+    else if ( hdr.nChars > 0 )
+    {
+      ULONG nRead    = 0;
+      PMYSTR pMyBuf  = BufferSet(hdr.nChars);
+
+      // If our character size matches the character size of the string
+      // we're trying to read, then we can read it directly into our
+      // buffer. Otherwise, we have to read into an intermediate buffer
+      // and convert.
+
+      if ( (hdr.byCtrl & SSSO_UNICODE) != 0 )
+      {
+        ULONG nBytes  = hdr.nChars * sizeof(wchar_t);
+        if ( sizeof(CT) == sizeof(wchar_t) )
+        {
+          if ( FAILED(hr=pStream->Read(pMyBuf, nBytes, &nRead)) )
+            TRACE(_T("StreamLoad: Cannot read string: 0x%X\n"), hr);
+        }
+        else
+        {
+          PWSTR pBufW = reinterpret_cast<PWSTR>(_alloca((nBytes)+1));
+          if ( FAILED(hr=pStream->Read(pBufW, nBytes, &nRead)) )
+            TRACE(_T("StreamLoad: Cannot read string: 0x%X\n"), hr);
+          else
+            sscpy(pMyBuf, pBufW, hdr.nChars);
+        }
+      }
+      else
+      {
+        ULONG nBytes  = hdr.nChars * sizeof(char);
+        if ( sizeof(CT) == sizeof(char) )
+        {
+          if ( FAILED(hr=pStream->Read(pMyBuf, nBytes, &nRead)) )
+            TRACE(_T("StreamLoad: Cannot read string: 0x%X\n"), hr);
+        }
+        else
+        {
+          PSTR pBufA = reinterpret_cast<PSTR>(_alloca(nBytes));
+          if ( FAILED(hr=pStream->Read(pBufA, hdr.nChars, &nRead)) )
+            TRACE(_T("StreamLoad: Cannot read string: 0x%X\n"), hr);
+          else
+            sscpy(pMyBuf, pBufA, hdr.nChars);
+        }
+      }
+    }
+    else
+    {
+      this->erase();
+    }
+    return hr;
+  }
+#endif // #ifdef SS_INC_COMDEF
+
+#ifndef SS_ANSI
+
+  // SetResourceHandle/GetResourceHandle.  In MFC builds, these map directly
+  // to AfxSetResourceHandle and AfxGetResourceHandle.  In non-MFC builds they
+  // point to a single static HINST so that those who call the member
+  // functions that take resource IDs can provide an alternate HINST of a DLL
+  // to search.  This is not exactly the list of HMODULES that MFC provides
+  // but it's better than nothing.
+
+  #ifdef _MFC_VER
+    static void SetResourceHandle(HMODULE hNew)
+    {
+      AfxSetResourceHandle(hNew);
+    }
+    static HMODULE GetResourceHandle()
+    {
+      return AfxGetResourceHandle();
+    }
+  #else
+    static void SetResourceHandle(HMODULE hNew)
+    {
+      SSResourceHandle() = hNew;
+    }
+    static HMODULE GetResourceHandle()
+    {
+      return SSResourceHandle();
+    }
+  #endif
+
+#endif
+};
+
+// -----------------------------------------------------------------------------
+// MSVC USERS: HOW TO EXPORT CSTDSTRING FROM A DLL
+//
+// If you are using MS Visual C++ and you want to export CStdStringA and
+// CStdStringW from a DLL, then all you need to
+//
+//    1.  make sure that all components link to the same DLL version
+//      of the CRT (not the static one).
+//    2.  Uncomment the 3 lines of code below
+//    3.  #define 2 macros per the instructions in MS KnowledgeBase
+//      article Q168958.  The macros are:
+//
+//    MACRO    DEFINTION WHEN EXPORTING    DEFINITION WHEN IMPORTING
+//    -----    ------------------------    -------------------------
+//    SSDLLEXP  (nothing, just #define it)    extern
+//    SSDLLSPEC  __declspec(dllexport)      __declspec(dllimport)
+//
+//    Note that these macros must be available to ALL clients who want to
+//    link to the DLL and use the class.  If they
+//
+// A word of advice: Don't bother.
+//
+// Really, it is not necessary to export CStdString functions from a DLL.  I
+// never do.  In my projects, I do generally link to the DLL version of the
+// Standard C++ Library, but I do NOT attempt to export CStdString functions.
+// I simply include the header where it is needed and allow for the code
+// redundancy.
+//
+// That redundancy is a lot less than you think.  This class does most of its
+// work via the Standard C++ Library, particularly the base_class basic_string<>
+// member functions.  Most of the functions here are small enough to be inlined
+// anyway.  Besides, you'll find that in actual practice you use less than 1/2
+// of the code here, even in big projects and different modules will use as
+// little as 10% of it.  That means a lot less functions actually get linked
+// your binaries.  If you export this code from a DLL, it ALL gets linked in.
+//
+// I've compared the size of the binaries from exporting vs NOT exporting.  Take
+// my word for it -- exporting this code is not worth the hassle.
+//
+// -----------------------------------------------------------------------------
+//#pragma warning(disable:4231) // non-standard extension ("extern template")
+//  SSDLLEXP template class SSDLLSPEC CStdStr<char>;
+//  SSDLLEXP template class SSDLLSPEC CStdStr<wchar_t>;
+
+
+// =============================================================================
+//            END OF CStdStr INLINE FUNCTION DEFINITIONS
+// =============================================================================
+
+//  Now typedef our class names based upon this humongous template
+
+typedef CStdStr<char>    CStdStringA;  // a better std::string
+typedef CStdStr<wchar_t>  CStdStringW;  // a better std::wstring
+typedef CStdStr<uint16_t>  CStdString16;  // a 16bit char string
+typedef CStdStr<uint32_t>  CStdString32;  // a 32bit char string
+typedef CStdStr<OLECHAR>  CStdStringO;  // almost always CStdStringW
+
+// -----------------------------------------------------------------------------
+// CStdStr addition functions defined as inline
+// -----------------------------------------------------------------------------
+
+
+inline CStdStringA operator+(const CStdStringA& s1, const CStdStringA& s2)
+{
+  CStdStringA sRet(SSREF(s1));
+  sRet.append(s2);
+  return sRet;
+}
+inline CStdStringA operator+(const CStdStringA& s1, CStdStringA::value_type t)
+{
+  CStdStringA sRet(SSREF(s1));
+  sRet.append(1, t);
+  return sRet;
+}
+inline CStdStringA operator+(const CStdStringA& s1, PCSTR pA)
+{
+  CStdStringA sRet(SSREF(s1));
+  sRet.append(pA);
+  return sRet;
+}
+inline CStdStringA operator+(PCSTR pA, const CStdStringA& sA)
+{
+  CStdStringA sRet;
+  CStdStringA::size_type nObjSize = sA.size();
+  CStdStringA::size_type nLitSize =
+    static_cast<CStdStringA::size_type>(sslen(pA));
+
+  sRet.reserve(nLitSize + nObjSize);
+  sRet.assign(pA);
+  sRet.append(sA);
+  return sRet;
+}
+
+
+inline CStdStringA operator+(const CStdStringA& s1, const CStdStringW& s2)
+{
+  return s1 + CStdStringA(s2);
+}
+inline CStdStringW operator+(const CStdStringW& s1, const CStdStringW& s2)
+{
+  CStdStringW sRet(SSREF(s1));
+  sRet.append(s2);
+  return sRet;
+}
+inline CStdStringA operator+(const CStdStringA& s1, PCWSTR pW)
+{
+  return s1 + CStdStringA(pW);
+}
+
+#ifdef UNICODE
+  inline CStdStringW operator+(PCWSTR pW, const CStdStringA& sA)
+  {
+    return CStdStringW(pW) + CStdStringW(SSREF(sA));
+  }
+  inline CStdStringW operator+(PCSTR pA, const CStdStringW& sW)
+  {
+    return CStdStringW(pA) + sW;
+  }
+#else
+  inline CStdStringA operator+(PCWSTR pW, const CStdStringA& sA)
+  {
+    return CStdStringA(pW) + sA;
+  }
+  inline CStdStringA operator+(PCSTR pA, const CStdStringW& sW)
+  {
+    return pA + CStdStringA(sW);
+  }
+#endif
+
+// ...Now the wide string versions.
+inline CStdStringW operator+(const CStdStringW& s1, CStdStringW::value_type t)
+{
+  CStdStringW sRet(SSREF(s1));
+  sRet.append(1, t);
+  return sRet;
+}
+inline CStdStringW operator+(const CStdStringW& s1, PCWSTR pW)
+{
+  CStdStringW sRet(SSREF(s1));
+  sRet.append(pW);
+  return sRet;
+}
+inline CStdStringW operator+(PCWSTR pW, const CStdStringW& sW)
+{
+  CStdStringW sRet;
+  CStdStringW::size_type nObjSize = sW.size();
+  CStdStringA::size_type nLitSize =
+    static_cast<CStdStringW::size_type>(sslen(pW));
+
+  sRet.reserve(nLitSize + nObjSize);
+  sRet.assign(pW);
+  sRet.append(sW);
+  return sRet;
+}
+
+inline CStdStringW operator+(const CStdStringW& s1, const CStdStringA& s2)
+{
+  return s1 + CStdStringW(s2);
+}
+inline CStdStringW operator+(const CStdStringW& s1, PCSTR pA)
+{
+  return s1 + CStdStringW(pA);
+}
+
+
+// New-style format function is a template
+
+#ifdef SS_SAFE_FORMAT
+
+template<>
+struct FmtArg<CStdStringA>
+{
+    explicit FmtArg(const CStdStringA& arg) : a_(arg) {}
+    PCSTR operator()() const { return a_.c_str(); }
+    const CStdStringA& a_;
+private:
+    FmtArg<CStdStringA>& operator=(const FmtArg<CStdStringA>&) { return *this; }
+};
+template<>
+struct FmtArg<CStdStringW>
+{
+    explicit FmtArg(const CStdStringW& arg) : a_(arg) {}
+    PCWSTR operator()() const { return a_.c_str(); }
+    const CStdStringW& a_;
+private:
+    FmtArg<CStdStringW>& operator=(const FmtArg<CStdStringW>&) { return *this; }
+};
+
+template<>
+struct FmtArg<std::string>
+{
+    explicit FmtArg(const std::string& arg) : a_(arg) {}
+    PCSTR operator()() const { return a_.c_str(); }
+    const std::string& a_;
+private:
+    FmtArg<std::string>& operator=(const FmtArg<std::string>&) { return *this; }
+};
+template<>
+struct FmtArg<std::wstring>
+{
+    explicit FmtArg(const std::wstring& arg) : a_(arg) {}
+    PCWSTR operator()() const { return a_.c_str(); }
+    const std::wstring& a_;
+private:
+    FmtArg<std::wstring>& operator=(const FmtArg<std::wstring>&) {return *this;}
+};
+#endif // #ifdef SS_SAFEFORMAT
+
+#ifndef SS_ANSI
+  // SSResourceHandle: our MFC-like resource handle
+  inline HMODULE& SSResourceHandle()
+  {
+    static HMODULE hModuleSS  = GetModuleHandle(0);
+    return hModuleSS;
+  }
+#endif
+
+
+// In MFC builds, define some global serialization operators
+// Special operators that allow us to serialize CStdStrings to CArchives.
+// Note that we use an intermediate CString object in order to ensure that
+// we use the exact same format.
+
+#ifdef _MFC_VER
+  inline CArchive& AFXAPI operator<<(CArchive& ar, const CStdStringA& strA)
+  {
+    CString strTemp  = strA;
+    return ar << strTemp;
+  }
+  inline CArchive& AFXAPI operator<<(CArchive& ar, const CStdStringW& strW)
+  {
+    CString strTemp  = strW;
+    return ar << strTemp;
+  }
+
+  inline CArchive& AFXAPI operator>>(CArchive& ar, CStdStringA& strA)
+  {
+    CString strTemp;
+    ar >> strTemp;
+    strA = strTemp;
+    return ar;
+  }
+  inline CArchive& AFXAPI operator>>(CArchive& ar, CStdStringW& strW)
+  {
+    CString strTemp;
+    ar >> strTemp;
+    strW = strTemp;
+    return ar;
+  }
+#endif  // #ifdef _MFC_VER -- (i.e. is this MFC?)
+
+
+
+// -----------------------------------------------------------------------------
+// GLOBAL FUNCTION:  WUFormat
+//    CStdStringA WUFormat(UINT nId, ...);
+//    CStdStringA WUFormat(PCSTR szFormat, ...);
+//
+// REMARKS:
+//    This function allows the caller for format and return a CStdStringA
+//    object with a single line of code.
+// -----------------------------------------------------------------------------
+#ifdef SS_ANSI
+#else
+  inline CStdStringA WUFormatA(UINT nId, ...)
+  {
+    va_list argList;
+    va_start(argList, nId);
+
+    CStdStringA strFmt;
+    CStdStringA strOut;
+    if ( strFmt.Load(nId) )
+      strOut.FormatV(strFmt, argList);
+
+    va_end(argList);
+    return strOut;
+  }
+  inline CStdStringA WUFormatA(PCSTR szFormat, ...)
+  {
+    va_list argList;
+    va_start(argList, szFormat);
+    CStdStringA strOut;
+    strOut.FormatV(szFormat, argList);
+    va_end(argList);
+    return strOut;
+  }
+  inline CStdStringW WUFormatW(UINT nId, ...)
+  {
+    va_list argList;
+    va_start(argList, nId);
+
+    CStdStringW strFmt;
+    CStdStringW strOut;
+    if ( strFmt.Load(nId) )
+      strOut.FormatV(strFmt, argList);
+
+    va_end(argList);
+    return strOut;
+  }
+  inline CStdStringW WUFormatW(PCWSTR szwFormat, ...)
+  {
+    va_list argList;
+    va_start(argList, szwFormat);
+    CStdStringW strOut;
+    strOut.FormatV(szwFormat, argList);
+    va_end(argList);
+    return strOut;
+  }
+#endif // #ifdef SS_ANSI
+
+
+
+#if defined(SS_WIN32) && !defined (SS_ANSI)
+  // -------------------------------------------------------------------------
+  // FUNCTION: WUSysMessage
+  //   CStdStringA WUSysMessageA(DWORD dwError, DWORD dwLangId=SS_DEFLANGID);
+  //   CStdStringW WUSysMessageW(DWORD dwError, DWORD dwLangId=SS_DEFLANGID);
+  //
+  // DESCRIPTION:
+  //   This function simplifies the process of obtaining a string equivalent
+  //   of a system error code returned from GetLastError().  You simply
+  //   supply the value returned by GetLastError() to this function and the
+  //   corresponding system string is returned in the form of a CStdStringA.
+  //
+  // PARAMETERS:
+  //   dwError - a DWORD value representing the error code to be translated
+  //   dwLangId - the language id to use.  defaults to english.
+  //
+  // RETURN VALUE:
+  //   a CStdStringA equivalent of the error code.  Currently, this function
+  //   only returns either English of the system default language strings.
+  // -------------------------------------------------------------------------
+  #define SS_DEFLANGID MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT)
+  inline CStdStringA WUSysMessageA(DWORD dwError, DWORD dwLangId=SS_DEFLANGID)
+  {
+    CHAR szBuf[512];
+
+    if ( 0 != ::FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwError,
+                   dwLangId, szBuf, 511, NULL) )
+      return WUFormatA("%s (0x%X)", szBuf, dwError);
+    else
+       return WUFormatA("Unknown error (0x%X)", dwError);
+  }
+  inline CStdStringW WUSysMessageW(DWORD dwError, DWORD dwLangId=SS_DEFLANGID)
+  {
+    WCHAR szBuf[512];
+
+    if ( 0 != ::FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwError,
+                   dwLangId, szBuf, 511, NULL) )
+      return WUFormatW(L"%s (0x%X)", szBuf, dwError);
+    else
+       return WUFormatW(L"Unknown error (0x%X)", dwError);
+  }
+#endif
+
+// Define TCHAR based friendly names for some of these functions
+
+#ifdef UNICODE
+  //#define CStdString        CStdStringW
+  typedef CStdStringW        CStdString;
+  #define WUSysMessage      WUSysMessageW
+  #define WUFormat        WUFormatW
+#else
+  //#define CStdString        CStdStringA
+  typedef CStdStringA        CStdString;
+  #define WUSysMessage      WUSysMessageA
+  #define WUFormat        WUFormatA
+#endif
+
+// ...and some shorter names for the space-efficient
+
+#define WUSysMsg          WUSysMessage
+#define WUSysMsgA          WUSysMessageA
+#define WUSysMsgW          WUSysMessageW
+#define WUFmtA            WUFormatA
+#define  WUFmtW            WUFormatW
+#define WUFmt            WUFormat
+#define WULastErrMsg()        WUSysMessage(::GetLastError())
+#define WULastErrMsgA()        WUSysMessageA(::GetLastError())
+#define WULastErrMsgW()        WUSysMessageW(::GetLastError())
+
+
+// -----------------------------------------------------------------------------
+// FUNCTIONAL COMPARATORS:
+// REMARKS:
+//    These structs are derived from the std::binary_function template.  They
+//    give us functional classes (which may be used in Standard C++ Library
+//    collections and algorithms) that perform case-insensitive comparisons of
+//    CStdString objects.  This is useful for maps in which the key may be the
+//     proper string but in the wrong case.
+// -----------------------------------------------------------------------------
+#define StdStringLessNoCaseW    SSLNCW  // avoid VC compiler warning 4786
+#define StdStringEqualsNoCaseW    SSENCW
+#define StdStringLessNoCaseA    SSLNCA
+#define StdStringEqualsNoCaseA    SSENCA
+
+#ifdef UNICODE
+  #define StdStringLessNoCase    SSLNCW
+  #define StdStringEqualsNoCase  SSENCW
+#else
+  #define StdStringLessNoCase    SSLNCA
+  #define StdStringEqualsNoCase  SSENCA
+#endif
+
+struct StdStringLessNoCaseW
+  : std::binary_function<CStdStringW, CStdStringW, bool>
+{
+  inline
+  bool operator()(const CStdStringW& sLeft, const CStdStringW& sRight) const
+  { return ssicmp(sLeft.c_str(), sRight.c_str()) < 0; }
+};
+struct StdStringEqualsNoCaseW
+  : std::binary_function<CStdStringW, CStdStringW, bool>
+{
+  inline
+  bool operator()(const CStdStringW& sLeft, const CStdStringW& sRight) const
+  { return ssicmp(sLeft.c_str(), sRight.c_str()) == 0; }
+};
+struct StdStringLessNoCaseA
+  : std::binary_function<CStdStringA, CStdStringA, bool>
+{
+  inline
+  bool operator()(const CStdStringA& sLeft, const CStdStringA& sRight) const
+  { return ssicmp(sLeft.c_str(), sRight.c_str()) < 0; }
+};
+struct StdStringEqualsNoCaseA
+  : std::binary_function<CStdStringA, CStdStringA, bool>
+{
+  inline
+  bool operator()(const CStdStringA& sLeft, const CStdStringA& sRight) const
+  { return ssicmp(sLeft.c_str(), sRight.c_str()) == 0; }
+};
+
+// If we had to define our own version of TRACE above, get rid of it now
+
+#ifdef TRACE_DEFINED_HERE
+  #undef TRACE
+  #undef TRACE_DEFINED_HERE
+#endif
+
+
+// These std::swap specializations come courtesy of Mike Crusader.
+
+//namespace std
+//{
+//  inline void swap(CStdStringA& s1, CStdStringA& s2) throw()
+//  {
+//    s1.swap(s2);
+//  }
+//  template<>
+//  inline void swap(CStdStringW& s1, CStdStringW& s2) throw()
+//  {
+//    s1.swap(s2);
+//  }
+//}
+
+// Turn back on any Borland warnings we turned off.
+
+#ifdef __BORLANDC__
+    #pragma option pop  // Turn back on inline function warnings
+//  #pragma warn +inl   // Turn back on inline function warnings
+#endif
+
+#endif  // #ifndef STDSTRING_H
diff --git a/xbmc/pvrclients/mythtv/client.cpp b/xbmc/pvrclients/mythtv/client.cpp
new file mode 100644 (file)
index 0000000..d8b249f
--- /dev/null
@@ -0,0 +1,542 @@
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, 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 "MythXml.h"
+
+using namespace std;
+
+#define SEEK_POSSIBLE 0x10 // flag used to check if protocol allows seeks
+
+/* User adjustable settings are saved here.
+ * Default values are defined inside client.h
+ * and exported to the other source files.
+ */
+CStdString   g_szHostname             = DEFAULT_HOST;         ///< The Host name or IP of MythTV
+int          g_iMythXmlPort           = DEFAULT_MYTHXML_PORT; ///< The MyhtXML Port of MythTV (default is 6544)
+int          g_iPin                   = DEFAULT_PIN; ///< The Mythtv server PIN (default is 0000)
+int          g_iMythXmlConnectTimeout = DEFAULT_MYTHXML_CONNECTION_TIMEOUT; ///< The MYTHXML Connection Timeout value (default is 30 seconds)
+///* Client member variables */
+
+bool         m_recordingFirstRead;
+char         m_noSignalStreamData[ 6 + 0xffff ];
+long         m_noSignalStreamSize     = 0;
+long         m_noSignalStreamReadPos  = 0;
+bool         m_bPlayingNoSignal       = false;
+int          m_iCurrentChannel        = 1;
+ADDON_STATUS m_CurStatus              = STATUS_UNKNOWN;
+bool         g_bCreated               = false;
+int          g_iClientID              = -1;
+CStdString   g_szUserPath             = "";
+CStdString   g_szClientPath           = "";
+MythXml                 *MythXmlApi                      = NULL;
+cHelper_libXBMC_addon *XBMC           = NULL;
+cHelper_libXBMC_pvr   *PVR            = NULL;
+
+
+extern "C" {
+
+/***********************************************************
+ * Standard AddOn related public library functions
+ ***********************************************************/
+
+ADDON_STATUS Create(void* hdl, void* props)
+{
+  if (!props)
+    return STATUS_UNKNOWN;
+
+  PVR_PROPS* pvrprops = (PVR_PROPS*)props;
+
+  XBMC = new cHelper_libXBMC_addon;
+  if (!XBMC->RegisterMe(hdl))
+    return STATUS_UNKNOWN;
+
+  PVR = new cHelper_libXBMC_pvr;
+  if (!PVR->RegisterMe(hdl))
+    return STATUS_UNKNOWN;
+
+  XBMC->Log(LOG_DEBUG, "Creating MythTV PVR-Client");
+
+  m_CurStatus    = STATUS_UNKNOWN;
+  g_iClientID    = pvrprops->clientID;
+  g_szUserPath   = pvrprops->userpath;
+  g_szClientPath = pvrprops->clientpath;
+
+  /* Read setting "host" from settings.xml */
+  char * buffer;
+  buffer = (char*) malloc (1024);
+  buffer[0] = 0; /* Set the end of string */
+
+  if (XBMC->GetSetting("host", buffer))
+    g_szHostname = buffer;
+  else
+  {
+    /* If setting is unknown fallback to defaults */
+    XBMC->Log(LOG_ERROR, "Couldn't get 'host' setting, falling back to '%s' as default", DEFAULT_HOST);
+    g_szHostname = DEFAULT_HOST;
+  }
+  free (buffer);
+
+  /* Read setting "port" from settings.xml */
+  if (!XBMC->GetSetting("mythXMLPort", &g_iMythXmlPort))
+  {
+    /* If setting is unknown fallback to defaults */
+    XBMC->Log(LOG_ERROR, "Couldn't get 'mythXMLPort' setting, falling back to '%i' as default", DEFAULT_MYTHXML_PORT);
+    g_iMythXmlPort = DEFAULT_MYTHXML_PORT;
+  }
+  
+  /* Read setting "pin" from settings.xml */
+  if (!XBMC->GetSetting("mythXMLTimeout", &g_iMythXmlConnectTimeout))
+  {
+    /* If setting is unknown fallback to defaults */
+    XBMC->Log(LOG_ERROR, "Couldn't get 'mythXMLTimeout' setting, falling back to '%i' as default", DEFAULT_MYTHXML_CONNECTION_TIMEOUT);
+    g_iMythXmlConnectTimeout = DEFAULT_MYTHXML_CONNECTION_TIMEOUT;
+  } else {
+       // we need to multiply by 1000 the value as the settings file is in seconds
+    g_iMythXmlConnectTimeout *= 1000;
+  }
+  
+  /* Read setting "pin" from settings.xml */
+  if (!XBMC->GetSetting("pin", &g_iPin))
+  {
+    /* If setting is unknown fallback to defaults */
+    XBMC->Log(LOG_ERROR, "Couldn't get 'pin' setting, falling back to '%i' as default", DEFAULT_PIN);
+    g_iPin = DEFAULT_PIN;
+  }
+
+  MythXmlApi = new MythXml();
+  if (!MythXmlApi->open(g_szHostname, g_iMythXmlPort, "", "", g_iPin, g_iMythXmlConnectTimeout))
+  {
+       m_CurStatus = STATUS_LOST_CONNECTION;
+    return m_CurStatus;
+  }
+
+  m_CurStatus = STATUS_OK;
+
+  g_bCreated = true;
+  return m_CurStatus;
+}
+
+void Destroy()
+{
+  if (g_bCreated)
+  {
+         delete MythXmlApi;
+         MythXmlApi = NULL;
+    g_bCreated = false;
+  }
+  m_CurStatus = STATUS_UNKNOWN;
+}
+
+ADDON_STATUS GetStatus()
+{
+  return m_CurStatus;
+}
+
+bool HasSettings()
+{
+  return true;
+}
+
+unsigned int GetSettings(StructSetting ***sSet)
+{
+  return 0;
+}
+
+ADDON_STATUS SetSetting(const char *settingName, const void *settingValue)
+{
+  string str = settingName;
+  if (str == "host")
+  {
+    string tmp_sHostname;
+    XBMC->Log(LOG_INFO, "Changed Setting 'host' from %s to %s", g_szHostname.c_str(), (const char*) settingValue);
+    tmp_sHostname = g_szHostname;
+    g_szHostname = (const char*) settingValue;
+    if (tmp_sHostname != g_szHostname)
+      return STATUS_NEED_RESTART;
+  }
+  else if (str == "mythXMLPort")
+  {
+    XBMC->Log(LOG_INFO, "Changed Setting 'port' from %u to %u", g_iMythXmlPort, *(int*) settingValue);
+    if (g_iMythXmlPort != *(int*) settingValue)
+    {
+         g_iMythXmlPort = *(int*) settingValue;
+      return STATUS_NEED_RESTART;
+    }
+  }
+  else if (str == "mythXMLTimeout")
+  {
+    XBMC->Log(LOG_INFO, "Changed Setting 'mythXMLTimeout' from %u to %u", g_iMythXmlConnectTimeout, *(int*) settingValue);
+    if (g_iMythXmlConnectTimeout / 1000  != *(int*) settingValue)
+    {
+         g_iMythXmlConnectTimeout = *(int*) settingValue;
+         g_iMythXmlConnectTimeout *= 1000;
+      return STATUS_NEED_RESTART;
+    }
+  }
+  else if (str == "pin")
+  {
+    XBMC->Log(LOG_INFO, "Changed Setting 'pin' from %u to %u", g_iPin, *(int*) settingValue);
+    if (g_iPin != *(int*) settingValue)
+    {
+         g_iPin = *(int*) settingValue;
+      return STATUS_NEED_RESTART;
+    }
+  }
+  return STATUS_OK;
+}
+
+void Stop()
+{
+  return;
+}
+
+void FreeSettings()
+{
+  return;
+}
+
+
+/***********************************************************
+ * PVR Client AddOn specific public library functions
+ ***********************************************************/
+
+PVR_ERROR GetProperties(PVR_SERVERPROPS* props)
+{
+  props->SupportChannelLogo        = false;
+  props->SupportTimeShift          = false;
+  props->SupportEPG                = true;
+  props->SupportRecordings         = false;
+  props->SupportTimers             = false;
+  props->SupportTV                 = false;
+  props->SupportRadio              = false;
+  props->SupportChannelSettings    = false;
+  props->SupportDirector           = false;
+  props->SupportBouquets           = false;
+  props->HandleInputStream         = false;
+  props->HandleDemuxing            = false;
+  props->SupportChannelScan        = false;
+
+  return PVR_ERROR_NO_ERROR;
+}
+
+PVR_ERROR GetStreamProperties(PVR_STREAMPROPS* props)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+const char * GetBackendName()
+{
+  return "";
+}
+
+const char * GetBackendVersion()
+{
+  return "";
+}
+
+const char * GetConnectionString()
+{
+  return "";
+}
+
+PVR_ERROR GetDriveSpace(long long *total, long long *used)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+PVR_ERROR GetBackendTime(time_t *localTime, int *gmtOffset)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+PVR_ERROR DialogChannelScan()
+{
+  return PVR_ERROR_NOT_POSSIBLE;
+}
+
+PVR_ERROR MenuHook(const PVR_MENUHOOK &menuhook)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+/*******************************************/
+/** PVR EPG Functions                     **/
+
+PVR_ERROR RequestEPGForChannel(PVRHANDLE handle, const PVR_CHANNEL &channel, time_t start, time_t end)
+{
+       if (MythXmlApi == NULL)
+           return PVR_ERROR_SERVER_ERROR;
+
+       return MythXmlApi->requestEPGForChannel(handle, channel, start, end);
+}
+
+
+/*******************************************/
+/** PVR Bouquets Functions                **/
+
+int GetNumBouquets()
+{
+  return 0;
+}
+
+PVR_ERROR RequestBouquetsList(PVRHANDLE handle, int radio)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+
+/*******************************************/
+/** PVR Channel Functions                 **/
+
+int GetNumChannels()
+{
+       if (MythXmlApi == NULL)
+               return PVR_ERROR_SERVER_ERROR;
+
+       return MythXmlApi->getNumChannels();
+}
+
+PVR_ERROR RequestChannelList(PVRHANDLE handle, int radio)
+{
+       if (MythXmlApi == NULL)
+                       return PVR_ERROR_SERVER_ERROR;
+
+       return MythXmlApi->requestChannelList(handle, radio);
+}
+
+PVR_ERROR DeleteChannel(unsigned int number)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+PVR_ERROR RenameChannel(unsigned int number, const char *newname)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+PVR_ERROR MoveChannel(unsigned int number, unsigned int newnumber)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+PVR_ERROR DialogChannelSettings(const PVR_CHANNEL &channelinfo)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+PVR_ERROR DialogAddChannel(const PVR_CHANNEL &channelinfo)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+
+/*******************************************/
+/** PVR Recording Functions               **/
+
+int GetNumRecordings(void)
+{
+  return 0;
+}
+
+PVR_ERROR RequestRecordingsList(PVRHANDLE handle)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+PVR_ERROR DeleteRecording(const PVR_RECORDINGINFO &recinfo)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+PVR_ERROR RenameRecording(const PVR_RECORDINGINFO &recinfo, const char *newname)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+
+/*******************************************/
+/** PVR Recording cut marks Functions     **/
+
+bool HaveCutmarks()
+{
+  return false;
+}
+
+PVR_ERROR RequestCutMarksList(PVRHANDLE handle)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+PVR_ERROR AddCutMark(const PVR_CUT_MARK &cutmark)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+PVR_ERROR DeleteCutMark(const PVR_CUT_MARK &cutmark)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+PVR_ERROR StartCut()
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+
+/*******************************************/
+/** PVR Timer Functions                   **/
+
+int GetNumTimers(void)
+{
+  return 0;
+}
+
+PVR_ERROR RequestTimerList(PVRHANDLE handle)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+PVR_ERROR AddTimer(const PVR_TIMERINFO &timerinfo)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+PVR_ERROR DeleteTimer(const PVR_TIMERINFO &timerinfo, bool force)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+PVR_ERROR RenameTimer(const PVR_TIMERINFO &timerinfo, const char *newname)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+PVR_ERROR UpdateTimer(const PVR_TIMERINFO &timerinfo)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+
+/*******************************************/
+/** PVR Live Stream Functions             **/
+
+bool OpenLiveStream(const PVR_CHANNEL &channelinfo)
+{
+  return false;
+}
+
+void CloseLiveStream()
+{
+  return;
+}
+
+int ReadLiveStream(unsigned char* buf, int buf_size)
+{
+       return -1;
+}
+
+int GetCurrentClientChannel()
+{
+  return m_iCurrentChannel;
+}
+
+bool SwitchChannel(const PVR_CHANNEL &channelinfo)
+{
+  return false;
+}
+
+PVR_ERROR SignalQuality(PVR_SIGNALQUALITY &qualityinfo)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+
+/*******************************************/
+/** PVR Secondary Stream Functions        **/
+
+bool SwapLiveTVSecondaryStream()
+{
+  return false;
+}
+
+bool OpenSecondaryStream(const PVR_CHANNEL &channelinfo)
+{
+  return false;
+}
+
+void CloseSecondaryStream()
+{
+  return;
+}
+
+int ReadSecondaryStream(unsigned char* buf, int buf_size)
+{
+  return 0;
+}
+
+
+/*******************************************/
+/** PVR Recording Stream Functions        **/
+
+bool OpenRecordedStream(const PVR_RECORDINGINFO &recinfo)
+{
+  return false;
+}
+
+void CloseRecordedStream(void)
+{
+  return;
+}
+
+int ReadRecordedStream(unsigned char* buf, int buf_size)
+{
+  return 0;
+}
+
+long long SeekRecordedStream(long long pos, int whence)
+{
+       return -1;
+}
+
+long long PositionRecordedStream(void)
+{
+  return -1;
+}
+
+long long LengthRecordedStream(void)
+{
+       return 0;
+}
+
+
+/** UNUSED API FUNCTIONS */
+DemuxPacket* DemuxRead() { return NULL; }
+void DemuxAbort() {}
+void DemuxReset() {}
+void DemuxFlush() {}
+long long SeekLiveStream(long long pos, int whence) { return -1; }
+long long PositionLiveStream(void) { return -1; }
+long long LengthLiveStream(void) { return -1; }
+const char * GetLiveStreamURL(const PVR_CHANNEL &channelinfo) { return ""; }
+
+} //end extern "C"
diff --git a/xbmc/pvrclients/mythtv/client.h b/xbmc/pvrclients/mythtv/client.h
new file mode 100644 (file)
index 0000000..dd2f610
--- /dev/null
@@ -0,0 +1,48 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef CLIENT_H
+#define CLIENT_H
+
+#include "StdString.h"
+#include "../../../addons/library.xbmc.addon/libXBMC_addon.h"
+#include "../../../addons/library.xbmc.pvr/libXBMC_pvr.h"
+
+#define DEFAULT_HOST          "127.0.0.1"
+#define DEFAULT_MYTHXML_PORT  6544
+#define DEFAULT_PIN           0000
+#define DEFAULT_MYTHXML_CONNECTION_TIMEOUT 30000
+
+extern bool         g_bCreated;           ///< Shows that the Create function was successfully called
+extern int          g_iClientID;          ///< The PVR client ID used by XBMC for this driver
+extern CStdString   g_szUserPath;         ///< The Path to the user directory inside user profile
+extern CStdString   g_szClientPath;       ///< The Path where this driver is located
+
+/* Client Settings */
+extern CStdString   g_szHostname;         ///< The Host name or IP of the mythtv server
+extern int          g_iMythXmlPort;       ///< The MYTHXML Port (default is 6544)
+extern int          g_iPin;               ///< The Mythtv server PIN (default is 0000)
+
+extern cHelper_libXBMC_addon *XBMC;
+extern cHelper_libXBMC_pvr   *PVR;
+
+#endif /* CLIENT_H */
diff --git a/xbmc/pvrclients/mythtv/libmythxml/GetChannelListCommand.h b/xbmc/pvrclients/mythtv/libmythxml/GetChannelListCommand.h
new file mode 100644 (file)
index 0000000..541b26a
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * GetChannelListCommand.h
+ *
+ *  Created on: Oct 7, 2010
+ *      Author: tafypz
+ */
+
+#ifndef XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETCHANNELLISTCOMMAND_H_
+#define XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETCHANNELLISTCOMMAND_H_
+
+#include "MythXmlCommand.h"
+
+class GetChannelListCommand: public MythXmlCommand {
+
+public:
+       GetChannelListCommand() {};
+       virtual ~GetChannelListCommand() {};
+protected:
+       virtual const CStdString& getCommand() const {
+               static CStdString result = "GetProgramGuide";
+               return result;
+       };
+};
+
+#endif /* XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETCHANNELLISTCOMMAND_H_ */
diff --git a/xbmc/pvrclients/mythtv/libmythxml/GetChannelListParameters.h b/xbmc/pvrclients/mythtv/libmythxml/GetChannelListParameters.h
new file mode 100644 (file)
index 0000000..6627ed0
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * GetChannelListParameters.h
+ *
+ *  Created on: Oct 8, 2010
+ *      Author: tafypz
+ */
+
+#ifndef XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETCHANNELLISTPARAMETERS_H_
+#define XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETCHANNELLISTPARAMETERS_H_
+
+#include "GetProgramGuideParameters.h"
+
+class GetChannelListParameters: public GetProgramGuideParameters {
+public:
+       GetChannelListParameters() : GetProgramGuideParameters(false) {};
+       virtual ~GetChannelListParameters(){};
+};
+
+#endif /* XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETCHANNELLISTPARAMETERS_H_ */
diff --git a/xbmc/pvrclients/mythtv/libmythxml/GetChannelListResult.cpp b/xbmc/pvrclients/mythtv/libmythxml/GetChannelListResult.cpp
new file mode 100644 (file)
index 0000000..9504c00
--- /dev/null
@@ -0,0 +1,53 @@
+#include "GetChannelListResult.h"
+#include <stdlib.h>
+
+#include "tinyXML/tinyxml.h"
+#include "utils/log.h"
+
+GetChannelListResult::GetChannelListResult() {
+}
+
+GetChannelListResult::~GetChannelListResult() {
+}
+
+void GetChannelListResult::parseData(const CStdString& xmlData) {
+       TiXmlDocument xml;
+       xml.Parse(xmlData.c_str(), 0, TIXML_ENCODING_LEGACY);
+
+       TiXmlElement* rootXmlNode = xml.RootElement();
+
+       if (!rootXmlNode) {
+               errors_.push_back(" No root node parsed");
+               CLog::Log(LOGDEBUG, "MythXML GetChannelListResult - No root node parsed");
+           return;
+       }
+
+       TiXmlElement* programGuideResponseNode = NULL;
+       CStdString strValue = rootXmlNode->Value();
+       if (strValue.Find("GetProgramGuideResponse") >= 0 ) {
+               programGuideResponseNode = rootXmlNode;
+       }
+       else if (strValue.Find("detail") >= 0 ) {
+               // process the error.
+               TiXmlElement* errorCodeXmlNode = rootXmlNode->FirstChildElement("errorCode");
+               TiXmlElement* errorDescXmlNode = rootXmlNode->FirstChildElement("errorDescription");
+               CStdString error;
+               error.Format("ErrorCode [%i] - %s", errorCodeXmlNode->GetText(), errorDescXmlNode->GetText());
+               errors_.push_back(error);
+               return;
+        }
+       else
+               return;
+
+       TiXmlElement* programGuideNode = programGuideResponseNode->FirstChildElement("ProgramGuide");
+       TiXmlElement* channelsNode = programGuideNode->FirstChildElement("Channels");
+       TiXmlElement* child = NULL;
+       for( child = channelsNode->FirstChildElement("Channel"); child; child = child->NextSiblingElement("Channel")){
+         SChannel channel;
+      channel.id = atoi(child->Attribute("chanId"));
+         channel.name = child->Attribute("channelName");
+         channel.callsign = child->Attribute("callSign");
+         channel.number = child->Attribute("chanNum");
+         channels_.push_back(channel);
+       }
+}
diff --git a/xbmc/pvrclients/mythtv/libmythxml/GetChannelListResult.h b/xbmc/pvrclients/mythtv/libmythxml/GetChannelListResult.h
new file mode 100644 (file)
index 0000000..daf2988
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETCHANNELLISTRESULT_H
+#define XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETCHANNELLISTRESULT_H
+
+#include "MythXmlCommandResult.h"
+#include "SChannel.h"
+
+class GetChannelListResult: public MythXmlCommandResult {
+public:
+  GetChannelListResult();
+  virtual ~GetChannelListResult();
+  virtual void parseData(const CStdString& xmlData);
+  inline const vector<SChannel>& getChannels() {return channels_;};
+private:
+  vector<SChannel> channels_;
+};
+
+#endif // XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETCHANNELLISTRESULT_H
diff --git a/xbmc/pvrclients/mythtv/libmythxml/GetNumChannelsCommand.h b/xbmc/pvrclients/mythtv/libmythxml/GetNumChannelsCommand.h
new file mode 100644 (file)
index 0000000..cede160
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * GetNumChannelsCommand.h
+ *
+ *  Created on: Oct 7, 2010
+ *      Author: tafypz
+ */
+
+#ifndef XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETNUMCHANNELSCOMMAND_H_
+#define XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETNUMCHANNELSCOMMAND_H_
+
+#include "MythXmlCommand.h"
+
+class GetNumChannelsCommand: public MythXmlCommand {
+public:
+       GetNumChannelsCommand(){};
+       virtual ~GetNumChannelsCommand(){};
+protected:
+       virtual const CStdString& getCommand() const{
+               static CStdString result = "GetProgramGuide";
+               return result;
+       }
+};
+
+#endif /* XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETNUMCHANNELSCOMMAND_H_ */
diff --git a/xbmc/pvrclients/mythtv/libmythxml/GetNumChannelsParameters.h b/xbmc/pvrclients/mythtv/libmythxml/GetNumChannelsParameters.h
new file mode 100644 (file)
index 0000000..fad18da
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * GetNumChannelsParameters.h
+ *
+ *  Created on: Oct 8, 2010
+ *      Author: tafypz
+ */
+
+#ifndef XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETNUMCHANNELSPARAMETERS_H_
+#define XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETNUMCHANNELSPARAMETERS_H_
+
+#include "GetProgramGuideParameters.h"
+
+class GetNumChannelsParameters: public GetProgramGuideParameters {
+public:
+       GetNumChannelsParameters(): GetProgramGuideParameters(false) {};
+       virtual ~GetNumChannelsParameters() {};
+};
+
+#endif /* XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETNUMCHANNELSPARAMETERS_H_ */
diff --git a/xbmc/pvrclients/mythtv/libmythxml/GetNumChannelsResult.cpp b/xbmc/pvrclients/mythtv/libmythxml/GetNumChannelsResult.cpp
new file mode 100644 (file)
index 0000000..79efc8b
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * GetNumChannelsResult.cpp
+ *
+ *  Created on: Oct 7, 2010
+ *      Author: tafypz
+ */
+
+#include "GetNumChannelsResult.h"
+#include <stdlib.h>
+
+#include "tinyXML/tinyxml.h"
+#include "utils/log.h"
+
+GetNumChannelsResult::GetNumChannelsResult() {
+       numberOfChannels_ = 0;
+}
+
+GetNumChannelsResult::~GetNumChannelsResult() {
+}
+
+void GetNumChannelsResult::parseData(const CStdString& xmlData) {
+       TiXmlDocument xml;
+       xml.Parse(xmlData.c_str(), 0, TIXML_ENCODING_LEGACY);
+
+       TiXmlElement* rootXmlNode = xml.RootElement();
+
+       if (!rootXmlNode) {
+               errors_.push_back(" No root node parsed");
+               CLog::Log(LOGDEBUG, "MythXML GetNumChannelsResult - No root node parsed");
+           return;
+       }
+
+       TiXmlElement* programGuideResponseNode = NULL;
+       CStdString strValue = rootXmlNode->Value();
+       if (strValue.Find("GetProgramGuideResponse") >= 0 ) {
+               programGuideResponseNode = rootXmlNode;
+       }
+       else if (strValue.Find("detail") >= 0 ) {
+               // process the error.
+               TiXmlElement* errorCodeXmlNode = rootXmlNode->FirstChildElement("errorCode");
+               TiXmlElement* errorDescXmlNode = rootXmlNode->FirstChildElement("errorDescription");
+               CStdString error;
+               error.Format("ErrorCode [%i] - %s", errorCodeXmlNode->GetText(), errorDescXmlNode->GetText());
+               errors_.push_back(error);
+               return;
+        }
+       else
+               return;
+
+       TiXmlElement* numOfChannelsXmlNode = programGuideResponseNode->FirstChildElement("NumOfChannels");
+       CStdString val = numOfChannelsXmlNode->GetText();
+       int numberOfChannels = atoi(val.c_str());
+       numberOfChannels_ = numberOfChannels;
+}
diff --git a/xbmc/pvrclients/mythtv/libmythxml/GetNumChannelsResult.h b/xbmc/pvrclients/mythtv/libmythxml/GetNumChannelsResult.h
new file mode 100644 (file)
index 0000000..b39919b
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * GetNumChannelsResult.h
+ *
+ *  Created on: Oct 7, 2010
+ *      Author: tafypz
+ */
+
+#ifndef XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETNUMCHANNELSRESULT_H_
+#define XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETNUMCHANNELSRESULT_H_
+
+#include "MythXmlCommandResult.h"
+
+class GetNumChannelsResult: public MythXmlCommandResult {
+public:
+       GetNumChannelsResult();
+       virtual ~GetNumChannelsResult();
+       virtual void parseData(const CStdString& xmlData);
+       inline int getNumberOfChannels() const {return numberOfChannels_;};
+private:
+       int numberOfChannels_;
+};
+
+#endif /* XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETNUMCHANNELSRESULT_H_ */
diff --git a/xbmc/pvrclients/mythtv/libmythxml/GetProgramGuideCommand.h b/xbmc/pvrclients/mythtv/libmythxml/GetProgramGuideCommand.h
new file mode 100644 (file)
index 0000000..4daf3bd
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETPROGRAMGUIDECOMMAND_H
+#define XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETPROGRAMGUIDECOMMAND_H
+// 
+#include "MythXmlCommand.h"
+
+class GetProgramGuideCommand: public MythXmlCommand {
+public:
+       GetProgramGuideCommand() {};
+       virtual ~GetProgramGuideCommand() {};
+protected:
+       virtual const CStdString& getCommand() const {
+               static CStdString result = "GetProgramGuide";
+               return result;
+       };
+};
+
+
+#endif // XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETPROGRAMGUIDECOMMAND_H
diff --git a/xbmc/pvrclients/mythtv/libmythxml/GetProgramGuideParameters.cpp b/xbmc/pvrclients/mythtv/libmythxml/GetProgramGuideParameters.cpp
new file mode 100644 (file)
index 0000000..4cab67f
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * GetProgramGuideParameters.cpp
+ *
+ *  Created on: Oct 8, 2010
+ *      Author: tafypz
+ */
+
+#include "GetProgramGuideParameters.h"
+#include "utils/TimeUtils.h"
+
+GetProgramGuideParameters::GetProgramGuideParameters(bool retrieveDetails) : MythXmlCommandParameters(true){
+       CDateTime now = CTimeUtils::GetLocalTime(time(NULL));
+       channelid_ = -1;
+       starttime_ = now;
+       endtime_ = now;
+       retrieveDetails_ = retrieveDetails_;
+}
+
+GetProgramGuideParameters::GetProgramGuideParameters(int channelid, CDateTime starttime, CDateTime endtime, bool retrieveDetails) : MythXmlCommandParameters(true){
+       channelid_ = channelid;
+       retrieveDetails_ = retrieveDetails;
+       starttime_ = starttime;
+       endtime_ = endtime;
+}
+
+GetProgramGuideParameters::~GetProgramGuideParameters() {
+}
+
+CStdString GetProgramGuideParameters::createParameterString() const{
+       CStdString result = "?StartTime=%s&EndTime=%s&NumOfChannels=%i";
+       CStdString start = MythXmlCommandParameters::convertTimeToString(starttime_);
+       CStdString end = MythXmlCommandParameters::convertTimeToString(endtime_);
+       int numChannels = 1;
+       if(channelid_ == -1)
+               numChannels = -1;
+       result.Format(result.c_str(), start.c_str(), end.c_str(),numChannels);
+       if(numChannels == 1){
+               CStdString chanid = "&StartChanId=%i";
+               chanid.Format(chanid.c_str(), channelid_);
+               result += chanid;
+       }
+       return result;
+};
diff --git a/xbmc/pvrclients/mythtv/libmythxml/GetProgramGuideParameters.h b/xbmc/pvrclients/mythtv/libmythxml/GetProgramGuideParameters.h
new file mode 100644 (file)
index 0000000..cb8cdfc
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * GetProgramGuideParameters.h
+ *
+ *  Created on: Oct 8, 2010
+ *      Author: tafypz
+ */
+
+#ifndef XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETPROGRAMGUIDEPARAMETERS_H_
+#define XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETPROGRAMGUIDEPARAMETERS_H_
+
+#include "DateTime.h"
+
+#include "MythXmlCommandParameters.h"
+
+class GetProgramGuideParameters: public MythXmlCommandParameters {
+public:
+       GetProgramGuideParameters(int channelid, CDateTime starttime, CDateTime endtime, bool retrieveDetails);
+       virtual ~GetProgramGuideParameters();
+
+       virtual CStdString createParameterString() const;
+       inline const CDateTime& get_starttime() const {return starttime_;};
+       inline const CDateTime& get_endtime() const {return endtime_;};
+       inline void set_channelid(int channelid) {channelid_ = channelid;};
+       inline int get_channelid() const { return channelid_;};
+       inline void set_retrievedetailsflag(bool retrievedetails) {retrieveDetails_ = retrievedetails;};
+       inline bool get_retrievedetailsflag() const {return retrieveDetails_;};
+
+protected:
+       GetProgramGuideParameters(bool retrieveDetails);
+
+private:
+       CDateTime starttime_;
+       CDateTime endtime_;
+       int channelid_;
+       bool retrieveDetails_;
+};
+
+#endif /* XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETPROGRAMGUIDEPARAMETERS_H_ */
diff --git a/xbmc/pvrclients/mythtv/libmythxml/GetProgramGuideResult.cpp b/xbmc/pvrclients/mythtv/libmythxml/GetProgramGuideResult.cpp
new file mode 100644 (file)
index 0000000..d7545eb
--- /dev/null
@@ -0,0 +1,331 @@
+#include "GetProgramGuideResult.h"
+#include <stdlib.h>
+
+#include "tinyXML/tinyxml.h"
+#include "utils/log.h"
+/*
+static const SContentType g_content_group[] =
+{ { 0x10, "Movie/Drama" }
+, { 0x20, "News/Current Affairs" }
+, { 0x30, "Show/Game show" }
+, { 0x40, "Sports" }
+, { 0x50, "Children's/Youth" }
+, { 0x60, "Music/Ballet/Dance" }
+, { 0x70, "Arts/Culture (without music)" }
+, { 0x80, "Social/Political issues/Economics" }
+, { 0x90, "Childrens/Youth Education/Science/Factual" }
+, { 0xa0, "Leisure hobbies" }
+, { 0xb0, "Misc" }
+, { 0xf0, "Unknown" }
+};
+
+static const SContentType g_content_type[] =
+{
+// movie/drama
+  { 0x11, "Detective/Thriller" }
+, { 0x12, "Adventure/Western/War" }
+, { 0x13, "Science Fiction/Fantasy/Horror" }
+, { 0x14, "Comedy" }
+, { 0x15, "Soap/Melodrama/Folkloric" }
+, { 0x16, "Romance" }
+, { 0x17, "Serious/ClassicalReligion/Historical" }
+, { 0x18, "Adult Movie/Drama" }
+
+// news/current affairs
+, { 0x21, "News/Weather Report" }
+, { 0x22, "Magazine" }
+, { 0x23, "Documentary" }
+, { 0x24, "Discussion/Interview/Debate" }
+
+// show/game show
+, { 0x31, "Game show/Quiz/Contest" }
+, { 0x32, "Variety" }
+, { 0x33, "Talk" }
+
+// sports
+, { 0x41, "Special Event (Olympics/World cup/...)" }
+, { 0x42, "Magazine" }
+, { 0x43, "Football/Soccer" }
+, { 0x44, "Tennis/Squash" }
+, { 0x45, "Team sports (excluding football)" }
+, { 0x46, "Athletics" }
+, { 0x47, "Motor Sport" }
+, { 0x48, "Water Sport" }
+, { 0x49, "Winter Sports" }
+, { 0x4a, "Equestrian" }
+, { 0x4b, "Martial sports" }
+
+// childrens/youth
+, { 0x51, "Pre-school" }
+, { 0x52, "Entertainment (6 to 14 year-olds)" }
+, { 0x53, "Entertainment (10 to 16 year-olds)" }
+, { 0x54, "Informational/Educational/Schools" }
+, { 0x55, "Cartoons/Puppets" }
+
+// music/ballet/dance
+, { 0x61, "Rock/Pop" }
+, { 0x62, "Serious music/Classical Music" }
+, { 0x63, "Folk/Traditional music" }
+, { 0x64, "Jazz" }
+, { 0x65, "Musical/Opera" }
+, { 0x66, "Ballet" }
+
+// arts/culture
+, { 0x71, "Performing Arts" }
+, { 0x72, "Fine Arts" }
+, { 0x73, "Religion" }
+, { 0x74, "Popular Culture/Tradital Arts" }
+, { 0x75, "Literature" }
+, { 0x76, "Film/Cinema" }
+, { 0x77, "Experimental Film/Video" }
+, { 0x78, "Broadcasting/Press" }
+, { 0x79, "New Media" }
+, { 0x7a, "Magazine" }
+, { 0x7b, "Fashion" }
+
+// social/political/economic
+, { 0x81, "Magazine/Report/Documentary" }
+, { 0x82, "Economics/Social Advisory" }
+, { 0x83, "Remarkable People" }
+
+// children's youth: educational/science/factual
+, { 0x91, "Nature/Animals/Environment" }
+, { 0x92, "Technology/Natural sciences" }
+, { 0x93, "Medicine/Physiology/Psychology" }
+, { 0x94, "Foreign Countries/Expeditions" }
+, { 0x95, "Social/Spiritual Sciences" }
+, { 0x96, "Further Education" }
+, { 0x97, "Languages" }
+
+// leisure hobbies
+, { 0xa1, "Tourism/Travel" }
+, { 0xa2, "Handicraft" }
+, { 0xa3, "Motoring" }
+, { 0xa4, "Fitness & Health" }
+, { 0xa5, "Cooking" }
+, { 0xa6, "Advertisement/Shopping" }
+, { 0xa7, "Gardening" }
+
+// misc
+, { 0xb0, "Original Language" }
+, { 0xb1, "Black and White" }
+, { 0xb2, "Unpublished" }
+, { 0xb3, "Live Broadcast" }
+};
+*/
+
+struct GenrePair
+{
+  GenrePair()
+  {
+       genretype_ = 0xf;
+       genresubtype_ = 0xb2;
+  };
+  
+  GenrePair(int type, int subtype)
+  {
+       genretype_ = type;
+       genresubtype_ = subtype;
+  };
+  
+  int genretype_;
+  int genresubtype_;
+};
+
+
+
+class GenreIdMapper 
+{
+public:
+  GenreIdMapper()
+  {
+       genreTypeIdMap_["Auction"] = GenrePair (0xB0, 0xb2);
+       genreTypeIdMap_["Awards"] = GenrePair (0xB0, 0xb2);
+       genreTypeIdMap_["Biography"] = GenrePair (0x70, 0x74);
+       genreTypeIdMap_["Educational"] = GenrePair (0x90, 0x96);
+       genreTypeIdMap_["Entertainment"] = GenrePair (0xB0, 0xb2);
+       genreTypeIdMap_["Holiday"] = GenrePair (0xB0, 0xb2);
+       genreTypeIdMap_["Holiday special"] = GenrePair (0xB0, 0xb2);
+       genreTypeIdMap_["Home improvement"] = GenrePair (0xA0, 0xa7);
+       genreTypeIdMap_["How-to"] = GenrePair (0x90, 0x96);
+       genreTypeIdMap_["Music"] = GenrePair (0x60, 0x61);
+       genreTypeIdMap_["Music special"] = GenrePair (0x60, 0x61);
+       genreTypeIdMap_["Music talk"] = GenrePair (0x60, 0x61);
+       genreTypeIdMap_["Shopping"] = GenrePair (0xA0, 0xa6);
+       genreTypeIdMap_["Sitcom"] = GenrePair (0x10, 0x14);
+       genreTypeIdMap_["Soap"] = GenrePair (0x10, 0x15);
+       genreTypeIdMap_["Soap talk"] = GenrePair (0x10, 0x15);
+       genreTypeIdMap_["Golf"] = GenrePair (0x40, 0x41);
+       genreTypeIdMap_["Lacrosse"] = GenrePair (0x40, 0x45);
+       genreTypeIdMap_["Law"] = GenrePair (0xB0, 0xb2);
+       genreTypeIdMap_["Card games"] = GenrePair (0xB0, 0xb2);
+       genreTypeIdMap_["Collectibles"] = GenrePair (0xB0, 0xb2);
+       genreTypeIdMap_["Community"] = GenrePair (0xB0, 0xb2);
+       genreTypeIdMap_["Computers"] = GenrePair (0x90, 0x92);
+       genreTypeIdMap_["Consumer"] = GenrePair (0xB0, 0xb2);
+       genreTypeIdMap_["Fundraiser"] = GenrePair (0xB0, 0xb2);
+       genreTypeIdMap_["Gaming"] = GenrePair (0xB0, 0xb2);
+       genreTypeIdMap_["Gay/lesbian"] = GenrePair (0xB0, 0xb2);
+       genreTypeIdMap_["Military"] = GenrePair (0xB0, 0xb2);
+       genreTypeIdMap_["Miniseries"] = GenrePair (0xB0, 0xb2);
+       genreTypeIdMap_["Opera"] = GenrePair (0x60, 0x65);
+       genreTypeIdMap_["Parade"] = GenrePair (0xB0, 0xb2);
+       genreTypeIdMap_["Paranormal"] = GenrePair (0xB0, 0xb2);
+       genreTypeIdMap_["Parenting"] = GenrePair (0xB0, 0xb2);
+       genreTypeIdMap_["Poker"] = GenrePair (0xB0, 0xb2);
+       genreTypeIdMap_["Reality"] = GenrePair (0xB0, 0xb2);
+       genreTypeIdMap_["Self improvement"] = GenrePair (0xB0, 0xb2);
+       genreTypeIdMap_["Special"] = GenrePair (0xB0, 0xb2);
+       genreTypeIdMap_["Standup"] = GenrePair (0x10, 0x14);
+       genreTypeIdMap_["Politics"] = GenrePair (0x80, 0x82);
+       genreTypeIdMap_["Public affairs"] = GenrePair (0x80, 0x82);
+       genreTypeIdMap_["Historical drama"] = GenrePair (0x10, 0x17);
+       genreTypeIdMap_["History"] = GenrePair (0x10, 0x17);
+       genreTypeIdMap_["Boat"] = GenrePair (0xA0, 0xa3);
+       genreTypeIdMap_["Bus./financial"] = GenrePair (0x80, 0x82);
+       genreTypeIdMap_["Auto"] = GenrePair (0xA0, 0xa3);
+       genreTypeIdMap_["Aviation"] = GenrePair (0xA0, 0xa3);
+       genreTypeIdMap_["Nature"] = GenrePair (0x90, 0x91);
+       genreTypeIdMap_["Agriculture"] = GenrePair (0x90, 0x91);
+       genreTypeIdMap_["Animals"] = GenrePair (0x90, 0x91);
+       genreTypeIdMap_["Environment"] = GenrePair (0x90, 0x91);
+       genreTypeIdMap_["French"] = GenrePair (0x90, 0x97);
+       genreTypeIdMap_["Horse"] = GenrePair (0x90, 0x91);
+       genreTypeIdMap_["Outdoors"] = GenrePair (0x90, 0x91);
+       genreTypeIdMap_["Science"] = GenrePair (0x90, 0x92);
+       genreTypeIdMap_["Technology"] = GenrePair (0x90, 0x92);
+       genreTypeIdMap_["Medical"] = GenrePair (0x90, 0x93);
+       genreTypeIdMap_["Hunting"] = GenrePair (0x90, 0x91);
+       genreTypeIdMap_["Fishing"] = GenrePair (0x90, 0x91);
+       genreTypeIdMap_["Health"] = GenrePair (0xA0, 0xa4);
+       genreTypeIdMap_["Cooking"] = GenrePair (0xA0, 0xa5);
+       genreTypeIdMap_["House/garden"] = GenrePair (0xA0, 0xa7);
+       genreTypeIdMap_["Motorcycle"] = GenrePair (0xA0, 0xa3);
+       genreTypeIdMap_["Travel"] = GenrePair (0xA0, 0xa1);
+       genreTypeIdMap_["Aerobics"] = GenrePair (0xA0, 0xa4);
+       genreTypeIdMap_["Exercise"] = GenrePair (0xA0, 0xa4);
+       genreTypeIdMap_["Anthology"] = GenrePair (0x70, 0x74);
+       genreTypeIdMap_["Art"] = GenrePair (0x70, 0x72);
+       genreTypeIdMap_["Arts/crafts"] = GenrePair (0x70, 0x74);
+       genreTypeIdMap_["Fashion"] = GenrePair (0x70, 0x7b);
+       genreTypeIdMap_["Performing arts"] = GenrePair (0x70, 0x71);
+       genreTypeIdMap_["Spanish"] = GenrePair (0x90, 0x97);
+       genreTypeIdMap_["Religious"] = GenrePair (0x70, 0x73);
+       genreTypeIdMap_["Dance"] = GenrePair (0x60, 0x66);
+       genreTypeIdMap_["Animated"] = GenrePair (0x50, 0x55);
+       genreTypeIdMap_["Anime"] = GenrePair (0x50, 0x55);
+       genreTypeIdMap_["Children"] = GenrePair (0x50, 0x52);
+       genreTypeIdMap_["Children-music"] = GenrePair (0x50, 0x52);
+       genreTypeIdMap_["Children-special"] = GenrePair (0x50, 0x53);
+       genreTypeIdMap_["Holiday-children"] = GenrePair (0x50, 0x52);
+       genreTypeIdMap_["Holiday-children special"] = GenrePair (0x50, 0x52);
+       genreTypeIdMap_["Game show"] = GenrePair (0x30, 0x31);
+       genreTypeIdMap_["Talk"] = GenrePair (0x30, 0x33);
+       genreTypeIdMap_["Variety"] = GenrePair (0x30, 0x32);
+       genreTypeIdMap_["Debate"] = GenrePair (0x20, 0x24);
+       genreTypeIdMap_["Docudrama"] = GenrePair (0x20, 0x23);
+       genreTypeIdMap_["Documentary"] = GenrePair (0x20, 0x23);
+       genreTypeIdMap_["Interview"] = GenrePair (0x20, 0x24);
+       genreTypeIdMap_["News"] = GenrePair (0x20, 0x21);
+       genreTypeIdMap_["Newsmagazine"] = GenrePair (0x20, 0x21);
+       genreTypeIdMap_["Weather"] = GenrePair (0x20, 0x21);
+       genreTypeIdMap_["Action"] = GenrePair (0x10, 0x12);
+       genreTypeIdMap_["Adults only"] = GenrePair (0x10, 0x18);
+       genreTypeIdMap_["Adventure"] = GenrePair (0x10, 0x12);
+       genreTypeIdMap_["Comedy"] = GenrePair (0x10, 0x14);
+       genreTypeIdMap_["Comedy-drama"] = GenrePair (0x10, 0x14);
+       genreTypeIdMap_["Crime"] = GenrePair (0x10, 0x11);
+       genreTypeIdMap_["Crime drama"] = GenrePair (0x10, 0x11);
+       genreTypeIdMap_["Drama"] = GenrePair (0x10, 0x18);
+       genreTypeIdMap_["Fantasy"] = GenrePair (0x10, 0x13);
+       genreTypeIdMap_["Horror"] = GenrePair (0x10, 0x13);
+       genreTypeIdMap_["Musical"] = GenrePair (0x60, 0x65);
+       genreTypeIdMap_["Musical comedy"] = GenrePair (0x60, 0x65);
+       genreTypeIdMap_["Mystery"] = GenrePair (0x10, 0x12);
+       genreTypeIdMap_["Romance"] = GenrePair (0x10, 0x16);
+       genreTypeIdMap_["Romance-comedy"] = GenrePair (0x10, 0x16);
+       genreTypeIdMap_["Science fiction"] = GenrePair (0x10, 0x13);
+       genreTypeIdMap_["Suspense"] = GenrePair (0x10, 0x11);
+       genreTypeIdMap_["War"] = GenrePair (0x10, 0x12);
+       genreTypeIdMap_["Western"] = GenrePair (0x10, 0x12);
+       genreTypeIdMap_["Action sports"] = GenrePair (0x40, 0x4b);
+  };
+  ~GenreIdMapper()
+  {
+  };
+  
+  const GenrePair& getGenreTypeId(CStdString& genre)
+  {
+       std::map<CStdString, GenrePair>::iterator it;
+       it = genreTypeIdMap_.find(genre);
+       if(it != genreTypeIdMap_.end())
+         return it->second;
+       return c_unknown_;
+  };
+  
+private:
+  std::map<CStdString, GenrePair> genreTypeIdMap_;
+  const GenrePair c_unknown_;
+};
+
+GenreIdMapper GetProgramGuideResult::s_mapper_;
+GetProgramGuideResult::GetProgramGuideResult() {
+}
+
+GetProgramGuideResult::~GetProgramGuideResult() {
+}
+
+void GetProgramGuideResult::parseData(const CStdString& xmlData) {
+       TiXmlDocument xml;
+       xml.Parse(xmlData.c_str(), 0, TIXML_ENCODING_LEGACY);
+
+       TiXmlElement* rootXmlNode = xml.RootElement();
+
+       if (!rootXmlNode) {
+               errors_.push_back(" No root node parsed");
+               CLog::Log(LOGDEBUG, "MythXML GetProgramGuideResult - No root node parsed");
+           return;
+       }
+
+       TiXmlElement* programGuideResponseNode = NULL;
+       CStdString strValue = rootXmlNode->Value();
+       if (strValue.Find("GetProgramGuideResponse") >= 0 ) {
+               programGuideResponseNode = rootXmlNode;
+       }
+       else if (strValue.Find("detail") >= 0 ) {
+               // process the error.
+               TiXmlElement* errorCodeXmlNode = rootXmlNode->FirstChildElement("errorCode");
+               TiXmlElement* errorDescXmlNode = rootXmlNode->FirstChildElement("errorDescription");
+               CStdString error;
+               error.Format("ErrorCode [%i] - %s", errorCodeXmlNode->GetText(), errorDescXmlNode->GetText());
+               errors_.push_back(error);
+               return;
+        }
+       else
+               return;
+
+       TiXmlElement* programGuideNode = programGuideResponseNode->FirstChildElement("ProgramGuide");
+       TiXmlElement* channelsNode = programGuideNode->FirstChildElement("Channels");
+       TiXmlElement* channelNode = NULL;
+       TiXmlElement* programNode = NULL;
+       for( channelNode = channelsNode->FirstChildElement("Channel"); channelNode; channelNode = channelNode->NextSiblingElement("Channel")){
+         int chanId = atoi(channelNode->Attribute("chanId"));
+      for( programNode = channelNode->FirstChildElement("Program"); programNode; programNode = programNode->NextSiblingElement("Program")){
+               CStdString category = programNode->Attribute("category");
+               CStdString itemStart = programNode->Attribute("startTime");
+               CStdString itemEnd = programNode->Attribute("endTime");
+               const GenrePair& genres = s_mapper_.getGenreTypeId(category);
+               SEpg epg;
+               epg.chan_num = chanId;
+               epg.description = programNode->GetText();
+               epg.title = programNode->Attribute("title");
+               epg.subtitle =  programNode->Attribute("subTitle");
+               epg.genre_type = genres.genretype_;
+               epg.genre_subtype = genres.genresubtype_;
+               epg.start_time = MythXmlCommandResult::convertTimeStringToObject(itemStart);
+               epg.end_time = MythXmlCommandResult::convertTimeStringToObject(itemEnd);
+               epg_.push_back(epg);
+         }
+       }
+}
diff --git a/xbmc/pvrclients/mythtv/libmythxml/GetProgramGuideResult.h b/xbmc/pvrclients/mythtv/libmythxml/GetProgramGuideResult.h
new file mode 100644 (file)
index 0000000..b54e0ef
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETPROGRAMGUIDERESULT_H
+#define XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETPROGRAMGUIDERESULT_H
+
+#include <map>
+
+#include "MythXmlCommandResult.h"
+#include "SEpg.h"
+
+class GenreIdMapper;
+
+class GetProgramGuideResult:public MythXmlCommandResult
+{
+public:
+  GetProgramGuideResult();
+  virtual ~GetProgramGuideResult();
+  virtual void parseData(const CStdString& xmlData);
+  inline const vector<SEpg>& getEpg() {return epg_;};
+
+private:
+  void initialize();
+  vector<SEpg> epg_;
+  static GenreIdMapper s_mapper_;
+};
+
+#endif // XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_GETPROGRAMGUIDERESULT_H
diff --git a/xbmc/pvrclients/mythtv/libmythxml/Makefile b/xbmc/pvrclients/mythtv/libmythxml/Makefile
new file mode 100644 (file)
index 0000000..27e24d2
--- /dev/null
@@ -0,0 +1,16 @@
+INCLUDES +=  -I. -I../../ -I../../../linux -I../../../ -I../../../../xbmc/addons/include -I../../../../guilib
+DEFINES += -D_LINUX -fPIC
+
+OBJS = GetProgramGuideResult.o \
+       GetChannelListResult.o \
+       GetNumChannelsResult.o \
+       GetProgramGuideParameters.o \
+       MythXmlCommand.o \
+       MythXmlCommandParameters.o 
+
+LIB = libmythxml.a
+
+# all is the default rule
+all: $(LIB)
+
+include ../../../../Makefile.include
diff --git a/xbmc/pvrclients/mythtv/libmythxml/MythXmlCommand.cpp b/xbmc/pvrclients/mythtv/libmythxml/MythXmlCommand.cpp
new file mode 100644 (file)
index 0000000..049fd3e
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * MythXmlCommand.cpp
+ *
+ *  Created on: Oct 7, 2010
+ *      Author: mythtv
+ */
+
+#include "MythXmlCommand.h"
+
+#include "FileSystem/FileCurl.h"
+
+#include "utils/log.h"
+
+using namespace XFILE;
+
+MythXmlCommand::MythXmlCommand() {
+}
+
+MythXmlCommand::~MythXmlCommand() {
+}
+
+void MythXmlCommand::execute(const CStdString& hostname, int port, const MythXmlCommandParameters& params, MythXmlCommandResult& result, int timeout){
+       CStdString strUrl = createRequestUrl(hostname, port, params);
+       CStdString strXML;
+       CFileCurl http;
+       http.SetTimeout(timeout);
+       if (http.Get(strUrl, strXML))
+       {
+               CLog::Log(LOGDEBUG, "Got response from mythtv backend: %s", strUrl.c_str());
+       }
+       http.Cancel();
+       result.parseData(strXML);
+}
+
+CStdString MythXmlCommand::createRequestUrl(const CStdString& hostname, int port, const MythXmlCommandParameters& params){
+       CStdString requestURL;
+       requestURL.Format("http://%s:%i/Myth/%s", hostname.c_str(), port, getCommand().c_str());
+       if(params.hasParameters()){
+               requestURL += params.createParameterString();
+       }
+       return requestURL;
+}
diff --git a/xbmc/pvrclients/mythtv/libmythxml/MythXmlCommand.h b/xbmc/pvrclients/mythtv/libmythxml/MythXmlCommand.h
new file mode 100644 (file)
index 0000000..9bc114c
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * MythXmlCommand.h
+ *
+ *  Created on: Oct 7, 2010
+ *      Author: tafypz
+ */
+
+#ifndef XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_MYTHXMLCOMMAND_H_
+#define XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_MYTHXMLCOMMAND_H_
+
+
+#include "MythXmlCommandResult.h"
+#include "MythXmlCommandParameters.h"
+
+#include "../StdString.h"
+
+/*! \class MythXmlCommand
+ \brief The base class for all MythXML Commands.
+ This class will be subclassed by all MythXML commands; it provides a few basic services:
+   - creation of the request url (complete with parameters).
+   - execution of the created request.
+ */
+class MythXmlCommand {
+public:
+       MythXmlCommand();
+       virtual ~MythXmlCommand();
+       /*! \brief Execute the command with the given request parameters.
+               \param hostname the mythtv backend hostname to connect to.
+               \param port the mythtv backend port to connect to.
+               \param params the parameters specific to the command.
+               \param result the result instance to use to handle the data returned by the request.
+               \param timeout the timeout value for this request.
+       */
+       void execute(const CStdString& hostname, int port, const MythXmlCommandParameters& params, MythXmlCommandResult& result, int timeout);
+protected:
+       /*! \brief The MythXML Command to use.
+               \return The MythXML Command to use.
+        */
+       virtual const CStdString& getCommand() const = 0;
+private:
+       /*! \brief creates the url to use to issue the request.
+               \param hostname the mythtv backend hostname to connect to.
+               \param port the mythtv backend port to connect to.
+               \param params the parameters specific to the command.
+       */
+       CStdString createRequestUrl(const CStdString& hostname, int port, const MythXmlCommandParameters& params);
+};
+
+#endif /* XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_MYTHXMLCOMMAND_H_ */
diff --git a/xbmc/pvrclients/mythtv/libmythxml/MythXmlCommandParameters.cpp b/xbmc/pvrclients/mythtv/libmythxml/MythXmlCommandParameters.cpp
new file mode 100644 (file)
index 0000000..076bc0b
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * MythXmlCommandParameters.cpp
+ *
+ *  Created on: Oct 8, 2010
+ *      Author: tafypz
+ */
+
+#include "MythXmlCommandParameters.h"
+#include "DateTime.h"
+
+MythXmlCommandParameters::MythXmlCommandParameters(bool hasParameters) {
+       hasParameters_ = hasParameters;
+}
+
+MythXmlCommandParameters::~MythXmlCommandParameters() {
+}
+
+CStdString MythXmlCommandParameters::convertTimeToString(const CDateTime& time){
+       CStdString result = "%i-%02.2i-%02.2iT%02.2i:%02.2i";
+       result.Format(result.c_str(), time.GetYear(), time.GetMonth(), time.GetDay(), time.GetHour(), time.GetMinute());
+       return result;
+}
+
+MythXmlEmptyCommandParameters::MythXmlEmptyCommandParameters() : MythXmlCommandParameters(false){
+}
+
+MythXmlEmptyCommandParameters::~MythXmlEmptyCommandParameters(){
+}
+
+CStdString MythXmlEmptyCommandParameters::createParameterString() const{
+       static CStdString result = "";
+       return result;
+};
diff --git a/xbmc/pvrclients/mythtv/libmythxml/MythXmlCommandParameters.h b/xbmc/pvrclients/mythtv/libmythxml/MythXmlCommandParameters.h
new file mode 100644 (file)
index 0000000..d163d75
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * MythXmlCommandParameters.h
+ *
+ *  Created on: Oct 8, 2010
+ *      Author: tafypz
+ */
+
+#ifndef XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_MYTHXMLCOMMANDPARAMETERS_H_
+#define XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_MYTHXMLCOMMANDPARAMETERS_H_
+
+#include "DateTime.h"
+
+#include "../StdString.h"
+
+/*!    \class MythXmlCommandParameters
+       \brief This is the base abstract class for all MythXML command parameters.
+       This class should be subclassed in order to provide parameters to the mythxml commands.
+       It has the responsibility to create a list of HTTP parameters to use for the request.
+       A subclass called \ref MythXmlEmptyCommandParameters MythXmlEmptyCommandParameters has been provided for convenience
+       in case the mythXML command doesn't need parameters.
+ */
+class MythXmlCommandParameters {
+public:
+       static CStdString convertTimeToString(const CDateTime& time);
+
+       MythXmlCommandParameters(bool hasParameters);
+       virtual ~MythXmlCommandParameters();
+       virtual CStdString createParameterString() const = 0;
+       inline bool hasParameters() const {return hasParameters_;};
+private:
+       bool hasParameters_;
+};
+
+/*!    \class MythXmlEmptyCommandParameters
+       \brief This class is to be used with commands that do not take parameters.
+ */
+class MythXmlEmptyCommandParameters : public MythXmlCommandParameters {
+       MythXmlEmptyCommandParameters();
+       virtual ~MythXmlEmptyCommandParameters();
+       virtual CStdString createParameterString() const;
+};
+
+#endif /* MYTHXMLCOMMANDPARAMETERS_H_ */
diff --git a/xbmc/pvrclients/mythtv/libmythxml/MythXmlCommandResult.h b/xbmc/pvrclients/mythtv/libmythxml/MythXmlCommandResult.h
new file mode 100644 (file)
index 0000000..fcf450a
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * MythXmlCommandResult.h
+ *
+ *  Created on: Oct 7, 2010
+ *      Author: tafypz
+ */
+
+#ifndef XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_MYTHXMLCOMMANDRESULT_H_
+#define XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_MYTHXMLCOMMANDRESULT_H_
+
+#include <vector>
+#include "DateTime.h"
+#include "../StdString.h"
+
+using std::vector;
+class MythXmlCommandResult {
+public:
+       static CDateTime convertTimeStringToObject(const CStdString& time)
+       {
+         int year = 0, month = 0, day = 0;
+         int hour = 0, minute = 0, second = 0;
+         sscanf(time.c_str(), "%d-%d-%dT%d:%d:%d", &year, &month, &day, &hour, &minute, &second);
+         return CDateTime(year, month, day,hour,minute, second);
+       }
+       
+       MythXmlCommandResult(){};
+       virtual ~MythXmlCommandResult(){};
+       inline bool isSuccess() const { return errors_.empty(); };
+       inline vector<CStdString> getErrors() const {return errors_;};
+       virtual void parseData(const CStdString& xmlData) = 0;
+protected:
+       vector<CStdString> errors_;
+};
+
+#endif /* XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_MYTHXMLCOMMANDRESULT_H_ */
diff --git a/xbmc/pvrclients/mythtv/libmythxml/SChannel.h b/xbmc/pvrclients/mythtv/libmythxml/SChannel.h
new file mode 100644 (file)
index 0000000..a4df434
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_SCHANNEL_H_
+#define XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_SCHANNEL_H_
+
+#include "../StdString.h"
+
+struct SChannel
+{
+  int             id;
+  CStdString      name;
+  CStdString      callsign;
+  CStdString      number;
+
+  SChannel() {
+       id = 0;
+  }
+};
+
+#endif /* XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_SCHANNEL_H_ */
diff --git a/xbmc/pvrclients/mythtv/libmythxml/SEpg.h b/xbmc/pvrclients/mythtv/libmythxml/SEpg.h
new file mode 100644 (file)
index 0000000..86bea1d
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_SEPG_H_
+#define XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_SEPG_H_
+
+#include "../StdString.h"
+#include "DateTime.h"
+
+struct SEpg
+{
+  int             id;
+  int             chan_num;
+  int             genre_type;
+  int             genre_subtype;
+  int             parental_rating;
+  CStdString      title;
+  CStdString      subtitle;
+  CStdString      description;
+  CDateTime       start_time;
+  CDateTime       end_time;
+  
+  SEpg() {
+       id = 0;
+       chan_num = 0;
+       genre_type = 0;
+       genre_subtype = 0;
+       parental_rating = 0;
+  }
+};
+
+#endif /* XBMC_PVRCLIENTS_MYTHTV_LIBMYTHXML_SEPG_H_ */
diff --git a/xbmc/pvrclients/mythtv/linux/pvrclient-mythtv_os_posix.h b/xbmc/pvrclients/mythtv/linux/pvrclient-mythtv_os_posix.h
new file mode 100644 (file)
index 0000000..02e1450
--- /dev/null
@@ -0,0 +1,108 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef PVRCLIENT_MYTHTV_OS_POSIX_H
+#define PVRCLIENT_MYTHTV_OS_POSIX_H
+
+#define _FILE_OFFSET_BITS 64
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/syscall.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/timeb.h>
+#include <sys/wait.h>
+#include <sys/signal.h>
+#include <sys/resource.h>
+#ifndef __APPLE__
+#include <sys/prctl.h> 
+#endif
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <pthread.h>
+#include <poll.h>
+
+typedef int bool_t;
+typedef int SOCKET;
+
+#define __close(a) close(a)
+#define __select select
+#define __recv recv
+#define __shutdown shutdown
+#define __socket socket
+#define __bind bind
+#define __getsockname getsockname
+#define __connect connect
+#define __getpeername getpeername
+#define __send send
+#define __getsockopt getsockopt
+#define __listen listen
+#define __accept accept
+#define __setsockopt setsockopt
+#define __fcntl fcntl
+#define __gethostbyname gethostbyname
+#define __read read
+#define __write write
+#define __poll poll
+#define SOCKET_ERROR   (-1)
+#define INVALID_SOCKET (-1)
+#define SD_BOTH SHUT_RDWR
+
+#define LIBTYPE
+#define sock_getlasterror errno
+#define sock_getlasterror_socktimeout (errno == EAGAIN)
+#define console_vprintf vprintf
+#define console_printf printf
+#define THREAD_FUNC_PREFIX void *
+
+#ifndef __STL_CONFIG_H
+template<class T> inline T min(T a, T b) { return a <= b ? a : b; }
+template<class T> inline T max(T a, T b) { return a >= b ? a : b; }
+template<class T> inline int sgn(T a) { return a < 0 ? -1 : a > 0 ? 1 : 0; }
+template<class T> inline void swap(T &a, T &b) { T t = a; a = b; b = t; }
+#endif
+
+#define Sleep(t) usleep(t*1000)
+
+static inline uint64_t getcurrenttime(void)
+{
+       struct timeval t;
+       gettimeofday(&t, NULL);
+       return ((uint64_t)t.tv_sec * 1000) + (t.tv_usec / 1000);
+}
+
+static inline int setsocktimeout(int s, int level, int optname, uint64_t timeout)
+{
+       struct timeval t;
+       t.tv_sec = timeout / 1000;
+       t.tv_usec = (timeout % 1000) * 1000;
+       return setsockopt(s, level, optname, (char *)&t, sizeof(t));
+}
+
+#endif
diff --git a/xbmc/pvrclients/mythtv/pvrclient-mythtv_os.h b/xbmc/pvrclients/mythtv/pvrclient-mythtv_os.h
new file mode 100644 (file)
index 0000000..032af6c
--- /dev/null
@@ -0,0 +1,44 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef PVRCLIENT_MYTHTV_OS_H
+#define PVRCLIENT_MYTHTV_OS_H
+
+#if defined(_WIN32) || defined(_WIN64)
+#define __WINDOWS__
+#endif
+
+#if defined(__WINDOWS__)
+/* windows code not in yet */
+#else
+#include "linux/pvrclient-mythtv_os_posix.h"
+#endif
+
+#if !defined(TRUE)
+#define TRUE 1
+#endif
+
+#if !defined(FALSE)
+#define FALSE 0
+#endif
+
+#endif
diff --git a/xbmc/pvrclients/tvheadend/HTSPData.cpp b/xbmc/pvrclients/tvheadend/HTSPData.cpp
new file mode 100644 (file)
index 0000000..cc177bc
--- /dev/null
@@ -0,0 +1,625 @@
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "HTSPData.h"
+
+extern "C" {
+#include "libhts/htsmsg.h"
+#include "libhts/htsmsg_binary.h"
+}
+
+#define CMD_LOCK cMutexLock CmdLock((cMutex*)&m_Mutex)
+
+cHTSPData::cHTSPData()
+{
+}
+
+cHTSPData::~cHTSPData()
+{
+  Close();
+}
+
+bool cHTSPData::Open(CStdString hostname, int port, CStdString user, CStdString pass, long timeout)
+{
+  if(!m_session.Connect(hostname, port))
+    return false;
+
+  if(m_session.GetProtocol() < 2)
+  {
+    XBMC->Log(LOG_ERROR, "%s - Incompatible protocol version %d", __FUNCTION__, m_session.GetProtocol());
+    return false;
+  }
+
+  if(!user.IsEmpty())
+    m_session.Auth(user, pass);
+
+  if(!m_session.SendEnableAsync())
+    return false;
+
+  SetDescription("HTSP Data Listener");
+  Start();
+
+  m_started.Wait(timeout);
+  return Running();
+}
+
+void cHTSPData::Close()
+{
+  Cancel(3);
+  m_session.Abort();
+  m_session.Close();
+}
+
+bool cHTSPData::CheckConnection()
+{
+  return m_session.IsConnected();
+}
+
+htsmsg_t* cHTSPData::ReadResult(htsmsg_t* m)
+{
+  m_Mutex.Lock();
+  unsigned    seq (m_session.AddSequence());
+
+  SMessage &message(m_queue[seq]);
+  message.event = new cCondWait();
+  message.msg   = NULL;
+
+  m_Mutex.Unlock();
+  htsmsg_add_u32(m, "seq", seq);
+  if(!m_session.SendMessage(m))
+  {
+    m_queue.erase(seq);
+    return NULL;
+  }
+
+  if(!message.event->Wait(2000))
+  {
+    XBMC->Log(LOG_ERROR, "%s - Timeout waiting for response", __FUNCTION__);
+    m_session.Close();
+  }
+  m_Mutex.Lock();
+
+  m =    message.msg;
+  delete message.event;
+
+  m_queue.erase(seq);
+
+  m_Mutex.Unlock();
+  return m;
+}
+
+bool cHTSPData::GetDriveSpace(long long *total, long long *used)
+{
+  htsmsg_t *msg = htsmsg_create_map();
+  htsmsg_add_str(msg, "method", "getDiskSpace");
+  if ((msg = ReadResult(msg)) == NULL)
+  {
+    XBMC->Log(LOG_DEBUG, "%s - failed to get getDiskSpace", __FUNCTION__);
+    return false;
+  }
+
+  int64_t freespace;
+  if (htsmsg_get_s64(msg, "freediskspace", &freespace) != 0)
+    return false;
+
+  int64_t totalspace;
+  if (htsmsg_get_s64(msg, "totaldiskspace", &totalspace) != 0)
+    return false;
+
+  *total = totalspace / 1024;
+  *used  = (totalspace - freespace) / 1024;
+  return true;
+}
+
+bool cHTSPData::GetTime(time_t *localTime, int *gmtOffset)
+{
+  XBMC->Log(LOG_DEBUG, "%s", __FUNCTION__);
+
+  htsmsg_t *msg = htsmsg_create_map();
+  htsmsg_add_str(msg, "method", "getSysTime");
+  if ((msg = ReadResult(msg)) == NULL)
+  {
+    XBMC->Log(LOG_ERROR, "%s - failed to get sysTime", __FUNCTION__);
+    return false;
+  }
+
+  unsigned int secs;
+  if (htsmsg_get_u32(msg, "time", &secs) != 0)
+    return false;
+
+  int offset;
+  if (htsmsg_get_s32(msg, "timezone", &offset) != 0)
+    return false;
+
+  XBMC->Log(LOG_DEBUG, "%s - tvheadend reported time=%u, timezone=%d, correction=%d"
+      , __FUNCTION__, secs, offset, g_iEpgOffsetCorrection * 60);
+
+  /* XBMC needs the timezone difference in seconds from GMT */
+  offset = (offset - (g_iEpgOffsetCorrection * 60)) * 60;
+
+  *localTime = secs + offset;
+  *gmtOffset = -offset;
+  return true;
+}
+
+int cHTSPData::GetNumChannels()
+{
+  return GetChannels().size();
+}
+
+PVR_ERROR cHTSPData::RequestChannelList(PVRHANDLE handle, int radio)
+{
+  if (!CheckConnection())
+    return PVR_ERROR_SERVER_ERROR;
+
+  SChannels channels = GetChannels();
+  for(SChannels::iterator it = channels.begin(); it != channels.end(); ++it)
+  {
+    SChannel& channel = it->second;
+
+    PVR_CHANNEL tag;
+    memset(&tag, 0 , sizeof(tag));
+    tag.uid           = channel.id;
+    tag.number        = channel.id;
+    tag.name          = channel.name.c_str();
+    tag.callsign      = channel.name.c_str();
+    tag.radio         = channel.radio;
+    tag.encryption    = channel.caid;
+    tag.input_format  = "";
+    tag.stream_url    = "";
+    tag.bouquet       = 0;
+
+    if(((bool)radio) == tag.radio)
+    {
+      PVR->TransferChannelEntry(handle, &tag);
+    }
+  }
+
+  return PVR_ERROR_NO_ERROR;
+}
+
+PVR_ERROR cHTSPData::RequestEPGForChannel(PVRHANDLE handle, const PVR_CHANNEL &channel, time_t start, time_t end)
+{
+  if (!CheckConnection())
+    return PVR_ERROR_SERVER_ERROR;
+
+  SChannels channels = GetChannels();
+
+  if (channels.find(channel.uid) != channels.end())
+  {
+    time_t stop;
+
+    SEvent event;
+    event.id = channels[channel.uid].event;
+    if (event.id == 0)
+      return PVR_ERROR_NO_ERROR;
+
+    do
+    {
+      bool success = GetEvent(event, event.id);
+      if (success)
+      {
+        PVR_PROGINFO broadcast;
+        memset(&broadcast, 0, sizeof(PVR_PROGINFO));
+
+        broadcast.channum         = event.chan_id >= 0 ? event.chan_id : channel.number;
+        broadcast.uid             = event.id;
+        broadcast.title           = event.title.c_str();
+        broadcast.subtitle        = event.title.c_str();
+        broadcast.description     = event.descs.c_str();
+        broadcast.starttime       = event.start;
+        broadcast.endtime         = event.stop;
+        broadcast.genre_type      = (event.content & 0x0F) << 4;
+        broadcast.genre_sub_type  = event.content & 0xF0;
+        PVR->TransferEpgEntry(handle, &broadcast);
+
+        event.id = event.next;
+        stop = event.stop;
+      }
+      else
+        break;
+
+    } while(end > stop);
+
+    return PVR_ERROR_NO_ERROR;
+  }
+
+  return PVR_ERROR_NO_ERROR;
+}
+
+SRecordings cHTSPData::GetDVREntries(bool recorded, bool scheduled)
+{
+  time_t localTime;
+  int gmtOffset;
+  GetTime(&localTime, &gmtOffset);
+
+  CMD_LOCK;
+  SRecordings recordings;
+
+  for(SRecordings::const_iterator it = m_recordings.begin(); it != m_recordings.end(); ++it)
+  {
+    SRecording recording = it->second;
+
+    if ((recorded && (recording.state == ST_COMPLETED || recording.state == ST_ABORTED)) ||
+        (scheduled && (recording.state == ST_SCHEDULED || recording.state == ST_RECORDING)))
+      recordings[recording.id] = recording;
+  }
+
+  return recordings;
+}
+
+int cHTSPData::GetNumRecordings()
+{
+  SRecordings recordings = GetDVREntries(true, false);
+  return recordings.size();
+}
+
+PVR_ERROR cHTSPData::RequestRecordingsList(PVRHANDLE handle)
+{
+  time_t localTime;
+  int gmtOffset;
+  GetTime(&localTime, &gmtOffset);
+
+  SRecordings recordings = GetDVREntries(true, false);
+
+  for(SRecordings::const_iterator it = recordings.begin(); it != recordings.end(); ++it)
+  {
+    SRecording recording = it->second;
+
+    PVR_RECORDINGINFO tag;
+    tag.index           = recording.id;
+    tag.directory       = "/";
+    tag.subtitle        = "";
+    tag.channel_name    = "";
+    tag.recording_time  = recording.start + gmtOffset;
+    tag.duration        = recording.stop - recording.start;
+    tag.description     = recording.description.c_str();
+    tag.title           = recording.title.c_str();
+
+    CStdString streamURL = "http://";
+    {
+      CMD_LOCK;
+      SChannels::const_iterator itr = m_channels.find(recording.channel);
+      if (itr != m_channels.end())
+        tag.channel_name = itr->second.name.c_str();
+      else
+        tag.channel_name = "";
+
+      if (g_szUsername != "")
+      {
+        streamURL += g_szUsername;
+        if (g_szPassword != "")
+        {
+          streamURL += ":";
+          streamURL += g_szPassword;
+        }
+        streamURL += "@";
+      }
+      streamURL.Format("%s%s:%i/dvrfile/%i", streamURL.c_str(), g_szHostname.c_str(), g_iPortHTTP, recording.id);
+    }
+    tag.stream_url      = streamURL.c_str();
+
+    PVR->TransferRecordingEntry(handle, &tag);
+  }
+
+  return PVR_ERROR_NO_ERROR;
+}
+
+PVR_ERROR cHTSPData::DeleteRecording(const PVR_RECORDINGINFO &recinfo)
+{
+  XBMC->Log(LOG_DEBUG, "%s", __FUNCTION__);
+
+  htsmsg_t *msg = htsmsg_create_map();
+  htsmsg_add_str(msg, "method", "deleteDvrEntry");
+  htsmsg_add_u32(msg, "id", recinfo.index);
+  if ((msg = ReadResult(msg)) == NULL)
+  {
+    XBMC->Log(LOG_DEBUG, "%s - Failed to get deleteDvrEntry", __FUNCTION__);
+    return PVR_ERROR_SERVER_ERROR;
+  }
+
+  unsigned int success;
+  if (htsmsg_get_u32(msg, "success", &success) != 0)
+  {
+    XBMC->Log(LOG_DEBUG, "%s - Failed to parse param", __FUNCTION__);
+    return PVR_ERROR_SERVER_ERROR;
+  }
+
+  return success > 0 ? PVR_ERROR_NO_ERROR : PVR_ERROR_NOT_DELETED;
+}
+
+int cHTSPData::GetNumTimers()
+{
+  SRecordings recordings = GetDVREntries(false, true);
+  return recordings.size();
+}
+
+PVR_ERROR cHTSPData::RequestTimerList(PVRHANDLE handle)
+{
+  time_t localTime;
+  int gmtOffset;
+  GetTime(&localTime, &gmtOffset);
+
+  SRecordings recordings = GetDVREntries(false, true);
+
+  for(SRecordings::const_iterator it = recordings.begin(); it != recordings.end(); ++it)
+  {
+    SRecording recording = it->second;
+
+    PVR_TIMERINFO tag;
+    tag.index       = recording.id;
+    tag.channelNum  = recording.channel;
+    tag.starttime   = recording.start;
+    tag.endtime     = recording.stop;
+    tag.active      = recording.state == ST_SCHEDULED || recording.state == ST_RECORDING;
+    tag.recording   = recording.state == ST_RECORDING;
+    tag.title       = recording.title.c_str();
+    tag.directory   = "/";
+    tag.priority    = 0;
+    tag.lifetime    = tag.endtime - tag.starttime;
+    tag.repeat      = false;
+    tag.repeatflags = 0;
+
+    PVR->TransferTimerEntry(handle, &tag);
+  }
+
+  return PVR_ERROR_NO_ERROR;
+}
+
+PVR_ERROR cHTSPData::DeleteTimer(const PVR_TIMERINFO &timerinfo, bool force)
+{
+  XBMC->Log(LOG_DEBUG, "%s", __FUNCTION__);
+
+  htsmsg_t *msg = htsmsg_create_map();
+  htsmsg_add_str(msg, "method", "deleteDvrEntry");
+  htsmsg_add_u32(msg, "id", timerinfo.index);
+  if ((msg = ReadResult(msg)) == NULL)
+  {
+    XBMC->Log(LOG_DEBUG, "%s - Failed to get deleteDvrEntry", __FUNCTION__);
+    return PVR_ERROR_SERVER_ERROR;
+  }
+
+  unsigned int success;
+  if (htsmsg_get_u32(msg, "success", &success) != 0)
+  {
+    XBMC->Log(LOG_DEBUG, "%s - Failed to parse param", __FUNCTION__);
+    return PVR_ERROR_SERVER_ERROR;
+  }
+
+  return success > 0 ? PVR_ERROR_NO_ERROR : PVR_ERROR_NOT_DELETED;
+}
+
+PVR_ERROR cHTSPData::AddTimer(const PVR_TIMERINFO &timerinfo)
+{
+  XBMC->Log(LOG_DEBUG, "%s - id=%d", __FUNCTION__, timerinfo.index);
+
+  htsmsg_t *msg = htsmsg_create_map();
+  htsmsg_add_str(msg, "method", "addDvrEntry");
+  htsmsg_add_u32(msg, "eventId", timerinfo.index);
+  htsmsg_add_str(msg, "title", timerinfo.title);
+  htsmsg_add_u32(msg, "starttime", timerinfo.starttime);
+  htsmsg_add_u32(msg, "endtime", timerinfo.endtime);
+
+  if ((msg = ReadResult(msg)) == NULL)
+  {
+    XBMC->Log(LOG_DEBUG, "%s - Failed to get addDvrEntry", __FUNCTION__);
+    return PVR_ERROR_SERVER_ERROR;
+  }
+
+  unsigned int success;
+  if (htsmsg_get_u32(msg, "success", &success) != 0)
+  {
+    XBMC->Log(LOG_DEBUG, "%s - Failed to parse param", __FUNCTION__);
+    return PVR_ERROR_SERVER_ERROR;
+  }
+
+  return success > 0 ? PVR_ERROR_NO_ERROR : PVR_ERROR_NOT_DELETED;
+}
+
+PVR_ERROR cHTSPData::UpdateTimer(const PVR_TIMERINFO &timerinfo)
+{
+  XBMC->Log(LOG_DEBUG, "%s - id=%d", __FUNCTION__, timerinfo.index);
+
+  htsmsg_t *msg = htsmsg_create_map();
+  htsmsg_add_str(msg, "method", "updateDvrEntry");
+  htsmsg_add_u32(msg, "id", timerinfo.index);
+  htsmsg_add_str(msg, "title", timerinfo.title);
+  htsmsg_add_u32(msg, "start", timerinfo.starttime);
+  htsmsg_add_u32(msg, "stop", timerinfo.endtime);
+  
+  if ((msg = ReadResult(msg)) == NULL)
+  {
+    XBMC->Log(LOG_DEBUG, "%s - Failed to get updateDvrEntry", __FUNCTION__);
+    return PVR_ERROR_SERVER_ERROR;
+  }
+
+  unsigned int success;
+  if (htsmsg_get_u32(msg, "success", &success) != 0)
+  {
+    XBMC->Log(LOG_DEBUG, "%s - Failed to parse param", __FUNCTION__);
+    return PVR_ERROR_SERVER_ERROR;
+  }
+
+  return success > 0 ? PVR_ERROR_NO_ERROR : PVR_ERROR_NOT_SAVED;
+}
+
+PVR_ERROR cHTSPData::RenameRecording(const PVR_RECORDINGINFO &recinfo, const char* newname)
+{
+  XBMC->Log(LOG_DEBUG, "%s - id=%d", __FUNCTION__, recinfo.index);
+
+  htsmsg_t *msg = htsmsg_create_map();
+  htsmsg_add_str(msg, "method", "updateDvrEntry");
+  htsmsg_add_u32(msg, "id", recinfo.index);
+  htsmsg_add_str(msg, "title", newname);
+  
+  if ((msg = ReadResult(msg)) == NULL)
+  {
+    XBMC->Log(LOG_DEBUG, "%s - Failed to get updateDvrEntry", __FUNCTION__);
+    return PVR_ERROR_SERVER_ERROR;
+  }
+
+  unsigned int success;
+  if (htsmsg_get_u32(msg, "success", &success) != 0)
+  {
+    XBMC->Log(LOG_DEBUG, "%s - Failed to parse param", __FUNCTION__);
+    return PVR_ERROR_SERVER_ERROR;
+  }
+
+  return success > 0 ? PVR_ERROR_NO_ERROR : PVR_ERROR_NOT_SAVED;
+}
+
+
+void cHTSPData::Action()
+{
+  XBMC->Log(LOG_DEBUG, "%s - Starting", __FUNCTION__);
+
+  htsmsg_t* msg;
+
+  while (Running())
+  {
+    if((msg = m_session.ReadMessage()) == NULL)
+      break;
+
+    uint32_t seq;
+    if(htsmsg_get_u32(msg, "seq", &seq) == 0)
+    {
+      CMD_LOCK;
+      SMessages::iterator it = m_queue.find(seq);
+      if(it != m_queue.end())
+      {
+        it->second.msg = msg;
+        it->second.event->Signal();
+        continue;
+      }
+    }
+
+    const char* method;
+    if((method = htsmsg_get_str(msg, "method")) == NULL)
+    {
+      htsmsg_destroy(msg);
+      continue;
+    }
+
+    CMD_LOCK;
+    if     (strstr(method, "channelAdd"))
+      cHTSPSession::ParseChannelUpdate(msg, m_channels);
+    else if(strstr(method, "channelUpdate"))
+      cHTSPSession::ParseChannelUpdate(msg, m_channels);
+    else if(strstr(method, "channelDelete"))
+      cHTSPSession::ParseChannelRemove(msg, m_channels);
+    else if(strstr(method, "tagAdd"))
+      cHTSPSession::ParseTagUpdate(msg, m_tags);
+    else if(strstr(method, "tagUpdate"))
+      cHTSPSession::ParseTagUpdate(msg, m_tags);
+    else if(strstr(method, "tagDelete"))
+      cHTSPSession::ParseTagRemove(msg, m_tags);
+    else if(strstr(method, "initialSyncCompleted"))
+      m_started.Signal();
+    else if(strstr(method, "dvrEntryAdd"))
+      cHTSPSession::ParseDVREntryUpdate(msg, m_recordings);
+    else if(strstr(method, "dvrEntryUpdate"))
+      cHTSPSession::ParseDVREntryUpdate(msg, m_recordings);
+    else if(strstr(method, "dvrEntryDelete"))
+      cHTSPSession::ParseDVREntryDelete(msg, m_recordings);
+    else
+      XBMC->Log(LOG_DEBUG, "%s - Unmapped action recieved '%s'", __FUNCTION__, method);
+
+    htsmsg_destroy(msg);
+  }
+
+  m_started.Signal();
+  XBMC->Log(LOG_DEBUG, "%s - Exiting", __FUNCTION__);
+}
+
+SChannels cHTSPData::GetChannels()
+{
+  return GetChannels(0);
+}
+
+SChannels cHTSPData::GetChannels(int tag)
+{
+  CMD_LOCK;
+  if(tag == 0)
+    return m_channels;
+
+  STags::iterator it = m_tags.find(tag);
+  if(it == m_tags.end())
+  {
+    SChannels channels;
+    return channels;
+  }
+  return GetChannels(it->second);
+}
+
+SChannels cHTSPData::GetChannels(STag& tag)
+{
+  CMD_LOCK;
+  SChannels channels;
+
+  std::vector<int>::iterator it;
+  for(it = tag.channels.begin(); it != tag.channels.end(); it++)
+  {
+    SChannels::iterator it2 = m_channels.find(*it);
+    if(it2 == m_channels.end())
+    {
+      XBMC->Log(LOG_ERROR, "%s - tag points to unknown channel %d", __FUNCTION__, *it);
+      continue;
+    }
+    channels[*it] = it2->second;
+  }
+  return channels;
+}
+
+STags cHTSPData::GetTags()
+{
+  CMD_LOCK;
+  return m_tags;
+}
+
+bool cHTSPData::GetEvent(SEvent& event, uint32_t id)
+{
+  if(id == 0)
+  {
+    event.Clear();
+    return false;
+  }
+
+  SEvents::iterator it = m_events.find(id);
+  if(it != m_events.end())
+  {
+    event = it->second;
+    return true;
+  }
+
+  htsmsg_t *msg = htsmsg_create_map();
+  htsmsg_add_str(msg, "method", "getEvent");
+  htsmsg_add_u32(msg, "eventId", id);
+  if((msg = ReadResult(msg)) == NULL)
+  {
+    XBMC->Log(LOG_DEBUG, "%s - failed to get event %u", __FUNCTION__, id);
+    return false;
+  }
+  if(!cHTSPSession::ParseEvent(msg, id, event))
+    return false;
+
+  m_events[id] = event;
+  return true;
+}
diff --git a/xbmc/pvrclients/tvheadend/HTSPData.h b/xbmc/pvrclients/tvheadend/HTSPData.h
new file mode 100644 (file)
index 0000000..6526d82
--- /dev/null
@@ -0,0 +1,84 @@
+#pragma once
+
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "client.h"
+#include "thread.h"
+#include "HTSPSession.h"
+
+class cHTSPData : public cThread
+{
+public:
+  cHTSPData();
+  ~cHTSPData();
+
+  bool Open(CStdString hostname, int port, CStdString user, CStdString pass, long timeout);
+  void Close();
+  bool CheckConnection();
+
+  htsmsg_t* ReadResult(htsmsg_t* m);
+  int GetProtocol()   { return m_session.GetProtocol(); }
+  CStdString GetServerName() { return m_session.GetServerName(); }
+  CStdString GetVersion()    { return m_session.GetVersion(); }
+  bool GetDriveSpace(long long *total, long long *used);
+  bool GetTime(time_t *localTime, int *gmtOffset);
+  int GetNumChannels();
+  int GetNumRecordings();
+  PVR_ERROR RequestChannelList(PVRHANDLE handle, int radio);
+  PVR_ERROR RequestEPGForChannel(PVRHANDLE handle, const PVR_CHANNEL &channel, time_t start, time_t end);
+  PVR_ERROR RequestRecordingsList(PVRHANDLE handle);
+  PVR_ERROR DeleteRecording(const PVR_RECORDINGINFO &recinfo);
+  PVR_ERROR AddTimer(const PVR_TIMERINFO &timerinfo);
+  PVR_ERROR UpdateTimer(const PVR_TIMERINFO &timerinfo);
+  PVR_ERROR RenameRecording(const PVR_RECORDINGINFO &recinfo, const char* newname);
+
+  int GetNumTimers();
+  PVR_ERROR RequestTimerList(PVRHANDLE handle);
+  PVR_ERROR DeleteTimer(const PVR_TIMERINFO &timerinfo, bool force);
+
+protected:
+  virtual void Action(void);
+
+private:
+  struct SMessage
+  {
+    cCondWait * event;
+    htsmsg_t  * msg;
+  };
+  typedef std::map<int, SMessage> SMessages;
+
+  SChannels GetChannels();
+  SChannels GetChannels(int tag);
+  SChannels GetChannels(STag &tag);
+  STags GetTags();
+  bool GetEvent(SEvent& event, uint32_t id);
+  SRecordings GetDVREntries(bool recorded, bool scheduled);
+
+  cHTSPSession    m_session;
+  cCondWait       m_started;
+  cMutex          m_Mutex;
+  SChannels       m_channels;
+  STags           m_tags;
+  SEvents         m_events;
+  SMessages       m_queue;
+  SRecordings     m_recordings;
+};
diff --git a/xbmc/pvrclients/tvheadend/HTSPDemux.cpp b/xbmc/pvrclients/tvheadend/HTSPDemux.cpp
new file mode 100644 (file)
index 0000000..984ff5f
--- /dev/null
@@ -0,0 +1,577 @@
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <stdint.h>
+#include <limits.h>
+#include <libavcodec/avcodec.h> // For codec id's
+#include "HTSPDemux.h"
+
+extern "C" {
+#include "libhts/net.h"
+#include "libhts/htsmsg.h"
+#include "libhts/htsmsg_binary.h"
+#include "libhts/sha1.h"
+}
+
+cHTSPDemux::cHTSPDemux()
+  : m_subs(0)
+  , m_channel(0)
+  , m_tag(0)
+  , m_StatusCount(0)
+  , m_SkipIFrame(0)
+{
+  m_Streams.nstreams = 0;
+}
+
+cHTSPDemux::~cHTSPDemux()
+{
+  Close();
+}
+
+bool cHTSPDemux::Open(const PVR_CHANNEL &channelinfo)
+{
+  m_channel = channelinfo.number;
+  m_tag     = channelinfo.bouquet;
+
+  if(!m_session.Connect(g_szHostname, g_iPortHTSP))
+    return false;
+
+  if(!g_szUsername.IsEmpty())
+    m_session.Auth(g_szUsername, g_szPassword);
+
+  m_session.SendEnableAsync();
+
+  if(!m_session.SendSubscribe(m_subs, m_channel))
+    return false;
+
+  m_StatusCount = 0;
+  m_SkipIFrame = 0;
+
+  while(m_Streams.nstreams == 0 && m_StatusCount == 0 )
+  {
+    DemuxPacket* pkg = Read();
+    if(!pkg)
+    {
+      Close();
+      return false;
+    }
+    PVR->FreeDemuxPacket(pkg);
+  }
+
+  return true;
+}
+
+void cHTSPDemux::Close()
+{
+  m_session.Close();
+}
+
+bool cHTSPDemux::GetStreamProperties(PVR_STREAMPROPS* props)
+{
+  props->nstreams = m_Streams.nstreams;
+  for (int i = 0; i < m_Streams.nstreams; i++)
+  {
+    props->stream[i].id           = m_Streams.stream[i].id;
+    props->stream[i].physid       = m_Streams.stream[i].physid;
+    props->stream[i].codec_type   = m_Streams.stream[i].codec_type;
+    props->stream[i].codec_id     = m_Streams.stream[i].codec_id;
+    props->stream[i].height       = m_Streams.stream[i].height;
+    props->stream[i].width        = m_Streams.stream[i].width;
+    props->stream[i].language[0]  = m_Streams.stream[i].language[0];
+    props->stream[i].language[1]  = m_Streams.stream[i].language[1];
+    props->stream[i].language[2]  = m_Streams.stream[i].language[2];
+    props->stream[i].language[3]  = m_Streams.stream[i].language[3];
+    props->stream[i].identifier   = m_Streams.stream[i].identifier;
+  }
+  return (props->nstreams > 0);
+}
+
+void cHTSPDemux::Abort()
+{
+  m_Streams.nstreams = 0;
+  m_session.Abort();
+}
+
+DemuxPacket* cHTSPDemux::Read()
+{
+  htsmsg_t *  msg;
+  const char* method;
+  while((msg = ReadStream()))
+  {
+    method = htsmsg_get_str(msg, "method");
+    if(method == NULL)
+      break;
+
+    if     (strcmp("subscriptionStart",  method) == 0)
+    {
+      SubscriptionStart(msg);
+      m_SkipIFrame      = 0;
+      DemuxPacket* pkt  = PVR->AllocateDemuxPacket(0);
+      pkt->iStreamId    = DMX_SPECIALID_STREAMCHANGE;
+      htsmsg_destroy(msg);
+      return pkt;
+    }
+    else if(strcmp("subscriptionStop",   method) == 0)
+      SubscriptionStop (msg);
+    else if(strcmp("subscriptionStatus", method) == 0)
+      SubscriptionStatus(msg);
+    else if(strcmp("queueStatus"       , method) == 0)
+      cHTSPSession::ParseQueueStatus(msg, m_QueueStatus);
+    else if(strcmp("signalStatus"       , method) == 0)
+      cHTSPSession::ParseSignalStatus(msg, m_Quality);
+    else if(strcmp("muxpkt"            , method) == 0)
+    {
+      uint32_t    index, duration, frametype;
+      const void* bin;
+      size_t      binlen;
+      int64_t     ts;
+      char        frametypechar[1];
+
+      htsmsg_get_u32(msg, "frametype", &frametype);
+      frametypechar[0] = static_cast<char>( frametype );
+      XBMC->Log(LOG_DEBUG, "%s - Frame type %c", __FUNCTION__, frametypechar[0]);
+
+      // Not the best solution - need to find how to pause video player for longer time.
+      // This way video player could get enough audio packets in buffer to play without audio discontinuity
+
+      // Jumpy video error on channel change is fixed if first I-frame is not send to demuxer on MPEG2 stream
+      // Problem exists on some HD H264 stream, but it can help if 2 I-frames are skipped :D
+      // SD H264 stream not tested
+      if (g_bSkipIFrame && m_SkipIFrame == 0 && frametypechar[0]=='I')
+      {
+        m_SkipIFrame++;
+        XBMC->Log(LOG_DEBUG, "%s - Skipped first I-frame", __FUNCTION__);
+        htsmsg_destroy(msg);
+        DemuxPacket* pkt  = PVR->AllocateDemuxPacket(0);
+        return pkt;
+      }
+
+      if(htsmsg_get_u32(msg, "stream" , &index)  ||
+         htsmsg_get_bin(msg, "payload", &bin, &binlen))
+        break;
+
+      DemuxPacket* pkt = PVR->AllocateDemuxPacket(binlen);
+      memcpy(pkt->pData, bin, binlen);
+
+      pkt->iSize = binlen;
+
+      if(!htsmsg_get_u32(msg, "duration", &duration))
+        pkt->duration = (double)duration * DVD_TIME_BASE / 1000000;
+
+      if(!htsmsg_get_s64(msg, "dts", &ts))
+        pkt->dts = (double)ts * DVD_TIME_BASE / 1000000;
+      else
+        pkt->dts = DVD_NOPTS_VALUE;
+
+      if(!htsmsg_get_s64(msg, "pts", &ts))
+        pkt->pts = (double)ts * DVD_TIME_BASE / 1000000;
+      else
+        pkt->pts = DVD_NOPTS_VALUE;
+
+      pkt->iStreamId = -1;
+      for(int i = 0; i < m_Streams.nstreams; i++)
+      {
+        if(m_Streams.stream[i].physid == (int)index)
+        {
+          pkt->iStreamId = i;
+          break;
+        }
+      }
+
+      htsmsg_destroy(msg);
+      return pkt;
+    }
+
+    break;
+  }
+
+  if(msg)
+  {
+    htsmsg_destroy(msg);
+    DemuxPacket* pkt  = PVR->AllocateDemuxPacket(0);
+    return pkt;
+  }
+  return NULL;
+}
+
+bool cHTSPDemux::SwitchChannel(const PVR_CHANNEL &channelinfo)
+{
+  XBMC->Log(LOG_DEBUG, "%s - changing to channel %d", __FUNCTION__, channelinfo.number);
+
+  if (!m_session.SendUnsubscribe(m_subs))
+    XBMC->Log(LOG_ERROR, "%s - failed to unsubscribe from previous channel", __FUNCTION__);
+
+  if (!m_session.SendSubscribe(m_subs+1, channelinfo.number))
+    XBMC->Log(LOG_ERROR, "%s - failed to set channel", __FUNCTION__);
+  else
+  {
+    m_channel           = channelinfo.number;
+    m_subs              = m_subs+1;
+    m_Streams.nstreams  = 0;
+    m_StatusCount       = 0;
+    while (m_Streams.nstreams == 0 && m_StatusCount == 0)
+    {
+      DemuxPacket* pkg = Read();
+      if (!pkg)
+      {
+        return false;
+      }
+      PVR->FreeDemuxPacket(pkg);
+    }
+    return true;
+  }
+  return false;
+}
+
+bool cHTSPDemux::GetSignalStatus(PVR_SIGNALQUALITY &qualityinfo)
+{
+  if (m_SourceInfo.si_adapter.IsEmpty() || m_Quality.fe_status.IsEmpty())
+    return false;
+
+  strncpy(qualityinfo.frontend_name, m_SourceInfo.si_adapter.c_str(), sizeof(qualityinfo.frontend_name));
+  strncpy(qualityinfo.frontend_status, m_Quality.fe_status.c_str(), sizeof(qualityinfo.frontend_status));
+  qualityinfo.signal = (uint16_t)m_Quality.fe_signal;
+  qualityinfo.snr = (uint16_t)m_Quality.fe_snr;
+  qualityinfo.ber = (uint32_t)m_Quality.fe_ber;
+  qualityinfo.unc = (uint32_t)m_Quality.fe_unc;
+  qualityinfo.video_bitrate = 0;
+  qualityinfo.audio_bitrate = 0;
+  qualityinfo.dolby_bitrate = 0;
+
+  return true;
+}
+
+void cHTSPDemux::SubscriptionStart (htsmsg_t *m)
+{
+  htsmsg_t       *streams;
+  htsmsg_field_t *f;
+  if((streams = htsmsg_get_list(m, "streams")) == NULL)
+  {
+    XBMC->Log(LOG_ERROR, "%s - malformed message", __FUNCTION__);
+    return;
+  }
+
+  m_Streams.nstreams = 0;
+
+  HTSMSG_FOREACH(f, streams)
+  {
+    uint32_t    index;
+    const char* type;
+    htsmsg_t* sub;
+
+    if (f->hmf_type != HMF_MAP)
+      continue;
+
+    sub = &f->hmf_msg;
+
+    if ((type = htsmsg_get_str(sub, "type")) == NULL)
+      continue;
+
+    if (htsmsg_get_u32(sub, "index", &index))
+      continue;
+
+    const char *language = htsmsg_get_str(sub, "language");
+    XBMC->Log(LOG_DEBUG, "%s - id: %d, type: %s, language: %s", __FUNCTION__, index, type, language);
+
+    m_Streams.stream[m_Streams.nstreams].fpsscale         = 0;
+    m_Streams.stream[m_Streams.nstreams].fpsrate          = 0;
+    m_Streams.stream[m_Streams.nstreams].height           = 0;
+    m_Streams.stream[m_Streams.nstreams].width            = 0;
+    m_Streams.stream[m_Streams.nstreams].aspect           = 0.0;
+
+    m_Streams.stream[m_Streams.nstreams].channels         = 0;
+    m_Streams.stream[m_Streams.nstreams].samplerate       = 0;
+    m_Streams.stream[m_Streams.nstreams].blockalign       = 0;
+    m_Streams.stream[m_Streams.nstreams].bitrate          = 0;
+    m_Streams.stream[m_Streams.nstreams].bits_per_sample  = 0;
+
+    if(!strcmp(type, "AC3"))
+    {
+      m_Streams.stream[m_Streams.nstreams].id         = m_Streams.nstreams;
+      m_Streams.stream[m_Streams.nstreams].physid     = index;
+      m_Streams.stream[m_Streams.nstreams].codec_type = CODEC_TYPE_AUDIO;
+      m_Streams.stream[m_Streams.nstreams].codec_id   = CODEC_ID_AC3;
+      if (language == NULL)
+      {
+        m_Streams.stream[m_Streams.nstreams].language[0]= 0;
+        m_Streams.stream[m_Streams.nstreams].language[1]= 0;
+        m_Streams.stream[m_Streams.nstreams].language[2]= 0;
+        m_Streams.stream[m_Streams.nstreams].language[3]= 0;
+      }
+      else
+      {
+        m_Streams.stream[m_Streams.nstreams].language[0]= language[0];
+        m_Streams.stream[m_Streams.nstreams].language[1]= language[1];
+        m_Streams.stream[m_Streams.nstreams].language[2]= language[2];
+        m_Streams.stream[m_Streams.nstreams].language[3]= 0;
+      }
+      m_Streams.stream[m_Streams.nstreams].identifier = -1;
+      m_Streams.nstreams++;
+    }
+    else if(!strcmp(type, "EAC3"))
+    {
+      m_Streams.stream[m_Streams.nstreams].id         = m_Streams.nstreams;
+      m_Streams.stream[m_Streams.nstreams].physid     = index;
+      m_Streams.stream[m_Streams.nstreams].codec_type = CODEC_TYPE_AUDIO;
+      m_Streams.stream[m_Streams.nstreams].codec_id   = CODEC_ID_EAC3;
+      if (language == NULL)
+      {
+        m_Streams.stream[m_Streams.nstreams].language[0]= 0;
+        m_Streams.stream[m_Streams.nstreams].language[1]= 0;
+        m_Streams.stream[m_Streams.nstreams].language[2]= 0;
+        m_Streams.stream[m_Streams.nstreams].language[3]= 0;
+      }
+      else
+      {
+        m_Streams.stream[m_Streams.nstreams].language[0]= language[0];
+        m_Streams.stream[m_Streams.nstreams].language[1]= language[1];
+        m_Streams.stream[m_Streams.nstreams].language[2]= language[2];
+        m_Streams.stream[m_Streams.nstreams].language[3]= 0;
+      }
+      m_Streams.stream[m_Streams.nstreams].identifier = -1;
+      m_Streams.nstreams++;
+    }
+    else if(!strcmp(type, "MPEG2AUDIO"))
+    {
+      m_Streams.stream[m_Streams.nstreams].id         = m_Streams.nstreams;
+      m_Streams.stream[m_Streams.nstreams].physid     = index;
+      m_Streams.stream[m_Streams.nstreams].codec_type = CODEC_TYPE_AUDIO;
+      m_Streams.stream[m_Streams.nstreams].codec_id   = CODEC_ID_MP2;
+      if (language == NULL)
+      {
+        m_Streams.stream[m_Streams.nstreams].language[0]= 0;
+        m_Streams.stream[m_Streams.nstreams].language[1]= 0;
+        m_Streams.stream[m_Streams.nstreams].language[2]= 0;
+        m_Streams.stream[m_Streams.nstreams].language[3]= 0;
+      }
+      else
+      {
+        m_Streams.stream[m_Streams.nstreams].language[0]= language[0];
+        m_Streams.stream[m_Streams.nstreams].language[1]= language[1];
+        m_Streams.stream[m_Streams.nstreams].language[2]= language[2];
+        m_Streams.stream[m_Streams.nstreams].language[3]= 0;
+      }
+      m_Streams.stream[m_Streams.nstreams].identifier = -1;
+      m_Streams.nstreams++;
+    }
+    else if(!strcmp(type, "AAC"))
+    {
+      m_Streams.stream[m_Streams.nstreams].id         = m_Streams.nstreams;
+      m_Streams.stream[m_Streams.nstreams].physid     = index;
+      m_Streams.stream[m_Streams.nstreams].codec_type = CODEC_TYPE_AUDIO;
+      m_Streams.stream[m_Streams.nstreams].codec_id   = CODEC_ID_AAC;
+      if (language == NULL)
+      {
+        m_Streams.stream[m_Streams.nstreams].language[0]= 0;
+        m_Streams.stream[m_Streams.nstreams].language[1]= 0;
+        m_Streams.stream[m_Streams.nstreams].language[2]= 0;
+        m_Streams.stream[m_Streams.nstreams].language[3]= 0;
+      }
+      else
+      {
+        m_Streams.stream[m_Streams.nstreams].language[0]= language[0];
+        m_Streams.stream[m_Streams.nstreams].language[1]= language[1];
+        m_Streams.stream[m_Streams.nstreams].language[2]= language[2];
+        m_Streams.stream[m_Streams.nstreams].language[3]= 0;
+      }
+      m_Streams.stream[m_Streams.nstreams].identifier = -1;
+      m_Streams.nstreams++;
+    }
+    else if(!strcmp(type, "MPEG2VIDEO"))
+    {
+      m_Streams.stream[m_Streams.nstreams].id         = m_Streams.nstreams;
+      m_Streams.stream[m_Streams.nstreams].physid     = index;
+      m_Streams.stream[m_Streams.nstreams].codec_type = CODEC_TYPE_VIDEO;
+      m_Streams.stream[m_Streams.nstreams].codec_id   = CODEC_ID_MPEG2VIDEO;
+      m_Streams.stream[m_Streams.nstreams].width      = htsmsg_get_u32_or_default(sub, "width" , 0);
+      m_Streams.stream[m_Streams.nstreams].height     = htsmsg_get_u32_or_default(sub, "height" , 0);
+      m_Streams.stream[m_Streams.nstreams].language[0]= 0;
+      m_Streams.stream[m_Streams.nstreams].language[1]= 0;
+      m_Streams.stream[m_Streams.nstreams].language[2]= 0;
+      m_Streams.stream[m_Streams.nstreams].language[3]= 0;
+      m_Streams.stream[m_Streams.nstreams].identifier = -1;
+      m_Streams.nstreams++;
+    }
+    else if(!strcmp(type, "H264"))
+    {
+      m_Streams.stream[m_Streams.nstreams].id         = m_Streams.nstreams;
+      m_Streams.stream[m_Streams.nstreams].physid     = index;
+      m_Streams.stream[m_Streams.nstreams].codec_type = CODEC_TYPE_VIDEO;
+      m_Streams.stream[m_Streams.nstreams].codec_id   = CODEC_ID_H264;
+      m_Streams.stream[m_Streams.nstreams].width      = htsmsg_get_u32_or_default(sub, "width" , 0);
+      m_Streams.stream[m_Streams.nstreams].height     = htsmsg_get_u32_or_default(sub, "height" , 0);
+      m_Streams.stream[m_Streams.nstreams].language[0]= 0;
+      m_Streams.stream[m_Streams.nstreams].language[1]= 0;
+      m_Streams.stream[m_Streams.nstreams].language[2]= 0;
+      m_Streams.stream[m_Streams.nstreams].language[3]= 0;
+      m_Streams.stream[m_Streams.nstreams].identifier = -1;
+      m_Streams.nstreams++;
+    }
+    else if(!strcmp(type, "DVBSUB"))
+    {
+      uint32_t composition_id = 0, ancillary_id = 0;
+      htsmsg_get_u32(sub, "composition_id", &composition_id);
+      htsmsg_get_u32(sub, "ancillary_id"  , &ancillary_id);
+
+      m_Streams.stream[m_Streams.nstreams].id         = m_Streams.nstreams;
+      m_Streams.stream[m_Streams.nstreams].physid     = index;
+      m_Streams.stream[m_Streams.nstreams].codec_type = CODEC_TYPE_SUBTITLE;
+      m_Streams.stream[m_Streams.nstreams].codec_id   = CODEC_ID_DVB_SUBTITLE;
+      if (language == NULL)
+      {
+        m_Streams.stream[m_Streams.nstreams].language[0]= 0;
+        m_Streams.stream[m_Streams.nstreams].language[1]= 0;
+        m_Streams.stream[m_Streams.nstreams].language[2]= 0;
+        m_Streams.stream[m_Streams.nstreams].language[3]= 0;
+      }
+      else
+      {
+        m_Streams.stream[m_Streams.nstreams].language[0]= language[0];
+        m_Streams.stream[m_Streams.nstreams].language[1]= language[1];
+        m_Streams.stream[m_Streams.nstreams].language[2]= language[2];
+        m_Streams.stream[m_Streams.nstreams].language[3]= 0;
+      }
+      m_Streams.stream[m_Streams.nstreams].identifier = (composition_id & 0xffff) | ((ancillary_id & 0xffff) << 16);
+      m_Streams.nstreams++;
+    }
+    else if(!strcmp(type, "TEXTSUB"))
+    {
+      m_Streams.stream[m_Streams.nstreams].id         = m_Streams.nstreams;
+      m_Streams.stream[m_Streams.nstreams].physid     = index;
+      m_Streams.stream[m_Streams.nstreams].codec_type = CODEC_TYPE_SUBTITLE;
+      m_Streams.stream[m_Streams.nstreams].codec_id   = CODEC_ID_TEXT;
+      if (language == NULL)
+      {
+        m_Streams.stream[m_Streams.nstreams].language[0]= 0;
+        m_Streams.stream[m_Streams.nstreams].language[1]= 0;
+        m_Streams.stream[m_Streams.nstreams].language[2]= 0;
+        m_Streams.stream[m_Streams.nstreams].language[3]= 0;
+      }
+      else
+      {
+        m_Streams.stream[m_Streams.nstreams].language[0]= language[0];
+        m_Streams.stream[m_Streams.nstreams].language[1]= language[1];
+        m_Streams.stream[m_Streams.nstreams].language[2]= language[2];
+        m_Streams.stream[m_Streams.nstreams].language[3]= 0;
+      }
+      m_Streams.stream[m_Streams.nstreams].identifier = -1;
+      m_Streams.nstreams++;
+    }
+    else if(!strcmp(type, "TELETEXT"))
+    {
+      m_Streams.stream[m_Streams.nstreams].id         = m_Streams.nstreams;
+      m_Streams.stream[m_Streams.nstreams].physid     = index;
+      m_Streams.stream[m_Streams.nstreams].codec_type = CODEC_TYPE_SUBTITLE;
+      m_Streams.stream[m_Streams.nstreams].codec_id   = CODEC_ID_DVB_TELETEXT;
+      m_Streams.stream[m_Streams.nstreams].language[0]= 0;
+      m_Streams.stream[m_Streams.nstreams].language[1]= 0;
+      m_Streams.stream[m_Streams.nstreams].language[2]= 0;
+      m_Streams.stream[m_Streams.nstreams].language[3]= 0;
+      m_Streams.stream[m_Streams.nstreams].identifier = -1;
+      m_Streams.nstreams++;
+    }
+
+    if (m_Streams.nstreams >= PVR_STREAM_MAX_STREAMS)
+    {
+      XBMC->Log(LOG_ERROR, "%s - max amount of streams reached", __FUNCTION__);
+      break;
+    }
+  }
+
+  if (cHTSPSession::ParseSourceInfo(m, m_SourceInfo))
+  {
+    XBMC->Log(LOG_DEBUG, "%s - subscription started on adapter %s, mux %s, network %s, provider %s, service %s"
+        , __FUNCTION__, m_SourceInfo.si_adapter.c_str(), m_SourceInfo.si_mux.c_str(),
+        m_SourceInfo.si_network.c_str(), m_SourceInfo.si_provider.c_str(),
+        m_SourceInfo.si_service.c_str());
+  }
+  else
+  {
+    XBMC->Log(LOG_DEBUG, "%s - subscription started on an unknown device", __FUNCTION__);
+  }
+}
+
+void cHTSPDemux::SubscriptionStop  (htsmsg_t *m)
+{
+  XBMC->Log(LOG_DEBUG, "%s - subscription ended on adapter %s", __FUNCTION__, m_SourceInfo.si_adapter.c_str());
+  m_Streams.nstreams = 0;
+
+  /* reset the signal status */
+  m_Quality.fe_status = "";
+  m_Quality.fe_ber    = -2;
+  m_Quality.fe_signal = -2;
+  m_Quality.fe_snr    = -2;
+  m_Quality.fe_unc    = -2;
+
+  /* reset the source info */
+  m_SourceInfo.si_adapter = "";
+  m_SourceInfo.si_mux = "";
+  m_SourceInfo.si_network = "";
+  m_SourceInfo.si_provider = "";
+  m_SourceInfo.si_service = "";
+}
+
+void cHTSPDemux::SubscriptionStatus(htsmsg_t *m)
+{
+  const char* status;
+  status = htsmsg_get_str(m, "status");
+  if(status == NULL)
+    m_Status = "";
+  else
+  {
+    m_StatusCount++;
+    m_Status = status;
+    XBMC->Log(LOG_DEBUG, "%s - %s", __FUNCTION__, status);
+    XBMC->QueueNotification(QUEUE_INFO, status);
+  }
+}
+
+htsmsg_t* cHTSPDemux::ReadStream()
+{
+  htsmsg_t* msg;
+
+  /* after anything has started reading, *
+   * we can guarantee a new stream       */
+
+  while((msg = m_session.ReadMessage(1000)))
+  {
+    const char* method;
+    if((method = htsmsg_get_str(msg, "method")) == NULL)
+      return msg;
+
+    if     (strstr(method, "channelAdd"))
+      cHTSPSession::ParseChannelUpdate(msg, m_channels);
+    else if(strstr(method, "channelUpdate"))
+      cHTSPSession::ParseChannelUpdate(msg, m_channels);
+    else if(strstr(method, "channelDelete"))
+      cHTSPSession::ParseChannelRemove(msg, m_channels);
+
+    uint32_t subs;
+    if(htsmsg_get_u32(msg, "subscriptionId", &subs) || subs != m_subs)
+    {
+      htsmsg_destroy(msg);
+      continue;
+    }
+
+    return msg;
+  }
+  return NULL;
+}
diff --git a/xbmc/pvrclients/tvheadend/HTSPDemux.h b/xbmc/pvrclients/tvheadend/HTSPDemux.h
new file mode 100644 (file)
index 0000000..e394f5b
--- /dev/null
@@ -0,0 +1,62 @@
+#pragma once
+
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "client.h"
+#include "HTSPSession.h"
+
+class cHTSPDemux
+{
+public:
+  cHTSPDemux();
+  ~cHTSPDemux();
+
+  bool Open(const PVR_CHANNEL &channelinfo);
+  void Close();
+  bool GetStreamProperties(PVR_STREAMPROPS* props);
+  void Abort();
+  DemuxPacket* Read();
+  bool SwitchChannel(const PVR_CHANNEL &channelinfo);
+  int CurrentChannel() { return m_channel; }
+  bool GetSignalStatus(PVR_SIGNALQUALITY &qualityinfo);
+
+protected:
+  void SubscriptionStart (htsmsg_t *m);
+  void SubscriptionStop  (htsmsg_t *m);
+  void SubscriptionStatus(htsmsg_t *m);
+
+  htsmsg_t* ReadStream();
+
+private:
+  unsigned        m_subs;
+  cHTSPSession    m_session;
+  int             m_channel;
+  int             m_tag;
+  int             m_StatusCount;
+  int             m_SkipIFrame;
+  CStdString      m_Status;
+  PVR_STREAMPROPS m_Streams;
+  SChannels       m_channels;
+  SQueueStatus    m_QueueStatus;
+  SQuality        m_Quality;
+  SSourceInfo     m_SourceInfo;
+};
diff --git a/xbmc/pvrclients/tvheadend/HTSPSession.cpp b/xbmc/pvrclients/tvheadend/HTSPSession.cpp
new file mode 100644 (file)
index 0000000..11983c1
--- /dev/null
@@ -0,0 +1,680 @@
+/*
+ *      Copyright (C) 2005-2008 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "HTSPSession.h"
+#include "client.h"
+#ifdef _MSC_VER
+#include <winsock2.h>
+#define SHUT_RDWR SD_BOTH
+#ifndef ETIMEDOUT
+#define ETIMEDOUT WSAETIMEDOUT
+#endif
+#else
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#endif
+
+extern "C" {
+#include "libhts/net.h"
+#include "libhts/htsmsg.h"
+#include "libhts/htsmsg_binary.h"
+#include "libhts/sha1.h"
+}
+
+using namespace std;
+
+cHTSPSession::cHTSPSession()
+  : m_fd(INVALID_SOCKET)
+  , m_seq(0)
+  , m_challenge(NULL)
+  , m_challenge_len(0)
+  , m_protocol(0)
+  , m_connected(false)
+  , m_queue_size(1000)
+{
+}
+
+cHTSPSession::~cHTSPSession()
+{
+  Close();
+}
+
+void cHTSPSession::Abort()
+{
+  shutdown(m_fd, SHUT_RDWR);
+}
+
+void cHTSPSession::Close()
+{
+  if (!m_connected) return;
+  m_connected = false;
+
+  if(m_fd != INVALID_SOCKET)
+  {
+    closesocket(m_fd);
+    m_fd = INVALID_SOCKET;
+  }
+
+  if(m_challenge)
+  {
+    free(m_challenge);
+    m_challenge     = NULL;
+    m_challenge_len = 0;
+  }
+}
+
+bool cHTSPSession::Connect(const std::string& hostname, int port)
+{
+  char errbuf[1024];
+  int  errlen = sizeof(errbuf);
+  htsmsg_t *m;
+  const char *method, *server, *version;
+  const void * chall = NULL;
+  size_t chall_len = 0;
+  int32_t proto = 0;
+
+  if(port == 0)
+    port = 9982;
+
+  XBMC->Log(LOG_DEBUG, "%s - connecting to '%s', port '%d'\n", __FUNCTION__, hostname.c_str(), port);
+
+  m_fd = htsp_tcp_connect(hostname.c_str()
+                        , port
+                        , errbuf, errlen, 3000);
+  if(m_fd == INVALID_SOCKET)
+  {
+    XBMC->Log(LOG_ERROR, "%s - failed to connect to server (%s)\n", __FUNCTION__, errbuf);
+    return false;
+  }
+
+  // send hello
+  m = htsmsg_create_map();
+  htsmsg_add_str(m, "method", "hello");
+  htsmsg_add_str(m, "clientname", "XBMC Media Center");
+  htsmsg_add_u32(m, "htspversion", 1);
+
+  // read welcome
+  if((m = ReadResult(m)) == NULL)
+  {
+    XBMC->Log(LOG_ERROR, "%s - failed to read greeting from server", __FUNCTION__);
+    return false;
+  }
+  method  = htsmsg_get_str(m, "method");
+            htsmsg_get_s32(m, "htspversion", &proto);
+  server  = htsmsg_get_str(m, "servername");
+  version = htsmsg_get_str(m, "serverversion");
+            htsmsg_get_bin(m, "challenge", &chall, &chall_len);
+
+  XBMC->Log(LOG_DEBUG, "%s - connected to server: [%s], version: [%s], proto: %d"
+      , __FUNCTION__, server ? server : "", version ? version : "", proto);
+
+  m_server   = server;
+  m_version  = version;
+  m_protocol = proto;
+
+  if(chall && chall_len)
+  {
+    m_challenge     = malloc(chall_len);
+    m_challenge_len = chall_len;
+    memcpy(m_challenge, chall, chall_len);
+  }
+
+  htsmsg_destroy(m);
+  m_connected = true;
+  return true;
+}
+
+bool cHTSPSession::Auth(const std::string& username, const std::string& password)
+{
+  htsmsg_t *m = htsmsg_create_map();
+  htsmsg_add_str(m, "method"  , "authenticate");
+  htsmsg_add_str(m, "username", username.c_str());
+
+  if(password != "" && m_challenge)
+  {
+    struct HTSSHA1* shactx = (struct HTSSHA1*) malloc(hts_sha1_size);
+    uint8_t d[20];
+    hts_sha1_init(shactx);
+    hts_sha1_update(shactx
+                 , (const uint8_t *)password.c_str()
+                 , password.length());
+    hts_sha1_update(shactx
+                 , (const uint8_t *)m_challenge
+                 , m_challenge_len);
+    hts_sha1_final(shactx, d);
+    htsmsg_add_bin(m, "digest", d, 20);
+    free(shactx);
+  }
+
+  return ReadSuccess(m, false, "get reply from authentication with server");
+}
+
+htsmsg_t* cHTSPSession::ReadMessage(int timeout)
+{
+  void*    buf;
+  uint32_t l;
+  int      x;
+
+  if(m_queue.size())
+  {
+    htsmsg_t* m = m_queue.front();
+    m_queue.pop_front();
+    return m;
+  }
+
+  x = htsp_tcp_read_timeout(m_fd, &l, 4, timeout);
+  if(x == ETIMEDOUT)
+    return htsmsg_create_map();
+
+  if(x)
+  {
+    XBMC->Log(LOG_ERROR, "%s - Failed to read packet size (%d)\n", __FUNCTION__, x);
+    return NULL;
+  }
+
+  l   = ntohl(l);
+  if(l == 0)
+    return htsmsg_create_map();
+
+  buf = malloc(l);
+
+  x = htsp_tcp_read(m_fd, buf, l);
+  if(x)
+  {
+    XBMC->Log(LOG_ERROR, "%s - Failed to read packet (%d)\n", __FUNCTION__, x);
+    free(buf);
+    return NULL;
+  }
+
+  return htsmsg_binary_deserialize(buf, l, buf); /* consumes 'buf' */
+}
+
+bool cHTSPSession::SendMessage(htsmsg_t* m)
+{
+  void*  buf;
+  size_t len;
+
+  if(htsmsg_binary_serialize(m, &buf, &len, -1) < 0)
+  {
+    htsmsg_destroy(m);
+    return false;
+  }
+  htsmsg_destroy(m);
+
+  if(send(m_fd, (char*)buf, len, 0) < 0)
+  {
+    free(buf);
+    Close();
+    return false;
+  }
+  free(buf);
+  return true;
+}
+
+htsmsg_t* cHTSPSession::ReadResult(htsmsg_t* m, bool sequence)
+{
+  if(sequence)
+    htsmsg_add_u32(m, "seq", ++m_seq);
+
+  if(!SendMessage(m))
+    return NULL;
+
+  std::deque<htsmsg_t*> queue;
+  m_queue.swap(queue);
+
+  while((m = ReadMessage()))
+  {
+    uint32_t seq;
+    if(!sequence)
+      break;
+    if(!htsmsg_get_u32(m, "seq", &seq) && seq == m_seq)
+      break;
+
+    queue.push_back(m);
+    if(queue.size() >= m_queue_size)
+    {
+      XBMC->Log(LOG_ERROR, "%s - maximum queue size (%u) reached", __FUNCTION__, m_queue_size);
+      m_queue.swap(queue);
+      return NULL;
+    }
+  }
+
+  m_queue.swap(queue);
+
+  const char* error;
+  if(m && (error = htsmsg_get_str(m, "error")))
+  {
+    XBMC->Log(LOG_ERROR, "%s - error (%s)", __FUNCTION__, error);
+    htsmsg_destroy(m);
+    return NULL;
+  }
+  uint32_t noaccess;
+  if(m && !htsmsg_get_u32(m, "noaccess", &noaccess) && noaccess)
+  {
+    XBMC->Log(LOG_ERROR, "%s - access denied (%d)", __FUNCTION__, noaccess);
+    htsmsg_destroy(m);
+    return NULL;
+  }
+
+  return m;
+}
+
+bool cHTSPSession::ReadSuccess(htsmsg_t* m, bool sequence, std::string action)
+{
+  if((m = ReadResult(m, sequence)) == NULL)
+  {
+    XBMC->Log(LOG_DEBUG, "%s - failed to %s", __FUNCTION__, action.c_str());
+    return false;
+  }
+  htsmsg_destroy(m);
+  return true;
+}
+
+bool cHTSPSession::SendSubscribe(int subscription, int channel)
+{
+  htsmsg_t *m = htsmsg_create_map();
+  htsmsg_add_str(m, "method"        , "subscribe");
+  htsmsg_add_s32(m, "channelId"     , channel);
+  htsmsg_add_s32(m, "subscriptionId", subscription);
+  return ReadSuccess(m, true, "subscribe to channel");
+}
+
+bool cHTSPSession::SendUnsubscribe(int subscription)
+{
+  htsmsg_t *m = htsmsg_create_map();
+  htsmsg_add_str(m, "method"        , "unsubscribe");
+  htsmsg_add_s32(m, "subscriptionId", subscription);
+  return ReadSuccess(m, true, "unsubscribe from channel");
+}
+
+bool cHTSPSession::SendEnableAsync()
+{
+  htsmsg_t *m = htsmsg_create_map();
+  htsmsg_add_str(m, "method", "enableAsyncMetadata");
+  return ReadSuccess(m, true, "enableAsyncMetadata failed");
+}
+
+bool cHTSPSession::GetEvent(SEvent& event, uint32_t id)
+{
+  if(id == 0)
+  {
+    event.Clear();
+    return false;
+  }
+
+  htsmsg_t *msg = htsmsg_create_map();
+  htsmsg_add_str(msg, "method", "getEvent");
+  htsmsg_add_u32(msg, "eventId", id);
+  if((msg = ReadResult(msg, true)) == NULL)
+  {
+    XBMC->Log(LOG_DEBUG, "%s - failed to get event %d", __FUNCTION__, id);
+    return false;
+  }
+  return ParseEvent(msg, id, event);
+}
+
+bool cHTSPSession::ParseEvent(htsmsg_t* msg, uint32_t id, SEvent &event)
+{
+  uint32_t start, stop, next, chan_id, content;
+  const char *title, *desc;
+  if(         htsmsg_get_u32(msg, "start", &start)
+  ||          htsmsg_get_u32(msg, "stop" , &stop)
+  || (title = htsmsg_get_str(msg, "title")) == NULL)
+  {
+    XBMC->Log(LOG_DEBUG, "%s - malformed event", __FUNCTION__);
+    htsmsg_print(msg);
+    htsmsg_destroy(msg);
+    return false;
+  }
+  event.Clear();
+  event.id    = id;
+  event.start = start;
+  event.stop  = stop;
+  event.title = title;
+
+  if((desc = htsmsg_get_str(msg, "description")))
+    event.descs = desc;
+  if(htsmsg_get_u32(msg, "nextEventId", &next))
+    event.next = 0;
+  else
+    event.next = next;
+  if(htsmsg_get_u32(msg, "channelId", &chan_id))
+    event.chan_id = -1;
+  else
+    event.chan_id = chan_id;
+  if(htsmsg_get_u32(msg, "contentType", &content))
+    event.content = -1;
+  else
+    event.content = content;
+
+  XBMC->Log(LOG_DEBUG, "%s - id:%u, chan_id:%u, title:'%s', genre_type:%u, genre_sub_type:%u, desc:'%s', start:%u, stop:%u, next:%u"
+                    , __FUNCTION__
+                    , event.id
+                    , event.chan_id
+                    , event.title.c_str()
+                    , event.content & 0x0F
+                    , event.content & 0xF0
+                    , event.descs.c_str()
+                    , event.start
+                    , event.stop
+                    , event.next);
+
+  return true;
+}
+
+void cHTSPSession::ParseChannelUpdate(htsmsg_t* msg, SChannels &channels)
+{
+  uint32_t id, event = 0, num = 0, caid = 0;
+  const char *name, *icon;
+  if(htsmsg_get_u32(msg, "channelId", &id))
+  {
+    XBMC->Log(LOG_ERROR, "%s - malformed message received", __FUNCTION__);
+    htsmsg_print(msg);
+    return;
+  }
+
+  SChannel &channel = channels[id];
+  channel.id = id;
+
+  if(htsmsg_get_u32(msg, "eventId", &event) == 0)
+    channel.event = event;
+
+  if((name = htsmsg_get_str(msg, "channelName")))
+    channel.name = name;
+
+  if((icon = htsmsg_get_str(msg, "channelIcon")))
+    channel.icon = icon;
+
+  if(htsmsg_get_u32(msg, "channelCaid", &caid) == 0)
+    channel.caid = caid;
+
+  if(htsmsg_get_u32(msg, "channelNumber", &num) == 0)
+  {
+    if(num == 0)
+      channel.num = id + 1000;
+    else
+      channel.num = num;
+  }
+  else
+    channel.num = id; // fallback older servers
+
+  htsmsg_t *tags;
+
+  if((tags = htsmsg_get_list(msg, "tags")))
+  {
+    channel.tags.clear();
+
+    htsmsg_field_t *f;
+    HTSMSG_FOREACH(f, tags)
+    {
+      if(f->hmf_type != HMF_S64)
+        continue;
+      channel.tags.push_back((int)f->hmf_s64);
+    }
+  }
+
+  htsmsg_t *services;
+  
+  if((services = htsmsg_get_list(msg, "services")))
+  {
+    htsmsg_field_t *f;
+    HTSMSG_FOREACH(f, services)
+    {
+      if(f->hmf_type != HMF_MAP)
+        continue;
+
+      htsmsg_t *service = &f->hmf_msg;
+      const char *service_type = htsmsg_get_str(service, "type");
+
+      if(service_type != NULL)
+      {
+        channel.radio = !strcmp(service_type, "Radio");
+      }
+    }
+  }
+  
+  XBMC->Log(LOG_DEBUG, "%s - id:%u, name:'%s', icon:'%s', event:%u"
+      , __FUNCTION__, id, name ? name : "(null)", icon ? icon : "(null)", event);
+}
+
+void cHTSPSession::ParseChannelRemove(htsmsg_t* msg, SChannels &channels)
+{
+  uint32_t id;
+  if(htsmsg_get_u32(msg, "channelId", &id))
+  {
+    XBMC->Log(LOG_ERROR, "%s - malformed message received", __FUNCTION__);
+    htsmsg_print(msg);
+    return;
+  }
+  XBMC->Log(LOG_DEBUG, "%s - id:%u", __FUNCTION__, id);
+
+  channels.erase(id);
+}
+
+void cHTSPSession::ParseTagUpdate(htsmsg_t* msg, STags &tags)
+{
+  uint32_t id;
+  const char *name, *icon;
+  if(htsmsg_get_u32(msg, "tagId", &id))
+  {
+    XBMC->Log(LOG_ERROR, "%s - malformed message received", __FUNCTION__);
+    htsmsg_print(msg);
+    return;
+  }
+  STag &tag = tags[id];
+  tag.id = id;
+
+  if((icon = htsmsg_get_str(msg, "tagIcon")))
+    tag.icon  = icon;
+
+  if((name = htsmsg_get_str(msg, "tagName")))
+    tag.name  = name;
+
+  htsmsg_t *channels;
+
+  if((channels = htsmsg_get_list(msg, "members")))
+  {
+    tag.channels.clear();
+
+    htsmsg_field_t *f;
+    HTSMSG_FOREACH(f, channels)
+    {
+      if(f->hmf_type != HMF_S64)
+        continue;
+      tag.channels.push_back((int)f->hmf_s64);
+    }
+  }
+
+  XBMC->Log(LOG_DEBUG, "%s - id:%u, name:'%s', icon:'%s'"
+      , __FUNCTION__, id, name ? name : "(null)", icon ? icon : "(null)");
+
+}
+
+void cHTSPSession::ParseTagRemove(htsmsg_t* msg, STags &tags)
+{
+  uint32_t id;
+  if(htsmsg_get_u32(msg, "tagId", &id))
+  {
+    XBMC->Log(LOG_ERROR, "%s - malformed message received", __FUNCTION__);
+    htsmsg_print(msg);
+    return;
+  }
+  XBMC->Log(LOG_DEBUG, "%s - id:%u", __FUNCTION__, id);
+
+  tags.erase(id);
+}
+
+bool cHTSPSession::ParseSignalStatus (htsmsg_t* msg, SQuality &quality)
+{
+  if(htsmsg_get_u32(msg, "feSNR", &quality.fe_snr))
+    quality.fe_snr = -2;
+
+  if(htsmsg_get_u32(msg, "feSignal", &quality.fe_signal))
+    quality.fe_signal = -2;
+
+  if(htsmsg_get_u32(msg, "feBER", &quality.fe_ber))
+    quality.fe_ber = -2;
+
+  if(htsmsg_get_u32(msg, "feUNC", &quality.fe_unc))
+    quality.fe_unc = -2;
+
+  const char* status;
+  if((status = htsmsg_get_str(msg, "feStatus")))
+    quality.fe_status = status;
+  else
+    quality.fe_status = "(unknown)";
+
+  XBMC->Log(LOG_DEBUG, "%s - updated signal status: snr=%d, signal=%d, ber=%d, unc=%d, status=%s"
+      , __FUNCTION__, quality.fe_snr, quality.fe_signal, quality.fe_ber
+      , quality.fe_unc, quality.fe_status.c_str());
+
+  return true;
+}
+
+bool cHTSPSession::ParseSourceInfo (htsmsg_t* msg, SSourceInfo &si)
+{
+  htsmsg_t       *sourceinfo;
+  if((sourceinfo = htsmsg_get_map(msg, "sourceinfo")) == NULL)
+  {
+    XBMC->Log(LOG_ERROR, "%s - malformed message", __FUNCTION__);
+    return false;
+  }
+
+  const char* str;
+  if((str = htsmsg_get_str(sourceinfo, "adapter")) == NULL)
+    si.si_adapter = "";
+  else
+    si.si_adapter = str;
+
+  if((str = htsmsg_get_str(sourceinfo, "mux")) == NULL)
+    si.si_mux = "";
+  else
+    si.si_mux = str;
+
+  if((str = htsmsg_get_str(sourceinfo, "network")) == NULL)
+    si.si_network = "";
+  else
+    si.si_network = str;
+
+  if((str = htsmsg_get_str(sourceinfo, "provider")) == NULL)
+    si.si_provider = "";
+  else
+    si.si_provider = str;
+
+  if((str = htsmsg_get_str(sourceinfo, "service")) == NULL)
+    si.si_service = "";
+  else
+    si.si_service = str;
+
+  return true;
+}
+
+bool cHTSPSession::ParseQueueStatus (htsmsg_t* msg, SQueueStatus &queue)
+{
+  if(htsmsg_get_u32(msg, "packets", &queue.packets)
+  || htsmsg_get_u32(msg, "bytes",   &queue.bytes)
+  || htsmsg_get_u32(msg, "Bdrops",  &queue.bdrops)
+  || htsmsg_get_u32(msg, "Pdrops",  &queue.pdrops)
+  || htsmsg_get_u32(msg, "Idrops",  &queue.idrops))
+  {
+    XBMC->Log(LOG_ERROR, "%s - malformed message received", __FUNCTION__);
+    htsmsg_print(msg);
+    return false;
+  }
+
+  /* delay isn't always transmitted */
+  if(htsmsg_get_u32(msg, "delay", &queue.delay))
+    queue.delay = 0;
+
+  return true;
+}
+
+void cHTSPSession::ParseDVREntryUpdate(htsmsg_t* msg, SRecordings &recordings)
+{
+  SRecording recording;
+  const char *state;
+
+  if(htsmsg_get_u32(msg, "id",      &recording.id)
+  || htsmsg_get_u32(msg, "channel", &recording.channel)
+  || htsmsg_get_u32(msg, "start",   &recording.start)
+  || htsmsg_get_u32(msg, "stop",    &recording.stop)
+  || (state = htsmsg_get_str(msg, "state")) == NULL)
+  {
+    XBMC->Log(LOG_ERROR, "%s - malformed message received", __FUNCTION__);
+    htsmsg_print(msg);
+    return;
+  }
+
+  /* parse the dvr entry's state */
+  if     (strstr(state, "scheduled"))
+    recording.state = ST_SCHEDULED;
+  else if(strstr(state, "recording"))
+    recording.state = ST_RECORDING;
+  else if(strstr(state, "completed"))
+    recording.state = ST_COMPLETED;
+  else if(strstr(state, "invalid"))
+    recording.state = ST_INVALID;
+
+  const char* str;
+  if((str = htsmsg_get_str(msg, "title")) == NULL)
+    recording.title = "";
+  else
+    recording.title = str;
+
+  if((str = htsmsg_get_str(msg, "description")) == NULL)
+    recording.description = "";
+  else
+    recording.description = str;
+
+  if((str = htsmsg_get_str(msg, "error")) == NULL)
+    recording.error = "";
+  else
+    recording.error = str;
+
+  // if the user has aborted the recording then the recording.error will be set to 300 by tvheadend
+  if (recording.error == "300")
+  {
+    recording.state = ST_ABORTED;
+    recording.error.clear();
+  }
+
+  XBMC->Log(LOG_DEBUG, "%s - id:%u, state:'%s', title:'%s', description: '%s'"
+      , __FUNCTION__, recording.id, state, recording.title.c_str()
+      , recording.description.c_str());
+
+  recordings[recording.id] = recording;
+}
+
+void cHTSPSession::ParseDVREntryDelete(htsmsg_t* msg, SRecordings &recordings)
+{
+  uint32_t id;
+
+  if(htsmsg_get_u32(msg, "id", &id))
+  {
+    XBMC->Log(LOG_ERROR, "%s - malformed message received", __FUNCTION__);
+    htsmsg_print(msg);
+    return;
+  }
+
+  XBMC->Log(LOG_DEBUG, "%s - Recording %i was deleted", __FUNCTION__, id);
+
+  recordings.erase(id);
+}
diff --git a/xbmc/pvrclients/tvheadend/HTSPSession.h b/xbmc/pvrclients/tvheadend/HTSPSession.h
new file mode 100644 (file)
index 0000000..daba797
--- /dev/null
@@ -0,0 +1,290 @@
+/*
+ *      Copyright (C) 2005-2008 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+#include <deque>
+#include "pvrclient-tvheadend_os.h"
+#include "StdString.h"
+
+#include <algorithm>
+#include <vector>
+#include <map>
+
+typedef struct htsmsg htsmsg_t;
+
+template<typename T>
+class const_circular_iter
+  : public std::iterator< typename std::iterator_traits<T>::iterator_category
+                        , typename std::iterator_traits<T>::value_type
+                        , typename std::iterator_traits<T>::difference_type
+                        , typename std::iterator_traits<T>::pointer
+                        , typename std::iterator_traits<T>::reference>
+{
+  protected:
+    T begin;
+    T end;
+    T iter;
+  public:
+    typedef typename std::iterator_traits<T>::value_type      value_type;
+    typedef typename std::iterator_traits<T>::difference_type difference_type;
+    typedef typename std::iterator_traits<T>::pointer         pointer;
+    typedef typename std::iterator_traits<T>::reference       reference;
+
+    const_circular_iter(const const_circular_iter& src)     : begin(src.begin), end(src.end), iter(src.iter) {};
+    const_circular_iter(const T& b, const T& e)             : begin(b), end(e), iter(b) {};
+    const_circular_iter(const T& b, const T& e, const T& c) : begin(b), end(e), iter(c) {};
+    const_circular_iter<T>& operator++()
+    {
+      if(begin == end)
+        return(*this);
+      ++iter;
+      if (iter == end)
+        iter = begin;
+      return(*this);
+    }
+
+    const_circular_iter<T>& operator--()
+    {
+      if(begin == end)
+        return(*this);
+      if (iter == begin)
+        iter = end;
+      iter--;
+      return(*this);
+    }
+
+          reference operator*()  const { return (*iter);  }
+    const pointer   operator->() const { return &(*iter); }
+    bool operator==(const const_circular_iter<T>&  rhs) const { return (iter == rhs.iter); }
+    bool operator==(const T&                       rhs) const { return (iter == rhs); }
+    bool operator!=(const const_circular_iter<T>&  rhs) const { return ! operator==(rhs); }
+    bool operator!=(const T&                       rhs) const { return ! operator==(rhs); }
+};
+
+struct STag
+{
+  int              id;
+  std::string      name;
+  std::string      icon;
+  std::vector<int> channels;
+
+  STag() { Clear(); }
+  void Clear()
+  {
+    id    = 0;
+    name.clear();
+    icon.clear();
+    channels.clear();
+  }
+  bool BelongsTo(int channel) const
+  {
+    return std::find(channels.begin(), channels.end(), channel) != channels.end();
+  }
+
+};
+
+struct SChannel
+{
+  int              id;
+  std::string      name;
+  std::string      icon;
+  int              event;
+  int              num;
+  bool             radio;
+  int              caid;
+  std::vector<int> tags;
+
+  SChannel() { Clear(); }
+  void Clear()
+  {
+    id    = 0;
+    event = 0;
+    num   = 0;
+    radio = false;
+    caid  = 0;
+    name.clear();
+    icon.clear();
+    tags.clear();
+  }
+  bool MemberOf(int tag) const
+  {
+    return std::find(tags.begin(), tags.end(), tag) != tags.end();
+  }
+  bool operator<(const SChannel &right) const
+  {
+    return num < right.num;
+  }
+};
+
+struct SEvent
+{
+  int         id;
+  int         next;
+  int         chan_id;
+
+  int         content;
+  int         start;
+  int         stop;
+  std::string title;
+  std::string descs;
+
+  SEvent() { Clear(); }
+  void Clear()
+  {
+    id    = 0;
+    next  = 0;
+    start = 0;
+    stop  = 0;
+    title.clear();
+    descs.clear();
+  }
+};
+
+typedef enum recording_state {
+  ST_SCHEDULED,
+  ST_RECORDING,
+  ST_COMPLETED,
+  ST_ABORTED,
+  ST_INVALID
+} ERecordingState;
+
+struct SRecording
+{
+  uint32_t         id;
+  uint32_t         channel;
+  uint32_t         start;
+  uint32_t         stop;
+  std::string      title;
+  std::string      description;
+  ERecordingState  state;
+  std::string      error;
+
+  SRecording() { Clear(); }
+  void Clear()
+  {
+    id = channel = start = stop = 0;
+    title.clear();
+    description.clear();
+    state = ST_INVALID;
+    error.clear();
+  }
+};
+
+struct SQueueStatus
+{
+  uint32_t packets; // Number of data packets in queue.
+  uint32_t bytes;   // Number of bytes in queue.
+  uint32_t delay;   // Estimated delay of queue (in µs)
+  uint32_t bdrops;  // Number of B-frames dropped
+  uint32_t pdrops;  // Number of P-frames dropped
+  uint32_t idrops;  // Number of I-frames dropped
+
+  SQueueStatus() { Clear(); }
+  void Clear()
+  {
+    packets = 0;
+    bytes   = 0;
+    delay   = 0;
+    bdrops  = 0;
+    pdrops  = 0;
+    idrops  = 0;
+  }
+};
+
+struct SQuality
+{
+//  CStdString  fe_name; // get the adapter name from the mux instead
+  CStdString  fe_status;
+  uint32_t    fe_snr;
+  uint32_t    fe_signal;
+  uint32_t    fe_ber;
+  uint32_t    fe_unc;
+};
+
+struct SSourceInfo
+{
+//  CStdString si_device; // currently not sent by tvheadend
+  CStdString si_adapter;
+  CStdString si_network;
+  CStdString si_mux;
+  CStdString si_provider;
+  CStdString si_service;
+};
+
+typedef std::map<int, SChannel> SChannels;
+typedef std::map<int, STag>     STags;
+typedef std::map<int, SEvent>   SEvents;
+typedef std::map<int, SRecording>     SRecordings;
+
+
+class cHTSPSession
+{
+public:
+  cHTSPSession();
+  ~cHTSPSession();
+
+  bool      Connect(const std::string& hostname, int port);
+  bool      IsConnected() { return m_connected; }
+  void      Close();
+  void      Abort();
+  bool      Auth(const std::string& username, const std::string& password);
+
+  htsmsg_t* ReadMessage(int timeout = 10000);
+  bool      SendMessage(htsmsg_t* m);
+
+  htsmsg_t* ReadResult (htsmsg_t* m, bool sequence = true);
+  bool      ReadSuccess(htsmsg_t* m, bool sequence = true, std::string action = "");
+
+  bool      SendSubscribe  (int subscription, int channel);
+  bool      SendUnsubscribe(int subscription);
+  bool      SendEnableAsync();
+  bool      GetEvent(SEvent& event, uint32_t id);
+
+  int         GetProtocol()   { return m_protocol; }
+  CStdString  GetServerName() { return m_server; }
+  CStdString  GetVersion()    { return m_version; }
+  unsigned    AddSequence()   { return ++m_seq; }
+
+  static bool ParseEvent         (htsmsg_t* msg, uint32_t id, SEvent &event);
+  static void ParseChannelUpdate (htsmsg_t* msg, SChannels &channels);
+  static void ParseChannelRemove (htsmsg_t* msg, SChannels &channels);
+  static void ParseTagUpdate     (htsmsg_t* msg, STags &tags);
+  static void ParseTagRemove     (htsmsg_t* msg, STags &tags);
+  static bool ParseQueueStatus   (htsmsg_t* msg, SQueueStatus &queue);
+  static bool ParseSignalStatus  (htsmsg_t* msg, SQuality &quality);
+  static bool ParseSourceInfo    (htsmsg_t* msg, SSourceInfo &si);
+  static void ParseDVREntryUpdate(htsmsg_t* msg, SRecordings &recordings);
+  static void ParseDVREntryDelete(htsmsg_t* msg, SRecordings &recordings);
+
+private:
+  SOCKET      m_fd;
+  unsigned    m_seq;
+  void*       m_challenge;
+  int         m_challenge_len;
+  int         m_protocol;
+  CStdString  m_server;
+  CStdString  m_version;
+  bool        m_connected;
+
+  std::deque<htsmsg_t*> m_queue;
+  const unsigned int    m_queue_size;
+
+};
diff --git a/xbmc/pvrclients/tvheadend/Makefile.in b/xbmc/pvrclients/tvheadend/Makefile.in
new file mode 100644 (file)
index 0000000..9385803
--- /dev/null
@@ -0,0 +1,27 @@
+#
+# Makefile for the XBMC HTS Tvheadend PVR AddOn
+#
+# See the README for copyright information and
+# how to reach the author.
+#
+
+LIBS   = ../../../xbmc/lib/libhts/libhts.a -ldl
+LIBDIR = ../../../addons/pvr.hts
+LIB    = ../../../addons/pvr.hts/XBMC_Tvheadend.pvr
+
+SRCS=client.cpp \
+       HTSPSession.cpp \
+       HTSPData.cpp \
+       HTSPDemux.cpp \
+       thread.cpp \
+       tools.cpp
+
+include ../Makefile.include
+
+clean:
+       -rm -f $(OBJS) $(LIB) *~
+       $(MAKE) -C ../../../xbmc/lib/libhts clean
+
+$(LIB): $(OBJS)
+       ${MAKE} -C ../../../xbmc/lib/libhts
+       $(CXX) $(CXXFLAGS) $(LDFLAGS) -shared -g $(OBJS) $(LIBS) $(LIBDIRS) $(SILIB) -o $(LIB)
diff --git a/xbmc/pvrclients/tvheadend/StdString.h b/xbmc/pvrclients/tvheadend/StdString.h
new file mode 100644 (file)
index 0000000..3757410
--- /dev/null
@@ -0,0 +1,4333 @@
+#pragma once
+#include <string>
+#include <stdint.h>
+#if !defined(_LINUX)
+#include <windows.h>
+#include "pvrclient-tvheadend_os.h"
+#endif
+
+// =============================================================================
+//  FILE:  StdString.h
+//  AUTHOR:  Joe O'Leary (with outside help noted in comments)
+//
+//    If you find any bugs in this code, please let me know:
+//
+//        jmoleary@earthlink.net
+//        http://www.joeo.net/stdstring.htm (a bit outdated)
+//
+//      The latest version of this code should always be available at the
+//      following link:
+//
+//              http://www.joeo.net/code/StdString.zip (Dec 6, 2003)
+//
+//
+//  REMARKS:
+//    This header file declares the CStdStr template.  This template derives
+//    the Standard C++ Library basic_string<> template and add to it the
+//    the following conveniences:
+//      - The full MFC CString set of functions (including implicit cast)
+//      - writing to/reading from COM IStream interfaces
+//      - Functional objects for use in STL algorithms
+//
+//    From this template, we intstantiate two classes:  CStdStringA and
+//    CStdStringW.  The name "CStdString" is just a #define of one of these,
+//    based upone the UNICODE macro setting
+//
+//    This header also declares our own version of the MFC/ATL UNICODE-MBCS
+//    conversion macros.  Our version looks exactly like the Microsoft's to
+//    facilitate portability.
+//
+//  NOTE:
+//    If you you use this in an MFC or ATL build, you should include either
+//    afx.h or atlbase.h first, as appropriate.
+//
+//  PEOPLE WHO HAVE CONTRIBUTED TO THIS CLASS:
+//
+//    Several people have helped me iron out problems and othewise improve
+//    this class.  OK, this is a long list but in my own defense, this code
+//    has undergone two major rewrites.  Many of the improvements became
+//    necessary after I rewrote the code as a template.  Others helped me
+//    improve the CString facade.
+//
+//    Anyway, these people are (in chronological order):
+//
+//      - Pete the Plumber (???)
+//      - Julian Selman
+//      - Chris (of Melbsys)
+//      - Dave Plummer
+//      - John C Sipos
+//      - Chris Sells
+//      - Nigel Nunn
+//      - Fan Xia
+//      - Matthew Williams
+//      - Carl Engman
+//      - Mark Zeren
+//      - Craig Watson
+//      - Rich Zuris
+//      - Karim Ratib
+//      - Chris Conti
+//      - Baptiste Lepilleur
+//      - Greg Pickles
+//      - Jim Cline
+//      - Jeff Kohn
+//      - Todd Heckel
+//      - Ullrich Poll�hne
+//      - Joe Vitaterna
+//      - Joe Woodbury
+//      - Aaron (no last name)
+//      - Joldakowski (???)
+//      - Scott Hathaway
+//      - Eric Nitzche
+//      - Pablo Presedo
+//      - Farrokh Nejadlotfi
+//      - Jason Mills
+//      - Igor Kholodov
+//      - Mike Crusader
+//      - John James
+//      - Wang Haifeng
+//      - Tim Dowty
+//          - Arnt Witteveen
+//          - Glen Maynard
+//          - Paul DeMarco
+//          - Bagira (full name?)
+//          - Ronny Schulz
+//          - Jakko Van Hunen
+//      - Charles Godwin
+//      - Henk Demper
+//      - Greg Marr
+//      - Bill Carducci
+//      - Brian Groose
+//      - MKingman
+//      - Don Beusee
+//
+//  REVISION HISTORY
+//
+//    2005-JAN-10 - Thanks to Don Beusee for pointing out the danger in mapping
+//          length-checked formatting functions to non-length-checked
+//          CRT equivalents.  Also thanks to him for motivating me to
+//          optimize my implementation of Replace()
+//
+//    2004-APR-22 - A big, big thank you to "MKingman" (whoever you are) for
+//          finally spotting a silly little error in StdCodeCvt that
+//          has been causing me (and users of CStdString) problems for
+//          years in some relatively rare conversions.  I had reversed
+//          two length arguments.
+//
+//    2003-NOV-24 - Thanks to a bunch of people for helping me clean up many
+//          compiler warnings (and yes, even a couple of actual compiler
+//          errors).  These include Henk Demper for figuring out how
+//          to make the Intellisense work on with CStdString on VC6,
+//          something I was never able to do.  Greg Marr pointed out
+//          a compiler warning about an unreferenced symbol and a
+//          problem with my version of Load in MFC builds.  Bill
+//          Carducci took a lot of time with me to help me figure out
+//          why some implementations of the Standard C++ Library were
+//          returning error codes for apparently successful conversions
+//          between ASCII and UNICODE.  Finally thanks to Brian Groose
+//          for helping me fix compiler signed unsigned warnings in
+//          several functions.
+//
+//    2003-JUL-10 - Thanks to Charles Godwin for making me realize my 'FmtArg'
+//          fixes had inadvertently broken the DLL-export code (which is
+//                  normally commented out.  I had to move it up higher.  Also
+//          this helped me catch a bug in ssicoll that would prevent
+//                  compilation, otherwise.
+//
+//    2003-MAR-14 - Thanks to Jakko Van Hunen for pointing out a copy-and-paste
+//                  bug in one of the overloads of FmtArg.
+//
+//    2003-MAR-10 - Thanks to Ronny Schulz for (twice!) sending me some changes
+//                  to help CStdString build on SGI and for pointing out an
+//                  error in placement of my preprocessor macros for ssfmtmsg.
+//
+//    2002-NOV-26 - Thanks to Bagira for pointing out that my implementation of
+//                  SpanExcluding was not properly handling the case in which
+//                  the string did NOT contain any of the given characters
+//
+//    2002-OCT-21 - Many thanks to Paul DeMarco who was invaluable in helping me
+//                  get this code working with Borland's free compiler as well
+//                  as the Dev-C++ compiler (available free at SourceForge).
+//
+//    2002-SEP-13 - Thanks to Glen Maynard who helped me get rid of some loud
+//                  but harmless warnings that were showing up on g++.  Glen
+//                  also pointed out that some pre-declarations of FmtArg<>
+//                  specializations were unnecessary (and no good on G++)
+//
+//    2002-JUN-26 - Thanks to Arnt Witteveen for pointing out that I was using
+//                  static_cast<> in a place in which I should have been using
+//                  reinterpret_cast<> (the ctor for unsigned char strings).
+//                  That's what happens when I don't unit-test properly!
+//                  Arnt also noticed that CString was silently correcting the
+//                  'nCount' argument to Left() and Right() where CStdString was
+//                  not (and crashing if it was bad).  That is also now fixed!
+//
+//    2002-FEB-25 - Thanks to Tim Dowty for pointing out (and giving me the fix
+//          for) a conversion problem with non-ASCII MBCS characters.
+//          CStdString is now used in my favorite commercial MP3 player!
+//
+//    2001-DEC-06 - Thanks to Wang Haifeng for spotting a problem in one of the
+//          assignment operators (for _bstr_t) that would cause compiler
+//          errors when refcounting protection was turned off.
+//
+//    2001-NOV-27 - Remove calls to operator!= which involve reverse_iterators
+//          due to a conflict with the rel_ops operator!=.  Thanks to
+//          John James for pointing this out.
+//
+//    2001-OCT-29 - Added a minor range checking fix for the Mid function to
+//          make it as forgiving as CString's version is.  Thanks to
+//          Igor Kholodov for noticing this.
+//          - Added a specialization of std::swap for CStdString.  Thanks
+//          to Mike Crusader for suggesting this!  It's commented out
+//          because you're not supposed to inject your own code into the
+//          'std' namespace.  But if you don't care about that, it's
+//          there if you want it
+//          - Thanks to Jason Mills for catching a case where CString was
+//          more forgiving in the Delete() function than I was.
+//
+//    2001-JUN-06 - I was violating the Standard name lookup rules stated
+//          in [14.6.2(3)].  None of the compilers I've tried so
+//          far apparently caught this but HP-UX aCC 3.30 did.  The
+//          fix was to add 'this->' prefixes in many places.
+//          Thanks to Farrokh Nejadlotfi for this!
+//
+//    2001-APR-27 - StreamLoad was calculating the number of BYTES in one
+//          case, not characters.  Thanks to Pablo Presedo for this.
+//
+//    2001-FEB-23 - Replace() had a bug which caused infinite loops if the
+//          source string was empty.  Fixed thanks to Eric Nitzsche.
+//
+//    2001-FEB-23 - Scott Hathaway was a huge help in providing me with the
+//          ability to build CStdString on Sun Unix systems.  He
+//          sent me detailed build reports about what works and what
+//          does not.  If CStdString compiles on your Unix box, you
+//          can thank Scott for it.
+//
+//    2000-DEC-29 - Joldakowski noticed one overload of Insert failed to do a
+//          range check as CString's does.  Now fixed -- thanks!
+//
+//    2000-NOV-07 - Aaron pointed out that I was calling static member
+//          functions of char_traits via a temporary.  This was not
+//          technically wrong, but it was unnecessary and caused
+//          problems for poor old buggy VC5.  Thanks Aaron!
+//
+//    2000-JUL-11 - Joe Woodbury noted that the CString::Find docs don't match
+//          what the CString::Find code really ends up doing.   I was
+//          trying to match the docs.  Now I match the CString code
+//          - Joe also caught me truncating strings for GetBuffer() calls
+//          when the supplied length was less than the current length.
+//
+//    2000-MAY-25 - Better support for STLPORT's Standard library distribution
+//          - Got rid of the NSP macro - it interfered with Koenig lookup
+//          - Thanks to Joe Woodbury for catching a TrimLeft() bug that
+//          I introduced in January.  Empty strings were not getting
+//          trimmed
+//
+//    2000-APR-17 - Thanks to Joe Vitaterna for pointing out that ReverseFind
+//          is supposed to be a const function.
+//
+//    2000-MAR-07 - Thanks to Ullrich Poll�hne for catching a range bug in one
+//          of the overloads of assign.
+//
+//    2000-FEB-01 - You can now use CStdString on the Mac with CodeWarrior!
+//          Thanks to Todd Heckel for helping out with this.
+//
+//    2000-JAN-23 - Thanks to Jim Cline for pointing out how I could make the
+//          Trim() function more efficient.
+//          - Thanks to Jeff Kohn for prompting me to find and fix a typo
+//          in one of the addition operators that takes _bstr_t.
+//          - Got rid of the .CPP file -  you only need StdString.h now!
+//
+//    1999-DEC-22 - Thanks to Greg Pickles for helping me identify a problem
+//          with my implementation of CStdString::FormatV in which
+//          resulting string might not be properly NULL terminated.
+//
+//    1999-DEC-06 - Chris Conti pointed yet another basic_string<> assignment
+//          bug that MS has not fixed.  CStdString did nothing to fix
+//          it either but it does now!  The bug was: create a string
+//          longer than 31 characters, get a pointer to it (via c_str())
+//          and then assign that pointer to the original string object.
+//          The resulting string would be empty.  Not with CStdString!
+//
+//    1999-OCT-06 - BufferSet was erasing the string even when it was merely
+//          supposed to shrink it.  Fixed.  Thanks to Chris Conti.
+//          - Some of the Q172398 fixes were not checking for assignment-
+//          to-self.  Fixed.  Thanks to Baptiste Lepilleur.
+//
+//    1999-AUG-20 - Improved Load() function to be more efficient by using
+//          SizeOfResource().  Thanks to Rich Zuris for this.
+//          - Corrected resource ID constructor, again thanks to Rich.
+//          - Fixed a bug that occurred with UNICODE characters above
+//          the first 255 ANSI ones.  Thanks to Craig Watson.
+//          - Added missing overloads of TrimLeft() and TrimRight().
+//          Thanks to Karim Ratib for pointing them out
+//
+//    1999-JUL-21 - Made all calls to GetBuf() with no args check length first.
+//
+//    1999-JUL-10 - Improved MFC/ATL independence of conversion macros
+//          - Added SS_NO_REFCOUNT macro to allow you to disable any
+//          reference-counting your basic_string<> impl. may do.
+//          - Improved ReleaseBuffer() to be as forgiving as CString.
+//          Thanks for Fan Xia for helping me find this and to
+//          Matthew Williams for pointing it out directly.
+//
+//    1999-JUL-06 - Thanks to Nigel Nunn for catching a very sneaky bug in
+//          ToLower/ToUpper.  They should call GetBuf() instead of
+//          data() in order to ensure the changed string buffer is not
+//          reference-counted (in those implementations that refcount).
+//
+//    1999-JUL-01 - Added a true CString facade.  Now you can use CStdString as
+//          a drop-in replacement for CString.  If you find this useful,
+//          you can thank Chris Sells for finally convincing me to give
+//          in and implement it.
+//          - Changed operators << and >> (for MFC CArchive) to serialize
+//          EXACTLY as CString's do.  So now you can send a CString out
+//          to a CArchive and later read it in as a CStdString.   I have
+//          no idea why you would want to do this but you can.
+//
+//    1999-JUN-21 - Changed the CStdString class into the CStdStr template.
+//          - Fixed FormatV() to correctly decrement the loop counter.
+//          This was harmless bug but a bug nevertheless.  Thanks to
+//          Chris (of Melbsys) for pointing it out
+//          - Changed Format() to try a normal stack-based array before
+//          using to _alloca().
+//          - Updated the text conversion macros to properly use code
+//          pages and to fit in better in MFC/ATL builds.  In other
+//          words, I copied Microsoft's conversion stuff again.
+//          - Added equivalents of CString::GetBuffer, GetBufferSetLength
+//          - new sscpy() replacement of CStdString::CopyString()
+//          - a Trim() function that combines TrimRight() and TrimLeft().
+//
+//    1999-MAR-13 - Corrected the "NotSpace" functional object to use _istpace()
+//          instead of _isspace()   Thanks to Dave Plummer for this.
+//
+//    1999-FEB-26 - Removed errant line (left over from testing) that #defined
+//          _MFC_VER.  Thanks to John C Sipos for noticing this.
+//
+//    1999-FEB-03 - Fixed a bug in a rarely-used overload of operator+() that
+//          caused infinite recursion and stack overflow
+//          - Added member functions to simplify the process of
+//          persisting CStdStrings to/from DCOM IStream interfaces
+//          - Added functional objects (e.g. StdStringLessNoCase) that
+//          allow CStdStrings to be used as keys STL map objects with
+//          case-insensitive comparison
+//          - Added array indexing operators (i.e. operator[]).  I
+//          originally assumed that these were unnecessary and would be
+//          inherited from basic_string.  However, without them, Visual
+//          C++ complains about ambiguous overloads when you try to use
+//          them.  Thanks to Julian Selman to pointing this out.
+//
+//    1998-FEB-?? - Added overloads of assign() function to completely account
+//          for Q172398 bug.  Thanks to "Pete the Plumber" for this
+//
+//    1998-FEB-?? - Initial submission
+//
+// COPYRIGHT:
+//    2002 Joseph M. O'Leary.  This code is 100% free.  Use it anywhere you
+//      want.  Rewrite it, restructure it, whatever.  If you can write software
+//      that makes money off of it, good for you.  I kinda like capitalism.
+//      Please don't blame me if it causes your $30 billion dollar satellite
+//      explode in orbit.  If you redistribute it in any form, I'd appreciate it
+//      if you would leave this notice here.
+// =============================================================================
+
+// Avoid multiple inclusion
+
+#ifndef STDSTRING_H
+#define STDSTRING_H
+
+// When using VC, turn off browser references
+// Turn off unavoidable compiler warnings
+
+#if defined(_MSC_VER) && (_MSC_VER > 1100)
+  #pragma component(browser, off, references, "CStdString")
+  #pragma warning (disable : 4290) // C++ Exception Specification ignored
+  #pragma warning (disable : 4127) // Conditional expression is constant
+  #pragma warning (disable : 4097) // typedef name used as synonym for class name
+#endif
+
+// Borland warnings to turn off
+
+#ifdef __BORLANDC__
+    #pragma option push -w-inl
+//  #pragma warn -inl   // Turn off inline function warnings
+#endif
+
+// SS_IS_INTRESOURCE
+// -----------------
+//    A copy of IS_INTRESOURCE from VC7.  Because old VC6 version of winuser.h
+//    doesn't have this.
+
+#define SS_IS_INTRESOURCE(_r) (false)
+
+#if !defined (SS_ANSI) && defined(_MSC_VER)
+  #undef SS_IS_INTRESOURCE
+  #if defined(_WIN64)
+    #define SS_IS_INTRESOURCE(_r) (((unsigned __int64)(_r) >> 16) == 0)
+  #else
+    #define SS_IS_INTRESOURCE(_r) (((unsigned long)(_r) >> 16) == 0)
+  #endif
+#endif
+
+
+// MACRO: SS_UNSIGNED
+// ------------------
+//      This macro causes the addition of a constructor and assignment operator
+//      which take unsigned characters.  CString has such functions and in order
+//      to provide maximum CString-compatability, this code needs them as well.
+//      In practice you will likely never need these functions...
+
+//#define SS_UNSIGNED
+
+#ifdef SS_ALLOW_UNSIGNED_CHARS
+  #define SS_UNSIGNED
+#endif
+
+// MACRO: SS_SAFE_FORMAT
+// ---------------------
+//      This macro provides limited compatability with a questionable CString
+//      "feature".  You can define it in order to avoid a common problem that
+//      people encounter when switching from CString to CStdString.
+//
+//      To illustrate the problem -- With CString, you can do this:
+//
+//          CString sName("Joe");
+//          CString sTmp;
+//          sTmp.Format("My name is %s", sName);                    // WORKS!
+//
+//      However if you were to try this with CStdString, your program would
+//      crash.
+//
+//          CStdString sName("Joe");
+//          CStdString sTmp;
+//          sTmp.Format("My name is %s", sName);                    // CRASHES!
+//
+//      You must explicitly call c_str() or cast the object to the proper type
+//
+//          sTmp.Format("My name is %s", sName.c_str());            // WORKS!
+//          sTmp.Format("My name is %s", static_cast<PCSTR>(sName));// WORKS!
+//          sTmp.Format("My name is %s", (PCSTR)sName);        // WORKS!
+//
+//      This is because it is illegal to pass anything but a POD type as a
+//      variadic argument to a variadic function (i.e. as one of the "..."
+//      arguments).  The type const char* is a POD type.  The type CStdString
+//      is not.  Of course, neither is the type CString, but CString lets you do
+//      it anyway due to the way they laid out the class in binary.  I have no
+//      control over this in CStdString since I derive from whatever
+//      implementation of basic_string is available.
+//
+//      However if you have legacy code (which does this) that you want to take
+//      out of the MFC world and you don't want to rewrite all your calls to
+//      Format(), then you can define this flag and it will no longer crash.
+//
+//      Note however that this ONLY works for Format(), not sprintf, fprintf,
+//      etc.  If you pass a CStdString object to one of those functions, your
+//      program will crash.  Not much I can do to get around this, short of
+//      writing substitutes for those functions as well.
+
+#define SS_SAFE_FORMAT  // use new template style Format() function
+
+
+// MACRO: SS_NO_IMPLICIT_CAST
+// --------------------------
+//      Some people don't like the implicit cast to const char* (or rather to
+//      const CT*) that CStdString (and MFC's CString) provide.  That was the
+//      whole reason I created this class in the first place, but hey, whatever
+//      bakes your cake.  Just #define this macro to get rid of the the implicit
+//      cast.
+
+//#define SS_NO_IMPLICIT_CAST // gets rid of operator const CT*()
+
+
+// MACRO: SS_NO_REFCOUNT
+// ---------------------
+//    turns off reference counting at the assignment level.  Only needed
+//    for the version of basic_string<> that comes with Visual C++ versions
+//    6.0 or earlier, and only then in some heavily multithreaded scenarios.
+//    Uncomment it if you feel you need it.
+
+//#define SS_NO_REFCOUNT
+
+// MACRO: SS_WIN32
+// ---------------
+//      When this flag is set, we are building code for the Win32 platform and
+//      may use Win32 specific functions (such as LoadString).  This gives us
+//      a couple of nice extras for the code.
+//
+//      Obviously, Microsoft's is not the only compiler available for Win32 out
+//      there.  So I can't just check to see if _MSC_VER is defined to detect
+//      if I'm building on Win32.  So for now, if you use MS Visual C++ or
+//      Borland's compiler, I turn this on.  Otherwise you may turn it on
+//      yourself, if you prefer
+
+#if defined(_MSC_VER) || defined(__BORLANDC__) || defined(_WIN32)
+ #define SS_WIN32
+#endif
+
+// MACRO: SS_ANSI
+// --------------
+//      When this macro is defined, the code attempts only to use ANSI/ISO
+//      standard library functions to do it's work.  It will NOT attempt to use
+//      any Win32 of Visual C++ specific functions -- even if they are
+//      available.  You may define this flag yourself to prevent any Win32
+//      of VC++ specific functions from being called.
+
+// If we're not on Win32, we MUST use an ANSI build
+
+#ifndef SS_WIN32
+    #if !defined(SS_NO_ANSI)
+        #define SS_ANSI
+    #endif
+#endif
+
+// MACRO: SS_ALLOCA
+// ----------------
+//      Some implementations of the Standard C Library have a non-standard
+//      function known as alloca().  This functions allows one to allocate a
+//      variable amount of memory on the stack.  It is needed to implement
+//      the ASCII/MBCS conversion macros.
+//
+//      I wanted to find some way to determine automatically if alloca() is
+//    available on this platform via compiler flags but that is asking for
+//    trouble.  The crude test presented here will likely need fixing on
+//    other platforms.  Therefore I'll leave it up to you to fiddle with
+//    this test to determine if it exists.  Just make sure SS_ALLOCA is or
+//    is not defined as appropriate and you control this feature.
+
+#if defined(_MSC_VER) && !defined(SS_ANSI)
+  #define SS_ALLOCA
+#endif
+
+
+// MACRO: SS_MBCS
+// --------------
+//    Setting this macro means you are using MBCS characters.  In MSVC builds,
+//    this macro gets set automatically by detection of the preprocessor flag
+//    _MBCS.  For other platforms you may set it manually if you wish.  The
+//    only effect it currently has is to cause the allocation of more space
+//    for wchar_t --> char conversions.
+//    Note that MBCS does not mean UNICODE.
+//
+//  #define SS_MBCS
+//
+
+#ifdef _MBCS
+  #define SS_MBCS
+#endif
+
+
+// MACRO SS_NO_LOCALE
+// ------------------
+// If your implementation of the Standard C++ Library lacks the <locale> header,
+// you can #define this macro to make your code build properly.  Note that this
+// is some of my newest code and frankly I'm not very sure of it, though it does
+// pass my unit tests.
+
+// #define SS_NO_LOCALE
+
+
+// Compiler Error regarding _UNICODE and UNICODE
+// -----------------------------------------------
+// Microsoft header files are screwy.  Sometimes they depend on a preprocessor
+// flag named "_UNICODE".  Other times they check "UNICODE" (note the lack of
+// leading underscore in the second version".  In several places, they silently
+// "synchronize" these two flags this by defining one of the other was defined.
+// In older version of this header, I used to try to do the same thing.
+//
+// However experience has taught me that this is a bad idea.  You get weird
+// compiler errors that seem to indicate things like LPWSTR and LPTSTR not being
+// equivalent in UNICODE builds, stuff like that (when they MUST be in a proper
+// UNICODE  build).  You end up scratching your head and saying, "But that HAS
+// to compile!".
+//
+// So what should you do if you get this error?
+//
+// Make sure that both macros (_UNICODE and UNICODE) are defined before this
+// file is included.  You can do that by either
+//
+//    a) defining both yourself before any files get included
+//    b) including the proper MS headers in the proper order
+//    c) including this file before any other file, uncommenting
+//       the #defines below, and commenting out the #errors
+//
+//  Personally I recommend solution a) but it's your call.
+
+#ifdef _MSC_VER
+  #if defined (_UNICODE) && !defined (UNICODE)
+    #error UNICODE defined  but not UNICODE
+  //  #define UNICODE  // no longer silently fix this
+  #endif
+  #if defined (UNICODE) && !defined (_UNICODE)
+    #error Warning, UNICODE defined  but not _UNICODE
+  //  #define _UNICODE  // no longer silently fix this
+  #endif
+#endif
+
+
+// -----------------------------------------------------------------------------
+// MIN and MAX.  The Standard C++ template versions go by so many names (at
+// at least in the MS implementation) that you never know what's available
+// -----------------------------------------------------------------------------
+template<class Type>
+inline const Type& SSMIN(const Type& arg1, const Type& arg2)
+{
+  return arg2 < arg1 ? arg2 : arg1;
+}
+template<class Type>
+inline const Type& SSMAX(const Type& arg1, const Type& arg2)
+{
+  return arg2 > arg1 ? arg2 : arg1;
+}
+
+// If they have not #included W32Base.h (part of my W32 utility library) then
+// we need to define some stuff.  Otherwise, this is all defined there.
+
+#if !defined(W32BASE_H)
+
+  // If they want us to use only standard C++ stuff (no Win32 stuff)
+
+  #ifdef SS_ANSI
+
+    // On Win32 we have TCHAR.H so just include it.  This is NOT violating
+        // the spirit of SS_ANSI as we are not calling any Win32 functions here.
+
+    #ifdef SS_WIN32
+
+      #include <TCHAR.H>
+      #include <WTYPES.H>
+      #ifndef STRICT
+        #define STRICT
+      #endif
+
+        // ... but on non-Win32 platforms, we must #define the types we need.
+
+    #else
+
+      typedef const char*    PCSTR;
+      typedef char*      PSTR;
+      typedef const wchar_t*  PCWSTR;
+      typedef wchar_t*    PWSTR;
+      #ifdef UNICODE
+        typedef wchar_t    TCHAR;
+      #else
+        typedef char    TCHAR;
+      #endif
+      typedef wchar_t      OLECHAR;
+
+    #endif  // #ifndef _WIN32
+
+
+    // Make sure ASSERT and verify are defined using only ANSI stuff
+
+    #ifndef ASSERT
+      #include <assert.h>
+      #define ASSERT(f) assert((f))
+    #endif
+    #ifndef VERIFY
+      #ifdef _DEBUG
+        #define VERIFY(x) ASSERT((x))
+      #else
+        #define VERIFY(x) x
+      #endif
+    #endif
+
+  #else // ...else SS_ANSI is NOT defined
+
+    #include <TCHAR.H>
+    #include <WTYPES.H>
+    #ifndef STRICT
+      #define STRICT
+    #endif
+
+    // Make sure ASSERT and verify are defined
+
+    #ifndef ASSERT
+      #include <crtdbg.h>
+      #define ASSERT(f) _ASSERTE((f))
+    #endif
+    #ifndef VERIFY
+      #ifdef _DEBUG
+        #define VERIFY(x) ASSERT((x))
+      #else
+        #define VERIFY(x) x
+      #endif
+    #endif
+
+  #endif // #ifdef SS_ANSI
+
+  #ifndef UNUSED
+    #define UNUSED(x) x
+  #endif
+
+#endif // #ifndef W32BASE_H
+
+// Standard headers needed
+
+#include <string>      // basic_string
+#include <algorithm>    // for_each, etc.
+#include <functional>    // for StdStringLessNoCase, et al
+#ifndef SS_NO_LOCALE
+  #include <locale>      // for various facets
+#endif
+
+// If this is a recent enough version of VC include comdef.h, so we can write
+// member functions to deal with COM types & compiler support classes e.g.
+// _bstr_t
+
+#if defined (_MSC_VER) && (_MSC_VER >= 1100)
+ #include <comdef.h>
+ #define SS_INC_COMDEF  // signal that we #included MS comdef.h file
+ #define STDSTRING_INC_COMDEF
+ #define SS_NOTHROW __declspec(nothrow)
+#else
+  #define SS_NOTHROW
+#endif
+
+#ifndef TRACE
+  #define TRACE_DEFINED_HERE
+  #define TRACE
+#endif
+
+// Microsoft defines PCSTR, PCWSTR, etc, but no PCTSTR.  I hate to use the
+// versions with the "L" in front of them because that's a leftover from Win 16
+// days, even though it evaluates to the same thing.  Therefore, Define a PCSTR
+// as an LPCTSTR.
+
+#if !defined(PCTSTR) && !defined(PCTSTR_DEFINED)
+  typedef const TCHAR*      PCTSTR;
+  #define PCTSTR_DEFINED
+#endif
+
+#if !defined(PCOLESTR) && !defined(PCOLESTR_DEFINED)
+  typedef const OLECHAR*      PCOLESTR;
+  #define PCOLESTR_DEFINED
+#endif
+
+#if !defined(POLESTR) && !defined(POLESTR_DEFINED)
+  typedef OLECHAR*        POLESTR;
+  #define POLESTR_DEFINED
+#endif
+
+#if !defined(PCUSTR) && !defined(PCUSTR_DEFINED)
+  typedef const unsigned char*  PCUSTR;
+  typedef unsigned char*      PUSTR;
+  #define PCUSTR_DEFINED
+#endif
+
+
+// SGI compiler 7.3 doesnt know these  types - oh and btw, remember to use
+// -LANG:std in the CXX Flags
+#if defined(__sgi)
+    typedef unsigned long           DWORD;
+    typedef void *                  LPCVOID;
+#endif
+
+
+// SS_USE_FACET macro and why we need it:
+//
+// Since I'm a good little Standard C++ programmer, I use locales.  Thus, I
+// need to make use of the use_facet<> template function here.   Unfortunately,
+// this need is complicated by the fact the MS' implementation of the Standard
+// C++ Library has a non-standard version of use_facet that takes more
+// arguments than the standard dictates.  Since I'm trying to write CStdString
+// to work with any version of the Standard library, this presents a problem.
+//
+// The upshot of this is that I can't do 'use_facet' directly.  The MS' docs
+// tell me that I have to use a macro, _USE() instead.  Since _USE obviously
+// won't be available in other implementations, this means that I have to write
+// my OWN macro -- SS_USE_FACET -- that evaluates either to _USE or to the
+// standard, use_facet.
+//
+// If you are having trouble with the SS_USE_FACET macro, in your implementation
+// of the Standard C++ Library, you can define your own version of SS_USE_FACET.
+
+#ifndef schMSG
+  #define schSTR(x)     #x
+  #define schSTR2(x)  schSTR(x)
+  #define schMSG(desc) message(__FILE__ "(" schSTR2(__LINE__) "):" #desc)
+#endif
+
+#ifndef SS_USE_FACET
+
+  // STLPort #defines a macro (__STL_NO_EXPLICIT_FUNCTION_TMPL_ARGS) for
+  // all MSVC builds, erroneously in my opinion.  It causes problems for
+  // my SS_ANSI builds.  In my code, I always comment out that line.  You'll
+  // find it in   \stlport\config\stl_msvc.h
+
+  #if defined(__SGI_STL_PORT) && (__SGI_STL_PORT >= 0x400 )
+
+    #if defined(__STL_NO_EXPLICIT_FUNCTION_TMPL_ARGS) && defined(_MSC_VER)
+      #ifdef SS_ANSI
+        #pragma schMSG(__STL_NO_EXPLICIT_FUNCTION_TMPL_ARGS defined!!)
+      #endif
+    #endif
+    #define SS_USE_FACET(loc, fac) std::use_facet<fac >(loc)
+
+  #elif defined(_MSC_VER )
+
+    #define SS_USE_FACET(loc, fac) std::_USE(loc, fac)
+
+  // ...and
+  #elif defined(_RWSTD_NO_TEMPLATE_ON_RETURN_TYPE)
+
+        #define SS_USE_FACET(loc, fac) std::use_facet(loc, (fac*)0)
+
+  #else
+
+    #define SS_USE_FACET(loc, fac) std::use_facet<fac >(loc)
+
+  #endif
+
+#endif
+
+// =============================================================================
+// UNICODE/MBCS conversion macros.  Made to work just like the MFC/ATL ones.
+// =============================================================================
+
+#include <wchar.h>      // Added to Std Library with Amendment #1.
+
+// First define the conversion helper functions.  We define these regardless of
+// any preprocessor macro settings since their names won't collide.
+
+// Not sure if we need all these headers.   I believe ANSI says we do.
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <wctype.h>
+#include <ctype.h>
+#include <stdlib.h>
+#ifndef va_start
+  #include <varargs.h>
+#endif
+
+
+#ifdef SS_NO_LOCALE
+
+  #if defined(_WIN32) || defined (_WIN32_WCE)
+
+    inline PWSTR StdCodeCvt(PWSTR pDstW, int nDst, PCSTR pSrcA, int nSrc,
+      UINT acp=CP_ACP)
+    {
+      ASSERT(0 != pSrcA);
+      ASSERT(0 != pDstW);
+      pDstW[0] = '\0';
+      MultiByteToWideChar(acp, 0, pSrcA, nSrc, pDstW, nDst);
+      return pDstW;
+    }
+    inline PWSTR StdCodeCvt(PWSTR pDstW, int nDst, PCUSTR pSrcA, int nSrc,
+      UINT acp=CP_ACP)
+    {
+      return StdCodeCvt(pDstW, nDst, (PCSTR)pSrcA, nSrc, acp);
+    }
+
+    inline PSTR StdCodeCvt(PSTR pDstA, int nDst, PCWSTR pSrcW, int nSrc,
+      UINT acp=CP_ACP)
+    {
+      ASSERT(0 != pDstA);
+      ASSERT(0 != pSrcW);
+      pDstA[0] = '\0';
+      WideCharToMultiByte(acp, 0, pSrcW, nSrc, pDstA, nDst, 0, 0);
+      return pDstA;
+    }
+    inline PUSTR StdCodeCvt(PUSTR pDstA, int nDst, PCWSTR pSrcW, int nSrc,
+      UINT acp=CP_ACP)
+    {
+      return (PUSTR)StdCodeCvt((PSTR)pDstA, nDst, pSrcW, nSrc, acp);
+    }
+  #else
+  #endif
+
+#else
+
+  // StdCodeCvt - made to look like Win32 functions WideCharToMultiByte
+  //        and MultiByteToWideChar but uses locales in SS_ANSI
+  //        builds.  There are a number of overloads.
+  //              First argument is the destination buffer.
+  //              Second argument is the source buffer
+  //#if defined (SS_ANSI) || !defined (SS_WIN32)
+
+  // 'SSCodeCvt' - shorthand name for the codecvt facet we use
+
+  typedef std::codecvt<wchar_t, char, mbstate_t> SSCodeCvt;
+
+  inline PWSTR StdCodeCvt(PWSTR pDstW, int nDst, PCSTR pSrcA, int nSrc,
+    const std::locale& loc=std::locale())
+  {
+    ASSERT(0 != pSrcA);
+    ASSERT(0 != pDstW);
+
+    pDstW[0]          = '\0';
+
+    if ( nSrc > 0 )
+    {
+      PCSTR pNextSrcA      = pSrcA;
+      PWSTR pNextDstW      = pDstW;
+      SSCodeCvt::result res  = SSCodeCvt::ok;
+      const SSCodeCvt& conv  = SS_USE_FACET(loc, SSCodeCvt);
+      SSCodeCvt::state_type st= { 0 };
+      res            = conv.in(st,
+                    pSrcA, pSrcA + nSrc, pNextSrcA,
+                    pDstW, pDstW + nDst, pNextDstW);
+#ifdef _LINUX
+#define ASSERT2(a) if (!(a)) {fprintf(stderr, "StdString: Assertion Failed on line %d\n", __LINE__);}
+#else
+#define ASSERT2 ASSERT
+#endif
+      ASSERT2(SSCodeCvt::ok == res);
+      ASSERT2(SSCodeCvt::error != res);
+      ASSERT2(pNextDstW >= pDstW);
+      ASSERT2(pNextSrcA >= pSrcA);
+#undef ASSERT2
+      // Null terminate the converted string
+
+      if ( pNextDstW - pDstW > nDst )
+        *(pDstW + nDst) = '\0';
+      else
+        *pNextDstW = '\0';
+    }
+    return pDstW;
+  }
+  inline PWSTR StdCodeCvt(PWSTR pDstW, int nDst, PCUSTR pSrcA, int nSrc,
+    const std::locale& loc=std::locale())
+  {
+    return StdCodeCvt(pDstW, nDst, (PCSTR)pSrcA, nSrc, loc);
+  }
+
+  inline PSTR StdCodeCvt(PSTR pDstA, int nDst, PCWSTR pSrcW, int nSrc,
+    const std::locale& loc=std::locale())
+  {
+    ASSERT(0 != pDstA);
+    ASSERT(0 != pSrcW);
+
+    pDstA[0]          = '\0';
+
+    if ( nSrc > 0 )
+    {
+      PSTR pNextDstA      = pDstA;
+      PCWSTR pNextSrcW    = pSrcW;
+      SSCodeCvt::result res  = SSCodeCvt::ok;
+      const SSCodeCvt& conv  = SS_USE_FACET(loc, SSCodeCvt);
+      SSCodeCvt::state_type st= { 0 };
+      res            = conv.out(st,
+                    pSrcW, pSrcW + nSrc, pNextSrcW,
+                    pDstA, pDstA + nDst, pNextDstA);
+#ifdef _LINUX
+#define ASSERT2(a) if (!(a)) {fprintf(stderr, "StdString: Assertion Failed on line %d\n", __LINE__);}
+#else
+#define ASSERT2 ASSERT
+#endif
+      ASSERT2(SSCodeCvt::error != res);
+      ASSERT2(SSCodeCvt::ok == res);  // strict, comment out for sanity
+      ASSERT2(pNextDstA >= pDstA);
+      ASSERT2(pNextSrcW >= pSrcW);
+#undef ASSERT2
+
+      // Null terminate the converted string
+
+      if ( pNextDstA - pDstA > nDst )
+        *(pDstA + nDst) = '\0';
+      else
+        *pNextDstA = '\0';
+    }
+    return pDstA;
+  }
+
+  inline PUSTR StdCodeCvt(PUSTR pDstA, int nDst, PCWSTR pSrcW, int nSrc,
+    const std::locale& loc=std::locale())
+  {
+    return (PUSTR)StdCodeCvt((PSTR)pDstA, nDst, pSrcW, nSrc, loc);
+  }
+
+#endif
+
+
+
+// Unicode/MBCS conversion macros are only available on implementations of
+// the "C" library that have the non-standard _alloca function.  As far as I
+// know that's only Microsoft's though I've heard that the function exists
+// elsewhere.
+
+#if defined(SS_ALLOCA) && !defined SS_NO_CONVERSION
+
+    #include <malloc.h>  // needed for _alloca
+
+    // Define our conversion macros to look exactly like Microsoft's to
+    // facilitate using this stuff both with and without MFC/ATL
+
+    #ifdef _CONVERSION_USES_THREAD_LOCALE
+
+      #ifndef _DEBUG
+        #define SSCVT int _cvt; _cvt; UINT _acp=GetACP(); \
+          _acp; PCWSTR _pw; _pw; PCSTR _pa; _pa
+      #else
+        #define SSCVT int _cvt = 0; _cvt; UINT _acp=GetACP();\
+           _acp; PCWSTR _pw=0; _pw; PCSTR _pa=0; _pa
+      #endif
+      #define SSA2W(pa) (\
+        ((_pa = pa) == 0) ? 0 : (\
+          _cvt = (sslen(_pa)),\
+          StdCodeCvt((PWSTR) _alloca((_cvt+1)*2), (_cvt+1)*2, \
+              _pa, _cvt, _acp)))
+      #define SSW2A(pw) (\
+        ((_pw = pw) == 0) ? 0 : (\
+          _cvt = sslen(_pw), \
+          StdCodeCvt((LPSTR) _alloca((_cvt+1)*2), (_cvt+1)*2, \
+          _pw, _cvt, _acp)))
+  #else
+
+      #ifndef _DEBUG
+        #define SSCVT int _cvt; _cvt; UINT _acp=CP_ACP; _acp;\
+           PCWSTR _pw; _pw; PCSTR _pa; _pa
+      #else
+        #define SSCVT int _cvt = 0; _cvt; UINT _acp=CP_ACP; \
+          _acp; PCWSTR _pw=0; _pw; PCSTR _pa=0; _pa
+      #endif
+      #define SSA2W(pa) (\
+        ((_pa = pa) == 0) ? 0 : (\
+          _cvt = (sslen(_pa)),\
+          StdCodeCvt((PWSTR) _alloca((_cvt+1)*2), (_cvt+1)*2, \
+          _pa, _cvt)))
+      #define SSW2A(pw) (\
+        ((_pw = pw) == 0) ? 0 : (\
+          _cvt = (sslen(_pw)),\
+          StdCodeCvt((LPSTR) _alloca((_cvt+1)*2), (_cvt+1)*2, \
+          _pw, _cvt)))
+    #endif
+
+    #define SSA2CW(pa) ((PCWSTR)SSA2W((pa)))
+    #define SSW2CA(pw) ((PCSTR)SSW2A((pw)))
+
+    #ifdef UNICODE
+      #define SST2A  SSW2A
+      #define SSA2T  SSA2W
+      #define SST2CA  SSW2CA
+      #define SSA2CT  SSA2CW
+    // (Did you get a compiler error here about not being able to convert
+    // PTSTR into PWSTR?  Then your _UNICODE and UNICODE flags are messed
+    // up.  Best bet: #define BOTH macros before including any MS headers.)
+      inline PWSTR  SST2W(PTSTR p)      { return p; }
+      inline PTSTR  SSW2T(PWSTR p)      { return p; }
+      inline PCWSTR  SST2CW(PCTSTR p)    { return p; }
+      inline PCTSTR  SSW2CT(PCWSTR p)    { return p; }
+    #else
+      #define SST2W  SSA2W
+      #define SSW2T  SSW2A
+      #define SST2CW  SSA2CW
+      #define SSW2CT  SSW2CA
+      inline PSTR    SST2A(PTSTR p)      { return p; }
+      inline PTSTR  SSA2T(PSTR p)      { return p; }
+      inline PCSTR  SST2CA(PCTSTR p)    { return p; }
+      inline PCTSTR  SSA2CT(PCSTR p)      { return p; }
+    #endif // #ifdef UNICODE
+
+    #if defined(UNICODE)
+    // in these cases the default (TCHAR) is the same as OLECHAR
+      inline PCOLESTR  SST2COLE(PCTSTR p)    { return p; }
+      inline PCTSTR  SSOLE2CT(PCOLESTR p)  { return p; }
+      inline POLESTR  SST2OLE(PTSTR p)    { return p; }
+      inline PTSTR  SSOLE2T(POLESTR p)    { return p; }
+    #elif defined(OLE2ANSI)
+    // in these cases the default (TCHAR) is the same as OLECHAR
+      inline PCOLESTR  SST2COLE(PCTSTR p)    { return p; }
+      inline PCTSTR  SSOLE2CT(PCOLESTR p)  { return p; }
+      inline POLESTR  SST2OLE(PTSTR p)    { return p; }
+      inline PTSTR  SSOLE2T(POLESTR p)    { return p; }
+    #else
+      //CharNextW doesn't work on Win95 so we use this
+      #define SST2COLE(pa)  SSA2CW((pa))
+      #define SST2OLE(pa)    SSA2W((pa))
+      #define SSOLE2CT(po)  SSW2CA((po))
+      #define SSOLE2T(po)    SSW2A((po))
+    #endif
+
+    #ifdef OLE2ANSI
+      #define SSW2OLE    SSW2A
+      #define SSOLE2W    SSA2W
+      #define SSW2COLE  SSW2CA
+      #define SSOLE2CW  SSA2CW
+      inline POLESTR    SSA2OLE(PSTR p)    { return p; }
+      inline PSTR      SSOLE2A(POLESTR p)  { return p; }
+      inline PCOLESTR    SSA2COLE(PCSTR p)  { return p; }
+      inline PCSTR    SSOLE2CA(PCOLESTR p){ return p; }
+    #else
+      #define SSA2OLE    SSA2W
+      #define SSOLE2A    SSW2A
+      #define SSA2COLE  SSA2CW
+      #define SSOLE2CA  SSW2CA
+      inline POLESTR    SSW2OLE(PWSTR p)  { return p; }
+      inline PWSTR    SSOLE2W(POLESTR p)  { return p; }
+      inline PCOLESTR    SSW2COLE(PCWSTR p)  { return p; }
+      inline PCWSTR    SSOLE2CW(PCOLESTR p){ return p; }
+    #endif
+
+    // Above we've defined macros that look like MS' but all have
+    // an 'SS' prefix.  Now we need the real macros.  We'll either
+    // get them from the macros above or from MFC/ATL.
+
+  #if defined (USES_CONVERSION)
+
+    #define _NO_STDCONVERSION  // just to be consistent
+
+  #else
+
+    #ifdef _MFC_VER
+
+      #include <afxconv.h>
+      #define _NO_STDCONVERSION // just to be consistent
+
+    #else
+
+      #define USES_CONVERSION SSCVT
+      #define A2CW      SSA2CW
+      #define W2CA      SSW2CA
+      #define T2A        SST2A
+      #define A2T        SSA2T
+      #define T2W        SST2W
+      #define W2T        SSW2T
+      #define T2CA      SST2CA
+      #define A2CT      SSA2CT
+      #define T2CW      SST2CW
+      #define W2CT      SSW2CT
+      #define ocslen      sslen
+      #define ocscpy      sscpy
+      #define T2COLE      SST2COLE
+      #define OLE2CT      SSOLE2CT
+      #define T2OLE      SST2COLE
+      #define OLE2T      SSOLE2CT
+      #define A2OLE      SSA2OLE
+      #define OLE2A      SSOLE2A
+      #define W2OLE      SSW2OLE
+      #define OLE2W      SSOLE2W
+      #define A2COLE      SSA2COLE
+      #define OLE2CA      SSOLE2CA
+      #define W2COLE      SSW2COLE
+      #define OLE2CW      SSOLE2CW
+
+    #endif // #ifdef _MFC_VER
+  #endif // #ifndef USES_CONVERSION
+#endif // #ifndef SS_NO_CONVERSION
+
+// Define ostring - generic name for std::basic_string<OLECHAR>
+
+#if !defined(ostring) && !defined(OSTRING_DEFINED)
+  typedef std::basic_string<OLECHAR> ostring;
+  #define OSTRING_DEFINED
+#endif
+
+// StdCodeCvt when there's no conversion to be done
+template <typename T>
+inline T* StdCodeCvt(T* pDst, int nDst, const T* pSrc, int nSrc)
+{
+  int nChars = SSMIN(nSrc, nDst);
+
+  if ( nChars > 0 )
+  {
+    pDst[0]        = '\0';
+    std::basic_string<T>::traits_type::copy(pDst, pSrc, nChars);
+//    std::char_traits<T>::copy(pDst, pSrc, nChars);
+    pDst[nChars]  = '\0';
+  }
+
+  return pDst;
+}
+inline PSTR StdCodeCvt(PSTR pDst, int nDst, PCUSTR pSrc, int nSrc)
+{
+  return StdCodeCvt(pDst, nDst, (PCSTR)pSrc, nSrc);
+}
+inline PUSTR StdCodeCvt(PUSTR pDst, int nDst, PCSTR pSrc, int nSrc)
+{
+  return (PUSTR)StdCodeCvt((PSTR)pDst, nDst, pSrc, nSrc);
+}
+
+// Define tstring -- generic name for std::basic_string<TCHAR>
+
+#if !defined(tstring) && !defined(TSTRING_DEFINED)
+  typedef std::basic_string<TCHAR> tstring;
+  #define TSTRING_DEFINED
+#endif
+
+// a very shorthand way of applying the fix for KB problem Q172398
+// (basic_string assignment bug)
+
+#if defined ( _MSC_VER ) && ( _MSC_VER < 1200 )
+  #define Q172398(x) (x).erase()
+#else
+  #define Q172398(x)
+#endif
+
+// =============================================================================
+// INLINE FUNCTIONS ON WHICH CSTDSTRING RELIES
+//
+// Usually for generic text mapping, we rely on preprocessor macro definitions
+// to map to string functions.  However the CStdStr<> template cannot use
+// macro-based generic text mappings because its character types do not get
+// resolved until template processing which comes AFTER macro processing.  In
+// other words, the preprocessor macro UNICODE is of little help to us in the
+// CStdStr template
+//
+// Therefore, to keep the CStdStr declaration simple, we have these inline
+// functions.  The template calls them often.  Since they are inline (and NOT
+// exported when this is built as a DLL), they will probably be resolved away
+// to nothing.
+//
+// Without these functions, the CStdStr<> template would probably have to broken
+// out into two, almost identical classes.  Either that or it would be a huge,
+// convoluted mess, with tons of "if" statements all over the place checking the
+// size of template parameter CT.
+// =============================================================================
+
+#ifdef SS_NO_LOCALE
+
+  // --------------------------------------------------------------------------
+  // Win32 GetStringTypeEx wrappers
+  // --------------------------------------------------------------------------
+  inline bool wsGetStringType(LCID lc, DWORD dwT, PCSTR pS, int nSize,
+    WORD* pWd)
+  {
+    return FALSE != GetStringTypeExA(lc, dwT, pS, nSize, pWd);
+  }
+  inline bool wsGetStringType(LCID lc, DWORD dwT, PCWSTR pS, int nSize,
+    WORD* pWd)
+  {
+    return FALSE != GetStringTypeExW(lc, dwT, pS, nSize, pWd);
+  }
+
+
+  template<typename CT>
+    inline bool ssisspace (CT t)
+  {
+    WORD toYourMother;
+    return  wsGetStringType(GetThreadLocale(), CT_CTYPE1, &t, 1, &toYourMother)
+      && 0 != (C1_BLANK & toYourMother);
+  }
+
+#endif
+
+// If they defined SS_NO_REFCOUNT, then we must convert all assignments
+
+#if defined (_MSC_VER) && (_MSC_VER < 1300)
+  #ifdef SS_NO_REFCOUNT
+    #define SSREF(x) (x).c_str()
+  #else
+    #define SSREF(x) (x)
+  #endif
+#else
+  #define SSREF(x) (x)
+#endif
+
+// -----------------------------------------------------------------------------
+// sslen: strlen/wcslen wrappers
+// -----------------------------------------------------------------------------
+template<typename CT> inline int sslen(const CT* pT)
+{
+  return 0 == pT ? 0 : (int)std::basic_string<CT>::traits_type::length(pT);
+//  return 0 == pT ? 0 : std::char_traits<CT>::length(pT);
+}
+inline SS_NOTHROW int sslen(const std::string& s)
+{
+  return static_cast<int>(s.length());
+}
+inline SS_NOTHROW int sslen(const std::wstring& s)
+{
+  return static_cast<int>(s.length());
+}
+
+// -----------------------------------------------------------------------------
+// sstolower/sstoupper -- convert characters to upper/lower case
+// -----------------------------------------------------------------------------
+
+#ifdef SS_NO_LOCALE
+  inline char sstoupper(char ch)    { return (char)::toupper(ch); }
+  inline wchar_t sstoupper(wchar_t ch){ return (wchar_t)::towupper(ch); }
+  inline char sstolower(char ch)    { return (char)::tolower(ch); }
+  inline wchar_t sstolower(wchar_t ch){ return (wchar_t)::tolower(ch); }
+#else
+  template<typename CT>
+  inline CT sstolower(const CT& t, const std::locale& loc = std::locale())
+  {
+    return std::tolower<CT>(t, loc);
+  }
+  template<typename CT>
+  inline CT sstoupper(const CT& t, const std::locale& loc = std::locale())
+  {
+    return std::toupper<CT>(t, loc);
+  }
+#endif
+
+// -----------------------------------------------------------------------------
+// ssasn: assignment functions -- assign "sSrc" to "sDst"
+// -----------------------------------------------------------------------------
+typedef std::string::size_type    SS_SIZETYPE; // just for shorthand, really
+typedef std::string::pointer    SS_PTRTYPE;
+typedef std::wstring::size_type    SW_SIZETYPE;
+typedef std::wstring::pointer    SW_PTRTYPE;
+
+
+template <typename T>
+inline void  ssasn(std::basic_string<T>& sDst, const std::basic_string<T>& sSrc)
+{
+  if ( sDst.c_str() != sSrc.c_str() )
+  {
+    sDst.erase();
+    sDst.assign(SSREF(sSrc));
+  }
+}
+template <typename T>
+inline void  ssasn(std::basic_string<T>& sDst, const T *pA)
+{
+  // Watch out for NULLs, as always.
+
+  if ( 0 == pA )
+  {
+    sDst.erase();
+  }
+
+  // If pA actually points to part of sDst, we must NOT erase(), but
+  // rather take a substring
+
+  else if ( pA >= sDst.c_str() && pA <= sDst.c_str() + sDst.size() )
+  {
+    sDst =sDst.substr(static_cast<typename std::basic_string<T>::size_type>(pA-sDst.c_str()));
+  }
+
+  // Otherwise (most cases) apply the assignment bug fix, if applicable
+  // and do the assignment
+
+  else
+  {
+    Q172398(sDst);
+    sDst.assign(pA);
+  }
+}
+inline void  ssasn(std::string& sDst, const std::wstring& sSrc)
+{
+  if ( sSrc.empty() )
+  {
+    sDst.erase();
+  }
+  else
+  {
+    int nDst  = static_cast<int>(sSrc.size());
+
+    // In MBCS builds, pad the buffer to account for the possibility of
+    // some 3 byte characters.  Not perfect but should get most cases.
+
+#ifdef SS_MBCS
+    // In MBCS builds, we don't know how long the destination string will be.
+    nDst  = static_cast<int>(static_cast<double>(nDst) * 1.3);
+    sDst.resize(nDst+1);
+    PCSTR szCvt = StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()), nDst,
+      sSrc.c_str(), static_cast<int>(sSrc.size()));
+    sDst.resize(sslen(szCvt));
+#else
+    sDst.resize(nDst+1);
+    StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()), nDst,
+      sSrc.c_str(), static_cast<int>(sSrc.size()));
+    sDst.resize(sSrc.size());
+#endif
+  }
+}
+inline void  ssasn(std::string& sDst, PCWSTR pW)
+{
+  int nSrc  = sslen(pW);
+  if ( nSrc > 0 )
+  {
+    int nSrc  = sslen(pW);
+    int nDst  = nSrc;
+
+    // In MBCS builds, pad the buffer to account for the possibility of
+    // some 3 byte characters.  Not perfect but should get most cases.
+
+#ifdef SS_MBCS
+    nDst  = static_cast<int>(static_cast<double>(nDst) * 1.3);
+    // In MBCS builds, we don't know how long the destination string will be.
+    sDst.resize(nDst + 1);
+    PCSTR szCvt = StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()), nDst,
+      pW, nSrc);
+    sDst.resize(sslen(szCvt));
+#else
+    sDst.resize(nDst + 1);
+    StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()), nDst, pW, nSrc);
+    sDst.resize(nDst);
+#endif
+  }
+  else
+  {
+    sDst.erase();
+  }
+}
+inline void ssasn(std::string& sDst, const int nNull)
+{
+  //UNUSED(nNull);
+  ASSERT(nNull==0);
+  sDst.assign("");
+}
+#undef StrSizeType
+inline void  ssasn(std::wstring& sDst, const std::string& sSrc)
+{
+  if ( sSrc.empty() )
+  {
+    sDst.erase();
+  }
+  else
+  {
+    int nSrc  = static_cast<int>(sSrc.size());
+    int nDst  = nSrc;
+
+    sDst.resize(nSrc+1);
+    PCWSTR szCvt = StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()), nDst,
+      sSrc.c_str(), nSrc);
+
+    sDst.resize(sslen(szCvt));
+  }
+}
+inline void  ssasn(std::wstring& sDst, PCSTR pA)
+{
+  int nSrc  = sslen(pA);
+
+  if ( 0 == nSrc )
+  {
+    sDst.erase();
+  }
+  else
+  {
+    int nDst  = nSrc;
+    sDst.resize(nDst+1);
+    PCWSTR szCvt = StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()), nDst, pA,
+      nSrc);
+
+    sDst.resize(sslen(szCvt));
+  }
+}
+inline void ssasn(std::wstring& sDst, const int nNull)
+{
+  //UNUSED(nNull);
+  ASSERT(nNull==0);
+  sDst.assign(L"");
+}
+
+// -----------------------------------------------------------------------------
+// ssadd: string object concatenation -- add second argument to first
+// -----------------------------------------------------------------------------
+inline void  ssadd(std::string& sDst, const std::wstring& sSrc)
+{
+  int nSrc  = static_cast<int>(sSrc.size());
+
+  if ( nSrc > 0 )
+  {
+    int nDst  = static_cast<int>(sDst.size());
+    int nAdd  = nSrc;
+
+    // In MBCS builds, pad the buffer to account for the possibility of
+    // some 3 byte characters.  Not perfect but should get most cases.
+
+#ifdef SS_MBCS
+    nAdd    = static_cast<int>(static_cast<double>(nAdd) * 1.3);
+    sDst.resize(nDst+nAdd+1);
+    PCSTR szCvt = StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()+nDst),
+      nAdd, sSrc.c_str(), nSrc);
+    sDst.resize(nDst + sslen(szCvt));
+#else
+    sDst.resize(nDst+nAdd+1);
+    StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()+nDst), nAdd, sSrc.c_str(), nSrc);
+    sDst.resize(nDst + nAdd);
+#endif
+  }
+}
+template <typename T>
+inline void  ssadd(typename std::basic_string<T>& sDst, const typename std::basic_string<T>& sSrc)
+{
+  sDst += sSrc;
+}
+inline void  ssadd(std::string& sDst, PCWSTR pW)
+{
+  int nSrc    = sslen(pW);
+  if ( nSrc > 0 )
+  {
+    int nDst  = static_cast<int>(sDst.size());
+    int nAdd  = nSrc;
+
+#ifdef SS_MBCS
+    nAdd  = static_cast<int>(static_cast<double>(nAdd) * 1.3);
+    sDst.resize(nDst + nAdd + 1);
+    PCSTR szCvt = StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()+nDst),
+      nAdd, pW, nSrc);
+    sDst.resize(nDst + sslen(szCvt));
+#else
+    sDst.resize(nDst + nAdd + 1);
+    StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()+nDst), nAdd, pW, nSrc);
+    sDst.resize(nDst + nSrc);
+#endif
+  }
+}
+template <typename T>
+inline void  ssadd(typename std::basic_string<T>& sDst, const T *pA)
+{
+  if ( pA )
+  {
+    // If the string being added is our internal string or a part of our
+    // internal string, then we must NOT do any reallocation without
+    // first copying that string to another object (since we're using a
+    // direct pointer)
+
+    if ( pA >= sDst.c_str() && pA <= sDst.c_str()+sDst.length())
+    {
+      if ( sDst.capacity() <= sDst.size()+sslen(pA) )
+        sDst.append(std::basic_string<T>(pA));
+      else
+        sDst.append(pA);
+    }
+    else
+    {
+      sDst.append(pA);
+    }
+  }
+}
+inline void  ssadd(std::wstring& sDst, const std::string& sSrc)
+{
+  if ( !sSrc.empty() )
+  {
+    int nSrc  = static_cast<int>(sSrc.size());
+    int nDst  = static_cast<int>(sDst.size());
+
+    sDst.resize(nDst + nSrc + 1);
+#ifdef SS_MBCS
+    PCWSTR szCvt = StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()+nDst),
+      nSrc, sSrc.c_str(), nSrc+1);
+    sDst.resize(nDst + sslen(szCvt));
+#else
+    StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()+nDst), nSrc, sSrc.c_str(), nSrc+1);
+    sDst.resize(nDst + nSrc);
+#endif
+  }
+}
+inline void  ssadd(std::wstring& sDst, PCSTR pA)
+{
+  int nSrc    = sslen(pA);
+
+  if ( nSrc > 0 )
+  {
+    int nDst  = static_cast<int>(sDst.size());
+
+    sDst.resize(nDst + nSrc + 1);
+#ifdef SS_MBCS
+    PCWSTR szCvt = StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()+nDst),
+      nSrc, pA, nSrc+1);
+    sDst.resize(nDst + sslen(szCvt));
+#else
+    StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()+nDst), nSrc, pA, nSrc+1);
+    sDst.resize(nDst + nSrc);
+#endif
+  }
+}
+
+// -----------------------------------------------------------------------------
+// sscmp: comparison (case sensitive, not affected by locale)
+// -----------------------------------------------------------------------------
+template<typename CT>
+inline int sscmp(const CT* pA1, const CT* pA2)
+{
+    CT f;
+    CT l;
+
+    do
+    {
+      f = *(pA1++);
+      l = *(pA2++);
+    } while ( (f) && (f == l) );
+
+    return (int)(f - l);
+}
+
+// -----------------------------------------------------------------------------
+// ssicmp: comparison (case INsensitive, not affected by locale)
+// -----------------------------------------------------------------------------
+template<typename CT>
+inline int ssicmp(const CT* pA1, const CT* pA2)
+{
+  // Using the "C" locale = "not affected by locale"
+
+  std::locale loc = std::locale::classic();
+    const std::ctype<CT>& ct = SS_USE_FACET(loc, std::ctype<CT>);
+    CT f;
+    CT l;
+
+    do
+    {
+      f = ct.tolower(*(pA1++));
+      l = ct.tolower(*(pA2++));
+    } while ( (f) && (f == l) );
+
+    return (int)(f - l);
+}
+
+// -----------------------------------------------------------------------------
+// ssupr/sslwr: Uppercase/Lowercase conversion functions
+// -----------------------------------------------------------------------------
+
+template<typename CT>
+inline void sslwr(CT* pT, size_t nLen, const std::locale& loc=std::locale())
+{
+  SS_USE_FACET(loc, std::ctype<CT>).tolower(pT, pT+nLen);
+}
+template<typename CT>
+inline void ssupr(CT* pT, size_t nLen, const std::locale& loc=std::locale())
+{
+  SS_USE_FACET(loc, std::ctype<CT>).toupper(pT, pT+nLen);
+}
+
+// -----------------------------------------------------------------------------
+// vsprintf/vswprintf or _vsnprintf/_vsnwprintf equivalents.  In standard
+// builds we can't use _vsnprintf/_vsnwsprintf because they're MS extensions.
+//
+// -----------------------------------------------------------------------------
+// Borland's headers put some ANSI "C" functions in the 'std' namespace.
+// Promote them to the global namespace so we can use them here.
+
+#if defined(__BORLANDC__)
+    using std::vsprintf;
+    using std::vswprintf;
+#endif
+
+  // GNU is supposed to have vsnprintf and vsnwprintf.  But only the newer
+  // distributions do.
+
+#if defined(__GNUC__)
+
+  inline int ssvsprintf(PSTR pA, size_t nCount, PCSTR pFmtA, va_list vl)
+  {
+    return vsnprintf(pA, nCount, pFmtA, vl);
+  }
+  inline int ssvsprintf(PWSTR pW, size_t nCount, PCWSTR pFmtW, va_list vl)
+  {
+    return vswprintf(pW, nCount, pFmtW, vl);
+  }
+
+  // Microsofties can use
+#elif defined(_MSC_VER) && !defined(SS_ANSI)
+
+  inline int  ssvsprintf(PSTR pA, size_t nCount, PCSTR pFmtA, va_list vl)
+  {
+    return _vsnprintf(pA, nCount, pFmtA, vl);
+  }
+  inline int  ssvsprintf(PWSTR pW, size_t nCount, PCWSTR pFmtW, va_list vl)
+  {
+    return _vsnwprintf(pW, nCount, pFmtW, vl);
+  }
+
+#elif defined (SS_DANGEROUS_FORMAT)  // ignore buffer size parameter if needed?
+
+  inline int ssvsprintf(PSTR pA, size_t /*nCount*/, PCSTR pFmtA, va_list vl)
+  {
+    return vsprintf(pA, pFmtA, vl);
+  }
+
+  inline int ssvsprintf(PWSTR pW, size_t nCount, PCWSTR pFmtW, va_list vl)
+  {
+    // JMO: Some distributions of the "C" have a version of vswprintf that
+        // takes 3 arguments (e.g. Microsoft, Borland, GNU).  Others have a
+        // version which takes 4 arguments (an extra "count" argument in the
+        // second position.  The best stab I can take at this so far is that if
+        // you are NOT running with MS, Borland, or GNU, then I'll assume you
+        // have the version that takes 4 arguments.
+        //
+        // I'm sure that these checks don't catch every platform correctly so if
+        // you get compiler errors on one of the lines immediately below, it's
+        // probably because your implemntation takes a different number of
+        // arguments.  You can comment out the offending line (and use the
+        // alternate version) or you can figure out what compiler flag to check
+        // and add that preprocessor check in.  Regardless, if you get an error
+        // on these lines, I'd sure like to hear from you about it.
+        //
+        // Thanks to Ronny Schulz for the SGI-specific checks here.
+
+//  #if !defined(__MWERKS__) && !defined(__SUNPRO_CC_COMPAT) && !defined(__SUNPRO_CC)
+    #if    !defined(_MSC_VER) \
+        && !defined (__BORLANDC__) \
+        && !defined(__GNUC__) \
+        && !defined(__sgi)
+
+        return vswprintf(pW, nCount, pFmtW, vl);
+
+    // suddenly with the current SGI 7.3 compiler there is no such function as
+    // vswprintf and the substitute needs explicit casts to compile
+
+    #elif defined(__sgi)
+
+        nCount;
+        return vsprintf( (char *)pW, (char *)pFmtW, vl);
+
+    #else
+
+        nCount;
+        return vswprintf(pW, pFmtW, vl);
+
+    #endif
+
+  }
+
+#endif
+
+  // GOT COMPILER PROBLEMS HERE?
+  // ---------------------------
+  // Does your compiler choke on one or more of the following 2 functions?  It
+  // probably means that you don't have have either vsnprintf or vsnwprintf in
+  // your version of the CRT.  This is understandable since neither is an ANSI
+  // "C" function.  However it still leaves you in a dilemma.  In order to make
+  // this code build, you're going to have to to use some non-length-checked
+  // formatting functions that every CRT has:  vsprintf and vswprintf.
+  //
+  // This is very dangerous.  With the proper erroneous (or malicious) code, it
+  // can lead to buffer overlows and crashing your PC.  Use at your own risk
+  // In order to use them, just #define SS_DANGEROUS_FORMAT at the top of
+  // this file.
+  //
+  // Even THEN you might not be all the way home due to some non-conforming
+  // distributions.  More on this in the comments below.
+
+  inline int  ssnprintf(PSTR pA, size_t nCount, PCSTR pFmtA, va_list vl)
+  {
+  #ifdef _MSC_VER
+      return _vsnprintf(pA, nCount, pFmtA, vl);
+  #else
+      return vsnprintf(pA, nCount, pFmtA, vl);
+  #endif
+  }
+  inline int  ssnprintf(PWSTR pW, size_t nCount, PCWSTR pFmtW, va_list vl)
+  {
+  #ifdef _MSC_VER
+      return _vsnwprintf(pW, nCount, pFmtW, vl);
+  #else
+      return vswprintf(pW, nCount, pFmtW, vl);
+  #endif
+  }
+
+
+
+
+// -----------------------------------------------------------------------------
+// ssload: Type safe, overloaded ::LoadString wrappers
+// There is no equivalent of these in non-Win32-specific builds.  However, I'm
+// thinking that with the message facet, there might eventually be one
+// -----------------------------------------------------------------------------
+#if defined (SS_WIN32) && !defined(SS_ANSI)
+  inline int ssload(HMODULE hInst, UINT uId, PSTR pBuf, int nMax)
+  {
+    return ::LoadStringA(hInst, uId, pBuf, nMax);
+  }
+  inline int ssload(HMODULE hInst, UINT uId, PWSTR pBuf, int nMax)
+  {
+    return ::LoadStringW(hInst, uId, pBuf, nMax);
+  }
+#if defined ( _MSC_VER ) && ( _MSC_VER >= 1500 )
+  inline int ssload(HMODULE hInst, UINT uId, uint16_t *pBuf, int nMax)
+  {
+    return 0;
+  }
+  inline int ssload(HMODULE hInst, UINT uId, uint32_t *pBuf, int nMax)
+  {
+    return 0;
+  }
+#endif
+#endif
+
+
+// -----------------------------------------------------------------------------
+// sscoll/ssicoll: Collation wrappers
+//    Note -- with MSVC I have reversed the arguments order here because the
+//    functions appear to return the opposite of what they should
+// -----------------------------------------------------------------------------
+#ifndef SS_NO_LOCALE
+template <typename CT>
+inline int sscoll(const CT* sz1, int nLen1, const CT* sz2, int nLen2)
+{
+  const std::collate<CT>& coll =
+    SS_USE_FACET(std::locale(), std::collate<CT>);
+
+  return coll.compare(sz2, sz2+nLen2, sz1, sz1+nLen1);
+}
+template <typename CT>
+inline int ssicoll(const CT* sz1, int nLen1, const CT* sz2, int nLen2)
+{
+  const std::locale loc;
+  const std::collate<CT>& coll = SS_USE_FACET(loc, std::collate<CT>);
+
+  // Some implementations seem to have trouble using the collate<>
+  // facet typedefs so we'll just default to basic_string and hope
+  // that's what the collate facet uses (which it generally should)
+
+//  std::collate<CT>::string_type s1(sz1);
+//  std::collate<CT>::string_type s2(sz2);
+  const std::basic_string<CT> sEmpty;
+    std::basic_string<CT> s1(sz1 ? sz1 : sEmpty.c_str());
+    std::basic_string<CT> s2(sz2 ? sz2 : sEmpty.c_str());
+
+  sslwr(const_cast<CT*>(s1.c_str()), nLen1, loc);
+  sslwr(const_cast<CT*>(s2.c_str()), nLen2, loc);
+  return coll.compare(s2.c_str(), s2.c_str()+nLen2,
+            s1.c_str(), s1.c_str()+nLen1);
+}
+#endif
+
+
+// -----------------------------------------------------------------------------
+// ssfmtmsg: FormatMessage equivalents.  Needed because I added a CString facade
+// Again -- no equivalent of these on non-Win32 builds but their might one day
+// be one if the message facet gets implemented
+// -----------------------------------------------------------------------------
+#if defined (SS_WIN32) && !defined(SS_ANSI)
+  inline DWORD ssfmtmsg(DWORD dwFlags, LPCVOID pSrc, DWORD dwMsgId,
+              DWORD dwLangId, PSTR pBuf, DWORD nSize,
+              va_list* vlArgs)
+  {
+    return FormatMessageA(dwFlags, pSrc, dwMsgId, dwLangId,
+                pBuf, nSize,vlArgs);
+  }
+  inline DWORD ssfmtmsg(DWORD dwFlags, LPCVOID pSrc, DWORD dwMsgId,
+              DWORD dwLangId, PWSTR pBuf, DWORD nSize,
+              va_list* vlArgs)
+  {
+    return FormatMessageW(dwFlags, pSrc, dwMsgId, dwLangId,
+                pBuf, nSize,vlArgs);
+  }
+#else
+#endif
+
+
+
+// FUNCTION: sscpy.  Copies up to 'nMax' characters from pSrc to pDst.
+// -----------------------------------------------------------------------------
+// FUNCTION:  sscpy
+//    inline int sscpy(PSTR pDst, PCSTR pSrc, int nMax=-1);
+//    inline int sscpy(PUSTR pDst,  PCSTR pSrc, int nMax=-1)
+//    inline int sscpy(PSTR pDst, PCWSTR pSrc, int nMax=-1);
+//    inline int sscpy(PWSTR pDst, PCWSTR pSrc, int nMax=-1);
+//    inline int sscpy(PWSTR pDst, PCSTR pSrc, int nMax=-1);
+//
+// DESCRIPTION:
+//    This function is very much (but not exactly) like strcpy.  These
+//    overloads simplify copying one C-style string into another by allowing
+//    the caller to specify two different types of strings if necessary.
+//
+//    The strings must NOT overlap
+//
+//    "Character" is expressed in terms of the destination string, not
+//    the source.  If no 'nMax' argument is supplied, then the number of
+//    characters copied will be sslen(pSrc).  A NULL terminator will
+//    also be added so pDst must actually be big enough to hold nMax+1
+//    characters.  The return value is the number of characters copied,
+//    not including the NULL terminator.
+//
+// PARAMETERS:
+//    pSrc - the string to be copied FROM.  May be a char based string, an
+//         MBCS string (in Win32 builds) or a wide string (wchar_t).
+//    pSrc - the string to be copied TO.  Also may be either MBCS or wide
+//    nMax - the maximum number of characters to be copied into szDest.  Note
+//         that this is expressed in whatever a "character" means to pDst.
+//         If pDst is a wchar_t type string than this will be the maximum
+//         number of wchar_ts that my be copied.  The pDst string must be
+//         large enough to hold least nMaxChars+1 characters.
+//         If the caller supplies no argument for nMax this is a signal to
+//         the routine to copy all the characters in pSrc, regardless of
+//         how long it is.
+//
+// RETURN VALUE: none
+// -----------------------------------------------------------------------------
+
+template<typename CT1, typename CT2>
+inline int sscpycvt(CT1* pDst, const CT2* pSrc, int nMax)
+{
+  // Note -- we assume pDst is big enough to hold pSrc.  If not, we're in
+  // big trouble.  No bounds checking.  Caveat emptor.
+
+  int nSrc = sslen(pSrc);
+
+  const CT1* szCvt = StdCodeCvt(pDst, nMax, pSrc, nSrc);
+
+  // If we're copying the same size characters, then all the "code convert"
+  // just did was basically memcpy so the #of characters copied is the same
+  // as the number requested.  I should probably specialize this function
+  // template to achieve this purpose as it is silly to do a runtime check
+  // of a fact known at compile time.  I'll get around to it.
+
+  return sslen(szCvt);
+}
+
+template<typename T>
+inline int sscpycvt(T* pDst, const T* pSrc, int nMax)
+{
+  int nCount = nMax;
+  for (; nCount > 0 && *pSrc; ++pSrc, ++pDst, --nCount)
+    std::basic_string<T>::traits_type::assign(*pDst, *pSrc);
+
+  *pDst = 0;
+  return nMax - nCount;
+}
+
+inline int sscpycvt(PWSTR pDst, PCSTR pSrc, int nMax)
+{
+  // Note -- we assume pDst is big enough to hold pSrc.  If not, we're in
+  // big trouble.  No bounds checking.  Caveat emptor.
+
+  const PWSTR szCvt = StdCodeCvt(pDst, nMax, pSrc, nMax);
+  return sslen(szCvt);
+}
+
+template<typename CT1, typename CT2>
+inline int sscpy(CT1* pDst, const CT2* pSrc, int nMax, int nLen)
+{
+  return sscpycvt(pDst, pSrc, SSMIN(nMax, nLen));
+}
+template<typename CT1, typename CT2>
+inline int sscpy(CT1* pDst, const CT2* pSrc, int nMax)
+{
+  return sscpycvt(pDst, pSrc, SSMIN(nMax, sslen(pSrc)));
+}
+template<typename CT1, typename CT2>
+inline int sscpy(CT1* pDst, const CT2* pSrc)
+{
+  return sscpycvt(pDst, pSrc, sslen(pSrc));
+}
+template<typename CT1, typename CT2>
+inline int sscpy(CT1* pDst, const std::basic_string<CT2>& sSrc, int nMax)
+{
+  return sscpycvt(pDst, sSrc.c_str(), SSMIN(nMax, (int)sSrc.length()));
+}
+template<typename CT1, typename CT2>
+inline int sscpy(CT1* pDst, const std::basic_string<CT2>& sSrc)
+{
+  return sscpycvt(pDst, sSrc.c_str(), (int)sSrc.length());
+}
+
+#ifdef SS_INC_COMDEF
+  template<typename CT1>
+  inline int sscpy(CT1* pDst, const _bstr_t& bs, int nMax)
+  {
+    return sscpycvt(pDst, static_cast<PCOLESTR>(bs),
+            SSMIN(nMax, static_cast<int>(bs.length())));
+  }
+  template<typename CT1>
+  inline int sscpy(CT1* pDst, const _bstr_t& bs)
+  {
+    return sscpy(pDst, bs, static_cast<int>(bs.length()));
+  }
+#endif
+
+
+// -----------------------------------------------------------------------------
+// Functional objects for changing case.  They also let you pass locales
+// -----------------------------------------------------------------------------
+
+#ifdef SS_NO_LOCALE
+  template<typename CT>
+  struct SSToUpper : public std::unary_function<CT, CT>
+  {
+    inline CT operator()(const CT& t) const
+    {
+      return sstoupper(t);
+    }
+  };
+  template<typename CT>
+  struct SSToLower : public std::unary_function<CT, CT>
+  {
+    inline CT operator()(const CT& t) const
+    {
+      return sstolower(t);
+    }
+  };
+#else
+  template<typename CT>
+  struct SSToUpper : public std::binary_function<CT, std::locale, CT>
+  {
+    inline CT operator()(const CT& t, const std::locale& loc) const
+    {
+      return sstoupper<CT>(t, loc);
+    }
+  };
+  template<typename CT>
+  struct SSToLower : public std::binary_function<CT, std::locale, CT>
+  {
+    inline CT operator()(const CT& t, const std::locale& loc) const
+    {
+      return sstolower<CT>(t, loc);
+    }
+  };
+#endif
+
+// This struct is used for TrimRight() and TrimLeft() function implementations.
+//template<typename CT>
+//struct NotSpace : public std::unary_function<CT, bool>
+//{
+//  const std::locale& loc;
+//  inline NotSpace(const std::locale& locArg) : loc(locArg) {}
+//  inline bool operator() (CT t) { return !std::isspace(t, loc); }
+//};
+template<typename CT>
+struct NotSpace : public std::unary_function<CT, bool>
+{
+  // DINKUMWARE BUG:
+  // Note -- using std::isspace in a COM DLL gives us access violations
+  // because it causes the dynamic addition of a function to be called
+  // when the library shuts down.  Unfortunately the list is maintained
+  // in DLL memory but the function is in static memory.  So the COM DLL
+  // goes away along with the function that was supposed to be called,
+  // and then later when the DLL CRT shuts down it unloads the list and
+  // tries to call the long-gone function.
+  // This is DinkumWare's implementation problem.  If you encounter this
+  // problem, you may replace the calls here with good old isspace() and
+  // iswspace() from the CRT unless they specify SS_ANSI
+
+#ifdef SS_NO_LOCALE
+
+  bool operator() (CT t) const { return !ssisspace(t); }
+
+#else
+  const std::locale loc;
+  NotSpace(const std::locale& locArg=std::locale()) : loc(locArg) {}
+  bool operator() (CT t) const { return !std::isspace(t, loc); }
+#endif
+};
+
+
+
+
+//      Now we can define the template (finally!)
+// =============================================================================
+// TEMPLATE: CStdStr
+//    template<typename CT> class CStdStr : public std::basic_string<CT>
+//
+// REMARKS:
+//    This template derives from basic_string<CT> and adds some MFC CString-
+//    like functionality
+//
+//    Basically, this is my attempt to make Standard C++ library strings as
+//    easy to use as the MFC CString class.
+//
+//    Note that although this is a template, it makes the assumption that the
+//    template argument (CT, the character type) is either char or wchar_t.
+// =============================================================================
+
+//#define CStdStr _SS  // avoid compiler warning 4786
+
+//    template<typename ARG> ARG& FmtArg(ARG& arg)  { return arg; }
+//    PCSTR  FmtArg(const std::string& arg)  { return arg.c_str(); }
+//    PCWSTR FmtArg(const std::wstring& arg) { return arg.c_str(); }
+
+template<typename ARG>
+struct FmtArg
+{
+    explicit FmtArg(const ARG& arg) : a_(arg) {}
+    const ARG& operator()() const { return a_; }
+    const ARG& a_;
+private:
+    FmtArg& operator=(const FmtArg&) { return *this; }
+};
+
+template<typename CT>
+class CStdStr : public std::basic_string<CT>
+{
+  // Typedefs for shorter names.  Using these names also appears to help
+  // us avoid some ambiguities that otherwise arise on some platforms
+
+  #define MYBASE std::basic_string<CT>         // my base class
+  //typedef typename std::basic_string<CT>    MYBASE;   // my base class
+  typedef CStdStr<CT>              MYTYPE;   // myself
+  typedef typename MYBASE::const_pointer    PCMYSTR; // PCSTR or PCWSTR
+  typedef typename MYBASE::pointer      PMYSTR;   // PSTR or PWSTR
+  typedef typename MYBASE::iterator      MYITER;  // my iterator type
+  typedef typename MYBASE::const_iterator    MYCITER; // you get the idea...
+  typedef typename MYBASE::reverse_iterator  MYRITER;
+  typedef typename MYBASE::size_type      MYSIZE;
+  typedef typename MYBASE::value_type      MYVAL;
+  typedef typename MYBASE::allocator_type    MYALLOC;
+
+public:
+  // shorthand conversion from PCTSTR to string resource ID
+  #define SSRES(pctstr)  LOWORD(reinterpret_cast<unsigned long>(pctstr))
+
+  bool TryLoad(const void* pT)
+  {
+    bool bLoaded = false;
+
+#if defined(SS_WIN32) && !defined(SS_ANSI)
+    if ( ( pT != NULL ) && SS_IS_INTRESOURCE(pT) )
+    {
+      UINT nId = LOWORD(reinterpret_cast<unsigned long>(pT));
+      if ( !LoadString(nId) )
+      {
+        TRACE(_T("Can't load string %u\n"), SSRES(pT));
+      }
+      bLoaded = true;
+    }
+#endif
+
+    return bLoaded;
+  }
+
+
+  // CStdStr inline constructors
+  CStdStr()
+  {
+  }
+
+  CStdStr(const MYTYPE& str) : MYBASE(SSREF(str))
+  {
+  }
+
+  CStdStr(const std::string& str)
+  {
+    ssasn(*this, SSREF(str));
+  }
+
+  CStdStr(const std::wstring& str)
+  {
+    ssasn(*this, SSREF(str));
+  }
+
+  CStdStr(PCMYSTR pT, MYSIZE n) : MYBASE(pT, n)
+  {
+  }
+
+#ifdef SS_UNSIGNED
+  CStdStr(PCUSTR pU)
+  {
+    *this = reinterpret_cast<PCSTR>(pU);
+  }
+#endif
+
+  CStdStr(PCSTR pA)
+  {
+  #ifdef SS_ANSI
+    *this = pA;
+  #else
+    if ( !TryLoad(pA) )
+      *this = pA;
+  #endif
+  }
+
+  CStdStr(PCWSTR pW)
+  {
+  #ifdef SS_ANSI
+    *this = pW;
+  #else
+    if ( !TryLoad(pW) )
+      *this = pW;
+  #endif
+  }
+
+  CStdStr(uint16_t* pW)
+  {
+  #ifdef SS_ANSI
+    *this = pW;
+  #else
+    if ( !TryLoad(pW) )
+      *this = pW;
+  #endif
+  }
+
+  CStdStr(uint32_t* pW)
+  {
+  #ifdef SS_ANSI
+    *this = pW;
+  #else
+    if ( !TryLoad(pW) )
+      *this = pW;
+  #endif
+  }
+
+  CStdStr(MYCITER first, MYCITER last)
+    : MYBASE(first, last)
+  {
+  }
+
+  CStdStr(MYSIZE nSize, MYVAL ch, const MYALLOC& al=MYALLOC())
+    : MYBASE(nSize, ch, al)
+  {
+  }
+
+  #ifdef SS_INC_COMDEF
+    CStdStr(const _bstr_t& bstr)
+    {
+      if ( bstr.length() > 0 )
+        this->append(static_cast<PCMYSTR>(bstr), bstr.length());
+    }
+  #endif
+
+  // CStdStr inline assignment operators -- the ssasn function now takes care
+  // of fixing  the MSVC assignment bug (see knowledge base article Q172398).
+  MYTYPE& operator=(const MYTYPE& str)
+  {
+    ssasn(*this, str);
+    return *this;
+  }
+
+  MYTYPE& operator=(const std::string& str)
+  {
+    ssasn(*this, str);
+    return *this;
+  }
+
+  MYTYPE& operator=(const std::wstring& str)
+  {
+    ssasn(*this, str);
+    return *this;
+  }
+
+  MYTYPE& operator=(PCSTR pA)
+  {
+    ssasn(*this, pA);
+    return *this;
+  }
+
+  MYTYPE& operator=(PCWSTR pW)
+  {
+    ssasn(*this, pW);
+    return *this;
+  }
+
+#ifdef SS_UNSIGNED
+  MYTYPE& operator=(PCUSTR pU)
+  {
+    ssasn(*this, reinterpret_cast<PCSTR>(pU));
+    return *this;
+  }
+#endif
+
+  MYTYPE& operator=(uint16_t* pA)
+  {
+    ssasn(*this, pA);
+    return *this;
+  }
+
+  MYTYPE& operator=(uint32_t* pA)
+  {
+    ssasn(*this, pA);
+    return *this;
+  }
+
+  MYTYPE& operator=(CT t)
+  {
+    Q172398(*this);
+    this->assign(1, t);
+    return *this;
+  }
+
+  #ifdef SS_INC_COMDEF
+    MYTYPE& operator=(const _bstr_t& bstr)
+    {
+      if ( bstr.length() > 0 )
+      {
+        this->assign(static_cast<PCMYSTR>(bstr), bstr.length());
+        return *this;
+      }
+      else
+      {
+        this->erase();
+        return *this;
+      }
+    }
+  #endif
+
+
+  // Overloads  also needed to fix the MSVC assignment bug (KB: Q172398)
+  //  *** Thanks to Pete The Plumber for catching this one ***
+  // They also are compiled if you have explicitly turned off refcounting
+  #if ( defined(_MSC_VER) && ( _MSC_VER < 1200 ) ) || defined(SS_NO_REFCOUNT)
+
+    MYTYPE& assign(const MYTYPE& str)
+    {
+      Q172398(*this);
+      sscpy(GetBuffer(str.size()+1), SSREF(str));
+      this->ReleaseBuffer(str.size());
+      return *this;
+    }
+
+    MYTYPE& assign(const MYTYPE& str, MYSIZE nStart, MYSIZE nChars)
+    {
+      // This overload of basic_string::assign is supposed to assign up to
+      // <nChars> or the NULL terminator, whichever comes first.  Since we
+      // are about to call a less forgiving overload (in which <nChars>
+      // must be a valid length), we must adjust the length here to a safe
+      // value.  Thanks to Ullrich Poll�hne for catching this bug
+
+      nChars    = SSMIN(nChars, str.length() - nStart);
+      MYTYPE strTemp(str.c_str()+nStart, nChars);
+      Q172398(*this);
+      this->assign(strTemp);
+      return *this;
+    }
+
+    MYTYPE& assign(const MYBASE& str)
+    {
+      ssasn(*this, str);
+      return *this;
+    }
+
+    MYTYPE& assign(const MYBASE& str, MYSIZE nStart, MYSIZE nChars)
+    {
+      // This overload of basic_string::assign is supposed to assign up to
+      // <nChars> or the NULL terminator, whichever comes first.  Since we
+      // are about to call a less forgiving overload (in which <nChars>
+      // must be a valid length), we must adjust the length here to a safe
+      // value. Thanks to Ullrich Poll�hne for catching this bug
+
+      nChars    = SSMIN(nChars, str.length() - nStart);
+
+      // Watch out for assignment to self
+
+      if ( this == &str )
+      {
+        MYTYPE strTemp(str.c_str() + nStart, nChars);
+        static_cast<MYBASE*>(this)->assign(strTemp);
+      }
+      else
+      {
+        Q172398(*this);
+        static_cast<MYBASE*>(this)->assign(str.c_str()+nStart, nChars);
+      }
+      return *this;
+    }
+
+    MYTYPE& assign(const CT* pC, MYSIZE nChars)
+    {
+      // Q172398 only fix -- erase before assigning, but not if we're
+      // assigning from our own buffer
+
+  #if defined ( _MSC_VER ) && ( _MSC_VER < 1200 )
+      if ( !this->empty() &&
+        ( pC < this->data() || pC > this->data() + this->capacity() ) )
+      {
+        this->erase();
+      }
+  #endif
+      Q172398(*this);
+      static_cast<MYBASE*>(this)->assign(pC, nChars);
+      return *this;
+    }
+
+    MYTYPE& assign(MYSIZE nChars, MYVAL val)
+    {
+      Q172398(*this);
+      static_cast<MYBASE*>(this)->assign(nChars, val);
+      return *this;
+    }
+
+    MYTYPE& assign(const CT* pT)
+    {
+      return this->assign(pT, MYBASE::traits_type::length(pT));
+    }
+
+    MYTYPE& assign(MYCITER iterFirst, MYCITER iterLast)
+    {
+  #if defined ( _MSC_VER ) && ( _MSC_VER < 1200 )
+      // Q172398 fix.  don't call erase() if we're assigning from ourself
+      if ( iterFirst < this->begin() ||
+                 iterFirst > this->begin() + this->size() )
+            {
+        this->erase()
+            }
+  #endif
+      this->replace(this->begin(), this->end(), iterFirst, iterLast);
+      return *this;
+    }
+  #endif
+
+
+  // -------------------------------------------------------------------------
+  // CStdStr inline concatenation.
+  // -------------------------------------------------------------------------
+  MYTYPE& operator+=(const MYTYPE& str)
+  {
+    ssadd(*this, str);
+    return *this;
+  }
+
+  MYTYPE& operator+=(const std::string& str)
+  {
+    ssadd(*this, str);
+    return *this;
+  }
+
+  MYTYPE& operator+=(const std::wstring& str)
+  {
+    ssadd(*this, str);
+    return *this;
+  }
+
+  MYTYPE& operator+=(PCSTR pA)
+  {
+    ssadd(*this, pA);
+    return *this;
+  }
+
+  MYTYPE& operator+=(PCWSTR pW)
+  {
+    ssadd(*this, pW);
+    return *this;
+  }
+
+  MYTYPE& operator+=(uint16_t* pW)
+  {
+    ssadd(*this, pW);
+    return *this;
+  }
+
+  MYTYPE& operator+=(uint32_t* pW)
+  {
+    ssadd(*this, pW);
+    return *this;
+  }
+
+  MYTYPE& operator+=(CT t)
+  {
+    this->append(1, t);
+    return *this;
+  }
+  #ifdef SS_INC_COMDEF  // if we have _bstr_t, define a += for it too.
+    MYTYPE& operator+=(const _bstr_t& bstr)
+    {
+      return this->operator+=(static_cast<PCMYSTR>(bstr));
+    }
+  #endif
+
+
+  // -------------------------------------------------------------------------
+  // Case changing functions
+  // -------------------------------------------------------------------------
+
+    MYTYPE& ToUpper(const std::locale& loc=std::locale())
+  {
+    // Note -- if there are any MBCS character sets in which the lowercase
+    // form a character takes up a different number of bytes than the
+    // uppercase form, this would probably not work...
+
+    std::transform(this->begin(),
+             this->end(),
+             this->begin(),
+#ifdef SS_NO_LOCALE
+             SSToUpper<CT>());
+#else
+             std::bind2nd(SSToUpper<CT>(), loc));
+#endif
+
+    // ...but if it were, this would probably work better.  Also, this way
+    // seems to be a bit faster when anything other then the "C" locale is
+    // used...
+
+//    if ( !empty() )
+//    {
+//      ssupr(this->GetBuf(), this->size(), loc);
+//      this->RelBuf();
+//    }
+
+    return *this;
+  }
+
+  MYTYPE& ToLower(const std::locale& loc=std::locale())
+  {
+    // Note -- if there are any MBCS character sets in which the lowercase
+    // form a character takes up a different number of bytes than the
+    // uppercase form, this would probably not work...
+
+    std::transform(this->begin(),
+             this->end(),
+             this->begin(),
+#ifdef SS_NO_LOCALE
+             SSToLower<CT>());
+#else
+             std::bind2nd(SSToLower<CT>(), loc));
+#endif
+
+    // ...but if it were, this would probably work better.  Also, this way
+    // seems to be a bit faster when anything other then the "C" locale is
+    // used...
+
+//    if ( !empty() )
+//    {
+//      sslwr(this->GetBuf(), this->size(), loc);
+//      this->RelBuf();
+//    }
+    return *this;
+  }
+
+
+  MYTYPE& Normalize()
+  {
+    return Trim().ToLower();
+  }
+
+
+  // -------------------------------------------------------------------------
+  // CStdStr -- Direct access to character buffer.  In the MS' implementation,
+  // the at() function that we use here also calls _Freeze() providing us some
+  // protection from multithreading problems associated with ref-counting.
+    // In VC 7 and later, of course, the ref-counting stuff is gone.
+  // -------------------------------------------------------------------------
+
+  CT* GetBuf(int nMinLen=-1)
+  {
+    if ( static_cast<int>(this->size()) < nMinLen )
+      this->resize(static_cast<MYSIZE>(nMinLen));
+
+    return this->empty() ? const_cast<CT*>(this->data()) : &(this->at(0));
+  }
+
+  CT* SetBuf(int nLen)
+  {
+    nLen = ( nLen > 0 ? nLen : 0 );
+    if ( this->capacity() < 1 && nLen == 0 )
+      this->resize(1);
+
+    this->resize(static_cast<MYSIZE>(nLen));
+    return const_cast<CT*>(this->data());
+  }
+  void RelBuf(int nNewLen=-1)
+  {
+    this->resize(static_cast<MYSIZE>(nNewLen > -1 ? nNewLen :
+                                                        sslen(this->c_str())));
+  }
+
+  void BufferRel()     { RelBuf(); }      // backwards compatability
+  CT*  Buffer()       { return GetBuf(); }  // backwards compatability
+  CT*  BufferSet(int nLen) { return SetBuf(nLen);}// backwards compatability
+
+  bool Equals(const CT* pT, bool bUseCase=false) const
+  {
+    return  0 == (bUseCase ? this->compare(pT) : ssicmp(this->c_str(), pT));
+  }
+
+  // -------------------------------------------------------------------------
+  // FUNCTION:  CStdStr::Load
+  // REMARKS:
+  //    Loads string from resource specified by nID
+  //
+  // PARAMETERS:
+  //    nID - resource Identifier.  Purely a Win32 thing in this case
+  //
+  // RETURN VALUE:
+  //    true if successful, false otherwise
+  // -------------------------------------------------------------------------
+
+#ifndef SS_ANSI
+
+  bool Load(UINT nId, HMODULE hModule=NULL)
+  {
+    bool bLoaded    = false;  // set to true of we succeed.
+
+  #ifdef _MFC_VER    // When in Rome (or MFC land)...
+
+    // If they gave a resource handle, use it.  Note - this is archaic
+    // and not really what I would recommend.  But then again, in MFC
+    // land, you ought to be using CString for resources anyway since
+    // it walks the resource chain for you.
+
+    HMODULE hModuleOld = NULL;
+
+    if ( NULL != hModule )
+    {
+      hModuleOld = AfxGetResourceHandle();
+      AfxSetResourceHandle(hModule);
+    }
+
+    // ...load the string
+
+    CString strRes;
+    bLoaded        = FALSE != strRes.LoadString(nId);
+
+    // ...and if we set the resource handle, restore it.
+
+    if ( NULL != hModuleOld )
+      AfxSetResourceHandle(hModule);
+
+    if ( bLoaded )
+      *this      = strRes;
+
+  #else // otherwise make our own hackneyed version of CString's Load
+
+    // Get the resource name and module handle
+
+    if ( NULL == hModule )
+      hModule      = GetResourceHandle();
+
+    PCTSTR szName    = MAKEINTRESOURCE((nId>>4)+1); // lifted
+    DWORD dwSize    = 0;
+
+    // No sense continuing if we can't find the resource
+
+    HRSRC hrsrc      = ::FindResource(hModule, szName, RT_STRING);
+
+    if ( NULL == hrsrc )
+    {
+      TRACE(_T("Cannot find resource %d: 0x%X"), nId, ::GetLastError());
+    }
+    else if ( 0 == (dwSize = ::SizeofResource(hModule, hrsrc) / sizeof(CT)))
+    {
+      TRACE(_T("Cant get size of resource %d 0x%X\n"),nId,GetLastError());
+    }
+    else
+    {
+      bLoaded      = 0 != ssload(hModule, nId, GetBuf(dwSize), dwSize);
+      ReleaseBuffer();
+    }
+
+  #endif  // #ifdef _MFC_VER
+
+    if ( !bLoaded )
+      TRACE(_T("String not loaded 0x%X\n"), ::GetLastError());
+
+    return bLoaded;
+  }
+
+#endif  // #ifdef SS_ANSI
+
+  // -------------------------------------------------------------------------
+  // FUNCTION:  CStdStr::Format
+  //    void _cdecl Formst(CStdStringA& PCSTR szFormat, ...)
+  //    void _cdecl Format(PCSTR szFormat);
+  //
+  // DESCRIPTION:
+  //    This function does sprintf/wsprintf style formatting on CStdStringA
+  //    objects.  It looks a lot like MFC's CString::Format.  Some people
+  //    might even call this identical.  Fortunately, these people are now
+  //    dead... heh heh.
+  //
+  // PARAMETERS:
+  //    nId - ID of string resource holding the format string
+  //    szFormat - a PCSTR holding the format specifiers
+  //    argList - a va_list holding the arguments for the format specifiers.
+  //
+  // RETURN VALUE:  None.
+  // -------------------------------------------------------------------------
+  // formatting (using wsprintf style formatting)
+
+    // If they want a Format() function that safely handles string objects
+    // without casting
+
+#ifdef SS_SAFE_FORMAT
+
+    // Question:  Joe, you wacky coder you, why do you have so many overloads
+    //      of the Format() function
+    // Answer:  One reason only - CString compatability.  In short, by making
+    //      the Format() function a template this way, I can do strong typing
+    //      and allow people to pass CStdString arguments as fillers for
+    //      "%s" format specifiers without crashing their program!  The downside
+    //      is that I need to overload on the number of arguments.   If you are
+    //      passing more arguments than I have listed below in any of my
+    //      overloads, just add another one.
+    //
+    //      Yes, yes, this is really ugly.  In essence what I am doing here is
+    //      protecting people from a bad (and incorrect) programming practice
+    //      that they should not be doing anyway.  I am protecting them from
+    //      themselves.  Why am I doing this?  Well, if you had any idea the
+    //      number of times I've been emailed by people about this
+    //      "incompatability" in my code, you wouldn't ask.
+
+  void Fmt(const CT* szFmt, ...)
+  {
+    va_list argList;
+    va_start(argList, szFmt);
+    FormatV(szFmt, argList);
+    va_end(argList);
+  }
+
+#ifndef SS_ANSI
+
+    void Format(UINT nId)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+            this->swap(strFmt);
+    }
+    template<class A1>
+    void Format(UINT nId, const A1& v)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+            Fmt(strFmt, FmtArg<A1>(v)());
+    }
+    template<class A1, class A2>
+    void Format(UINT nId, const A1& v1, const A2& v2)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+           Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)());
+    }
+    template<class A1, class A2, class A3>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(),FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(),FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+           Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)(), FmtArg<A10>(v10)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)(),FmtArg<A10>(v10)(),FmtArg<A11>(v11)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+                FmtArg<A12>(v12)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+                FmtArg<A12>(v12)(), FmtArg<A13>(v13)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13, class A14>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13, const A14& v14)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+                FmtArg<A12>(v12)(), FmtArg<A13>(v13)(),FmtArg<A14>(v14)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13, class A14, class A15>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13, const A14& v14, const A15& v15)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+                FmtArg<A12>(v12)(),FmtArg<A13>(v13)(),FmtArg<A14>(v14)(),
+                FmtArg<A15>(v15)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13, class A14, class A15, class A16>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13, const A14& v14, const A15& v15,
+                const A16& v16)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+                FmtArg<A12>(v12)(),FmtArg<A13>(v13)(),FmtArg<A14>(v14)(),
+                FmtArg<A15>(v15)(), FmtArg<A16>(v16)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13, class A14, class A15, class A16, class A17>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13, const A14& v14, const A15& v15,
+                const A16& v16, const A17& v17)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+                FmtArg<A12>(v12)(),FmtArg<A13>(v13)(),FmtArg<A14>(v14)(),
+                FmtArg<A15>(v15)(),FmtArg<A16>(v16)(),FmtArg<A17>(v17)());
+        }
+    }
+
+#endif // #ifndef SS_ANSI
+
+    // ...now the other overload of Format: the one that takes a string literal
+
+    void Format(const CT* szFmt)
+    {
+        *this = szFmt;
+    }
+    template<class A1>
+    void Format(const CT* szFmt, const A1& v)
+    {
+        Fmt(szFmt, FmtArg<A1>(v)());
+    }
+    template<class A1, class A2>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)());
+    }
+    template<class A1, class A2, class A3>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)());
+    }
+    template<class A1, class A2, class A3, class A4>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)(), FmtArg<A10>(v10)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)(),FmtArg<A10>(v10)(),FmtArg<A11>(v11)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+            FmtArg<A12>(v12)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+            FmtArg<A12>(v12)(), FmtArg<A13>(v13)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13, class A14>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13, const A14& v14)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+            FmtArg<A12>(v12)(), FmtArg<A13>(v13)(),FmtArg<A14>(v14)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13, class A14, class A15>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13, const A14& v14, const A15& v15)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+            FmtArg<A12>(v12)(),FmtArg<A13>(v13)(),FmtArg<A14>(v14)(),
+            FmtArg<A15>(v15)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13, class A14, class A15, class A16>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13, const A14& v14, const A15& v15,
+                const A16& v16)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+            FmtArg<A12>(v12)(),FmtArg<A13>(v13)(),FmtArg<A14>(v14)(),
+            FmtArg<A15>(v15)(), FmtArg<A16>(v16)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13, class A14, class A15, class A16, class A17>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13, const A14& v14, const A15& v15,
+                const A16& v16, const A17& v17)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+            FmtArg<A12>(v12)(),FmtArg<A13>(v13)(),FmtArg<A14>(v14)(),
+            FmtArg<A15>(v15)(),FmtArg<A16>(v16)(),FmtArg<A17>(v17)());
+    }
+
+#else  // #ifdef SS_SAFE_FORMAT
+
+
+#ifndef SS_ANSI
+
+  void Format(UINT nId, ...)
+  {
+    va_list argList;
+    va_start(argList, nId);
+
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+      FormatV(strFmt, argList);
+
+    va_end(argList);
+  }
+
+#endif  // #ifdef SS_ANSI
+
+  void Format(const CT* szFmt, ...)
+  {
+    va_list argList;
+    va_start(argList, szFmt);
+    FormatV(szFmt, argList);
+    va_end(argList);
+  }
+
+#endif // #ifdef SS_SAFE_FORMAT
+
+  void AppendFormat(const CT* szFmt, ...)
+  {
+    va_list argList;
+    va_start(argList, szFmt);
+    AppendFormatV(szFmt, argList);
+    va_end(argList);
+  }
+
+  #define MAX_FMT_TRIES    5   // #of times we try
+  #define FMT_BLOCK_SIZE    2048 // # of bytes to increment per try
+  #define BUFSIZE_1ST  256
+  #define BUFSIZE_2ND 512
+  #define STD_BUF_SIZE    1024
+
+  // an efficient way to add formatted characters to the string.  You may only
+  // add up to STD_BUF_SIZE characters at a time, though
+  void AppendFormatV(const CT* szFmt, va_list argList)
+  {
+    CT szBuf[STD_BUF_SIZE];
+    int nLen = ssnprintf(szBuf, STD_BUF_SIZE-1, szFmt, argList);
+
+    if ( 0 < nLen )
+      this->append(szBuf, nLen);
+  }
+
+  // -------------------------------------------------------------------------
+  // FUNCTION:  FormatV
+  //    void FormatV(PCSTR szFormat, va_list, argList);
+  //
+  // DESCRIPTION:
+  //    This function formats the string with sprintf style format-specs.
+  //    It makes a general guess at required buffer size and then tries
+  //    successively larger buffers until it finds one big enough or a
+  //    threshold (MAX_FMT_TRIES) is exceeded.
+  //
+  // PARAMETERS:
+  //    szFormat - a PCSTR holding the format of the output
+  //    argList - a Microsoft specific va_list for variable argument lists
+  //
+  // RETURN VALUE:
+  // -------------------------------------------------------------------------
+
+  // NOTE: Changed by JM to actually function under non-win32,
+  //       and to remove the upper limit on size.
+  void FormatV(const CT* szFormat, va_list argList)
+  {
+    // try and grab a sufficient buffersize
+    int nChars = FMT_BLOCK_SIZE;
+    va_list argCopy;
+
+    CT *p = reinterpret_cast<CT*>(malloc(sizeof(CT)*nChars));
+    if (!p) return;
+
+    while (1)
+    {
+      va_copy(argCopy, argList);
+
+      int nActual = ssvsprintf(p, nChars, szFormat, argCopy);
+      /* If that worked, return the string. */
+      if (nActual > -1 && nActual < nChars)
+      { /* make sure it's NULL terminated */
+        p[nActual] = '\0';
+        this->assign(p, nActual);
+        free(p);
+        va_end(argCopy);
+        return;
+      }
+      /* Else try again with more space. */
+      if (nActual > -1)        /* glibc 2.1 */
+        nChars = nActual + 1;  /* precisely what is needed */
+      else                     /* glibc 2.0 */
+        nChars *= 2;           /* twice the old size */
+
+      CT *np = reinterpret_cast<CT*>(realloc(p, sizeof(CT)*nChars));
+      if (np == NULL)
+      {
+        free(p);
+        va_end(argCopy);
+        return;   // failed :(
+      }
+      p = np;
+      va_end(argCopy);
+    }
+  }
+
+  // -------------------------------------------------------------------------
+  // CString Facade Functions:
+  //
+  // The following methods are intended to allow you to use this class as a
+  // near drop-in replacement for CString.
+  // -------------------------------------------------------------------------
+  #ifdef SS_WIN32
+    BSTR AllocSysString() const
+    {
+      ostring os;
+      ssasn(os, *this);
+      return ::SysAllocString(os.c_str());
+    }
+  #endif
+
+#ifndef SS_NO_LOCALE
+  int Collate(PCMYSTR szThat) const
+  {
+    return sscoll(this->c_str(), this->length(), szThat, sslen(szThat));
+  }
+
+  int CollateNoCase(PCMYSTR szThat) const
+  {
+    return ssicoll(this->c_str(), this->length(), szThat, sslen(szThat));
+  }
+#endif
+  int Compare(PCMYSTR szThat) const
+  {
+    return this->compare(szThat);
+  }
+
+  int CompareNoCase(PCMYSTR szThat)  const
+  {
+    return ssicmp(this->c_str(), szThat);
+  }
+
+  int Delete(int nIdx, int nCount=1)
+  {
+        if ( nIdx < 0 )
+      nIdx = 0;
+
+    if ( nIdx < this->GetLength() )
+      this->erase(static_cast<MYSIZE>(nIdx), static_cast<MYSIZE>(nCount));
+
+    return GetLength();
+  }
+
+  void Empty()
+  {
+    this->erase();
+  }
+
+  int Find(CT ch) const
+  {
+    MYSIZE nIdx  = this->find_first_of(ch);
+    return static_cast<int>(MYBASE::npos == nIdx  ? -1 : nIdx);
+  }
+
+  int Find(PCMYSTR szSub) const
+  {
+    MYSIZE nIdx  = this->find(szSub);
+    return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);
+  }
+
+  int Find(CT ch, int nStart) const
+  {
+    // CString::Find docs say add 1 to nStart when it's not zero
+    // CString::Find code doesn't do that however.  We'll stick
+    // with what the code does
+
+    MYSIZE nIdx  = this->find_first_of(ch, static_cast<MYSIZE>(nStart));
+    return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);
+  }
+
+  int Find(PCMYSTR szSub, int nStart) const
+  {
+    // CString::Find docs say add 1 to nStart when it's not zero
+    // CString::Find code doesn't do that however.  We'll stick
+    // with what the code does
+
+    MYSIZE nIdx  = this->find(szSub, static_cast<MYSIZE>(nStart));
+    return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);
+  }
+
+  int FindOneOf(PCMYSTR szCharSet) const
+  {
+    MYSIZE nIdx = this->find_first_of(szCharSet);
+    return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);
+  }
+
+#ifndef SS_ANSI
+  void FormatMessage(PCMYSTR szFormat, ...) throw(std::exception)
+  {
+    va_list argList;
+    va_start(argList, szFormat);
+    PMYSTR szTemp;
+    if ( ssfmtmsg(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER,
+             szFormat, 0, 0,
+             reinterpret_cast<PMYSTR>(&szTemp), 0, &argList) == 0 ||
+       szTemp == 0 )
+    {
+      throw std::runtime_error("out of memory");
+    }
+    *this = szTemp;
+    LocalFree(szTemp);
+    va_end(argList);
+  }
+
+  void FormatMessage(UINT nFormatId, ...) throw(std::exception)
+  {
+    MYTYPE sFormat;
+    VERIFY(sFormat.LoadString(nFormatId));
+    va_list argList;
+    va_start(argList, nFormatId);
+    PMYSTR szTemp;
+    if ( ssfmtmsg(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER,
+             sFormat, 0, 0,
+             reinterpret_cast<PMYSTR>(&szTemp), 0, &argList) == 0 ||
+      szTemp == 0)
+    {
+      throw std::runtime_error("out of memory");
+    }
+    *this = szTemp;
+    LocalFree(szTemp);
+    va_end(argList);
+  }
+#endif
+
+  // GetAllocLength -- an MSVC7 function but it costs us nothing to add it.
+
+  int GetAllocLength()
+  {
+    return static_cast<int>(this->capacity());
+  }
+
+  // -------------------------------------------------------------------------
+  // GetXXXX -- Direct access to character buffer
+  // -------------------------------------------------------------------------
+  CT GetAt(int nIdx) const
+  {
+    return this->at(static_cast<MYSIZE>(nIdx));
+  }
+
+  CT* GetBuffer(int nMinLen=-1)
+  {
+    return GetBuf(nMinLen);
+  }
+
+  CT* GetBufferSetLength(int nLen)
+  {
+    return BufferSet(nLen);
+  }
+
+  // GetLength() -- MFC docs say this is the # of BYTES but
+  // in truth it is the number of CHARACTERs (chars or wchar_ts)
+  int GetLength() const
+  {
+    return static_cast<int>(this->length());
+  }
+
+  int Insert(int nIdx, CT ch)
+  {
+    if ( static_cast<MYSIZE>(nIdx) > this->size()-1 )
+      this->append(1, ch);
+    else
+      this->insert(static_cast<MYSIZE>(nIdx), 1, ch);
+
+    return GetLength();
+  }
+  int Insert(int nIdx, PCMYSTR sz)
+  {
+    if ( static_cast<MYSIZE>(nIdx) >= this->size() )
+      this->append(sz, static_cast<MYSIZE>(sslen(sz)));
+    else
+      this->insert(static_cast<MYSIZE>(nIdx), sz);
+
+    return GetLength();
+  }
+
+  bool IsEmpty() const
+  {
+    return this->empty();
+  }
+
+  MYTYPE Left(int nCount) const
+  {
+        // Range check the count.
+
+    nCount = SSMAX(0, SSMIN(nCount, static_cast<int>(this->size())));
+    return this->substr(0, static_cast<MYSIZE>(nCount));
+  }
+
+#ifndef SS_ANSI
+  bool LoadString(UINT nId)
+  {
+    return this->Load(nId);
+  }
+#endif
+
+  void MakeLower()
+  {
+    ToLower();
+  }
+
+  void MakeReverse()
+  {
+    std::reverse(this->begin(), this->end());
+  }
+
+  void MakeUpper()
+  {
+    ToUpper();
+  }
+
+  MYTYPE Mid(int nFirst) const
+  {
+    return Mid(nFirst, this->GetLength()-nFirst);
+  }
+
+  MYTYPE Mid(int nFirst, int nCount) const
+  {
+    // CString does range checking here.  Since we're trying to emulate it,
+    // we must check too.
+
+    if ( nFirst < 0 )
+      nFirst = 0;
+    if ( nCount < 0 )
+      nCount = 0;
+
+    int nSize = static_cast<int>(this->size());
+
+    if ( nFirst + nCount > nSize )
+      nCount = nSize - nFirst;
+
+    if ( nFirst > nSize )
+      return MYTYPE();
+
+    ASSERT(nFirst >= 0);
+    ASSERT(nFirst + nCount <= nSize);
+
+    return this->substr(static_cast<MYSIZE>(nFirst),
+              static_cast<MYSIZE>(nCount));
+  }
+
+  void ReleaseBuffer(int nNewLen=-1)
+  {
+    RelBuf(nNewLen);
+  }
+
+  int Remove(CT ch)
+  {
+    MYSIZE nIdx    = 0;
+    int nRemoved  = 0;
+    while ( (nIdx=this->find_first_of(ch)) != MYBASE::npos )
+    {
+      this->erase(nIdx, 1);
+      nRemoved++;
+    }
+    return nRemoved;
+  }
+
+  int Replace(CT chOld, CT chNew)
+  {
+    int nReplaced  = 0;
+
+    for ( MYITER iter=this->begin(); iter != this->end(); iter++ )
+    {
+      if ( *iter == chOld )
+      {
+        *iter = chNew;
+        nReplaced++;
+      }
+    }
+
+    return nReplaced;
+  }
+
+  int Replace(PCMYSTR szOld, PCMYSTR szNew)
+  {
+    int nReplaced    = 0;
+    MYSIZE nIdx      = 0;
+    MYSIZE nOldLen    = sslen(szOld);
+
+    if ( 0 != nOldLen )
+    {
+      // If the replacement string is longer than the one it replaces, this
+      // string is going to have to grow in size,  Figure out how much
+      // and grow it all the way now, rather than incrementally
+
+      MYSIZE nNewLen    = sslen(szNew);
+      if ( nNewLen > nOldLen )
+      {
+        int nFound      = 0;
+        while ( nIdx < this->length() &&
+          (nIdx=this->find(szOld, nIdx)) != MYBASE::npos )
+        {
+          nFound++;
+          nIdx += nOldLen;
+        }
+        this->reserve(this->size() + nFound * (nNewLen - nOldLen));
+      }
+
+
+      static const CT ch  = CT(0);
+      PCMYSTR szRealNew  = szNew == 0 ? &ch : szNew;
+      nIdx        = 0;
+
+      while ( nIdx < this->length() &&
+        (nIdx=this->find(szOld, nIdx)) != MYBASE::npos )
+      {
+        this->replace(this->begin()+nIdx, this->begin()+nIdx+nOldLen,
+          szRealNew);
+
+        nReplaced++;
+        nIdx += nNewLen;
+      }
+    }
+
+    return nReplaced;
+  }
+
+  int ReverseFind(CT ch) const
+  {
+    MYSIZE nIdx  = this->find_last_of(ch);
+    return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);
+  }
+
+  // ReverseFind overload that's not in CString but might be useful
+  int ReverseFind(PCMYSTR szFind, MYSIZE pos=MYBASE::npos) const
+  {
+    //yuvalt - this does not compile with g++ since MYTTYPE() is different type
+    //MYSIZE nIdx  = this->rfind(0 == szFind ? MYTYPE() : szFind, pos);
+    MYSIZE nIdx  = this->rfind(0 == szFind ? "" : szFind, pos);
+    return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);
+  }
+
+  MYTYPE Right(int nCount) const
+  {
+        // Range check the count.
+
+    nCount = SSMAX(0, SSMIN(nCount, static_cast<int>(this->size())));
+    return this->substr(this->size()-static_cast<MYSIZE>(nCount));
+  }
+
+  void SetAt(int nIndex, CT ch)
+  {
+    ASSERT(this->size() > static_cast<MYSIZE>(nIndex));
+    this->at(static_cast<MYSIZE>(nIndex))    = ch;
+  }
+
+#ifndef SS_ANSI
+  BSTR SetSysString(BSTR* pbstr) const
+  {
+    ostring os;
+    ssasn(os, *this);
+    if ( !::SysReAllocStringLen(pbstr, os.c_str(), os.length()) )
+      throw std::runtime_error("out of memory");
+
+    ASSERT(*pbstr != 0);
+    return *pbstr;
+  }
+#endif
+
+  MYTYPE SpanExcluding(PCMYSTR szCharSet) const
+  {
+        MYSIZE pos = this->find_first_of(szCharSet);
+        return pos == MYBASE::npos ? *this : Left(pos);
+  }
+
+  MYTYPE SpanIncluding(PCMYSTR szCharSet) const
+  {
+        MYSIZE pos = this->find_first_not_of(szCharSet);
+        return pos == MYBASE::npos ? *this : Left(pos);
+  }
+
+#if defined SS_WIN32 && !defined(UNICODE) && !defined(SS_ANSI)
+
+  // CString's OemToAnsi and AnsiToOem functions are available only in
+  // Unicode builds.  However since we're a template we also need a
+  // runtime check of CT and a reinterpret_cast to account for the fact
+  // that CStdStringW gets instantiated even in non-Unicode builds.
+
+  void AnsiToOem()
+  {
+    if ( sizeof(CT) == sizeof(char) && !empty() )
+    {
+      ::CharToOem(reinterpret_cast<PCSTR>(this->c_str()),
+            reinterpret_cast<PSTR>(GetBuf()));
+    }
+    else
+    {
+      ASSERT(false);
+    }
+  }
+
+  void OemToAnsi()
+  {
+    if ( sizeof(CT) == sizeof(char) && !empty() )
+    {
+      ::OemToChar(reinterpret_cast<PCSTR>(this->c_str()),
+            reinterpret_cast<PSTR>(GetBuf()));
+    }
+    else
+    {
+      ASSERT(false);
+    }
+  }
+
+#endif
+
+
+  // -------------------------------------------------------------------------
+  // Trim and its variants
+  // -------------------------------------------------------------------------
+  MYTYPE& Trim()
+  {
+    return TrimLeft().TrimRight();
+  }
+
+  MYTYPE& TrimLeft()
+  {
+    this->erase(this->begin(),
+      std::find_if(this->begin(), this->end(), NotSpace<CT>()));
+
+    return *this;
+  }
+
+  MYTYPE&  TrimLeft(CT tTrim)
+  {
+    this->erase(0, this->find_first_not_of(tTrim));
+    return *this;
+  }
+
+  MYTYPE&  TrimLeft(PCMYSTR szTrimChars)
+  {
+    this->erase(0, this->find_first_not_of(szTrimChars));
+    return *this;
+  }
+
+  MYTYPE& TrimRight()
+  {
+    // NOTE:  When comparing reverse_iterators here (MYRITER), I avoid using
+    // operator!=.  This is because namespace rel_ops also has a template
+    // operator!= which conflicts with the global operator!= already defined
+    // for reverse_iterator in the header <utility>.
+    // Thanks to John James for alerting me to this.
+
+    MYRITER it = std::find_if(this->rbegin(), this->rend(), NotSpace<CT>());
+    if ( !(this->rend() == it) )
+      this->erase(this->rend() - it);
+
+    this->erase(!(it == this->rend()) ? this->find_last_of(*it) + 1 : 0);
+    return *this;
+  }
+
+  MYTYPE&  TrimRight(CT tTrim)
+  {
+    MYSIZE nIdx  = this->find_last_not_of(tTrim);
+    this->erase(MYBASE::npos == nIdx ? 0 : ++nIdx);
+    return *this;
+  }
+
+  MYTYPE&  TrimRight(PCMYSTR szTrimChars)
+  {
+    MYSIZE nIdx  = this->find_last_not_of(szTrimChars);
+    this->erase(MYBASE::npos == nIdx ? 0 : ++nIdx);
+    return *this;
+  }
+
+  void      FreeExtra()
+  {
+    MYTYPE mt;
+    this->swap(mt);
+    if ( !mt.empty() )
+      this->assign(mt.c_str(), mt.size());
+  }
+
+  // I have intentionally not implemented the following CString
+  // functions.   You cannot make them work without taking advantage
+  // of implementation specific behavior.  However if you absolutely
+  // MUST have them, uncomment out these lines for "sort-of-like"
+  // their behavior.  You're on your own.
+
+//  CT*        LockBuffer()  { return GetBuf(); }// won't really lock
+//  void      UnlockBuffer(); { }  // why have UnlockBuffer w/o LockBuffer?
+
+  // Array-indexing operators.  Required because we defined an implicit cast
+  // to operator const CT* (Thanks to Julian Selman for pointing this out)
+
+  CT& operator[](int nIdx)
+  {
+    return static_cast<MYBASE*>(this)->operator[](static_cast<MYSIZE>(nIdx));
+  }
+
+  const CT& operator[](int nIdx) const
+  {
+    return static_cast<const MYBASE*>(this)->operator[](static_cast<MYSIZE>(nIdx));
+  }
+
+  CT& operator[](unsigned int nIdx)
+  {
+    return static_cast<MYBASE*>(this)->operator[](static_cast<MYSIZE>(nIdx));
+  }
+
+  const CT& operator[](unsigned int nIdx) const
+  {
+    return static_cast<const MYBASE*>(this)->operator[](static_cast<MYSIZE>(nIdx));
+  }
+
+  CT& operator[](unsigned long nIdx)
+  {
+    return static_cast<MYBASE*>(this)->operator[](static_cast<MYSIZE>(nIdx));
+  }
+
+  const CT& operator[](unsigned long nIdx) const
+  {
+    return static_cast<const MYBASE*>(this)->operator[](static_cast<MYSIZE>(nIdx));
+  }
+
+#ifndef SS_NO_IMPLICIT_CAST
+  operator const CT*() const
+  {
+    return this->c_str();
+  }
+#endif
+
+  // IStream related functions.  Useful in IPersistStream implementations
+
+#ifdef SS_INC_COMDEF
+
+  // struct SSSHDR - useful for non Std C++ persistence schemes.
+  typedef struct SSSHDR
+  {
+    BYTE  byCtrl;
+    ULONG  nChars;
+  } SSSHDR;  // as in "Standard String Stream Header"
+
+  #define SSSO_UNICODE  0x01  // the string is a wide string
+  #define SSSO_COMPRESS  0x02  // the string is compressed
+
+  // -------------------------------------------------------------------------
+  // FUNCTION: StreamSize
+  // REMARKS:
+  //    Returns how many bytes it will take to StreamSave() this CStdString
+  //    object to an IStream.
+  // -------------------------------------------------------------------------
+  ULONG StreamSize() const
+  {
+    // Control header plus string
+    ASSERT(this->size()*sizeof(CT) < 0xffffffffUL - sizeof(SSSHDR));
+    return (this->size() * sizeof(CT)) + sizeof(SSSHDR);
+  }
+
+  // -------------------------------------------------------------------------
+  // FUNCTION: StreamSave
+  // REMARKS:
+  //    Saves this CStdString object to a COM IStream.
+  // -------------------------------------------------------------------------
+  HRESULT StreamSave(IStream* pStream) const
+  {
+    ASSERT(this->size()*sizeof(CT) < 0xffffffffUL - sizeof(SSSHDR));
+    HRESULT hr    = E_FAIL;
+    ASSERT(pStream != 0);
+    SSSHDR hdr;
+    hdr.byCtrl    = sizeof(CT) == 2 ? SSSO_UNICODE : 0;
+    hdr.nChars    = this->size();
+
+
+    if ( FAILED(hr=pStream->Write(&hdr, sizeof(SSSHDR), 0)) )
+    {
+      TRACE(_T("StreamSave: Cannot write control header, ERR=0x%X\n"),hr);
+    }
+    else if ( empty() )
+    {
+      ;    // nothing to write
+    }
+    else if ( FAILED(hr=pStream->Write(this->c_str(),
+      this->size()*sizeof(CT), 0)) )
+    {
+      TRACE(_T("StreamSave: Cannot write string to stream 0x%X\n"), hr);
+    }
+
+    return hr;
+  }
+
+
+  // -------------------------------------------------------------------------
+  // FUNCTION: StreamLoad
+  // REMARKS:
+  //    This method loads the object from an IStream.
+  // -------------------------------------------------------------------------
+  HRESULT StreamLoad(IStream* pStream)
+  {
+    ASSERT(pStream != 0);
+    SSSHDR hdr;
+    HRESULT hr      = E_FAIL;
+
+    if ( FAILED(hr=pStream->Read(&hdr, sizeof(SSSHDR), 0)) )
+    {
+      TRACE(_T("StreamLoad: Cant read control header, ERR=0x%X\n"), hr);
+    }
+    else if ( hdr.nChars > 0 )
+    {
+      ULONG nRead    = 0;
+      PMYSTR pMyBuf  = BufferSet(hdr.nChars);
+
+      // If our character size matches the character size of the string
+      // we're trying to read, then we can read it directly into our
+      // buffer. Otherwise, we have to read into an intermediate buffer
+      // and convert.
+
+      if ( (hdr.byCtrl & SSSO_UNICODE) != 0 )
+      {
+        ULONG nBytes  = hdr.nChars * sizeof(wchar_t);
+        if ( sizeof(CT) == sizeof(wchar_t) )
+        {
+          if ( FAILED(hr=pStream->Read(pMyBuf, nBytes, &nRead)) )
+            TRACE(_T("StreamLoad: Cannot read string: 0x%X\n"), hr);
+        }
+        else
+        {
+          PWSTR pBufW = reinterpret_cast<PWSTR>(_alloca((nBytes)+1));
+          if ( FAILED(hr=pStream->Read(pBufW, nBytes, &nRead)) )
+            TRACE(_T("StreamLoad: Cannot read string: 0x%X\n"), hr);
+          else
+            sscpy(pMyBuf, pBufW, hdr.nChars);
+        }
+      }
+      else
+      {
+        ULONG nBytes  = hdr.nChars * sizeof(char);
+        if ( sizeof(CT) == sizeof(char) )
+        {
+          if ( FAILED(hr=pStream->Read(pMyBuf, nBytes, &nRead)) )
+            TRACE(_T("StreamLoad: Cannot read string: 0x%X\n"), hr);
+        }
+        else
+        {
+          PSTR pBufA = reinterpret_cast<PSTR>(_alloca(nBytes));
+          if ( FAILED(hr=pStream->Read(pBufA, hdr.nChars, &nRead)) )
+            TRACE(_T("StreamLoad: Cannot read string: 0x%X\n"), hr);
+          else
+            sscpy(pMyBuf, pBufA, hdr.nChars);
+        }
+      }
+    }
+    else
+    {
+      this->erase();
+    }
+    return hr;
+  }
+#endif // #ifdef SS_INC_COMDEF
+
+#ifndef SS_ANSI
+
+  // SetResourceHandle/GetResourceHandle.  In MFC builds, these map directly
+  // to AfxSetResourceHandle and AfxGetResourceHandle.  In non-MFC builds they
+  // point to a single static HINST so that those who call the member
+  // functions that take resource IDs can provide an alternate HINST of a DLL
+  // to search.  This is not exactly the list of HMODULES that MFC provides
+  // but it's better than nothing.
+
+  #ifdef _MFC_VER
+    static void SetResourceHandle(HMODULE hNew)
+    {
+      AfxSetResourceHandle(hNew);
+    }
+    static HMODULE GetResourceHandle()
+    {
+      return AfxGetResourceHandle();
+    }
+  #else
+    static void SetResourceHandle(HMODULE hNew)
+    {
+      SSResourceHandle() = hNew;
+    }
+    static HMODULE GetResourceHandle()
+    {
+      return SSResourceHandle();
+    }
+  #endif
+
+#endif
+};
+
+// -----------------------------------------------------------------------------
+// MSVC USERS: HOW TO EXPORT CSTDSTRING FROM A DLL
+//
+// If you are using MS Visual C++ and you want to export CStdStringA and
+// CStdStringW from a DLL, then all you need to
+//
+//    1.  make sure that all components link to the same DLL version
+//      of the CRT (not the static one).
+//    2.  Uncomment the 3 lines of code below
+//    3.  #define 2 macros per the instructions in MS KnowledgeBase
+//      article Q168958.  The macros are:
+//
+//    MACRO    DEFINTION WHEN EXPORTING    DEFINITION WHEN IMPORTING
+//    -----    ------------------------    -------------------------
+//    SSDLLEXP  (nothing, just #define it)    extern
+//    SSDLLSPEC  __declspec(dllexport)      __declspec(dllimport)
+//
+//    Note that these macros must be available to ALL clients who want to
+//    link to the DLL and use the class.  If they
+//
+// A word of advice: Don't bother.
+//
+// Really, it is not necessary to export CStdString functions from a DLL.  I
+// never do.  In my projects, I do generally link to the DLL version of the
+// Standard C++ Library, but I do NOT attempt to export CStdString functions.
+// I simply include the header where it is needed and allow for the code
+// redundancy.
+//
+// That redundancy is a lot less than you think.  This class does most of its
+// work via the Standard C++ Library, particularly the base_class basic_string<>
+// member functions.  Most of the functions here are small enough to be inlined
+// anyway.  Besides, you'll find that in actual practice you use less than 1/2
+// of the code here, even in big projects and different modules will use as
+// little as 10% of it.  That means a lot less functions actually get linked
+// your binaries.  If you export this code from a DLL, it ALL gets linked in.
+//
+// I've compared the size of the binaries from exporting vs NOT exporting.  Take
+// my word for it -- exporting this code is not worth the hassle.
+//
+// -----------------------------------------------------------------------------
+//#pragma warning(disable:4231) // non-standard extension ("extern template")
+//  SSDLLEXP template class SSDLLSPEC CStdStr<char>;
+//  SSDLLEXP template class SSDLLSPEC CStdStr<wchar_t>;
+
+
+// =============================================================================
+//            END OF CStdStr INLINE FUNCTION DEFINITIONS
+// =============================================================================
+
+//  Now typedef our class names based upon this humongous template
+
+typedef CStdStr<char>    CStdStringA;  // a better std::string
+typedef CStdStr<wchar_t>  CStdStringW;  // a better std::wstring
+typedef CStdStr<uint16_t>  CStdString16;  // a 16bit char string
+typedef CStdStr<uint32_t>  CStdString32;  // a 32bit char string
+typedef CStdStr<OLECHAR>  CStdStringO;  // almost always CStdStringW
+
+// -----------------------------------------------------------------------------
+// CStdStr addition functions defined as inline
+// -----------------------------------------------------------------------------
+
+
+inline CStdStringA operator+(const CStdStringA& s1, const CStdStringA& s2)
+{
+  CStdStringA sRet(SSREF(s1));
+  sRet.append(s2);
+  return sRet;
+}
+inline CStdStringA operator+(const CStdStringA& s1, CStdStringA::value_type t)
+{
+  CStdStringA sRet(SSREF(s1));
+  sRet.append(1, t);
+  return sRet;
+}
+inline CStdStringA operator+(const CStdStringA& s1, PCSTR pA)
+{
+  CStdStringA sRet(SSREF(s1));
+  sRet.append(pA);
+  return sRet;
+}
+inline CStdStringA operator+(PCSTR pA, const CStdStringA& sA)
+{
+  CStdStringA sRet;
+  CStdStringA::size_type nObjSize = sA.size();
+  CStdStringA::size_type nLitSize =
+    static_cast<CStdStringA::size_type>(sslen(pA));
+
+  sRet.reserve(nLitSize + nObjSize);
+  sRet.assign(pA);
+  sRet.append(sA);
+  return sRet;
+}
+
+
+inline CStdStringA operator+(const CStdStringA& s1, const CStdStringW& s2)
+{
+  return s1 + CStdStringA(s2);
+}
+inline CStdStringW operator+(const CStdStringW& s1, const CStdStringW& s2)
+{
+  CStdStringW sRet(SSREF(s1));
+  sRet.append(s2);
+  return sRet;
+}
+inline CStdStringA operator+(const CStdStringA& s1, PCWSTR pW)
+{
+  return s1 + CStdStringA(pW);
+}
+
+#ifdef UNICODE
+  inline CStdStringW operator+(PCWSTR pW, const CStdStringA& sA)
+  {
+    return CStdStringW(pW) + CStdStringW(SSREF(sA));
+  }
+  inline CStdStringW operator+(PCSTR pA, const CStdStringW& sW)
+  {
+    return CStdStringW(pA) + sW;
+  }
+#else
+  inline CStdStringA operator+(PCWSTR pW, const CStdStringA& sA)
+  {
+    return CStdStringA(pW) + sA;
+  }
+  inline CStdStringA operator+(PCSTR pA, const CStdStringW& sW)
+  {
+    return pA + CStdStringA(sW);
+  }
+#endif
+
+// ...Now the wide string versions.
+inline CStdStringW operator+(const CStdStringW& s1, CStdStringW::value_type t)
+{
+  CStdStringW sRet(SSREF(s1));
+  sRet.append(1, t);
+  return sRet;
+}
+inline CStdStringW operator+(const CStdStringW& s1, PCWSTR pW)
+{
+  CStdStringW sRet(SSREF(s1));
+  sRet.append(pW);
+  return sRet;
+}
+inline CStdStringW operator+(PCWSTR pW, const CStdStringW& sW)
+{
+  CStdStringW sRet;
+  CStdStringW::size_type nObjSize = sW.size();
+  CStdStringA::size_type nLitSize =
+    static_cast<CStdStringW::size_type>(sslen(pW));
+
+  sRet.reserve(nLitSize + nObjSize);
+  sRet.assign(pW);
+  sRet.append(sW);
+  return sRet;
+}
+
+inline CStdStringW operator+(const CStdStringW& s1, const CStdStringA& s2)
+{
+  return s1 + CStdStringW(s2);
+}
+inline CStdStringW operator+(const CStdStringW& s1, PCSTR pA)
+{
+  return s1 + CStdStringW(pA);
+}
+
+
+// New-style format function is a template
+
+#ifdef SS_SAFE_FORMAT
+
+template<>
+struct FmtArg<CStdStringA>
+{
+    explicit FmtArg(const CStdStringA& arg) : a_(arg) {}
+    PCSTR operator()() const { return a_.c_str(); }
+    const CStdStringA& a_;
+private:
+    FmtArg<CStdStringA>& operator=(const FmtArg<CStdStringA>&) { return *this; }
+};
+template<>
+struct FmtArg<CStdStringW>
+{
+    explicit FmtArg(const CStdStringW& arg) : a_(arg) {}
+    PCWSTR operator()() const { return a_.c_str(); }
+    const CStdStringW& a_;
+private:
+    FmtArg<CStdStringW>& operator=(const FmtArg<CStdStringW>&) { return *this; }
+};
+
+template<>
+struct FmtArg<std::string>
+{
+    explicit FmtArg(const std::string& arg) : a_(arg) {}
+    PCSTR operator()() const { return a_.c_str(); }
+    const std::string& a_;
+private:
+    FmtArg<std::string>& operator=(const FmtArg<std::string>&) { return *this; }
+};
+template<>
+struct FmtArg<std::wstring>
+{
+    explicit FmtArg(const std::wstring& arg) : a_(arg) {}
+    PCWSTR operator()() const { return a_.c_str(); }
+    const std::wstring& a_;
+private:
+    FmtArg<std::wstring>& operator=(const FmtArg<std::wstring>&) {return *this;}
+};
+#endif // #ifdef SS_SAFEFORMAT
+
+#ifndef SS_ANSI
+  // SSResourceHandle: our MFC-like resource handle
+  inline HMODULE& SSResourceHandle()
+  {
+    static HMODULE hModuleSS  = GetModuleHandle(0);
+    return hModuleSS;
+  }
+#endif
+
+
+// In MFC builds, define some global serialization operators
+// Special operators that allow us to serialize CStdStrings to CArchives.
+// Note that we use an intermediate CString object in order to ensure that
+// we use the exact same format.
+
+#ifdef _MFC_VER
+  inline CArchive& AFXAPI operator<<(CArchive& ar, const CStdStringA& strA)
+  {
+    CString strTemp  = strA;
+    return ar << strTemp;
+  }
+  inline CArchive& AFXAPI operator<<(CArchive& ar, const CStdStringW& strW)
+  {
+    CString strTemp  = strW;
+    return ar << strTemp;
+  }
+
+  inline CArchive& AFXAPI operator>>(CArchive& ar, CStdStringA& strA)
+  {
+    CString strTemp;
+    ar >> strTemp;
+    strA = strTemp;
+    return ar;
+  }
+  inline CArchive& AFXAPI operator>>(CArchive& ar, CStdStringW& strW)
+  {
+    CString strTemp;
+    ar >> strTemp;
+    strW = strTemp;
+    return ar;
+  }
+#endif  // #ifdef _MFC_VER -- (i.e. is this MFC?)
+
+
+
+// -----------------------------------------------------------------------------
+// GLOBAL FUNCTION:  WUFormat
+//    CStdStringA WUFormat(UINT nId, ...);
+//    CStdStringA WUFormat(PCSTR szFormat, ...);
+//
+// REMARKS:
+//    This function allows the caller for format and return a CStdStringA
+//    object with a single line of code.
+// -----------------------------------------------------------------------------
+#ifdef SS_ANSI
+#else
+  inline CStdStringA WUFormatA(UINT nId, ...)
+  {
+    va_list argList;
+    va_start(argList, nId);
+
+    CStdStringA strFmt;
+    CStdStringA strOut;
+    if ( strFmt.Load(nId) )
+      strOut.FormatV(strFmt, argList);
+
+    va_end(argList);
+    return strOut;
+  }
+  inline CStdStringA WUFormatA(PCSTR szFormat, ...)
+  {
+    va_list argList;
+    va_start(argList, szFormat);
+    CStdStringA strOut;
+    strOut.FormatV(szFormat, argList);
+    va_end(argList);
+    return strOut;
+  }
+  inline CStdStringW WUFormatW(UINT nId, ...)
+  {
+    va_list argList;
+    va_start(argList, nId);
+
+    CStdStringW strFmt;
+    CStdStringW strOut;
+    if ( strFmt.Load(nId) )
+      strOut.FormatV(strFmt, argList);
+
+    va_end(argList);
+    return strOut;
+  }
+  inline CStdStringW WUFormatW(PCWSTR szwFormat, ...)
+  {
+    va_list argList;
+    va_start(argList, szwFormat);
+    CStdStringW strOut;
+    strOut.FormatV(szwFormat, argList);
+    va_end(argList);
+    return strOut;
+  }
+#endif // #ifdef SS_ANSI
+
+
+
+#if defined(SS_WIN32) && !defined (SS_ANSI)
+  // -------------------------------------------------------------------------
+  // FUNCTION: WUSysMessage
+  //   CStdStringA WUSysMessageA(DWORD dwError, DWORD dwLangId=SS_DEFLANGID);
+  //   CStdStringW WUSysMessageW(DWORD dwError, DWORD dwLangId=SS_DEFLANGID);
+  //
+  // DESCRIPTION:
+  //   This function simplifies the process of obtaining a string equivalent
+  //   of a system error code returned from GetLastError().  You simply
+  //   supply the value returned by GetLastError() to this function and the
+  //   corresponding system string is returned in the form of a CStdStringA.
+  //
+  // PARAMETERS:
+  //   dwError - a DWORD value representing the error code to be translated
+  //   dwLangId - the language id to use.  defaults to english.
+  //
+  // RETURN VALUE:
+  //   a CStdStringA equivalent of the error code.  Currently, this function
+  //   only returns either English of the system default language strings.
+  // -------------------------------------------------------------------------
+  #define SS_DEFLANGID MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT)
+  inline CStdStringA WUSysMessageA(DWORD dwError, DWORD dwLangId=SS_DEFLANGID)
+  {
+    CHAR szBuf[512];
+
+    if ( 0 != ::FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwError,
+                   dwLangId, szBuf, 511, NULL) )
+      return WUFormatA("%s (0x%X)", szBuf, dwError);
+    else
+       return WUFormatA("Unknown error (0x%X)", dwError);
+  }
+  inline CStdStringW WUSysMessageW(DWORD dwError, DWORD dwLangId=SS_DEFLANGID)
+  {
+    WCHAR szBuf[512];
+
+    if ( 0 != ::FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwError,
+                   dwLangId, szBuf, 511, NULL) )
+      return WUFormatW(L"%s (0x%X)", szBuf, dwError);
+    else
+       return WUFormatW(L"Unknown error (0x%X)", dwError);
+  }
+#endif
+
+// Define TCHAR based friendly names for some of these functions
+
+#ifdef UNICODE
+  //#define CStdString        CStdStringW
+  typedef CStdStringW        CStdString;
+  #define WUSysMessage      WUSysMessageW
+  #define WUFormat        WUFormatW
+#else
+  //#define CStdString        CStdStringA
+  typedef CStdStringA        CStdString;
+  #define WUSysMessage      WUSysMessageA
+  #define WUFormat        WUFormatA
+#endif
+
+// ...and some shorter names for the space-efficient
+
+#define WUSysMsg          WUSysMessage
+#define WUSysMsgA          WUSysMessageA
+#define WUSysMsgW          WUSysMessageW
+#define WUFmtA            WUFormatA
+#define  WUFmtW            WUFormatW
+#define WUFmt            WUFormat
+#define WULastErrMsg()        WUSysMessage(::GetLastError())
+#define WULastErrMsgA()        WUSysMessageA(::GetLastError())
+#define WULastErrMsgW()        WUSysMessageW(::GetLastError())
+
+
+// -----------------------------------------------------------------------------
+// FUNCTIONAL COMPARATORS:
+// REMARKS:
+//    These structs are derived from the std::binary_function template.  They
+//    give us functional classes (which may be used in Standard C++ Library
+//    collections and algorithms) that perform case-insensitive comparisons of
+//    CStdString objects.  This is useful for maps in which the key may be the
+//     proper string but in the wrong case.
+// -----------------------------------------------------------------------------
+#define StdStringLessNoCaseW    SSLNCW  // avoid VC compiler warning 4786
+#define StdStringEqualsNoCaseW    SSENCW
+#define StdStringLessNoCaseA    SSLNCA
+#define StdStringEqualsNoCaseA    SSENCA
+
+#ifdef UNICODE
+  #define StdStringLessNoCase    SSLNCW
+  #define StdStringEqualsNoCase  SSENCW
+#else
+  #define StdStringLessNoCase    SSLNCA
+  #define StdStringEqualsNoCase  SSENCA
+#endif
+
+struct StdStringLessNoCaseW
+  : std::binary_function<CStdStringW, CStdStringW, bool>
+{
+  inline
+  bool operator()(const CStdStringW& sLeft, const CStdStringW& sRight) const
+  { return ssicmp(sLeft.c_str(), sRight.c_str()) < 0; }
+};
+struct StdStringEqualsNoCaseW
+  : std::binary_function<CStdStringW, CStdStringW, bool>
+{
+  inline
+  bool operator()(const CStdStringW& sLeft, const CStdStringW& sRight) const
+  { return ssicmp(sLeft.c_str(), sRight.c_str()) == 0; }
+};
+struct StdStringLessNoCaseA
+  : std::binary_function<CStdStringA, CStdStringA, bool>
+{
+  inline
+  bool operator()(const CStdStringA& sLeft, const CStdStringA& sRight) const
+  { return ssicmp(sLeft.c_str(), sRight.c_str()) < 0; }
+};
+struct StdStringEqualsNoCaseA
+  : std::binary_function<CStdStringA, CStdStringA, bool>
+{
+  inline
+  bool operator()(const CStdStringA& sLeft, const CStdStringA& sRight) const
+  { return ssicmp(sLeft.c_str(), sRight.c_str()) == 0; }
+};
+
+// If we had to define our own version of TRACE above, get rid of it now
+
+#ifdef TRACE_DEFINED_HERE
+  #undef TRACE
+  #undef TRACE_DEFINED_HERE
+#endif
+
+
+// These std::swap specializations come courtesy of Mike Crusader.
+
+//namespace std
+//{
+//  inline void swap(CStdStringA& s1, CStdStringA& s2) throw()
+//  {
+//    s1.swap(s2);
+//  }
+//  template<>
+//  inline void swap(CStdStringW& s1, CStdStringW& s2) throw()
+//  {
+//    s1.swap(s2);
+//  }
+//}
+
+// Turn back on any Borland warnings we turned off.
+
+#ifdef __BORLANDC__
+    #pragma option pop  // Turn back on inline function warnings
+//  #pragma warn +inl   // Turn back on inline function warnings
+#endif
+
+#endif  // #ifndef STDSTRING_H
diff --git a/xbmc/pvrclients/tvheadend/client.cpp b/xbmc/pvrclients/tvheadend/client.cpp
new file mode 100644 (file)
index 0000000..2051cc5
--- /dev/null
@@ -0,0 +1,543 @@
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, 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 "HTSPData.h"
+#include "HTSPDemux.h"
+
+using namespace std;
+
+//cPVRClientTvheadend *g_client = NULL;
+bool m_bCreated               = false;
+ADDON_STATUS m_CurStatus      = STATUS_UNKNOWN;
+int g_clientID                = -1;
+
+/* User adjustable settings are saved here.
+ * Default values are defined inside client.h
+ * and exported to the other source files.
+ */
+CStdString g_szHostname       = DEFAULT_HOST;
+int g_iPortHTSP               = DEFAULT_HTSP_PORT;
+int g_iPortHTTP               = DEFAULT_HTTP_PORT;
+int g_iConnectTimout          = DEFAULT_TIMEOUT;
+bool g_bSkipIFrame            = DEFAULT_SKIP_I_FRAME;
+int g_iEpgOffsetCorrection    = DEFAULT_EPG_OFFSET_CORRECTION;
+CStdString g_szUsername       = "";
+CStdString g_szPassword       = "";
+CStdString g_szUserPath       = "";
+CStdString g_szClientPath     = "";
+cHelper_libXBMC_addon *XBMC   = NULL;
+cHelper_libXBMC_pvr   *PVR    = NULL;
+cHTSPDemux *HTSPDemuxer       = NULL;
+cHTSPData  *HTSPData          = NULL;
+
+extern "C" {
+
+/***********************************************************
+ * Standart AddOn related public library functions
+ ***********************************************************/
+
+ADDON_STATUS Create(void* hdl, void* props)
+{
+  if (!hdl || !props)
+    return STATUS_UNKNOWN;
+
+  PVR_PROPS* pvrprops = (PVR_PROPS*)props;
+
+  XBMC = new cHelper_libXBMC_addon;
+  if (!XBMC->RegisterMe(hdl))
+    return STATUS_UNKNOWN;
+
+  PVR = new cHelper_libXBMC_pvr;
+  if (!PVR->RegisterMe(hdl))
+    return STATUS_UNKNOWN;
+
+  XBMC->Log(LOG_DEBUG, "%s - Creating Tvheadend PVR-Client", __FUNCTION__);
+
+  m_CurStatus    = STATUS_UNKNOWN;
+  g_clientID     = pvrprops->clientID;
+  g_szUserPath   = pvrprops->userpath;
+  g_szClientPath = pvrprops->clientpath;
+
+  /* Read setting "host" from settings.xml */
+  char * buffer;
+  buffer = (char*) malloc (1024);
+  buffer[0] = 0; /* Set the end of string */
+
+  if (XBMC->GetSetting("host", buffer))
+    g_szHostname = buffer;
+  else
+  {
+    /* If setting is unknown fallback to defaults */
+    XBMC->Log(LOG_ERROR, "%s - Couldn't get 'host' setting, falling back to '%s' as default", __FUNCTION__, DEFAULT_HOST);
+    g_szHostname = DEFAULT_HOST;
+  }
+  buffer[0] = 0; /* Set the end of string */
+
+  if (XBMC->GetSetting("user", buffer))
+    g_szUsername = buffer;
+  else
+  {
+    /* If setting is unknown fallback to defaults */
+    XBMC->Log(LOG_ERROR, "%s - Couldn't get 'user' setting", __FUNCTION__);
+    g_szUsername = "";
+  }
+  buffer[0] = 0; /* Set the end of string */
+
+  if (XBMC->GetSetting("pass", buffer))
+    g_szPassword = buffer;
+  else
+  {
+    /* If setting is unknown fallback to defaults */
+    XBMC->Log(LOG_ERROR, "%s - Couldn't get 'pass' setting", __FUNCTION__);
+    g_szPassword = "";
+  }
+  free (buffer);
+
+  /* Read setting "port" from settings.xml */
+  if (!XBMC->GetSetting("htsp_port", &g_iPortHTSP))
+  {
+    /* If setting is unknown fallback to defaults */
+    XBMC->Log(LOG_ERROR, "%s - Couldn't get 'htsp_port' setting, falling back to '%i' as default", __FUNCTION__, DEFAULT_HTSP_PORT);
+    g_iPortHTSP = DEFAULT_HTSP_PORT;
+  }
+
+  /* Read setting "port" from settings.xml */
+  if (!XBMC->GetSetting("http_port", &g_iPortHTTP))
+  {
+    /* If setting is unknown fallback to defaults */
+    XBMC->Log(LOG_ERROR, "%s - Couldn't get 'http_port' setting, falling back to '%i' as default", __FUNCTION__, DEFAULT_HTTP_PORT);
+    g_iPortHTTP = DEFAULT_HTTP_PORT;
+  }
+
+  /* Read setting "skip_I_frame" from settings.xml */
+  if (!XBMC->GetSetting("skip_I_frame", &g_bSkipIFrame))
+  {
+    /* If setting is unknown fallback to defaults */
+    XBMC->Log(LOG_ERROR, "%s - Couldn't get 'skip_I_frame' setting, falling back to 'true' as default", __FUNCTION__);
+    g_bSkipIFrame = DEFAULT_SKIP_I_FRAME;
+  }
+
+  /* Read setting "epg_offset_correction" from settings.xml */
+  if (!XBMC->GetSetting("epg_offset_correction", &g_iEpgOffsetCorrection))
+  {
+    /* If setting is unknown fallback to defaults */
+    XBMC->Log(LOG_ERROR, "%s - Couldn't get 'epg_offset_correction' setting, falling back to '%i' as default", __FUNCTION__, DEFAULT_EPG_OFFSET_CORRECTION);
+    g_iEpgOffsetCorrection = DEFAULT_EPG_OFFSET_CORRECTION;
+  }else
+  {
+    g_iEpgOffsetCorrection -= 12;
+  }
+
+  HTSPData = new cHTSPData;
+  if (!HTSPData->Open(g_szHostname, g_iPortHTSP, g_szUsername, g_szPassword, g_iConnectTimout))
+  {
+    m_CurStatus = STATUS_LOST_CONNECTION;
+    return m_CurStatus;
+  }
+
+  m_CurStatus = STATUS_OK;
+  m_bCreated = true;
+  return m_CurStatus;
+}
+
+ADDON_STATUS GetStatus()
+{
+  return m_CurStatus;
+}
+
+void Destroy()
+{
+  if (m_bCreated)
+  {
+    delete HTSPData;
+    HTSPData = NULL;
+  }
+  m_CurStatus = STATUS_UNKNOWN;
+}
+
+bool HasSettings()
+{
+  return true;
+}
+
+unsigned int GetSettings(StructSetting ***sSet)
+{
+  return 0;
+}
+
+ADDON_STATUS SetSetting(const char *settingName, const void *settingValue)
+{
+  string str = settingName;
+  if (str == "host")
+  {
+    string tmp_sHostname;
+    XBMC->Log(LOG_INFO, "%s - Changed Setting 'host' from %s to %s", __FUNCTION__, g_szHostname.c_str(), (const char*) settingValue);
+    tmp_sHostname = g_szHostname;
+    g_szHostname = (const char*) settingValue;
+    if (tmp_sHostname != g_szHostname)
+      return STATUS_NEED_RESTART;
+  }
+  else if (str == "user")
+  {
+    XBMC->Log(LOG_INFO, "%s - Changed Setting 'user'", __FUNCTION__);
+    string tmp_sUsername = g_szUsername;
+    g_szUsername = (const char*) settingValue;
+    if (tmp_sUsername != g_szUsername)
+      return STATUS_NEED_RESTART;
+  }
+  else if (str == "pass")
+  {
+    XBMC->Log(LOG_INFO, "%s - Changed Setting 'pass'", __FUNCTION__);
+    string tmp_sPassword = g_szPassword;
+    g_szPassword = (const char*) settingValue;
+    if (tmp_sPassword != g_szPassword)
+      return STATUS_NEED_RESTART;
+  }
+  else if (str == "htsp_port")
+  {
+    XBMC->Log(LOG_INFO, "%s - Changed Setting 'port' from %u to %u", __FUNCTION__, g_iPortHTSP, *(int*) settingValue);
+    if (g_iPortHTSP != *(int*) settingValue)
+    {
+      g_iPortHTSP = *(int*) settingValue;
+      return STATUS_NEED_RESTART;
+    }
+  }
+  else if (str == "http_port")
+  {
+    XBMC->Log(LOG_INFO, "%s - Changed Setting 'port' from %u to %u", __FUNCTION__, g_iPortHTTP, *(int*) settingValue);
+    if (g_iPortHTTP != *(int*) settingValue)
+    {
+      g_iPortHTTP = *(int*) settingValue;
+      return STATUS_NEED_RESTART;
+    }
+  }
+  else if (str == "skip_I_frame")
+  {
+    XBMC->Log(LOG_INFO, "%s - Changed Setting 'skip_I_frame' from %u to %u", __FUNCTION__, g_bSkipIFrame, *(bool*) settingValue);
+    if (g_bSkipIFrame != *(bool*) settingValue)
+    {
+      g_bSkipIFrame = *(bool*) settingValue;
+      return STATUS_OK;
+    }
+  }
+  else if (str == "epg_offset_correction")
+  {
+    XBMC->Log(LOG_INFO, "%s - Changed Setting 'epg_offset_correction' from %d to %d", __FUNCTION__, g_iEpgOffsetCorrection, *(int*) settingValue);
+    if (g_iEpgOffsetCorrection != *(int*) settingValue - 12)
+    {
+      g_iEpgOffsetCorrection = *(int*) settingValue - 12;
+      return STATUS_NEED_RESTART;
+    }
+  }
+
+  return STATUS_OK;
+}
+
+void Stop()
+{
+}
+
+void FreeSettings()
+{
+
+}
+
+/***********************************************************
+ * PVR Client AddOn specific public library functions
+ ***********************************************************/
+
+PVR_ERROR GetProperties(PVR_SERVERPROPS* props)
+{
+  props->SupportChannelLogo        = false;
+  props->SupportTimeShift          = false;
+  props->SupportEPG                = true;
+  props->SupportRecordings         = true;
+  props->SupportTimers             = true;
+  props->SupportTV                 = true;
+  props->SupportRadio              = true;
+  props->SupportChannelSettings    = false;
+  props->SupportDirector           = false;
+  props->SupportBouquets           = false;
+  props->HandleInputStream         = true;
+  props->HandleDemuxing            = true;
+  props->SupportChannelScan        = false;
+
+  return PVR_ERROR_NO_ERROR;
+}
+
+const char * GetBackendName()
+{
+  static CStdString BackendName = HTSPData ? HTSPData->GetServerName() : "unknown";
+  return BackendName.c_str();
+}
+
+const char * GetBackendVersion()
+{
+  static CStdString BackendVersion;
+  if (HTSPData)
+    BackendVersion.Format("%s (Protocol: %i)", HTSPData->GetVersion(), HTSPData->GetProtocol());
+  return BackendVersion.c_str();
+}
+
+const char * GetConnectionString()
+{
+  static CStdString ConnectionString;
+  if (HTSPData)
+    ConnectionString.Format("%s:%i%s", g_szHostname.c_str(), g_iPortHTSP, HTSPData->CheckConnection() ? "" : " (Not connected!)");
+  else
+    ConnectionString.Format("%s:%i (addon error!)", g_szHostname.c_str(), g_iPortHTSP);
+  return ConnectionString.c_str();
+}
+
+PVR_ERROR GetDriveSpace(long long *total, long long *used)
+{
+  if (HTSPData && HTSPData->GetDriveSpace(total, used))
+    return PVR_ERROR_NO_ERROR;
+
+  return PVR_ERROR_SERVER_ERROR;
+}
+
+PVR_ERROR GetBackendTime(time_t *localTime, int *gmtOffset)
+{
+  if (HTSPData && HTSPData->GetTime(localTime, gmtOffset))
+    return PVR_ERROR_NO_ERROR;
+
+  return PVR_ERROR_SERVER_ERROR;
+}
+
+
+/*******************************************/
+/** PVR EPG Functions                     **/
+
+PVR_ERROR RequestEPGForChannel(PVRHANDLE handle, const PVR_CHANNEL &channel, time_t start, time_t end)
+{
+  if (!HTSPData)
+    return PVR_ERROR_SERVER_ERROR;
+
+  return HTSPData->RequestEPGForChannel(handle, channel, start, end);
+}
+
+
+/*******************************************/
+/** PVR Channel Functions                 **/
+
+int GetNumChannels()
+{
+  if (!HTSPData)
+    return 0;
+
+  return HTSPData->GetNumChannels();
+}
+
+PVR_ERROR RequestChannelList(PVRHANDLE handle, int radio)
+{
+  if (!HTSPData)
+    return PVR_ERROR_SERVER_ERROR;
+
+  return HTSPData->RequestChannelList(handle, radio);
+}
+
+/*******************************************/
+/** PVR Live Stream Functions             **/
+
+bool OpenLiveStream(const PVR_CHANNEL &channelinfo)
+{
+  CloseLiveStream();
+
+  HTSPDemuxer = new cHTSPDemux;
+  return HTSPDemuxer->Open(channelinfo);
+}
+
+void CloseLiveStream()
+{
+  if (HTSPDemuxer)
+  {
+    HTSPDemuxer->Close();
+    delete HTSPDemuxer;
+    HTSPDemuxer = NULL;
+  }
+}
+
+PVR_ERROR GetStreamProperties(PVR_STREAMPROPS* props)
+{
+  if (HTSPDemuxer && HTSPDemuxer->GetStreamProperties(props))
+    return PVR_ERROR_NO_ERROR;
+
+  return PVR_ERROR_SERVER_ERROR;
+}
+
+void DemuxAbort()
+{
+  if (HTSPDemuxer) HTSPDemuxer->Abort();
+}
+
+DemuxPacket* DemuxRead()
+{
+  return HTSPDemuxer->Read();
+}
+
+int GetCurrentClientChannel()
+{
+  if (HTSPDemuxer)
+    return HTSPDemuxer->CurrentChannel();
+
+  return -1;
+}
+
+bool SwitchChannel(const PVR_CHANNEL &channelinfo)
+{
+  if (HTSPDemuxer)
+    return HTSPDemuxer->SwitchChannel(channelinfo);
+
+  return false;
+}
+
+PVR_ERROR SignalQuality(PVR_SIGNALQUALITY &qualityinfo)
+{
+  if (HTSPDemuxer && HTSPDemuxer->GetSignalStatus(qualityinfo))
+    return PVR_ERROR_NO_ERROR;
+
+  return PVR_ERROR_SERVER_ERROR;
+}
+
+/*******************************************/
+/** PVR Recording Functions               **/
+
+PVR_ERROR RequestRecordingsList(PVRHANDLE handle)
+{
+  if (!HTSPData)
+    return PVR_ERROR_SERVER_ERROR;
+
+  return HTSPData->RequestRecordingsList(handle);
+}
+
+int GetNumRecordings(void)
+{
+  if (!HTSPData)
+    return PVR_ERROR_SERVER_ERROR;
+
+  return HTSPData->GetNumRecordings();
+}
+
+PVR_ERROR DeleteRecording(const PVR_RECORDINGINFO &recinfo)
+{
+  if (!HTSPData)
+    return PVR_ERROR_SERVER_ERROR;
+
+  return HTSPData->DeleteRecording(recinfo);
+}
+
+/*******************************************/
+/** PVR Timer Functions               **/
+
+int GetNumTimers(void)
+{
+  if (!HTSPData)
+    return PVR_ERROR_SERVER_ERROR;
+
+  return HTSPData->GetNumTimers();
+}
+
+PVR_ERROR RequestTimerList(PVRHANDLE handle)
+{
+  if (!HTSPData)
+    return PVR_ERROR_SERVER_ERROR;
+
+  return HTSPData->RequestTimerList(handle);
+}
+
+PVR_ERROR DeleteTimer(const PVR_TIMERINFO &timerinfo, bool force)
+{
+  if (!HTSPData)
+    return PVR_ERROR_SERVER_ERROR;
+
+  return HTSPData->DeleteTimer(timerinfo, force);
+}
+
+PVR_ERROR AddTimer(const PVR_TIMERINFO &timerinfo)
+{
+  if (!HTSPData)
+    return PVR_ERROR_SERVER_ERROR;
+
+  return HTSPData->AddTimer(timerinfo);
+}
+
+PVR_ERROR UpdateTimer(const PVR_TIMERINFO &timerinfo) 
+{ 
+  if(!HTSPData)
+    return PVR_ERROR_SERVER_ERROR;
+
+  return HTSPData->UpdateTimer(timerinfo);
+}
+
+PVR_ERROR RenameTimer(const PVR_TIMERINFO &timerinfo, const char *newname) 
+{ 
+  if(!HTSPData)
+    return PVR_ERROR_SERVER_ERROR;
+
+  // The new name is already in the timerinfo.title, so we don't need it
+  return HTSPData->UpdateTimer(timerinfo);
+}
+
+PVR_ERROR RenameRecording(const PVR_RECORDINGINFO &recinfo, const char *newname) 
+{ 
+  if(!HTSPData)
+    return PVR_ERROR_SERVER_ERROR;
+
+  return HTSPData->RenameRecording(recinfo, newname);
+}
+
+/** UNUSED API FUNCTIONS */
+PVR_ERROR DialogChannelScan() { return PVR_ERROR_NOT_IMPLEMENTED; }
+PVR_ERROR MenuHook(const PVR_MENUHOOK &menuhook) { return PVR_ERROR_NOT_IMPLEMENTED; }
+int GetNumBouquets() { return 0; }
+PVR_ERROR RequestBouquetsList(PVRHANDLE handle, int radio) { return PVR_ERROR_NOT_IMPLEMENTED; }
+PVR_ERROR DeleteChannel(unsigned int number) { return PVR_ERROR_NOT_IMPLEMENTED; }
+PVR_ERROR RenameChannel(unsigned int number, const char *newname) { return PVR_ERROR_NOT_IMPLEMENTED; }
+PVR_ERROR MoveChannel(unsigned int number, unsigned int newnumber) { return PVR_ERROR_NOT_IMPLEMENTED; }
+PVR_ERROR DialogChannelSettings(const PVR_CHANNEL &channelinfo) { return PVR_ERROR_NOT_IMPLEMENTED; }
+PVR_ERROR DialogAddChannel(const PVR_CHANNEL &channelinfo) { return PVR_ERROR_NOT_IMPLEMENTED; }
+bool HaveCutmarks() { return false; }
+PVR_ERROR RequestCutMarksList(PVRHANDLE handle) { return PVR_ERROR_NOT_IMPLEMENTED; }
+PVR_ERROR AddCutMark(const PVR_CUT_MARK &cutmark) { return PVR_ERROR_NOT_IMPLEMENTED; }
+PVR_ERROR DeleteCutMark(const PVR_CUT_MARK &cutmark) { return PVR_ERROR_NOT_IMPLEMENTED; }
+PVR_ERROR StartCut() { return PVR_ERROR_NOT_IMPLEMENTED; }
+bool SwapLiveTVSecondaryStream() { return false; }
+bool OpenSecondaryStream(const PVR_CHANNEL &channelinfo) { return false; }
+void CloseSecondaryStream() {}
+int ReadSecondaryStream(unsigned char* buf, int buf_size) { return 0; }
+bool OpenRecordedStream(const PVR_RECORDINGINFO &recinfo) { return false; }
+void CloseRecordedStream(void) {}
+int ReadRecordedStream(unsigned char* buf, int buf_size) { return 0; }
+long long SeekRecordedStream(long long pos, int whence) { return 0; }
+long long PositionRecordedStream(void) { return -1; }
+long long LengthRecordedStream(void) { return 0; }
+void DemuxReset(){}
+void DemuxFlush(){}
+int ReadLiveStream(unsigned char* buf, int buf_size) { return 0; }
+long long SeekLiveStream(long long pos, int whence) { return -1; }
+long long PositionLiveStream(void) { return -1; }
+long long LengthLiveStream(void) { return -1; }
+const char * GetLiveStreamURL(const PVR_CHANNEL &channelinfo) { return ""; }
+
+}
diff --git a/xbmc/pvrclients/tvheadend/client.h b/xbmc/pvrclients/tvheadend/client.h
new file mode 100644 (file)
index 0000000..c8d14d6
--- /dev/null
@@ -0,0 +1,52 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef CLIENT_H
+#define CLIENT_H
+
+#include "StdString.h"
+#include "../../../addons/library.xbmc.addon/libXBMC_addon.h"
+#include "../../../addons/library.xbmc.pvr/libXBMC_pvr.h"
+
+#define DEFAULT_HOST                  "127.0.0.1"
+#define DEFAULT_HTTP_PORT             9981
+#define DEFAULT_HTSP_PORT             9982
+#define DEFAULT_TIMEOUT               30000
+#define DEFAULT_SKIP_I_FRAME          true
+#define DEFAULT_EPG_OFFSET_CORRECTION 0
+
+extern bool         m_bCreated;
+extern CStdString   g_szHostname;
+extern int          g_iPortHTSP;
+extern int          g_iPortHTTP;
+extern CStdString   g_szUsername;
+extern CStdString   g_szPassword;
+extern int          g_iConnectTimout;
+extern bool         g_bSkipIFrame;
+extern int          g_iEpgOffsetCorrection;
+extern int          g_clientID;
+extern CStdString   g_szUserPath;
+extern CStdString   g_szClientPath;
+extern cHelper_libXBMC_addon *XBMC;
+extern cHelper_libXBMC_pvr   *PVR;
+
+#endif /* CLIENT_H */
diff --git a/xbmc/pvrclients/tvheadend/linux/os_posix.h b/xbmc/pvrclients/tvheadend/linux/os_posix.h
new file mode 100644 (file)
index 0000000..0adf70d
--- /dev/null
@@ -0,0 +1,93 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef PVRCLIENT_VDR_OS_POSIX_H
+#define PVRCLIENT_VDR_OS_POSIX_H
+
+#define _FILE_OFFSET_BITS 64
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/syscall.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/timeb.h>
+#include <sys/wait.h>
+#include <sys/signal.h>
+#include <sys/resource.h>
+#ifndef __APPLE__
+#include <sys/prctl.h> 
+#endif
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <pthread.h>
+#include <poll.h>
+
+typedef int bool_t;
+typedef int SOCKET;
+
+#ifndef closesocket
+#define closesocket(a) close(a)
+#endif
+
+#define SOCKET_ERROR   (-1)
+#define INVALID_SOCKET (-1)
+#define SD_BOTH SHUT_RDWR
+
+#define LIBTYPE
+#define sock_getlasterror errno
+#define sock_getlasterror_socktimeout (errno == EAGAIN)
+#define console_vprintf vprintf
+#define console_printf printf
+#define THREAD_FUNC_PREFIX void *
+
+#ifndef __STL_CONFIG_H
+template<class T> inline T min(T a, T b) { return a <= b ? a : b; }
+template<class T> inline T max(T a, T b) { return a >= b ? a : b; }
+template<class T> inline int sgn(T a) { return a < 0 ? -1 : a > 0 ? 1 : 0; }
+template<class T> inline void swap(T &a, T &b) { T t = a; a = b; b = t; }
+#endif
+
+#define Sleep(t) usleep(t*1000)
+
+static inline uint64_t getcurrenttime(void)
+{
+       struct timeval t;
+       gettimeofday(&t, NULL);
+       return ((uint64_t)t.tv_sec * 1000) + (t.tv_usec / 1000);
+}
+
+static inline int setsocktimeout(int s, int level, int optname, uint64_t timeout)
+{
+       struct timeval t;
+       t.tv_sec = timeout / 1000;
+       t.tv_usec = (timeout % 1000) * 1000;
+       return setsockopt(s, level, optname, (char *)&t, sizeof(t));
+}
+
+#endif
diff --git a/xbmc/pvrclients/tvheadend/project/VS2008Express/XBMC_hts.vcproj b/xbmc/pvrclients/tvheadend/project/VS2008Express/XBMC_hts.vcproj
new file mode 100644 (file)
index 0000000..bae3c88
--- /dev/null
@@ -0,0 +1,268 @@
+<?xml version="1.0" encoding="Windows-1252"?>\r
+<VisualStudioProject\r
+       ProjectType="Visual C++"\r
+       Version="9,00"\r
+       Name="pvrclient_hts"\r
+       ProjectGUID="{2F4A2296-3E00-4881-B6BC-1B7ADAC3CBF4}"\r
+       RootNamespace="XBMC_HTS"\r
+       TargetFrameworkVersion="196613"\r
+       >\r
+       <Platforms>\r
+               <Platform\r
+                       Name="Win32"\r
+               />\r
+       </Platforms>\r
+       <ToolFiles>\r
+       </ToolFiles>\r
+       <Configurations>\r
+               <Configuration\r
+                       Name="Debug|Win32"\r
+                       OutputDirectory="Debug"\r
+                       IntermediateDirectory="Debug"\r
+                       ConfigurationType="2"\r
+                       CharacterSet="2"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               Optimization="0"\r
+                               AdditionalIncludeDirectories="..\..\..\..\cores\dvdplayer\Codecs\ffmpeg;..\..\..\..\cores\dvdplayer\Codecs\ffmpeg\include;..\..\;..\..\pthread_win32;..\..\windows;..\..\..\..\addons\include;..\..\..\..\cores\dvdplayer\DVDDemuxers;..\..\..\..\..\xbmc\lib;..\..\..\..\..\xbmc\lib\libhts\Win32\include"\r
+                               PreprocessorDefinitions="_WIN32;WIN32;_WINDOWS;_WIN32PC;_USE_32BIT_TIME_T;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_WINSOCKAPI_;USE_DEMUX"\r
+                               MinimalRebuild="true"\r
+                               BasicRuntimeChecks="3"\r
+                               RuntimeLibrary="3"\r
+                               WarningLevel="3"\r
+                               DebugInformationFormat="4"\r
+                               DisableSpecificWarnings="4996"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               AdditionalDependencies="ws2_32.lib ..\..\pthread_win32\pthreadVC2.lib"\r
+                               OutputFile="../../../../../addons/pvr.hts/XBMC_Tvheadend_win32.pvr"\r
+                               IgnoreDefaultLibraryNames="libcmtd"\r
+                               GenerateDebugInformation="true"\r
+                               TargetMachine="1"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManifestTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCAppVerifierTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                       />\r
+               </Configuration>\r
+               <Configuration\r
+                       Name="Release|Win32"\r
+                       OutputDirectory="$(SolutionDir)$(ConfigurationName)"\r
+                       IntermediateDirectory="$(ConfigurationName)"\r
+                       ConfigurationType="2"\r
+                       CharacterSet="2"\r
+                       WholeProgramOptimization="1"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               Optimization="2"\r
+                               EnableIntrinsicFunctions="true"\r
+                               AdditionalIncludeDirectories="..\..\..\..\cores\dvdplayer\Codecs\ffmpeg;..\..\..\..\cores\dvdplayer\Codecs\ffmpeg\include;..\..\;..\..\pthread_win32;..\..\windows;..\..\..\..\addons\include;..\..\..\..\cores\dvdplayer\DVDDemuxers;..\..\..\..\..\xbmc\lib;..\..\..\..\..\xbmc\lib\libhts\Win32\include"\r
+                               PreprocessorDefinitions="_WIN32;WIN32;_WINDOWS;_WIN32PC;_USE_32BIT_TIME_T;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_WINSOCKAPI_;USE_DEMUX"\r
+                               RuntimeLibrary="2"\r
+                               EnableFunctionLevelLinking="true"\r
+                               WarningLevel="3"\r
+                               DebugInformationFormat="3"\r
+                               DisableSpecificWarnings="4996"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               AdditionalDependencies="ws2_32.lib ..\..\pthread_win32\pthreadVC2.lib"\r
+                               OutputFile="../../../../../addons/pvr.hts/XBMC_Tvheadend_win32.pvr"\r
+                               IgnoreDefaultLibraryNames="libcmt"\r
+                               GenerateDebugInformation="true"\r
+                               OptimizeReferences="2"\r
+                               EnableCOMDATFolding="2"\r
+                               TargetMachine="1"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManifestTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCAppVerifierTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                       />\r
+               </Configuration>\r
+       </Configurations>\r
+       <References>\r
+       </References>\r
+       <Files>\r
+               <Filter\r
+                       Name="Source Files"\r
+                       Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"\r
+                       UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"\r
+                       >\r
+                       <File\r
+                               RelativePath="..\..\client.cpp"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\windows\dirent.cpp"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\..\..\addons\library.xbmc.addon\dlfcn-win32.cpp"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\HTSPData.cpp"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\HTSPDemux.cpp"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\HTSPSession.cpp"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\windows\os_windows.cpp"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\thread.cpp"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\tools.cpp"\r
+                               >\r
+                       </File>\r
+               </Filter>\r
+               <Filter\r
+                       Name="Header Files"\r
+                       Filter="h;hpp;hxx;hm;inl;inc;xsd"\r
+                       UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"\r
+                       >\r
+                       <File\r
+                               RelativePath="..\..\client.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\windows\dirent.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\HTSPData.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\HTSPDemux.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\HTSPSession.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\windows\os_windows.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\pvrclient-tvheadend_os.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\StdString.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\thread.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\tools.h"\r
+                               >\r
+                       </File>\r
+               </Filter>\r
+               <Filter\r
+                       Name="Resource Files"\r
+                       Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"\r
+                       UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"\r
+                       >\r
+               </Filter>\r
+       </Files>\r
+       <Globals>\r
+       </Globals>\r
+</VisualStudioProject>\r
diff --git a/xbmc/pvrclients/tvheadend/project/VS2010Express/XBMC_tvheadend.vcxproj b/xbmc/pvrclients/tvheadend/project/VS2010Express/XBMC_tvheadend.vcxproj
new file mode 100644 (file)
index 0000000..95b7a67
--- /dev/null
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\..\..\..\addons\library.xbmc.addon\dlfcn-win32.cpp" />
+    <ClCompile Include="..\..\client.cpp" />
+    <ClCompile Include="..\..\HTSPData.cpp" />
+    <ClCompile Include="..\..\HTSPDemux.cpp" />
+    <ClCompile Include="..\..\HTSPSession.cpp" />
+    <ClCompile Include="..\..\thread.cpp" />
+    <ClCompile Include="..\..\tools.cpp" />
+    <ClCompile Include="..\..\windows\dirent.cpp" />
+    <ClCompile Include="..\..\windows\os_windows.cpp" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\..\..\..\addons\library.xbmc.addon\dlfcn-win32.h" />
+    <ClInclude Include="..\..\client.h" />
+    <ClInclude Include="..\..\HTSPData.h" />
+    <ClInclude Include="..\..\HTSPDemux.h" />
+    <ClInclude Include="..\..\HTSPSession.h" />
+    <ClInclude Include="..\..\pvrclient-tvheadend_os.h" />
+    <ClInclude Include="..\..\StdString.h" />
+    <ClInclude Include="..\..\thread.h" />
+    <ClInclude Include="..\..\tools.h" />
+    <ClInclude Include="..\..\windows\dirent.h" />
+    <ClInclude Include="..\..\windows\os_windows.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\..\..\lib\libhts\Win32\libhts_2010.vcxproj">
+      <Project>{00700e12-a63b-4e54-b962-4011a90584bd}</Project>
+    </ProjectReference>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{C04B0FB1-667D-4F1C-BDAE-A07CDFFA74A3}</ProjectGuid>
+    <RootNamespace>XBMC_tvheadend</RootNamespace>
+    <ProjectName>pvrclient_tvheadend</ProjectName>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <OutDir>..\..\..\..\..\addons\pvr.hts\</OutDir>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <TargetName>XBMC_Tvheadend_win32</TargetName>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <TargetExt>.pvr</TargetExt>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ExtensionsToDeleteOnClean>*.cdf;*.cache;*.obj;*.ilk;*.resources;*.tlb;*.tli;*.tlh;*.tmp;*.rsp;*.pgc;*.pgd;*.meta;*.tlog;*.manifest;*.res;*.pch;*.exp;*.idb;*.rep;*.xdc;*.pdb;*_manifest.rc;*.bsc;*.sbr;*.xml;*.pvr</ExtensionsToDeleteOnClean>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <OutDir>..\..\..\..\..\addons\pvr.hts\</OutDir>
+    <TargetName>XBMC_Tvheadend_win32</TargetName>
+    <TargetExt>.pvr</TargetExt>
+    <ExtensionsToDeleteOnClean>*.cdf;*.cache;*.obj;*.ilk;*.resources;*.tlb;*.tli;*.tlh;*.tmp;*.rsp;*.pgc;*.pgd;*.meta;*.tlog;*.manifest;*.res;*.pch;*.exp;*.idb;*.rep;*.xdc;*.pdb;*_manifest.rc;*.bsc;*.sbr;*.xml;*.pvr</ExtensionsToDeleteOnClean>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_USE_32BIT_TIME_T;_WINSOCKAPI_;USE_DEMUX;__STDC_CONSTANT_MACROS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>..\..\..\..\cores\dvdplayer\Codecs\ffmpeg;..\..\..\..\cores\dvdplayer\Codecs\ffmpeg\include;..\..\;..\..\pthread_win32;..\..\windows;..\..\..\..\addons\include;..\..\..\..\cores\dvdplayer\DVDDemuxers;..\..\..\..\..\xbmc\lib;..\..\..\..\..\xbmc\lib\libhts\Win32\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <DisableSpecificWarnings>4996</DisableSpecificWarnings>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <OutputFile>..\..\..\..\..\addons\pvr.hts\XBMC_Tvheadend_win32.pvr</OutputFile>
+      <AdditionalDependencies>ws2_32.lib;..\..\pthread_win32\pthreadVC2.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <IgnoreSpecificDefaultLibraries>libcmtd</IgnoreSpecificDefaultLibraries>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <AdditionalIncludeDirectories>..\..\..\..\cores\dvdplayer\Codecs\ffmpeg;..\..\..\..\cores\dvdplayer\Codecs\ffmpeg\include;..\..\;..\..\pthread_win32;..\..\windows;..\..\..\..\addons\include;..\..\..\..\cores\dvdplayer\DVDDemuxers;..\..\..\..\..\xbmc\lib;..\..\..\..\..\xbmc\lib\libhts\Win32\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_USE_32BIT_TIME_T;_WINSOCKAPI_;USE_DEMUX;__STDC_CONSTANT_MACROS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <DisableSpecificWarnings>4996</DisableSpecificWarnings>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <OutputFile>..\..\..\..\..\addons\pvr.hts\XBMC_Tvheadend_win32.pvr</OutputFile>
+      <AdditionalDependencies>ws2_32.lib;..\..\pthread_win32\pthreadVC2.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <IgnoreSpecificDefaultLibraries>libcmt</IgnoreSpecificDefaultLibraries>
+    </Link>
+  </ItemDefinitionGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/xbmc/pvrclients/tvheadend/project/VS2010Express/XBMC_tvheadend.vcxproj.filters b/xbmc/pvrclients/tvheadend/project/VS2010Express/XBMC_tvheadend.vcxproj.filters
new file mode 100644 (file)
index 0000000..1878758
--- /dev/null
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+    <Filter Include="Resource Files">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\tools.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\client.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\HTSPData.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\HTSPDemux.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\HTSPSession.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\thread.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\windows\os_windows.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\windows\dirent.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\..\..\addons\library.xbmc.addon\dlfcn-win32.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\..\..\..\addons\library.xbmc.addon\dlfcn-win32.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\pvrclient-tvheadend_os.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\StdString.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\thread.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\tools.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\client.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\HTSPData.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\HTSPDemux.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\HTSPSession.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\windows\os_windows.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\windows\dirent.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/xbmc/pvrclients/tvheadend/pthread_win32/pthread.h b/xbmc/pvrclients/tvheadend/pthread_win32/pthread.h
new file mode 100644 (file)
index 0000000..9f2868b
--- /dev/null
@@ -0,0 +1,1368 @@
+/* This is an implementation of the threads API of POSIX 1003.1-2001.\r
+ *\r
+ * --------------------------------------------------------------------------\r
+ *\r
+ *      Pthreads-win32 - POSIX Threads Library for Win32\r
+ *      Copyright(C) 1998 John E. Bossom\r
+ *      Copyright(C) 1999,2005 Pthreads-win32 contributors\r
+ * \r
+ *      Contact Email: rpj@callisto.canberra.edu.au\r
+ * \r
+ *      The current list of contributors is contained\r
+ *      in the file CONTRIBUTORS included with the source\r
+ *      code distribution. The list can also be seen at the\r
+ *      following World Wide Web location:\r
+ *      http://sources.redhat.com/pthreads-win32/contributors.html\r
+ * \r
+ *      This library is free software; you can redistribute it and/or\r
+ *      modify it under the terms of the GNU Lesser General Public\r
+ *      License as published by the Free Software Foundation; either\r
+ *      version 2 of the License, or (at your option) any later version.\r
+ * \r
+ *      This library is distributed in the hope that it will be useful,\r
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+ *      Lesser General Public License for more details.\r
+ * \r
+ *      You should have received a copy of the GNU Lesser General Public\r
+ *      License along with this library in the file COPYING.LIB;\r
+ *      if not, write to the Free Software Foundation, Inc.,\r
+ *      59 Temple Place - Suite 330, Boston, MA 02111-1307, USA\r
+ */\r
+\r
+#if !defined( PTHREAD_H )\r
+#define PTHREAD_H\r
+\r
+/*\r
+ * See the README file for an explanation of the pthreads-win32 version\r
+ * numbering scheme and how the DLL is named etc.\r
+ */\r
+#define PTW32_VERSION 2,8,0,0\r
+#define PTW32_VERSION_STRING "2, 8, 0, 0\0"\r
+\r
+/* There are three implementations of cancel cleanup.\r
+ * Note that pthread.h is included in both application\r
+ * compilation units and also internally for the library.\r
+ * The code here and within the library aims to work\r
+ * for all reasonable combinations of environments.\r
+ *\r
+ * The three implementations are:\r
+ *\r
+ *   WIN32 SEH\r
+ *   C\r
+ *   C++\r
+ *\r
+ * Please note that exiting a push/pop block via\r
+ * "return", "exit", "break", or "continue" will\r
+ * lead to different behaviour amongst applications\r
+ * depending upon whether the library was built\r
+ * using SEH, C++, or C. For example, a library built\r
+ * with SEH will call the cleanup routine, while both\r
+ * C++ and C built versions will not.\r
+ */\r
+\r
+/*\r
+ * Define defaults for cleanup code.\r
+ * Note: Unless the build explicitly defines one of the following, then\r
+ * we default to standard C style cleanup. This style uses setjmp/longjmp\r
+ * in the cancelation and thread exit implementations and therefore won't\r
+ * do stack unwinding if linked to applications that have it (e.g.\r
+ * C++ apps). This is currently consistent with most/all commercial Unix\r
+ * POSIX threads implementations.\r
+ */\r
+#if !defined( __CLEANUP_SEH ) && !defined( __CLEANUP_CXX ) && !defined( __CLEANUP_C )\r
+# define __CLEANUP_C\r
+#endif\r
+\r
+#if defined( __CLEANUP_SEH ) && ( !defined( _MSC_VER ) && !defined(PTW32_RC_MSC))\r
+#error ERROR [__FILE__, line __LINE__]: SEH is not supported for this compiler.\r
+#endif\r
+\r
+/*\r
+ * Stop here if we are being included by the resource compiler.\r
+ */\r
+#ifndef RC_INVOKED\r
+\r
+#undef PTW32_LEVEL\r
+\r
+#if defined(_POSIX_SOURCE)\r
+#define PTW32_LEVEL 0\r
+/* Early POSIX */\r
+#endif\r
+\r
+#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309\r
+#undef PTW32_LEVEL\r
+#define PTW32_LEVEL 1\r
+/* Include 1b, 1c and 1d */\r
+#endif\r
+\r
+#if defined(INCLUDE_NP)\r
+#undef PTW32_LEVEL\r
+#define PTW32_LEVEL 2\r
+/* Include Non-Portable extensions */\r
+#endif\r
+\r
+#define PTW32_LEVEL_MAX 3\r
+\r
+#if !defined(PTW32_LEVEL)\r
+#define PTW32_LEVEL PTW32_LEVEL_MAX\r
+/* Include everything */\r
+#endif\r
+\r
+#ifdef _UWIN\r
+#   define HAVE_STRUCT_TIMESPEC 1\r
+#   define HAVE_SIGNAL_H        1\r
+#   undef HAVE_CONFIG_H\r
+#   pragma comment(lib, "pthread")\r
+#endif\r
+\r
+/*\r
+ * -------------------------------------------------------------\r
+ *\r
+ *\r
+ * Module: pthread.h\r
+ *\r
+ * Purpose:\r
+ *      Provides an implementation of PThreads based upon the\r
+ *      standard:\r
+ *\r
+ *              POSIX 1003.1-2001\r
+ *  and\r
+ *    The Single Unix Specification version 3\r
+ *\r
+ *    (these two are equivalent)\r
+ *\r
+ *      in order to enhance code portability between Windows,\r
+ *  various commercial Unix implementations, and Linux.\r
+ *\r
+ *      See the ANNOUNCE file for a full list of conforming\r
+ *      routines and defined constants, and a list of missing\r
+ *      routines and constants not defined in this implementation.\r
+ *\r
+ * Authors:\r
+ *      There have been many contributors to this library.\r
+ *      The initial implementation was contributed by\r
+ *      John Bossom, and several others have provided major\r
+ *      sections or revisions of parts of the implementation.\r
+ *      Often significant effort has been contributed to\r
+ *      find and fix important bugs and other problems to\r
+ *      improve the reliability of the library, which sometimes\r
+ *      is not reflected in the amount of code which changed as\r
+ *      result.\r
+ *      As much as possible, the contributors are acknowledged\r
+ *      in the ChangeLog file in the source code distribution\r
+ *      where their changes are noted in detail.\r
+ *\r
+ *      Contributors are listed in the CONTRIBUTORS file.\r
+ *\r
+ *      As usual, all bouquets go to the contributors, and all\r
+ *      brickbats go to the project maintainer.\r
+ *\r
+ * Maintainer:\r
+ *      The code base for this project is coordinated and\r
+ *      eventually pre-tested, packaged, and made available by\r
+ *\r
+ *              Ross Johnson <rpj@callisto.canberra.edu.au>\r
+ *\r
+ * QA Testers:\r
+ *      Ultimately, the library is tested in the real world by\r
+ *      a host of competent and demanding scientists and\r
+ *      engineers who report bugs and/or provide solutions\r
+ *      which are then fixed or incorporated into subsequent\r
+ *      versions of the library. Each time a bug is fixed, a\r
+ *      test case is written to prove the fix and ensure\r
+ *      that later changes to the code don't reintroduce the\r
+ *      same error. The number of test cases is slowly growing\r
+ *      and therefore so is the code reliability.\r
+ *\r
+ * Compliance:\r
+ *      See the file ANNOUNCE for the list of implemented\r
+ *      and not-implemented routines and defined options.\r
+ *      Of course, these are all defined is this file as well.\r
+ *\r
+ * Web site:\r
+ *      The source code and other information about this library\r
+ *      are available from\r
+ *\r
+ *              http://sources.redhat.com/pthreads-win32/\r
+ *\r
+ * -------------------------------------------------------------\r
+ */\r
+\r
+/* Try to avoid including windows.h */\r
+#if defined(__MINGW32__) && defined(__cplusplus)\r
+#define PTW32_INCLUDE_WINDOWS_H\r
+#endif\r
+\r
+#ifdef PTW32_INCLUDE_WINDOWS_H\r
+#include <windows.h>\r
+#endif\r
+\r
+#if defined(_MSC_VER) && _MSC_VER < 1300 || defined(__DMC__)\r
+/*\r
+ * VC++6.0 or early compiler's header has no DWORD_PTR type.\r
+ */\r
+typedef unsigned long DWORD_PTR;\r
+#endif\r
+/*\r
+ * -----------------\r
+ * autoconf switches\r
+ * -----------------\r
+ */\r
+\r
+#if HAVE_CONFIG_H\r
+#include "config.h"\r
+#endif /* HAVE_CONFIG_H */\r
+\r
+#ifndef NEED_FTIME\r
+#include <time.h>\r
+#else /* NEED_FTIME */\r
+/* use native WIN32 time API */\r
+#endif /* NEED_FTIME */\r
+\r
+#if HAVE_SIGNAL_H\r
+#include <signal.h>\r
+#endif /* HAVE_SIGNAL_H */\r
+\r
+#include <setjmp.h>\r
+#include <limits.h>\r
+\r
+/*\r
+ * Boolean values to make us independent of system includes.\r
+ */\r
+enum {\r
+  PTW32_FALSE = 0,\r
+  PTW32_TRUE = (! PTW32_FALSE)\r
+};\r
+\r
+/*\r
+ * This is a duplicate of what is in the autoconf config.h,\r
+ * which is only used when building the pthread-win32 libraries.\r
+ */\r
+\r
+#ifndef PTW32_CONFIG_H\r
+#  if defined(WINCE)\r
+#    define NEED_ERRNO\r
+#    define NEED_SEM\r
+#  endif\r
+#  if defined(_UWIN) || defined(__MINGW32__)\r
+#    define HAVE_MODE_T\r
+#  endif\r
+#endif\r
+\r
+/*\r
+ *\r
+ */\r
+\r
+#if PTW32_LEVEL >= PTW32_LEVEL_MAX\r
+#ifdef NEED_ERRNO\r
+#include "need_errno.h"\r
+#else\r
+#include <errno.h>\r
+#endif\r
+#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */\r
+\r
+/*\r
+ * Several systems don't define some error numbers.\r
+ */\r
+#ifndef ENOTSUP\r
+#  define ENOTSUP 48   /* This is the value in Solaris. */\r
+#endif\r
+\r
+#ifndef ETIMEDOUT\r
+#  define ETIMEDOUT 10060     /* This is the value in winsock.h. */\r
+#endif\r
+\r
+#ifndef ENOSYS\r
+#  define ENOSYS 140     /* Semi-arbitrary value */\r
+#endif\r
+\r
+#ifndef EDEADLK\r
+#  ifdef EDEADLOCK\r
+#    define EDEADLK EDEADLOCK\r
+#  else\r
+#    define EDEADLK 36     /* This is the value in MSVC. */\r
+#  endif\r
+#endif\r
+\r
+#include <sched.h>\r
+\r
+/*\r
+ * To avoid including windows.h we define only those things that we\r
+ * actually need from it.\r
+ */\r
+#ifndef PTW32_INCLUDE_WINDOWS_H\r
+#ifndef HANDLE\r
+# define PTW32__HANDLE_DEF\r
+# define HANDLE void *\r
+#endif\r
+#ifndef DWORD\r
+# define PTW32__DWORD_DEF\r
+# define DWORD unsigned long\r
+#endif\r
+#endif\r
+\r
+#ifndef HAVE_STRUCT_TIMESPEC\r
+#define HAVE_STRUCT_TIMESPEC 1\r
+struct timespec {\r
+        long tv_sec;\r
+        long tv_nsec;\r
+};\r
+#endif /* HAVE_STRUCT_TIMESPEC */\r
+\r
+#ifndef SIG_BLOCK\r
+#define SIG_BLOCK 0\r
+#endif /* SIG_BLOCK */\r
+\r
+#ifndef SIG_UNBLOCK \r
+#define SIG_UNBLOCK 1\r
+#endif /* SIG_UNBLOCK */\r
+\r
+#ifndef SIG_SETMASK\r
+#define SIG_SETMASK 2\r
+#endif /* SIG_SETMASK */\r
+\r
+#ifdef __cplusplus\r
+extern "C"\r
+{\r
+#endif                          /* __cplusplus */\r
+\r
+/*\r
+ * -------------------------------------------------------------\r
+ *\r
+ * POSIX 1003.1-2001 Options\r
+ * =========================\r
+ *\r
+ * Options are normally set in <unistd.h>, which is not provided\r
+ * with pthreads-win32.\r
+ *\r
+ * For conformance with the Single Unix Specification (version 3), all of the\r
+ * options below are defined, and have a value of either -1 (not supported)\r
+ * or 200112L (supported).\r
+ *\r
+ * These options can neither be left undefined nor have a value of 0, because\r
+ * either indicates that sysconf(), which is not implemented, may be used at\r
+ * runtime to check the status of the option.\r
+ *\r
+ * _POSIX_THREADS (== 200112L)\r
+ *                      If == 200112L, you can use threads\r
+ *\r
+ * _POSIX_THREAD_ATTR_STACKSIZE (== 200112L)\r
+ *                      If == 200112L, you can control the size of a thread's\r
+ *                      stack\r
+ *                              pthread_attr_getstacksize\r
+ *                              pthread_attr_setstacksize\r
+ *\r
+ * _POSIX_THREAD_ATTR_STACKADDR (== -1)\r
+ *                      If == 200112L, you can allocate and control a thread's\r
+ *                      stack. If not supported, the following functions\r
+ *                      will return ENOSYS, indicating they are not\r
+ *                      supported:\r
+ *                              pthread_attr_getstackaddr\r
+ *                              pthread_attr_setstackaddr\r
+ *\r
+ * _POSIX_THREAD_PRIORITY_SCHEDULING (== -1)\r
+ *                      If == 200112L, you can use realtime scheduling.\r
+ *                      This option indicates that the behaviour of some\r
+ *                      implemented functions conforms to the additional TPS\r
+ *                      requirements in the standard. E.g. rwlocks favour\r
+ *                      writers over readers when threads have equal priority.\r
+ *\r
+ * _POSIX_THREAD_PRIO_INHERIT (== -1)\r
+ *                      If == 200112L, you can create priority inheritance\r
+ *                      mutexes.\r
+ *                              pthread_mutexattr_getprotocol +\r
+ *                              pthread_mutexattr_setprotocol +\r
+ *\r
+ * _POSIX_THREAD_PRIO_PROTECT (== -1)\r
+ *                      If == 200112L, you can create priority ceiling mutexes\r
+ *                      Indicates the availability of:\r
+ *                              pthread_mutex_getprioceiling\r
+ *                              pthread_mutex_setprioceiling\r
+ *                              pthread_mutexattr_getprioceiling\r
+ *                              pthread_mutexattr_getprotocol     +\r
+ *                              pthread_mutexattr_setprioceiling\r
+ *                              pthread_mutexattr_setprotocol     +\r
+ *\r
+ * _POSIX_THREAD_PROCESS_SHARED (== -1)\r
+ *                      If set, you can create mutexes and condition\r
+ *                      variables that can be shared with another\r
+ *                      process.If set, indicates the availability\r
+ *                      of:\r
+ *                              pthread_mutexattr_getpshared\r
+ *                              pthread_mutexattr_setpshared\r
+ *                              pthread_condattr_getpshared\r
+ *                              pthread_condattr_setpshared\r
+ *\r
+ * _POSIX_THREAD_SAFE_FUNCTIONS (== 200112L)\r
+ *                      If == 200112L you can use the special *_r library\r
+ *                      functions that provide thread-safe behaviour\r
+ *\r
+ * _POSIX_READER_WRITER_LOCKS (== 200112L)\r
+ *                      If == 200112L, you can use read/write locks\r
+ *\r
+ * _POSIX_SPIN_LOCKS (== 200112L)\r
+ *                      If == 200112L, you can use spin locks\r
+ *\r
+ * _POSIX_BARRIERS (== 200112L)\r
+ *                      If == 200112L, you can use barriers\r
+ *\r
+ *      + These functions provide both 'inherit' and/or\r
+ *        'protect' protocol, based upon these macro\r
+ *        settings.\r
+ *\r
+ * -------------------------------------------------------------\r
+ */\r
+\r
+/*\r
+ * POSIX Options\r
+ */\r
+#undef _POSIX_THREADS\r
+#define _POSIX_THREADS 200112L\r
+\r
+#undef _POSIX_READER_WRITER_LOCKS\r
+#define _POSIX_READER_WRITER_LOCKS 200112L\r
+\r
+#undef _POSIX_SPIN_LOCKS\r
+#define _POSIX_SPIN_LOCKS 200112L\r
+\r
+#undef _POSIX_BARRIERS\r
+#define _POSIX_BARRIERS 200112L\r
+\r
+#undef _POSIX_THREAD_SAFE_FUNCTIONS\r
+#define _POSIX_THREAD_SAFE_FUNCTIONS 200112L\r
+\r
+#undef _POSIX_THREAD_ATTR_STACKSIZE\r
+#define _POSIX_THREAD_ATTR_STACKSIZE 200112L\r
+\r
+/*\r
+ * The following options are not supported\r
+ */\r
+#undef _POSIX_THREAD_ATTR_STACKADDR\r
+#define _POSIX_THREAD_ATTR_STACKADDR -1\r
+\r
+#undef _POSIX_THREAD_PRIO_INHERIT\r
+#define _POSIX_THREAD_PRIO_INHERIT -1\r
+\r
+#undef _POSIX_THREAD_PRIO_PROTECT\r
+#define _POSIX_THREAD_PRIO_PROTECT -1\r
+\r
+/* TPS is not fully supported.  */\r
+#undef _POSIX_THREAD_PRIORITY_SCHEDULING\r
+#define _POSIX_THREAD_PRIORITY_SCHEDULING -1\r
+\r
+#undef _POSIX_THREAD_PROCESS_SHARED\r
+#define _POSIX_THREAD_PROCESS_SHARED -1\r
+\r
+\r
+/*\r
+ * POSIX 1003.1-2001 Limits\r
+ * ===========================\r
+ *\r
+ * These limits are normally set in <limits.h>, which is not provided with\r
+ * pthreads-win32.\r
+ *\r
+ * PTHREAD_DESTRUCTOR_ITERATIONS\r
+ *                      Maximum number of attempts to destroy\r
+ *                      a thread's thread-specific data on\r
+ *                      termination (must be at least 4)\r
+ *\r
+ * PTHREAD_KEYS_MAX\r
+ *                      Maximum number of thread-specific data keys\r
+ *                      available per process (must be at least 128)\r
+ *\r
+ * PTHREAD_STACK_MIN\r
+ *                      Minimum supported stack size for a thread\r
+ *\r
+ * PTHREAD_THREADS_MAX\r
+ *                      Maximum number of threads supported per\r
+ *                      process (must be at least 64).\r
+ *\r
+ * SEM_NSEMS_MAX\r
+ *                      The maximum number of semaphores a process can have.\r
+ *                      (must be at least 256)\r
+ *\r
+ * SEM_VALUE_MAX\r
+ *                      The maximum value a semaphore can have.\r
+ *                      (must be at least 32767)\r
+ *\r
+ */\r
+#undef _POSIX_THREAD_DESTRUCTOR_ITERATIONS\r
+#define _POSIX_THREAD_DESTRUCTOR_ITERATIONS     4\r
+\r
+#undef PTHREAD_DESTRUCTOR_ITERATIONS\r
+#define PTHREAD_DESTRUCTOR_ITERATIONS           _POSIX_THREAD_DESTRUCTOR_ITERATIONS\r
+\r
+#undef _POSIX_THREAD_KEYS_MAX\r
+#define _POSIX_THREAD_KEYS_MAX                  128\r
+\r
+#undef PTHREAD_KEYS_MAX\r
+#define PTHREAD_KEYS_MAX                        _POSIX_THREAD_KEYS_MAX\r
+\r
+#undef PTHREAD_STACK_MIN\r
+#define PTHREAD_STACK_MIN                       0\r
+\r
+#undef _POSIX_THREAD_THREADS_MAX\r
+#define _POSIX_THREAD_THREADS_MAX               64\r
+\r
+  /* Arbitrary value */\r
+#undef PTHREAD_THREADS_MAX\r
+#define PTHREAD_THREADS_MAX                     2019\r
+\r
+#undef _POSIX_SEM_NSEMS_MAX\r
+#define _POSIX_SEM_NSEMS_MAX                    256\r
+\r
+  /* Arbitrary value */\r
+#undef SEM_NSEMS_MAX\r
+#define SEM_NSEMS_MAX                           1024\r
+\r
+#undef _POSIX_SEM_VALUE_MAX\r
+#define _POSIX_SEM_VALUE_MAX                    32767\r
+\r
+#undef SEM_VALUE_MAX\r
+#define SEM_VALUE_MAX                           INT_MAX\r
+\r
+\r
+#if __GNUC__ && ! defined (__declspec)\r
+# error Please upgrade your GNU compiler to one that supports __declspec.\r
+#endif\r
+\r
+/*\r
+ * When building the DLL code, you should define PTW32_BUILD so that\r
+ * the variables/functions are exported correctly. When using the DLL,\r
+ * do NOT define PTW32_BUILD, and then the variables/functions will\r
+ * be imported correctly.\r
+ */\r
+#ifndef PTW32_STATIC_LIB\r
+#  ifdef PTW32_BUILD\r
+#    define PTW32_DLLPORT __declspec (dllexport)\r
+#  else\r
+#    define PTW32_DLLPORT __declspec (dllimport)\r
+#  endif\r
+#else\r
+#  define PTW32_DLLPORT\r
+#endif\r
+\r
+/*\r
+ * The Open Watcom C/C++ compiler uses a non-standard calling convention\r
+ * that passes function args in registers unless __cdecl is explicitly specified\r
+ * in exposed function prototypes.\r
+ *\r
+ * We force all calls to cdecl even though this could slow Watcom code down\r
+ * slightly. If you know that the Watcom compiler will be used to build both\r
+ * the DLL and application, then you can probably define this as a null string.\r
+ * Remember that pthread.h (this file) is used for both the DLL and application builds.\r
+ */\r
+#define PTW32_CDECL __cdecl\r
+\r
+#if defined(_UWIN) && PTW32_LEVEL >= PTW32_LEVEL_MAX\r
+#   include     <sys/types.h>\r
+#else\r
+/*\r
+ * Generic handle type - intended to extend uniqueness beyond\r
+ * that available with a simple pointer. It should scale for either\r
+ * IA-32 or IA-64.\r
+ */\r
+typedef struct {\r
+    void * p;                   /* Pointer to actual object */\r
+    unsigned int x;             /* Extra information - reuse count etc */\r
+} ptw32_handle_t;\r
+\r
+typedef ptw32_handle_t pthread_t;\r
+typedef struct pthread_attr_t_ * pthread_attr_t;\r
+typedef struct pthread_once_t_ pthread_once_t;\r
+typedef struct pthread_key_t_ * pthread_key_t;\r
+typedef struct pthread_mutex_t_ * pthread_mutex_t;\r
+typedef struct pthread_mutexattr_t_ * pthread_mutexattr_t;\r
+typedef struct pthread_cond_t_ * pthread_cond_t;\r
+typedef struct pthread_condattr_t_ * pthread_condattr_t;\r
+#endif\r
+typedef struct pthread_rwlock_t_ * pthread_rwlock_t;\r
+typedef struct pthread_rwlockattr_t_ * pthread_rwlockattr_t;\r
+typedef struct pthread_spinlock_t_ * pthread_spinlock_t;\r
+typedef struct pthread_barrier_t_ * pthread_barrier_t;\r
+typedef struct pthread_barrierattr_t_ * pthread_barrierattr_t;\r
+\r
+/*\r
+ * ====================\r
+ * ====================\r
+ * POSIX Threads\r
+ * ====================\r
+ * ====================\r
+ */\r
+\r
+enum {\r
+/*\r
+ * pthread_attr_{get,set}detachstate\r
+ */\r
+  PTHREAD_CREATE_JOINABLE       = 0,  /* Default */\r
+  PTHREAD_CREATE_DETACHED       = 1,\r
+\r
+/*\r
+ * pthread_attr_{get,set}inheritsched\r
+ */\r
+  PTHREAD_INHERIT_SCHED         = 0,\r
+  PTHREAD_EXPLICIT_SCHED        = 1,  /* Default */\r
+\r
+/*\r
+ * pthread_{get,set}scope\r
+ */\r
+  PTHREAD_SCOPE_PROCESS         = 0,\r
+  PTHREAD_SCOPE_SYSTEM          = 1,  /* Default */\r
+\r
+/*\r
+ * pthread_setcancelstate paramters\r
+ */\r
+  PTHREAD_CANCEL_ENABLE         = 0,  /* Default */\r
+  PTHREAD_CANCEL_DISABLE        = 1,\r
+\r
+/*\r
+ * pthread_setcanceltype parameters\r
+ */\r
+  PTHREAD_CANCEL_ASYNCHRONOUS   = 0,\r
+  PTHREAD_CANCEL_DEFERRED       = 1,  /* Default */\r
+\r
+/*\r
+ * pthread_mutexattr_{get,set}pshared\r
+ * pthread_condattr_{get,set}pshared\r
+ */\r
+  PTHREAD_PROCESS_PRIVATE       = 0,\r
+  PTHREAD_PROCESS_SHARED        = 1,\r
+\r
+/*\r
+ * pthread_barrier_wait\r
+ */\r
+  PTHREAD_BARRIER_SERIAL_THREAD = -1\r
+};\r
+\r
+/*\r
+ * ====================\r
+ * ====================\r
+ * Cancelation\r
+ * ====================\r
+ * ====================\r
+ */\r
+#define PTHREAD_CANCELED       ((void *) -1)\r
+\r
+\r
+/*\r
+ * ====================\r
+ * ====================\r
+ * Once Key\r
+ * ====================\r
+ * ====================\r
+ */\r
+#define PTHREAD_ONCE_INIT       { PTW32_FALSE, 0, 0, 0}\r
+\r
+struct pthread_once_t_\r
+{\r
+  int          done;        /* indicates if user function has been executed */\r
+  void *       lock;\r
+  int          reserved1;\r
+  int          reserved2;\r
+};\r
+\r
+\r
+/*\r
+ * ====================\r
+ * ====================\r
+ * Object initialisers\r
+ * ====================\r
+ * ====================\r
+ */\r
+#define PTHREAD_MUTEX_INITIALIZER ((pthread_mutex_t) -1)\r
+#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER ((pthread_mutex_t) -2)\r
+#define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER ((pthread_mutex_t) -3)\r
+\r
+/*\r
+ * Compatibility with LinuxThreads\r
+ */\r
+#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP PTHREAD_RECURSIVE_MUTEX_INITIALIZER\r
+#define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP PTHREAD_ERRORCHECK_MUTEX_INITIALIZER\r
+\r
+#define PTHREAD_COND_INITIALIZER ((pthread_cond_t) -1)\r
+\r
+#define PTHREAD_RWLOCK_INITIALIZER ((pthread_rwlock_t) -1)\r
+\r
+#define PTHREAD_SPINLOCK_INITIALIZER ((pthread_spinlock_t) -1)\r
+\r
+\r
+/*\r
+ * Mutex types.\r
+ */\r
+enum\r
+{\r
+  /* Compatibility with LinuxThreads */\r
+  PTHREAD_MUTEX_FAST_NP,\r
+  PTHREAD_MUTEX_RECURSIVE_NP,\r
+  PTHREAD_MUTEX_ERRORCHECK_NP,\r
+  PTHREAD_MUTEX_TIMED_NP = PTHREAD_MUTEX_FAST_NP,\r
+  PTHREAD_MUTEX_ADAPTIVE_NP = PTHREAD_MUTEX_FAST_NP,\r
+  /* For compatibility with POSIX */\r
+  PTHREAD_MUTEX_NORMAL = PTHREAD_MUTEX_FAST_NP,\r
+  PTHREAD_MUTEX_RECURSIVE = PTHREAD_MUTEX_RECURSIVE_NP,\r
+  PTHREAD_MUTEX_ERRORCHECK = PTHREAD_MUTEX_ERRORCHECK_NP,\r
+  PTHREAD_MUTEX_DEFAULT = PTHREAD_MUTEX_NORMAL\r
+};\r
+\r
+\r
+typedef struct ptw32_cleanup_t ptw32_cleanup_t;\r
+\r
+#if defined(_MSC_VER)\r
+/* Disable MSVC 'anachronism used' warning */\r
+#pragma warning( disable : 4229 )\r
+#endif\r
+\r
+typedef void (* PTW32_CDECL ptw32_cleanup_callback_t)(void *);\r
+\r
+#if defined(_MSC_VER)\r
+#pragma warning( default : 4229 )\r
+#endif\r
+\r
+struct ptw32_cleanup_t\r
+{\r
+  ptw32_cleanup_callback_t routine;\r
+  void *arg;\r
+  struct ptw32_cleanup_t *prev;\r
+};\r
+\r
+#ifdef __CLEANUP_SEH\r
+        /*\r
+         * WIN32 SEH version of cancel cleanup.\r
+         */\r
+\r
+#define pthread_cleanup_push( _rout, _arg ) \\r
+        { \\r
+            ptw32_cleanup_t     _cleanup; \\r
+            \\r
+        _cleanup.routine        = (ptw32_cleanup_callback_t)(_rout); \\r
+            _cleanup.arg        = (_arg); \\r
+            __try \\r
+              { \\r
+\r
+#define pthread_cleanup_pop( _execute ) \\r
+              } \\r
+            __finally \\r
+                { \\r
+                    if( _execute || AbnormalTermination()) \\r
+                      { \\r
+                          (*(_cleanup.routine))( _cleanup.arg ); \\r
+                      } \\r
+                } \\r
+        }\r
+\r
+#else /* __CLEANUP_SEH */\r
+\r
+#ifdef __CLEANUP_C\r
+\r
+        /*\r
+         * C implementation of PThreads cancel cleanup\r
+         */\r
+\r
+#define pthread_cleanup_push( _rout, _arg ) \\r
+        { \\r
+            ptw32_cleanup_t     _cleanup; \\r
+            \\r
+            ptw32_push_cleanup( &_cleanup, (ptw32_cleanup_callback_t) (_rout), (_arg) ); \\r
+\r
+#define pthread_cleanup_pop( _execute ) \\r
+            (void) ptw32_pop_cleanup( _execute ); \\r
+        }\r
+\r
+#else /* __CLEANUP_C */\r
+\r
+#ifdef __CLEANUP_CXX\r
+\r
+        /*\r
+         * C++ version of cancel cleanup.\r
+         * - John E. Bossom.\r
+         */\r
+\r
+        class PThreadCleanup {\r
+          /*\r
+           * PThreadCleanup\r
+           *\r
+           * Purpose\r
+           *      This class is a C++ helper class that is\r
+           *      used to implement pthread_cleanup_push/\r
+           *      pthread_cleanup_pop.\r
+           *      The destructor of this class automatically\r
+           *      pops the pushed cleanup routine regardless\r
+           *      of how the code exits the scope\r
+           *      (i.e. such as by an exception)\r
+           */\r
+      ptw32_cleanup_callback_t cleanUpRout;\r
+          void    *       obj;\r
+          int             executeIt;\r
+\r
+        public:\r
+          PThreadCleanup() :\r
+            cleanUpRout( 0 ),\r
+            obj( 0 ),\r
+            executeIt( 0 )\r
+            /*\r
+             * No cleanup performed\r
+             */\r
+            {\r
+            }\r
+\r
+          PThreadCleanup(\r
+             ptw32_cleanup_callback_t routine,\r
+                         void    *       arg ) :\r
+            cleanUpRout( routine ),\r
+            obj( arg ),\r
+            executeIt( 1 )\r
+            /*\r
+             * Registers a cleanup routine for 'arg'\r
+             */\r
+            {\r
+            }\r
+\r
+          ~PThreadCleanup()\r
+            {\r
+              if ( executeIt && ((void *) cleanUpRout != (void *) 0) )\r
+                {\r
+                  (void) (*cleanUpRout)( obj );\r
+                }\r
+            }\r
+\r
+          void execute( int exec )\r
+            {\r
+              executeIt = exec;\r
+            }\r
+        };\r
+\r
+        /*\r
+         * C++ implementation of PThreads cancel cleanup;\r
+         * This implementation takes advantage of a helper\r
+         * class who's destructor automatically calls the\r
+         * cleanup routine if we exit our scope weirdly\r
+         */\r
+#define pthread_cleanup_push( _rout, _arg ) \\r
+        { \\r
+            PThreadCleanup  cleanup((ptw32_cleanup_callback_t)(_rout), \\r
+                                    (void *) (_arg) );\r
+\r
+#define pthread_cleanup_pop( _execute ) \\r
+            cleanup.execute( _execute ); \\r
+        }\r
+\r
+#else\r
+\r
+#error ERROR [__FILE__, line __LINE__]: Cleanup type undefined.\r
+\r
+#endif /* __CLEANUP_CXX */\r
+\r
+#endif /* __CLEANUP_C */\r
+\r
+#endif /* __CLEANUP_SEH */\r
+\r
+/*\r
+ * ===============\r
+ * ===============\r
+ * Methods\r
+ * ===============\r
+ * ===============\r
+ */\r
+\r
+/*\r
+ * PThread Attribute Functions\r
+ */\r
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_init (pthread_attr_t * attr);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_destroy (pthread_attr_t * attr);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_getdetachstate (const pthread_attr_t * attr,\r
+                                         int *detachstate);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_getstackaddr (const pthread_attr_t * attr,\r
+                                       void **stackaddr);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_getstacksize (const pthread_attr_t * attr,\r
+                                       size_t * stacksize);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_setdetachstate (pthread_attr_t * attr,\r
+                                         int detachstate);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_setstackaddr (pthread_attr_t * attr,\r
+                                       void *stackaddr);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_setstacksize (pthread_attr_t * attr,\r
+                                       size_t stacksize);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_getschedparam (const pthread_attr_t *attr,\r
+                                        struct sched_param *param);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_setschedparam (pthread_attr_t *attr,\r
+                                        const struct sched_param *param);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_setschedpolicy (pthread_attr_t *,\r
+                                         int);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_getschedpolicy (pthread_attr_t *,\r
+                                         int *);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_setinheritsched(pthread_attr_t * attr,\r
+                                         int inheritsched);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_getinheritsched(pthread_attr_t * attr,\r
+                                         int * inheritsched);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_setscope (pthread_attr_t *,\r
+                                   int);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_getscope (const pthread_attr_t *,\r
+                                   int *);\r
+\r
+/*\r
+ * PThread Functions\r
+ */\r
+PTW32_DLLPORT int PTW32_CDECL pthread_create (pthread_t * tid,\r
+                            const pthread_attr_t * attr,\r
+                            void *(*start) (void *),\r
+                            void *arg);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_detach (pthread_t tid);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_equal (pthread_t t1,\r
+                           pthread_t t2);\r
+\r
+PTW32_DLLPORT void PTW32_CDECL pthread_exit (void *value_ptr);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_join (pthread_t thread,\r
+                          void **value_ptr);\r
+\r
+PTW32_DLLPORT pthread_t PTW32_CDECL pthread_self (void);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_cancel (pthread_t thread);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_setcancelstate (int state,\r
+                                    int *oldstate);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_setcanceltype (int type,\r
+                                   int *oldtype);\r
+\r
+PTW32_DLLPORT void PTW32_CDECL pthread_testcancel (void);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_once (pthread_once_t * once_control,\r
+                          void (*init_routine) (void));\r
+\r
+#if PTW32_LEVEL >= PTW32_LEVEL_MAX\r
+PTW32_DLLPORT ptw32_cleanup_t * PTW32_CDECL ptw32_pop_cleanup (int execute);\r
+\r
+PTW32_DLLPORT void PTW32_CDECL ptw32_push_cleanup (ptw32_cleanup_t * cleanup,\r
+                                 void (*routine) (void *),\r
+                                 void *arg);\r
+#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */\r
+\r
+/*\r
+ * Thread Specific Data Functions\r
+ */\r
+PTW32_DLLPORT int PTW32_CDECL pthread_key_create (pthread_key_t * key,\r
+                                void (*destructor) (void *));\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_key_delete (pthread_key_t key);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_setspecific (pthread_key_t key,\r
+                                 const void *value);\r
+\r
+PTW32_DLLPORT void * PTW32_CDECL pthread_getspecific (pthread_key_t key);\r
+\r
+\r
+/*\r
+ * Mutex Attribute Functions\r
+ */\r
+PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_init (pthread_mutexattr_t * attr);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_destroy (pthread_mutexattr_t * attr);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_getpshared (const pthread_mutexattr_t\r
+                                          * attr,\r
+                                          int *pshared);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_setpshared (pthread_mutexattr_t * attr,\r
+                                          int pshared);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_settype (pthread_mutexattr_t * attr, int kind);\r
+PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_gettype (pthread_mutexattr_t * attr, int *kind);\r
+\r
+/*\r
+ * Barrier Attribute Functions\r
+ */\r
+PTW32_DLLPORT int PTW32_CDECL pthread_barrierattr_init (pthread_barrierattr_t * attr);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_barrierattr_destroy (pthread_barrierattr_t * attr);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_barrierattr_getpshared (const pthread_barrierattr_t\r
+                                            * attr,\r
+                                            int *pshared);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_barrierattr_setpshared (pthread_barrierattr_t * attr,\r
+                                            int pshared);\r
+\r
+/*\r
+ * Mutex Functions\r
+ */\r
+PTW32_DLLPORT int PTW32_CDECL pthread_mutex_init (pthread_mutex_t * mutex,\r
+                                const pthread_mutexattr_t * attr);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_mutex_destroy (pthread_mutex_t * mutex);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_mutex_lock (pthread_mutex_t * mutex);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_mutex_timedlock(pthread_mutex_t *mutex,\r
+                                    const struct timespec *abstime);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_mutex_trylock (pthread_mutex_t * mutex);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_mutex_unlock (pthread_mutex_t * mutex);\r
+\r
+/*\r
+ * Spinlock Functions\r
+ */\r
+PTW32_DLLPORT int PTW32_CDECL pthread_spin_init (pthread_spinlock_t * lock, int pshared);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_spin_destroy (pthread_spinlock_t * lock);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_spin_lock (pthread_spinlock_t * lock);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_spin_trylock (pthread_spinlock_t * lock);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_spin_unlock (pthread_spinlock_t * lock);\r
+\r
+/*\r
+ * Barrier Functions\r
+ */\r
+PTW32_DLLPORT int PTW32_CDECL pthread_barrier_init (pthread_barrier_t * barrier,\r
+                                  const pthread_barrierattr_t * attr,\r
+                                  unsigned int count);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_barrier_destroy (pthread_barrier_t * barrier);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_barrier_wait (pthread_barrier_t * barrier);\r
+\r
+/*\r
+ * Condition Variable Attribute Functions\r
+ */\r
+PTW32_DLLPORT int PTW32_CDECL pthread_condattr_init (pthread_condattr_t * attr);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_condattr_destroy (pthread_condattr_t * attr);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_condattr_getpshared (const pthread_condattr_t * attr,\r
+                                         int *pshared);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_condattr_setpshared (pthread_condattr_t * attr,\r
+                                         int pshared);\r
+\r
+/*\r
+ * Condition Variable Functions\r
+ */\r
+PTW32_DLLPORT int PTW32_CDECL pthread_cond_init (pthread_cond_t * cond,\r
+                               const pthread_condattr_t * attr);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_cond_destroy (pthread_cond_t * cond);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_cond_wait (pthread_cond_t * cond,\r
+                               pthread_mutex_t * mutex);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_cond_timedwait (pthread_cond_t * cond,\r
+                                    pthread_mutex_t * mutex,\r
+                                    const struct timespec *abstime);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_cond_signal (pthread_cond_t * cond);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_cond_broadcast (pthread_cond_t * cond);\r
+\r
+/*\r
+ * Scheduling\r
+ */\r
+PTW32_DLLPORT int PTW32_CDECL pthread_setschedparam (pthread_t thread,\r
+                                   int policy,\r
+                                   const struct sched_param *param);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_getschedparam (pthread_t thread,\r
+                                   int *policy,\r
+                                   struct sched_param *param);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_setconcurrency (int);\r
\r
+PTW32_DLLPORT int PTW32_CDECL pthread_getconcurrency (void);\r
+\r
+/*\r
+ * Read-Write Lock Functions\r
+ */\r
+PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_init(pthread_rwlock_t *lock,\r
+                                const pthread_rwlockattr_t *attr);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_destroy(pthread_rwlock_t *lock);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_tryrdlock(pthread_rwlock_t *);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_trywrlock(pthread_rwlock_t *);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_rdlock(pthread_rwlock_t *lock);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_timedrdlock(pthread_rwlock_t *lock,\r
+                                       const struct timespec *abstime);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_wrlock(pthread_rwlock_t *lock);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_timedwrlock(pthread_rwlock_t *lock,\r
+                                       const struct timespec *abstime);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_unlock(pthread_rwlock_t *lock);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_rwlockattr_init (pthread_rwlockattr_t * attr);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_rwlockattr_destroy (pthread_rwlockattr_t * attr);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_rwlockattr_getpshared (const pthread_rwlockattr_t * attr,\r
+                                           int *pshared);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_rwlockattr_setpshared (pthread_rwlockattr_t * attr,\r
+                                           int pshared);\r
+\r
+#if PTW32_LEVEL >= PTW32_LEVEL_MAX - 1\r
+\r
+/*\r
+ * Signal Functions. Should be defined in <signal.h> but MSVC and MinGW32\r
+ * already have signal.h that don't define these.\r
+ */\r
+PTW32_DLLPORT int PTW32_CDECL pthread_kill(pthread_t thread, int sig);\r
+\r
+/*\r
+ * Non-portable functions\r
+ */\r
+\r
+/*\r
+ * Compatibility with Linux.\r
+ */\r
+PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_setkind_np(pthread_mutexattr_t * attr,\r
+                                         int kind);\r
+PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_getkind_np(pthread_mutexattr_t * attr,\r
+                                         int *kind);\r
+\r
+/*\r
+ * Possibly supported by other POSIX threads implementations\r
+ */\r
+PTW32_DLLPORT int PTW32_CDECL pthread_delay_np (struct timespec * interval);\r
+PTW32_DLLPORT int PTW32_CDECL pthread_num_processors_np(void);\r
+\r
+/*\r
+ * Useful if an application wants to statically link\r
+ * the lib rather than load the DLL at run-time.\r
+ */\r
+PTW32_DLLPORT int PTW32_CDECL pthread_win32_process_attach_np(void);\r
+PTW32_DLLPORT int PTW32_CDECL pthread_win32_process_detach_np(void);\r
+PTW32_DLLPORT int PTW32_CDECL pthread_win32_thread_attach_np(void);\r
+PTW32_DLLPORT int PTW32_CDECL pthread_win32_thread_detach_np(void);\r
+\r
+/*\r
+ * Features that are auto-detected at load/run time.\r
+ */\r
+PTW32_DLLPORT int PTW32_CDECL pthread_win32_test_features_np(int);\r
+enum ptw32_features {\r
+  PTW32_SYSTEM_INTERLOCKED_COMPARE_EXCHANGE = 0x0001, /* System provides it. */\r
+  PTW32_ALERTABLE_ASYNC_CANCEL              = 0x0002  /* Can cancel blocked threads. */\r
+};\r
+\r
+/*\r
+ * Register a system time change with the library.\r
+ * Causes the library to perform various functions\r
+ * in response to the change. Should be called whenever\r
+ * the application's top level window receives a\r
+ * WM_TIMECHANGE message. It can be passed directly to\r
+ * pthread_create() as a new thread if desired.\r
+ */\r
+PTW32_DLLPORT void * PTW32_CDECL pthread_timechange_handler_np(void *);\r
+\r
+#endif /*PTW32_LEVEL >= PTW32_LEVEL_MAX - 1 */\r
+\r
+#if PTW32_LEVEL >= PTW32_LEVEL_MAX\r
+\r
+/*\r
+ * Returns the Win32 HANDLE for the POSIX thread.\r
+ */\r
+PTW32_DLLPORT HANDLE PTW32_CDECL pthread_getw32threadhandle_np(pthread_t thread);\r
+\r
+\r
+/*\r
+ * Protected Methods\r
+ *\r
+ * This function blocks until the given WIN32 handle\r
+ * is signaled or pthread_cancel had been called.\r
+ * This function allows the caller to hook into the\r
+ * PThreads cancel mechanism. It is implemented using\r
+ *\r
+ *              WaitForMultipleObjects\r
+ *\r
+ * on 'waitHandle' and a manually reset WIN32 Event\r
+ * used to implement pthread_cancel. The 'timeout'\r
+ * argument to TimedWait is simply passed to\r
+ * WaitForMultipleObjects.\r
+ */\r
+PTW32_DLLPORT int PTW32_CDECL pthreadCancelableWait (HANDLE waitHandle);\r
+PTW32_DLLPORT int PTW32_CDECL pthreadCancelableTimedWait (HANDLE waitHandle,\r
+                                        DWORD timeout);\r
+\r
+#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */\r
+\r
+/*\r
+ * Thread-Safe C Runtime Library Mappings.\r
+ */\r
+#ifndef _UWIN\r
+#  if defined(NEED_ERRNO)\r
+     PTW32_DLLPORT int * PTW32_CDECL _errno( void );\r
+#  else\r
+#    ifndef errno\r
+#      if (defined(_MT) || defined(_DLL))\r
+         __declspec(dllimport) extern int * __cdecl _errno(void);\r
+#        define errno   (*_errno())\r
+#      endif\r
+#    endif\r
+#  endif\r
+#endif\r
+\r
+/*\r
+ * WIN32 C runtime library had been made thread-safe\r
+ * without affecting the user interface. Provide\r
+ * mappings from the UNIX thread-safe versions to\r
+ * the standard C runtime library calls.\r
+ * Only provide function mappings for functions that\r
+ * actually exist on WIN32.\r
+ */\r
+\r
+#if !defined(__MINGW32__)\r
+#define strtok_r( _s, _sep, _lasts ) \\r
+        ( *(_lasts) = strtok( (_s), (_sep) ) )\r
+#endif /* !__MINGW32__ */\r
+\r
+#define asctime_r( _tm, _buf ) \\r
+        ( strcpy( (_buf), asctime( (_tm) ) ), \\r
+          (_buf) )\r
+\r
+#define ctime_r( _clock, _buf ) \\r
+        ( strcpy( (_buf), ctime( (_clock) ) ),  \\r
+          (_buf) )\r
+\r
+#define gmtime_r( _clock, _result ) \\r
+        ( *(_result) = *gmtime( (_clock) ), \\r
+          (_result) )\r
+\r
+#define localtime_r( _clock, _result ) \\r
+        ( *(_result) = *localtime( (_clock) ), \\r
+          (_result) )\r
+\r
+#define rand_r( _seed ) \\r
+        ( _seed == _seed? rand() : rand() )\r
+\r
+\r
+/*\r
+ * Some compiler environments don't define some things.\r
+ */\r
+#if defined(__BORLANDC__)\r
+#  define _ftime ftime\r
+#  define _timeb timeb\r
+#endif\r
+\r
+#ifdef __cplusplus\r
+\r
+/*\r
+ * Internal exceptions\r
+ */\r
+class ptw32_exception {};\r
+class ptw32_exception_cancel : public ptw32_exception {};\r
+class ptw32_exception_exit   : public ptw32_exception {};\r
+\r
+#endif\r
+\r
+#if PTW32_LEVEL >= PTW32_LEVEL_MAX\r
+\r
+/* FIXME: This is only required if the library was built using SEH */\r
+/*\r
+ * Get internal SEH tag\r
+ */\r
+PTW32_DLLPORT DWORD PTW32_CDECL ptw32_get_exception_services_code(void);\r
+\r
+#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */\r
+\r
+#ifndef PTW32_BUILD\r
+\r
+#ifdef __CLEANUP_SEH\r
+\r
+/*\r
+ * Redefine the SEH __except keyword to ensure that applications\r
+ * propagate our internal exceptions up to the library's internal handlers.\r
+ */\r
+#define __except( E ) \\r
+        __except( ( GetExceptionCode() == ptw32_get_exception_services_code() ) \\r
+                 ? EXCEPTION_CONTINUE_SEARCH : ( E ) )\r
+\r
+#endif /* __CLEANUP_SEH */\r
+\r
+#ifdef __CLEANUP_CXX\r
+\r
+/*\r
+ * Redefine the C++ catch keyword to ensure that applications\r
+ * propagate our internal exceptions up to the library's internal handlers.\r
+ */\r
+#ifdef _MSC_VER\r
+        /*\r
+         * WARNING: Replace any 'catch( ... )' with 'PtW32CatchAll'\r
+         * if you want Pthread-Win32 cancelation and pthread_exit to work.\r
+         */\r
+\r
+#ifndef PtW32NoCatchWarn\r
+\r
+#pragma message("Specify \"/DPtW32NoCatchWarn\" compiler flag to skip this message.")\r
+#pragma message("------------------------------------------------------------------")\r
+#pragma message("When compiling applications with MSVC++ and C++ exception handling:")\r
+#pragma message("  Replace any 'catch( ... )' in routines called from POSIX threads")\r
+#pragma message("  with 'PtW32CatchAll' or 'CATCHALL' if you want POSIX thread")\r
+#pragma message("  cancelation and pthread_exit to work. For example:")\r
+#pragma message("")\r
+#pragma message("    #ifdef PtW32CatchAll")\r
+#pragma message("      PtW32CatchAll")\r
+#pragma message("    #else")\r
+#pragma message("      catch(...)")\r
+#pragma message("    #endif")\r
+#pragma message("        {")\r
+#pragma message("          /* Catchall block processing */")\r
+#pragma message("        }")\r
+#pragma message("------------------------------------------------------------------")\r
+\r
+#endif\r
+\r
+#define PtW32CatchAll \\r
+        catch( ptw32_exception & ) { throw; } \\r
+        catch( ... )\r
+\r
+#else /* _MSC_VER */\r
+\r
+#define catch( E ) \\r
+        catch( ptw32_exception & ) { throw; } \\r
+        catch( E )\r
+\r
+#endif /* _MSC_VER */\r
+\r
+#endif /* __CLEANUP_CXX */\r
+\r
+#endif /* ! PTW32_BUILD */\r
+\r
+#ifdef __cplusplus\r
+}                               /* End of extern "C" */\r
+#endif                          /* __cplusplus */\r
+\r
+#ifdef PTW32__HANDLE_DEF\r
+# undef HANDLE\r
+#endif\r
+#ifdef PTW32__DWORD_DEF\r
+# undef DWORD\r
+#endif\r
+\r
+#undef PTW32_LEVEL\r
+#undef PTW32_LEVEL_MAX\r
+\r
+#endif /* ! RC_INVOKED */\r
+\r
+#endif /* PTHREAD_H */\r
diff --git a/xbmc/pvrclients/tvheadend/pthread_win32/pthreadVC2.lib b/xbmc/pvrclients/tvheadend/pthread_win32/pthreadVC2.lib
new file mode 100644 (file)
index 0000000..bc36770
Binary files /dev/null and b/xbmc/pvrclients/tvheadend/pthread_win32/pthreadVC2.lib differ
diff --git a/xbmc/pvrclients/tvheadend/pthread_win32/pthreadVC2d.lib b/xbmc/pvrclients/tvheadend/pthread_win32/pthreadVC2d.lib
new file mode 100644 (file)
index 0000000..0df71c7
Binary files /dev/null and b/xbmc/pvrclients/tvheadend/pthread_win32/pthreadVC2d.lib differ
diff --git a/xbmc/pvrclients/tvheadend/pthread_win32/sched.h b/xbmc/pvrclients/tvheadend/pthread_win32/sched.h
new file mode 100644 (file)
index 0000000..10ecb5d
--- /dev/null
@@ -0,0 +1,178 @@
+/*\r
+ * Module: sched.h\r
+ *\r
+ * Purpose:\r
+ *      Provides an implementation of POSIX realtime extensions\r
+ *      as defined in \r
+ *\r
+ *              POSIX 1003.1b-1993      (POSIX.1b)\r
+ *\r
+ * --------------------------------------------------------------------------\r
+ *\r
+ *      Pthreads-win32 - POSIX Threads Library for Win32\r
+ *      Copyright(C) 1998 John E. Bossom\r
+ *      Copyright(C) 1999,2005 Pthreads-win32 contributors\r
+ * \r
+ *      Contact Email: rpj@callisto.canberra.edu.au\r
+ * \r
+ *      The current list of contributors is contained\r
+ *      in the file CONTRIBUTORS included with the source\r
+ *      code distribution. The list can also be seen at the\r
+ *      following World Wide Web location:\r
+ *      http://sources.redhat.com/pthreads-win32/contributors.html\r
+ * \r
+ *      This library is free software; you can redistribute it and/or\r
+ *      modify it under the terms of the GNU Lesser General Public\r
+ *      License as published by the Free Software Foundation; either\r
+ *      version 2 of the License, or (at your option) any later version.\r
+ * \r
+ *      This library is distributed in the hope that it will be useful,\r
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+ *      Lesser General Public License for more details.\r
+ * \r
+ *      You should have received a copy of the GNU Lesser General Public\r
+ *      License along with this library in the file COPYING.LIB;\r
+ *      if not, write to the Free Software Foundation, Inc.,\r
+ *      59 Temple Place - Suite 330, Boston, MA 02111-1307, USA\r
+ */\r
+#ifndef _SCHED_H\r
+#define _SCHED_H\r
+\r
+#undef PTW32_LEVEL\r
+\r
+#if defined(_POSIX_SOURCE)\r
+#define PTW32_LEVEL 0\r
+/* Early POSIX */\r
+#endif\r
+\r
+#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309\r
+#undef PTW32_LEVEL\r
+#define PTW32_LEVEL 1\r
+/* Include 1b, 1c and 1d */\r
+#endif\r
+\r
+#if defined(INCLUDE_NP)\r
+#undef PTW32_LEVEL\r
+#define PTW32_LEVEL 2\r
+/* Include Non-Portable extensions */\r
+#endif\r
+\r
+#define PTW32_LEVEL_MAX 3\r
+\r
+#if !defined(PTW32_LEVEL)\r
+#define PTW32_LEVEL PTW32_LEVEL_MAX\r
+/* Include everything */\r
+#endif\r
+\r
+\r
+#if __GNUC__ && ! defined (__declspec)\r
+# error Please upgrade your GNU compiler to one that supports __declspec.\r
+#endif\r
+\r
+/*\r
+ * When building the DLL code, you should define PTW32_BUILD so that\r
+ * the variables/functions are exported correctly. When using the DLL,\r
+ * do NOT define PTW32_BUILD, and then the variables/functions will\r
+ * be imported correctly.\r
+ */\r
+#ifndef PTW32_STATIC_LIB\r
+#  ifdef PTW32_BUILD\r
+#    define PTW32_DLLPORT __declspec (dllexport)\r
+#  else\r
+#    define PTW32_DLLPORT __declspec (dllimport)\r
+#  endif\r
+#else\r
+#  define PTW32_DLLPORT\r
+#endif\r
+\r
+/*\r
+ * This is a duplicate of what is in the autoconf config.h,\r
+ * which is only used when building the pthread-win32 libraries.\r
+ */\r
+\r
+#ifndef PTW32_CONFIG_H\r
+#  if defined(WINCE)\r
+#    define NEED_ERRNO\r
+#    define NEED_SEM\r
+#  endif\r
+#  if defined(_UWIN) || defined(__MINGW32__)\r
+#    define HAVE_MODE_T\r
+#  endif\r
+#endif\r
+\r
+/*\r
+ *\r
+ */\r
+\r
+#if PTW32_LEVEL >= PTW32_LEVEL_MAX\r
+#ifdef NEED_ERRNO\r
+#include "need_errno.h"\r
+#else\r
+#include <errno.h>\r
+#endif\r
+#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */\r
+\r
+#if defined(__MINGW32__) || defined(_UWIN)\r
+#if PTW32_LEVEL >= PTW32_LEVEL_MAX\r
+/* For pid_t */\r
+#  include <sys/types.h>\r
+/* Required by Unix 98 */\r
+#  include <time.h>\r
+#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */\r
+#else\r
+typedef int pid_t;\r
+#endif\r
+\r
+/* Thread scheduling policies */\r
+\r
+enum {\r
+  SCHED_OTHER = 0,\r
+  SCHED_FIFO,\r
+  SCHED_RR,\r
+  SCHED_MIN   = SCHED_OTHER,\r
+  SCHED_MAX   = SCHED_RR\r
+};\r
+\r
+struct sched_param {\r
+  int sched_priority;\r
+};\r
+\r
+#ifdef __cplusplus\r
+extern "C"\r
+{\r
+#endif                          /* __cplusplus */\r
+\r
+PTW32_DLLPORT int __cdecl sched_yield (void);\r
+\r
+PTW32_DLLPORT int __cdecl sched_get_priority_min (int policy);\r
+\r
+PTW32_DLLPORT int __cdecl sched_get_priority_max (int policy);\r
+\r
+PTW32_DLLPORT int __cdecl sched_setscheduler (pid_t pid, int policy);\r
+\r
+PTW32_DLLPORT int __cdecl sched_getscheduler (pid_t pid);\r
+\r
+/*\r
+ * Note that this macro returns ENOTSUP rather than\r
+ * ENOSYS as might be expected. However, returning ENOSYS\r
+ * should mean that sched_get_priority_{min,max} are\r
+ * not implemented as well as sched_rr_get_interval.\r
+ * This is not the case, since we just don't support\r
+ * round-robin scheduling. Therefore I have chosen to\r
+ * return the same value as sched_setscheduler when\r
+ * SCHED_RR is passed to it.\r
+ */\r
+#define sched_rr_get_interval(_pid, _interval) \\r
+  ( errno = ENOTSUP, (int) -1 )\r
+\r
+\r
+#ifdef __cplusplus\r
+}                               /* End of extern "C" */\r
+#endif                          /* __cplusplus */\r
+\r
+#undef PTW32_LEVEL\r
+#undef PTW32_LEVEL_MAX\r
+\r
+#endif                          /* !_SCHED_H */\r
+\r
diff --git a/xbmc/pvrclients/tvheadend/pthread_win32/semaphore.h b/xbmc/pvrclients/tvheadend/pthread_win32/semaphore.h
new file mode 100644 (file)
index 0000000..ea42ce3
--- /dev/null
@@ -0,0 +1,166 @@
+/*\r
+ * Module: semaphore.h\r
+ *\r
+ * Purpose:\r
+ *     Semaphores aren't actually part of the PThreads standard.\r
+ *     They are defined by the POSIX Standard:\r
+ *\r
+ *             POSIX 1003.1b-1993      (POSIX.1b)\r
+ *\r
+ * --------------------------------------------------------------------------\r
+ *\r
+ *      Pthreads-win32 - POSIX Threads Library for Win32\r
+ *      Copyright(C) 1998 John E. Bossom\r
+ *      Copyright(C) 1999,2005 Pthreads-win32 contributors\r
+ * \r
+ *      Contact Email: rpj@callisto.canberra.edu.au\r
+ * \r
+ *      The current list of contributors is contained\r
+ *      in the file CONTRIBUTORS included with the source\r
+ *      code distribution. The list can also be seen at the\r
+ *      following World Wide Web location:\r
+ *      http://sources.redhat.com/pthreads-win32/contributors.html\r
+ * \r
+ *      This library is free software; you can redistribute it and/or\r
+ *      modify it under the terms of the GNU Lesser General Public\r
+ *      License as published by the Free Software Foundation; either\r
+ *      version 2 of the License, or (at your option) any later version.\r
+ * \r
+ *      This library is distributed in the hope that it will be useful,\r
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+ *      Lesser General Public License for more details.\r
+ * \r
+ *      You should have received a copy of the GNU Lesser General Public\r
+ *      License along with this library in the file COPYING.LIB;\r
+ *      if not, write to the Free Software Foundation, Inc.,\r
+ *      59 Temple Place - Suite 330, Boston, MA 02111-1307, USA\r
+ */\r
+#if !defined( SEMAPHORE_H )\r
+#define SEMAPHORE_H\r
+\r
+#undef PTW32_LEVEL\r
+\r
+#if defined(_POSIX_SOURCE)\r
+#define PTW32_LEVEL 0\r
+/* Early POSIX */\r
+#endif\r
+\r
+#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309\r
+#undef PTW32_LEVEL\r
+#define PTW32_LEVEL 1\r
+/* Include 1b, 1c and 1d */\r
+#endif\r
+\r
+#if defined(INCLUDE_NP)\r
+#undef PTW32_LEVEL\r
+#define PTW32_LEVEL 2\r
+/* Include Non-Portable extensions */\r
+#endif\r
+\r
+#define PTW32_LEVEL_MAX 3\r
+\r
+#if !defined(PTW32_LEVEL)\r
+#define PTW32_LEVEL PTW32_LEVEL_MAX\r
+/* Include everything */\r
+#endif\r
+\r
+#if __GNUC__ && ! defined (__declspec)\r
+# error Please upgrade your GNU compiler to one that supports __declspec.\r
+#endif\r
+\r
+/*\r
+ * When building the DLL code, you should define PTW32_BUILD so that\r
+ * the variables/functions are exported correctly. When using the DLL,\r
+ * do NOT define PTW32_BUILD, and then the variables/functions will\r
+ * be imported correctly.\r
+ */\r
+#ifndef PTW32_STATIC_LIB\r
+#  ifdef PTW32_BUILD\r
+#    define PTW32_DLLPORT __declspec (dllexport)\r
+#  else\r
+#    define PTW32_DLLPORT __declspec (dllimport)\r
+#  endif\r
+#else\r
+#  define PTW32_DLLPORT\r
+#endif\r
+\r
+/*\r
+ * This is a duplicate of what is in the autoconf config.h,\r
+ * which is only used when building the pthread-win32 libraries.\r
+ */\r
+\r
+#ifndef PTW32_CONFIG_H\r
+#  if defined(WINCE)\r
+#    define NEED_ERRNO\r
+#    define NEED_SEM\r
+#  endif\r
+#  if defined(_UWIN) || defined(__MINGW32__)\r
+#    define HAVE_MODE_T\r
+#  endif\r
+#endif\r
+\r
+/*\r
+ *\r
+ */\r
+\r
+#if PTW32_LEVEL >= PTW32_LEVEL_MAX\r
+#ifdef NEED_ERRNO\r
+#include "need_errno.h"\r
+#else\r
+#include <errno.h>\r
+#endif\r
+#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */\r
+\r
+#define _POSIX_SEMAPHORES\r
+\r
+#ifdef __cplusplus\r
+extern "C"\r
+{\r
+#endif                         /* __cplusplus */\r
+\r
+#ifndef HAVE_MODE_T\r
+typedef unsigned int mode_t;\r
+#endif\r
+\r
+\r
+typedef struct sem_t_ * sem_t;\r
+\r
+PTW32_DLLPORT int __cdecl sem_init (sem_t * sem,\r
+                           int pshared,\r
+                           unsigned int value);\r
+\r
+PTW32_DLLPORT int __cdecl sem_destroy (sem_t * sem);\r
+\r
+PTW32_DLLPORT int __cdecl sem_trywait (sem_t * sem);\r
+\r
+PTW32_DLLPORT int __cdecl sem_wait (sem_t * sem);\r
+\r
+PTW32_DLLPORT int __cdecl sem_timedwait (sem_t * sem,\r
+                                const struct timespec * abstime);\r
+\r
+PTW32_DLLPORT int __cdecl sem_post (sem_t * sem);\r
+\r
+PTW32_DLLPORT int __cdecl sem_post_multiple (sem_t * sem,\r
+                                    int count);\r
+\r
+PTW32_DLLPORT int __cdecl sem_open (const char * name,\r
+                           int oflag,\r
+                           mode_t mode,\r
+                           unsigned int value);\r
+\r
+PTW32_DLLPORT int __cdecl sem_close (sem_t * sem);\r
+\r
+PTW32_DLLPORT int __cdecl sem_unlink (const char * name);\r
+\r
+PTW32_DLLPORT int __cdecl sem_getvalue (sem_t * sem,\r
+                               int * sval);\r
+\r
+#ifdef __cplusplus\r
+}                              /* End of extern "C" */\r
+#endif                         /* __cplusplus */\r
+\r
+#undef PTW32_LEVEL\r
+#undef PTW32_LEVEL_MAX\r
+\r
+#endif                         /* !SEMAPHORE_H */\r
diff --git a/xbmc/pvrclients/tvheadend/pvrclient-tvheadend_os.h b/xbmc/pvrclients/tvheadend/pvrclient-tvheadend_os.h
new file mode 100644 (file)
index 0000000..9575847
--- /dev/null
@@ -0,0 +1,44 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef PVRCLIENT_TVHEADEND_OS_H
+#define PVRCLIENT_TVHEADEND_OS_H
+
+#if defined(_WIN32) || defined(_WIN64)
+#define __WINDOWS__
+#endif
+
+#if defined(__WINDOWS__)
+#include "windows/os_windows.h"
+#else
+#include "linux/os_posix.h"
+#endif
+
+#if !defined(TRUE)
+#define TRUE 1
+#endif
+
+#if !defined(FALSE)
+#define FALSE 0
+#endif
+
+#endif
diff --git a/xbmc/pvrclients/tvheadend/thread.cpp b/xbmc/pvrclients/tvheadend/thread.cpp
new file mode 100644 (file)
index 0000000..5187006
--- /dev/null
@@ -0,0 +1,408 @@
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+/*
+ * Most of this code is taken from thread.c in the Video Disk Recorder ('VDR')
+ */
+
+#include "thread.h"
+#include <errno.h>
+#ifndef __APPLE__
+#include <malloc.h>
+#endif
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include "StdString.h"
+
+static bool GetAbsTime(struct timespec *Abstime, int MillisecondsFromNow)
+{
+  struct timeval now;
+  if (gettimeofday(&now, NULL) == 0) {           // get current time
+     now.tv_sec  += MillisecondsFromNow / 1000;  // add full seconds
+     now.tv_usec += (MillisecondsFromNow % 1000) * 1000;  // add microseconds
+     if (now.tv_usec >= 1000000) {               // take care of an overflow
+        now.tv_sec++;
+        now.tv_usec -= 1000000;
+        }
+     Abstime->tv_sec = now.tv_sec;          // seconds
+     Abstime->tv_nsec = now.tv_usec * 1000; // nano seconds
+     return true;
+     }
+  return false;
+}
+
+// --- cCondWait -------------------------------------------------------------
+
+cCondWait::cCondWait(void)
+{
+  signaled = false;
+  pthread_mutex_init(&mutex, NULL);
+  pthread_cond_init(&cond, NULL);
+}
+
+cCondWait::~cCondWait()
+{
+  pthread_cond_broadcast(&cond); // wake up any sleepers
+  pthread_cond_destroy(&cond);
+  pthread_mutex_destroy(&mutex);
+}
+
+void cCondWait::SleepMs(int TimeoutMs)
+{
+  cCondWait w;
+  w.Wait(max(TimeoutMs, 3)); // making sure the time is >2ms to avoid a possible busy wait
+}
+
+bool cCondWait::Wait(int TimeoutMs)
+{
+  pthread_mutex_lock(&mutex);
+  if (!signaled) {
+     if (TimeoutMs) {
+        struct timespec abstime;
+        if (GetAbsTime(&abstime, TimeoutMs)) {
+           while (!signaled) {
+                 if (pthread_cond_timedwait(&cond, &mutex, &abstime) == ETIMEDOUT)
+                    break;
+                 }
+           }
+        }
+     else
+        pthread_cond_wait(&cond, &mutex);
+     }
+  bool r = signaled;
+  signaled = false;
+  pthread_mutex_unlock(&mutex);
+  return r;
+}
+
+void cCondWait::Signal(void)
+{
+  pthread_mutex_lock(&mutex);
+  signaled = true;
+  pthread_cond_broadcast(&cond);
+  pthread_mutex_unlock(&mutex);
+}
+
+// --- cCondVar --------------------------------------------------------------
+
+cCondVar::cCondVar(void)
+{
+  pthread_cond_init(&cond, 0);
+}
+
+cCondVar::~cCondVar()
+{
+  pthread_cond_broadcast(&cond); // wake up any sleepers
+  pthread_cond_destroy(&cond);
+}
+
+void cCondVar::Wait(cMutex &Mutex)
+{
+  if (Mutex.locked) {
+     int locked = Mutex.locked;
+     Mutex.locked = 0; // have to clear the locked count here, as pthread_cond_wait
+                       // does an implicit unlock of the mutex
+     pthread_cond_wait(&cond, &Mutex.mutex);
+     Mutex.locked = locked;
+     }
+}
+
+bool cCondVar::TimedWait(cMutex &Mutex, int TimeoutMs)
+{
+  bool r = true; // true = condition signaled, false = timeout
+
+  if (Mutex.locked) {
+     struct timespec abstime;
+     if (GetAbsTime(&abstime, TimeoutMs)) {
+        int locked = Mutex.locked;
+        Mutex.locked = 0; // have to clear the locked count here, as pthread_cond_timedwait
+                          // does an implicit unlock of the mutex.
+        if (pthread_cond_timedwait(&cond, &Mutex.mutex, &abstime) == ETIMEDOUT)
+           r = false;
+        Mutex.locked = locked;
+        }
+     }
+  return r;
+}
+
+void cCondVar::Broadcast(void)
+{
+  pthread_cond_broadcast(&cond);
+}
+
+// --- cMutex ----------------------------------------------------------------
+
+cMutex::cMutex(void)
+{
+  locked = 0;
+  pthread_mutexattr_t attr;
+  pthread_mutexattr_init(&attr);
+#ifndef __APPLE__
+  pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK_NP);
+#else
+  pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
+#endif
+  pthread_mutex_init(&mutex, &attr);
+}
+
+cMutex::~cMutex()
+{
+  pthread_mutex_destroy(&mutex);
+}
+
+void cMutex::Lock(void)
+{
+  pthread_mutex_lock(&mutex);
+  locked++;
+}
+
+void cMutex::Unlock(void)
+{
+ if (!--locked)
+    pthread_mutex_unlock(&mutex);
+}
+
+// --- cThread ---------------------------------------------------------------
+
+tThreadId cThread::mainThreadId = 0;
+
+cThread::cThread(const char *Description)
+{
+  active = running = false;
+#if !defined(__WINDOWS__)
+  childTid = 0;
+#endif
+  childThreadId = 0;
+  description = NULL;
+  if (Description)
+     SetDescription("%s", Description);
+}
+
+cThread::~cThread()
+{
+  Cancel(); // just in case the derived class didn't call it
+  free(description);
+}
+
+void cThread::SetPriority(int Priority)
+{
+#if !defined(__WINDOWS__)
+  if (setpriority(PRIO_PROCESS, 0, Priority) < 0)
+     XBMC->Log(LOG_ERROR, "ERROR (%s,%d): %m", __FILE__, __LINE__);
+#endif
+}
+
+void cThread::SetIOPriority(int Priority)
+{
+#if !defined(__WINDOWS__)
+#ifdef HAVE_LINUXIOPRIO
+  if (syscall(SYS_ioprio_set, 1, 0, (Priority & 0xff) | (2 << 13)) < 0) // best effort class
+     XBMC->Log(LOG_ERROR, "ERROR (%s,%d): %m", __FILE__, __LINE__);
+#endif
+#endif
+}
+
+void cThread::SetDescription(const char *Description, ...)
+{
+  free(description);
+  description = NULL;
+  if (Description)
+  {
+     va_list ap;
+     va_start(ap, Description);
+     CStdString desc;
+     desc.FormatV(Description, ap);
+     description = strdup(desc.c_str());
+     va_end(ap);
+  }
+}
+
+void *cThread::StartThread(cThread *Thread)
+{
+  Thread->childThreadId = ThreadId();
+  if (Thread->description) {
+     XBMC->Log(LOG_DEBUG, "%s thread started (pid=%d, tid=%d)", Thread->description, getpid(), Thread->childThreadId);
+#ifdef PR_SET_NAME
+     if (prctl(PR_SET_NAME, Thread->description, 0, 0, 0) < 0)
+        XBMC->Log(LOG_ERROR, "%s thread naming failed (pid=%d, tid=%d)", Thread->description, getpid(), Thread->childThreadId);
+#endif
+     }
+  Thread->Action();
+  if (Thread->description)
+     XBMC->Log(LOG_DEBUG, "%s thread ended (pid=%d, tid=%d)", Thread->description, getpid(), Thread->childThreadId);
+  Thread->running = false;
+  Thread->active = false;
+  return NULL;
+}
+
+#define THREAD_STOP_TIMEOUT  3000 // ms to wait for a thread to stop before newly starting it
+#define THREAD_STOP_SLEEP      30 // ms to sleep while waiting for a thread to stop
+
+bool cThread::Start(void)
+{
+  if (!running) {
+     if (active) {
+        // Wait until the previous incarnation of this thread has completely ended
+        // before starting it newly:
+        cTimeMs RestartTimeout;
+        while (!running && active && RestartTimeout.Elapsed() < THREAD_STOP_TIMEOUT)
+              cCondWait::SleepMs(THREAD_STOP_SLEEP);
+        }
+     if (!active) {
+        active = running = true;
+        if (pthread_create(&childTid, NULL, (void *(*) (void *))&StartThread, (void *)this) == 0) {
+           pthread_detach(childTid); // auto-reap
+           }
+        else {
+           XBMC->Log(LOG_ERROR, "ERROR (%s,%d): %m", __FILE__, __LINE__);
+           active = running = false;
+           return false;
+           }
+        }
+     }
+  return true;
+}
+
+bool cThread::Active(void)
+{
+  if (active) {
+     //
+     // Single UNIX Spec v2 says:
+     //
+     // The pthread_kill() function is used to request
+     // that a signal be delivered to the specified thread.
+     //
+     // As in kill(), if sig is zero, error checking is
+     // performed but no signal is actually sent.
+     //
+     int err;
+     if ((err = pthread_kill(childTid, 0)) != 0) {
+        if (err != ESRCH)
+           XBMC->Log(LOG_ERROR, "ERROR (%s,%d): %m", __FILE__, __LINE__);
+#if !defined(__WINDOWS__)
+        childTid = 0;
+#endif
+        active = running = false;
+        }
+     else
+        return true;
+     }
+  return false;
+}
+
+void cThread::Cancel(int WaitSeconds)
+{
+  running = false;
+  if (active && WaitSeconds > -1)
+  {
+    if (WaitSeconds > 0)
+    {
+      for (time_t t0 = time(NULL) + WaitSeconds; time(NULL) < t0; )
+      {
+        if (!Active())
+          return;
+        cCondWait::SleepMs(10);
+      }
+      XBMC->Log(LOG_ERROR, "ERROR: %s thread %d won't end (waited %d seconds) - canceling it...", description ? description : "", childThreadId, WaitSeconds);
+    }
+    pthread_cancel(childTid);
+#if !defined(__WINDOWS__)
+    childTid = 0;
+#endif
+    active = false;
+  }
+}
+
+tThreadId cThread::ThreadId(void)
+{
+#ifdef __APPLE__
+    return (int)pthread_self();
+#else
+#ifdef __WINDOWS__
+  return GetCurrentThreadId();
+#else
+  return syscall(__NR_gettid);
+#endif
+#endif
+}
+
+void cThread::SetMainThreadId(void)
+{
+  if (mainThreadId == 0)
+     mainThreadId = ThreadId();
+  else
+     XBMC->Log(LOG_ERROR, "ERROR: attempt to set main thread id to %d while it already is %d", ThreadId(), mainThreadId);
+}
+
+// --- cMutexLock ------------------------------------------------------------
+
+cMutexLock::cMutexLock(cMutex *Mutex)
+{
+  mutex = NULL;
+  locked = false;
+  Lock(Mutex);
+}
+
+cMutexLock::~cMutexLock()
+{
+  if (mutex && locked)
+    mutex->Unlock();
+}
+
+bool cMutexLock::Lock(cMutex *Mutex)
+{
+  if (Mutex && !mutex)
+  {
+    mutex = Mutex;
+    Mutex->Lock();
+    locked = true;
+    return true;
+  }
+  return false;
+}
+
+// --- cThreadLock -----------------------------------------------------------
+
+cThreadLock::cThreadLock(cThread *Thread)
+{
+  thread = NULL;
+  locked = false;
+  Lock(Thread);
+}
+
+cThreadLock::~cThreadLock()
+{
+  if (thread && locked)
+    thread->Unlock();
+}
+
+bool cThreadLock::Lock(cThread *Thread)
+{
+  if (Thread && !thread)
+  {
+  thread = Thread;
+  Thread->Lock();
+  locked = true;
+  return true;
+  }
+  return false;
+}
diff --git a/xbmc/pvrclients/tvheadend/thread.h b/xbmc/pvrclients/tvheadend/thread.h
new file mode 100644 (file)
index 0000000..960a6e9
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef __THREAD_H
+#define __THREAD_H
+
+#include <stdio.h>
+#include <sys/types.h>
+#include "tools.h"
+
+class cCondWait {
+private:
+  pthread_mutex_t mutex;
+  pthread_cond_t cond;
+  bool signaled;
+public:
+  cCondWait(void);
+  ~cCondWait();
+  static void SleepMs(int TimeoutMs);
+       ///< Creates a cCondWait object and uses it to sleep for TimeoutMs
+       ///< milliseconds, immediately giving up the calling thread's time
+       ///< slice and thus avoiding a "busy wait".
+       ///< In order to avoid a possible busy wait, TimeoutMs will be automatically
+       ///< limited to values >2.
+  bool Wait(int TimeoutMs = 0);
+       ///< Waits at most TimeoutMs milliseconds for a call to Signal(), or
+       ///< forever if TimeoutMs is 0.
+       ///< \return Returns true if Signal() has been called, false it the given
+       ///< timeout has expired.
+  void Signal(void);
+       ///< Signals a caller of Wait() that the condition it is waiting for is met.
+  };
+
+class cMutex;
+
+class cCondVar {
+private:
+  pthread_cond_t cond;
+public:
+  cCondVar(void);
+  ~cCondVar();
+  void Wait(cMutex &Mutex);
+  bool TimedWait(cMutex &Mutex, int TimeoutMs);
+  void Broadcast(void);
+  };
+
+class cMutex {
+  friend class cCondVar;
+private:
+  pthread_mutex_t mutex;
+  int locked;
+public:
+  cMutex(void);
+  ~cMutex();
+  void Lock(void);
+  void Unlock(void);
+  };
+
+typedef pid_t tThreadId;
+
+class cThread {
+  friend class cThreadLock;
+private:
+  bool active;
+  bool running;
+  pthread_t childTid;
+  tThreadId childThreadId;
+  cMutex mutex;
+  char *description;
+  static tThreadId mainThreadId;
+  static void *StartThread(cThread *Thread);
+protected:
+  void SetPriority(int Priority);
+  void SetIOPriority(int Priority);
+  void Lock(void) { mutex.Lock(); }
+  void Unlock(void) { mutex.Unlock(); }
+  virtual void Action(void) = 0;
+       ///< A derived cThread class must implement the code it wants to
+       ///< execute as a separate thread in this function. If this is
+       ///< a loop, it must check Running() repeatedly to see whether
+       ///< it's time to stop.
+  bool Running(void) { return running; }
+       ///< Returns false if a derived cThread object shall leave its Action()
+       ///< function.
+  void Cancel(int WaitSeconds = 0);
+       ///< Cancels the thread by first setting 'running' to false, so that
+       ///< the Action() loop can finish in an orderly fashion and then waiting
+       ///< up to WaitSeconds seconds for the thread to actually end. If the
+       ///< thread doesn't end by itself, it is killed.
+       ///< If WaitSeconds is -1, only 'running' is set to false and Cancel()
+       ///< returns immediately, without killing the thread.
+public:
+  cThread(const char *Description = NULL);
+       ///< Creates a new thread.
+       ///< If Description is present, a log file entry will be made when
+       ///< the thread starts and stops. The Start() function must be called
+       ///< to actually start the thread.
+  virtual ~cThread();
+#ifdef __WINDOWS__
+  void SetDescription(const char *Description, ...);
+#else
+  void SetDescription(const char *Description, ...) __attribute__ ((format (printf, 2, 3)));
+#endif
+  bool Start(void);
+       ///< Actually starts the thread.
+       ///< If the thread is already running, nothing happens.
+  bool Active(void);
+       ///< Checks whether the thread is still alive.
+  static tThreadId ThreadId(void);
+  static tThreadId IsMainThread(void) { return ThreadId() == mainThreadId; }
+  static void SetMainThreadId(void);
+  };
+
+// cMutexLock can be used to easily set a lock on mutex and make absolutely
+// sure that it will be unlocked when the block will be left. Several locks can
+// be stacked, so a function that makes many calls to another function which uses
+// cMutexLock may itself use a cMutexLock to make one longer lock instead of many
+// short ones.
+
+class cMutexLock {
+private:
+  cMutex *mutex;
+  bool locked;
+public:
+  cMutexLock(cMutex *Mutex = NULL);
+  ~cMutexLock();
+  bool Lock(cMutex *Mutex);
+  };
+
+// cThreadLock can be used to easily set a lock in a thread and make absolutely
+// sure that it will be unlocked when the block will be left. Several locks can
+// be stacked, so a function that makes many calls to another function which uses
+// cThreadLock may itself use a cThreadLock to make one longer lock instead of many
+// short ones.
+
+class cThreadLock {
+private:
+  cThread *thread;
+  bool locked;
+public:
+  cThreadLock(cThread *Thread = NULL);
+  ~cThreadLock();
+  bool Lock(cThread *Thread);
+  };
+
+#define LOCK_THREAD cThreadLock ThreadLock(this)
+
+#endif //__THREAD_H
diff --git a/xbmc/pvrclients/tvheadend/tools.cpp b/xbmc/pvrclients/tvheadend/tools.cpp
new file mode 100644 (file)
index 0000000..4467f39
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+/*
+ * Most of this code is taken from tools.c in the Video Disk Recorder ('VDR')
+ */
+
+#include "tools.h"
+
+// --- cTimeMs ---------------------------------------------------------------
+
+cTimeMs::cTimeMs(int Ms)
+{
+  Set(Ms);
+}
+
+uint64_t cTimeMs::Now(void)
+{
+#if _POSIX_TIMERS > 0 && defined(_POSIX_MONOTONIC_CLOCK)
+#define MIN_RESOLUTION 5 // ms
+  static bool initialized = false;
+  static bool monotonic = false;
+  struct timespec tp;
+  if (!initialized) {
+     // check if monotonic timer is available and provides enough accurate resolution:
+     if (clock_getres(CLOCK_MONOTONIC, &tp) == 0) {
+        long Resolution = tp.tv_nsec;
+        // require a minimum resolution:
+        if (tp.tv_sec == 0 && tp.tv_nsec <= MIN_RESOLUTION * 1000000) {
+           if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0) {
+              XBMC->Log(LOG_DEBUG, "cTimeMs: using monotonic clock (resolution is %ld ns)", Resolution);
+              monotonic = true;
+              }
+           else
+              XBMC->Log(LOG_ERROR, "cTimeMs: clock_gettime(CLOCK_MONOTONIC) failed");
+           }
+        else
+           XBMC->Log(LOG_DEBUG, "cTimeMs: not using monotonic clock - resolution is too bad (%ld s %ld ns)", tp.tv_sec, tp.tv_nsec);
+        }
+     else
+        XBMC->Log(LOG_ERROR, "cTimeMs: clock_getres(CLOCK_MONOTONIC) failed");
+     initialized = true;
+     }
+  if (monotonic) {
+     if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0)
+        return (uint64_t(tp.tv_sec)) * 1000 + tp.tv_nsec / 1000000;
+     XBMC->Log(LOG_ERROR, "cTimeMs: clock_gettime(CLOCK_MONOTONIC) failed");
+     monotonic = false;
+     // fall back to gettimeofday()
+     }
+#else
+#if !defined(__WINDOWS__)
+#  warning Posix monotonic clock not available
+#endif
+#endif
+  struct timeval t;
+  if (gettimeofday(&t, NULL) == 0)
+     return (uint64_t(t.tv_sec)) * 1000 + t.tv_usec / 1000;
+  return 0;
+}
+
+void cTimeMs::Set(int Ms)
+{
+  begin = Now() + Ms;
+}
+
+bool cTimeMs::TimedOut(void)
+{
+  return Now() >= begin;
+}
+
+uint64_t cTimeMs::Elapsed(void)
+{
+  return Now() - begin;
+}
+
diff --git a/xbmc/pvrclients/tvheadend/tools.h b/xbmc/pvrclients/tvheadend/tools.h
new file mode 100644 (file)
index 0000000..8b1e197
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef __TOOLS_H
+#define __TOOLS_H
+
+#include "pvrclient-tvheadend_os.h"
+#include "client.h"
+#include "StdString.h"
+#include <errno.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <dirent.h>
+
+#define ERRNUL(e) {errno=e;return 0;}
+#define ERRSYS(e) {errno=e;return -1;}
+
+#define SECSINDAY  86400
+
+#define KILOBYTE(n) ((n) * 1024)
+#define MEGABYTE(n) ((n) * 1024LL * 1024LL)
+
+#define MALLOC(type, size)  (type *)malloc(sizeof(type) * (size))
+
+#define DELETENULL(p) (delete (p), p = NULL)
+//
+//#define CHECK(s) { if ((s) < 0) LOG_ERROR; } // used for 'ioctl()' calls
+#define FATALERRNO (errno && errno != EAGAIN && errno != EINTR)
+
+class cTimeMs
+{
+private:
+  uint64_t begin;
+public:
+  cTimeMs(int Ms = 0);
+      ///< Creates a timer with ms resolution and an initial timeout of Ms.
+  static uint64_t Now(void);
+  void Set(int Ms = 0);
+  bool TimedOut(void);
+  uint64_t Elapsed(void);
+};
+
+
+#endif //__TOOLS_H
diff --git a/xbmc/pvrclients/tvheadend/windows/dirent.cpp b/xbmc/pvrclients/tvheadend/windows/dirent.cpp
new file mode 100644 (file)
index 0000000..90b26e7
--- /dev/null
@@ -0,0 +1,159 @@
+#include <windows.h>\r
+#include <io.h>\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <sys/types.h>\r
+#include <direct.h>\r
+#include <string.h>\r
+#include <errno.h>\r
+\r
+#include "pvrclient-tvheadend_os.h"\r
+#include <dirent.h>\r
+\r
+/**********************************************************************\r
+ * Implement dirent-style opendir/readdir/rewinddir/closedir on Win32\r
+ *\r
+ * Functions defined are opendir(), readdir(), rewinddir() and\r
+ * closedir() with the same prototypes as the normal dirent.h\r
+ * implementation.\r
+ *\r
+ * Does not implement telldir(), seekdir(), or scandir().  The dirent\r
+ * struct is compatible with Unix, except that d_ino is always 1 and\r
+ * d_off is made up as we go along.\r
+ *\r
+ * The DIR typedef is not compatible with Unix.\r
+ **********************************************************************/\r
+\r
+DIR *opendir(const char *dir)\r
+{\r
+       DIR             *dirp;\r
+       char    *filespec;\r
+       long    handle;\r
+       int             index;\r
+\r
+       filespec = (char *)malloc(strlen(dir) + 2 + 1);\r
+       strcpy(filespec, dir);\r
+       index = (int)strlen(filespec) - 1;\r
+       if (index >= 0 && (filespec[index] == '/' || \r
+          (filespec[index] == '\\' && !IsDBCSLeadByte(filespec[index-1]))))\r
+               filespec[index] = '\0';\r
+       strcat(filespec, "/*");\r
+\r
+       dirp = (DIR *) malloc(sizeof(DIR));\r
+       dirp->offset = 0;\r
+       dirp->finished = 0;\r
+\r
+       if ((handle = _findfirst(filespec, &(dirp->fileinfo))) < 0) \r
+       {\r
+               if (errno == ENOENT || errno == EINVAL) \r
+                       dirp->finished = 1;\r
+               else \r
+               {\r
+                       free(dirp);\r
+                       free(filespec);\r
+                       return NULL;\r
+               }\r
+       }\r
+       dirp->dirname = strdup(dir);\r
+       dirp->handle = handle;\r
+       free(filespec);\r
+\r
+       return dirp;\r
+}\r
+\r
+int closedir(DIR *dp)\r
+{\r
+       int iret = -1;\r
+       if (!dp)\r
+               return iret;\r
+       iret = _findclose(dp->handle);\r
+       if (iret == 0 && dp->dirname)\r
+               free(dp->dirname);\r
+       if (iret == 0 && dp)\r
+               free(dp);\r
+\r
+       return iret;\r
+}\r
+\r
+struct dirent *readdir(DIR *dp)\r
+{\r
+       if (!dp || dp->finished)\r
+               return NULL;\r
+\r
+       if (dp->offset != 0) \r
+       {\r
+               if (_findnext(dp->handle, &(dp->fileinfo)) < 0) \r
+               {\r
+                       dp->finished = 1;\r
+                       return NULL;\r
+               }\r
+       }\r
+       dp->offset++;\r
+\r
+       strcpy(dp->dent.d_name, dp->fileinfo.name);/*, _MAX_FNAME+1);*/\r
+       dp->dent.d_ino = 1;\r
+       dp->dent.d_reclen = (unsigned short)strlen(dp->dent.d_name);\r
+       dp->dent.d_off = dp->offset;\r
+\r
+       return &(dp->dent);\r
+}\r
+\r
+int readdir_r(DIR *dp, struct dirent *entry, struct dirent **result)\r
+{\r
+       if (!dp || dp->finished) \r
+       {\r
+               *result = NULL;\r
+               return -1;\r
+       }\r
+\r
+       if (dp->offset != 0) \r
+       {\r
+               if (_findnext(dp->handle, &(dp->fileinfo)) < 0) \r
+               {\r
+                       dp->finished = 1;\r
+                       *result = NULL;\r
+                       return -1;\r
+               }\r
+       }\r
+       dp->offset++;\r
+\r
+       strcpy(dp->dent.d_name, dp->fileinfo.name);/*, _MAX_FNAME+1);*/\r
+       dp->dent.d_ino = 1;\r
+       dp->dent.d_reclen = (unsigned short)strlen(dp->dent.d_name);\r
+       dp->dent.d_off = dp->offset;\r
+\r
+       memcpy(entry, &dp->dent, sizeof(*entry));\r
+\r
+       *result = &dp->dent;\r
+\r
+       return 0;\r
+}\r
+\r
+int rewinddir(DIR *dp)\r
+{\r
+       char    *filespec;\r
+       long    handle;\r
+       int             index;\r
+\r
+       _findclose(dp->handle);\r
+\r
+       dp->offset = 0;\r
+       dp->finished = 0;\r
+\r
+       filespec = (char *)malloc(strlen(dp->dirname) + 2 + 1);\r
+       strcpy(filespec, dp->dirname);\r
+       index = (int)(strlen(filespec) - 1);\r
+       if (index >= 0 && (filespec[index] == '/' || filespec[index] == '\\'))\r
+               filespec[index] = '\0';\r
+       strcat(filespec, "/*");\r
+\r
+       if ((handle = _findfirst(filespec, &(dp->fileinfo))) < 0) \r
+       {\r
+               if (errno == ENOENT || errno == EINVAL)\r
+                       dp->finished = 1;\r
+       }\r
+       dp->handle = handle;\r
+       free(filespec);\r
+\r
+       return 0;\r
+}\r
diff --git a/xbmc/pvrclients/tvheadend/windows/dirent.h b/xbmc/pvrclients/tvheadend/windows/dirent.h
new file mode 100644 (file)
index 0000000..4b12733
--- /dev/null
@@ -0,0 +1,103 @@
+/*****************************************************************************\r
+ * dirent.h - dirent API for Microsoft Visual Studio\r
+ *\r
+ * Copyright (C) 2006 Toni Ronkko\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person obtaining\r
+ * a copy of this software and associated documentation files (the\r
+ * ``Software''), to deal in the Software without restriction, including\r
+ * without limitation the rights to use, copy, modify, merge, publish,\r
+ * distribute, sublicense, and/or sell copies of the Software, and to\r
+ * permit persons to whom the Software is furnished to do so, subject to\r
+ * the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be included\r
+ * in all copies or substantial portions of the Software.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS\r
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\r
+ * IN NO EVENT SHALL TONI RONKKO BE LIABLE FOR ANY CLAIM, DAMAGES OR\r
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\r
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\r
+ * OTHER DEALINGS IN THE SOFTWARE.\r
+ *\r
+ * Dec 15, 2009, John Cunningham\r
+ * Added rewinddir member function\r
+ *\r
+ * Jan 18, 2008, Toni Ronkko\r
+ * Using FindFirstFileA and WIN32_FIND_DATAA to avoid converting string\r
+ * between multi-byte and unicode representations.  This makes the\r
+ * code simpler and also allows the code to be compiled under MingW.  Thanks\r
+ * to Azriel Fasten for the suggestion.\r
+ *\r
+ * Mar 4, 2007, Toni Ronkko\r
+ * Bug fix: due to the strncpy_s() function this file only compiled in\r
+ * Visual Studio 2005.  Using the new string functions only when the\r
+ * compiler version allows.\r
+ *\r
+ * Nov  2, 2006, Toni Ronkko\r
+ * Major update: removed support for Watcom C, MS-DOS and Turbo C to\r
+ * simplify the file, updated the code to compile cleanly on Visual\r
+ * Studio 2005 with both unicode and multi-byte character strings,\r
+ * removed rewinddir() as it had a bug.\r
+ *\r
+ * Aug 20, 2006, Toni Ronkko\r
+ * Removed all remarks about MSVC 1.0, which is antiqued now.  Simplified\r
+ * comments by removing SGML tags.\r
+ *\r
+ * May 14 2002, Toni Ronkko\r
+ * Embedded the function definitions directly to the header so that no\r
+ * source modules need to be included in the Visual Studio project.  Removed\r
+ * all the dependencies to other projects so that this very header can be\r
+ * used independently.\r
+ *\r
+ * May 28 1998, Toni Ronkko\r
+ * First version.\r
+ *****************************************************************************/\r
+#ifndef DIRENT_H\r
+#define DIRENT_H\r
+\r
+#include <windows.h>\r
+#include <string.h>\r
+#include <assert.h>\r
+\r
+/* struct dirent - same as Unix dirent.h */\r
+struct dirent \r
+{\r
+       long                            d_ino;                                          /* inode number (always 1 in WIN32) */\r
+       off_t                           d_off;                                  /* offset to this dirent */\r
+       unsigned short          d_reclen;               /* length of d_name */\r
+       char                            d_name[_MAX_FNAME + 1]; /* filename (null terminated) */\r
+       /*unsigned char         d_type;*/               /*type of file*/\r
+};\r
+\r
+\r
+/* def struct DIR - different from Unix DIR */\r
+typedef struct\r
+{\r
+       long                            handle;                                         /* _findfirst/_findnext handle */\r
+       short                           offset;                                         /* offset into directory */\r
+       short                           finished;                                               /* 1 if there are not more files */\r
+       struct _finddata_t      fileinfo;               /* from _findfirst/_findnext */\r
+       char                            *dirname;                                               /* the dir we are reading */\r
+       struct dirent           dent;                                   /* the dirent to return */\r
+} DIR;\r
+\r
+/* Function prototypes */\r
+DIR *opendir(const char *);\r
+struct dirent *readdir(DIR *);\r
+int readdir_r(DIR *, struct dirent *, struct dirent **);\r
+int closedir(DIR *);\r
+int rewinddir(DIR *);\r
+\r
+\r
+/* Use the new safe string functions introduced in Visual Studio 2005 */\r
+#if defined(_MSC_VER) && _MSC_VER >= 1400\r
+# define STRNCPY(dest,src,size) strncpy_s((dest),(size),(src),_TRUNCATE)\r
+#else\r
+# define STRNCPY(dest,src,size) strncpy((dest),(src),(size))\r
+#endif\r
+\r
+\r
+#endif /*DIRENT_H*/\r
diff --git a/xbmc/pvrclients/tvheadend/windows/os_windows.cpp b/xbmc/pvrclients/tvheadend/windows/os_windows.cpp
new file mode 100644 (file)
index 0000000..7e222a3
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "os_windows.h"
+#include <sys/timeb.h>
+
+THREADLOCAL int ws32_result;
+THREADLOCAL int _so_err;
+THREADLOCAL int _so_err_siz = sizeof(int);
+
+int gettimeofday(struct timeval *pcur_time, struct timezone *tz)
+{
+  struct _timeb current;
+
+  _ftime(&current);
+
+  pcur_time->tv_sec = current.time;
+  pcur_time->tv_usec = current.millitm * 1000L;
+  if (tz)
+  {
+    tz->tz_minuteswest = current.timezone;     /* minutes west of Greenwich  */
+    tz->tz_dsttime = current.dstflag;  /* type of dst correction  */
+  }
+  return 0;
+}
diff --git a/xbmc/pvrclients/tvheadend/windows/os_windows.h b/xbmc/pvrclients/tvheadend/windows/os_windows.h
new file mode 100644 (file)
index 0000000..8671c3e
--- /dev/null
@@ -0,0 +1,371 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef PVRCLIENT_VDR_OS_WIN_H
+#define PVRCLIENT_VDR_OS_WIN_H
+
+#if defined _FILE_OFFSET_BITS && _FILE_OFFSET_BITS == 64
+# define __USE_FILE_OFFSET64   1
+#endif
+
+//typedef int ssize_t;
+typedef int mode_t;
+typedef int bool_t;
+typedef __int8 int8_t;
+typedef signed __int16 int16_t;
+typedef signed __int32 int32_t;
+typedef signed __int64 int64_t;
+typedef unsigned __int8 uint8_t;
+typedef unsigned __int16 uint16_t;
+typedef unsigned __int32 uint32_t;
+typedef unsigned __int64 uint64_t;
+
+#if defined __USE_FILE_OFFSET64
+typedef int64_t off_t;
+typedef uint64_t ino_t;
+#else
+typedef long off_t;
+#endif
+
+#define NAME_MAX         255   /* # chars in a file name */
+#define MAXPATHLEN       255
+#define INT64_MAX _I64_MAX
+#define INT64_MIN _I64_MIN
+
+#ifndef S_ISLNK
+# define S_ISLNK(x) 0
+#endif
+
+#ifndef S_ISREG
+#define S_ISREG(x) (((x) & S_IFMT) == S_IFREG)
+#endif
+
+#ifndef S_ISDIR
+#define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR)
+#endif
+
+/* Some tricks for MS Compilers */
+#define THREADLOCAL __declspec(thread)
+
+#ifndef DEFFILEMODE
+#define DEFFILEMODE 0
+#endif
+
+#define alloca _alloca
+#define chdir _chdir
+#define dup _dup
+#define dup2 _dup2
+#define fdopen _fdopen
+#define fileno _fileno
+#define getcwd _getcwd
+#define getpid _getpid
+#define ioctl ioctlsocket
+#define mkdir(p) _mkdir(p)
+#define mktemp _mktemp
+#define open _open
+#define pclose _pclose
+#define popen _popen
+#define putenv _putenv
+#define setmode _setmode
+#define sleep(t) Sleep((t)*1000)
+#define usleep(t) Sleep((t)/1000)
+#define snprintf _snprintf
+#define strcasecmp _stricmp
+#define strdup _strdup
+#define strlwr _strlwr
+#define strncasecmp _strnicmp
+#define tempnam _tempnam
+#define umask _umask
+#define unlink _unlink
+#define close _close
+
+#define O_RDONLY        _O_RDONLY
+#define O_WRONLY        _O_WRONLY
+#define O_RDWR          _O_RDWR
+#define O_APPEND        _O_APPEND
+
+#define O_CREAT         _O_CREAT
+#define O_TRUNC         _O_TRUNC
+#define O_EXCL          _O_EXCL
+
+#define O_TEXT          _O_TEXT
+#define O_BINARY        _O_BINARY
+#define O_RAW           _O_BINARY
+#define O_TEMPORARY     _O_TEMPORARY
+#define O_NOINHERIT     _O_NOINHERIT
+#define O_SEQUENTIAL    _O_SEQUENTIAL
+#define O_RANDOM        _O_RANDOM
+#define O_NDELAY       0
+
+#define S_IRWXO 007
+//#define      S_ISDIR(m) (((m) & _S_IFDIR) == _S_IFDIR)
+//#define      S_ISREG(m) (((m) & _S_IFREG) == _S_IFREG)
+
+#ifndef SIGHUP
+#define        SIGHUP  1       /* hangup */
+#endif
+#ifndef SIGBUS
+#define        SIGBUS  7       /* bus error */
+#endif
+#ifndef SIGKILL
+#define        SIGKILL 9       /* kill (cannot be caught or ignored) */
+#endif
+#ifndef        SIGSEGV
+#define        SIGSEGV 11      /* segment violation */
+#endif
+#ifndef SIGPIPE
+#define        SIGPIPE 13      /* write on a pipe with no one to read it */
+#endif
+#ifndef SIGCHLD
+#define        SIGCHLD 20      /* to parent on child stop or exit */
+#endif
+#ifndef SIGUSR1
+#define SIGUSR1 30     /* user defined signal 1 */
+#endif
+#ifndef SIGUSR2
+#define SIGUSR2 31     /* user defined signal 2 */
+#endif
+
+typedef unsigned short in_port_t;
+typedef unsigned short int ushort;
+typedef unsigned int in_addr_t;
+typedef int socklen_t;
+typedef int uid_t;
+typedef int gid_t;
+
+#if defined __USE_FILE_OFFSET64
+#define stat _stati64
+#define lseek _lseeki64
+#define fstat _fstati64
+#define tell _telli64
+#else
+#define stat _stat
+#define lseek _lseek
+#define fstat _fstat
+#define tell _tell
+#endif
+
+#define atoll _atoi64
+#ifndef va_copy
+#define va_copy(x, y) x = y
+#endif
+
+#include <stddef.h>
+#include <process.h>
+#if defined(_MSC_VER) /* Microsoft C Compiler ONLY */
+#pragma warning (push)
+/* Hack to suppress compiler warnings on FD_SET() & FD_CLR() */
+#pragma warning (disable:4142)
+/* Suppress compiler warnings about double definition of _WINSOCKAPI_ */
+#pragma warning (disable:4005)
+#endif
+/* prevent inclusion of wingdi.h */
+#define NOGDI
+#include <ws2spi.h>
+#include <ws2ipdef.h>
+#if defined(_MSC_VER) /* Microsoft C Compiler ONLY */
+#pragma warning (pop)
+#endif
+#include <io.h>
+#include <stdlib.h>
+#include <errno.h>
+#include "pthread_win32/pthread.h"
+
+typedef char * caddr_t;
+
+#undef FD_CLOSE
+#undef FD_OPEN
+#undef FD_READ
+#undef FD_WRITE
+
+#if (_MSC_VER < 1600)
+// Not yet defined in errno.h under VS2008
+#define EISCONN WSAEISCONN
+#define EINPROGRESS WSAEINPROGRESS
+#define EWOULDBLOCK WSAEWOULDBLOCK
+#define EALREADY WSAEALREADY
+#ifndef ETIMEDOUT
+#define ETIMEDOUT WSAETIMEDOUT
+#endif
+#define ECONNABORTED WSAECONNABORTED
+#define ECONNREFUSED WSAECONNREFUSED
+#define ECONNRESET WSAECONNRESET
+#define ERESTART WSATRY_AGAIN
+#define ENOTCONN WSAENOTCONN
+#define ENOBUFS WSAENOBUFS
+#define EOVERFLOW 2006
+#endif
+
+#undef h_errno
+#define h_errno errno /* we'll set it ourselves */
+
+struct timezone
+{
+  int  tz_minuteswest; /* minutes west of Greenwich */
+  int  tz_dsttime;     /* type of dst correction */
+};
+
+extern int gettimeofday(struct timeval *, struct timezone *);
+
+/* Unix socket emulation macros */
+#define __close closesocket
+
+#undef FD_CLR
+#define FD_CLR(fd, set) do { \
+    u_int __i; \
+    SOCKET __sock = _get_osfhandle(fd); \
+    for (__i = 0; __i < ((fd_set FAR *)(set))->fd_count ; __i++) { \
+        if (((fd_set FAR *)(set))->fd_array[__i] == __sock) { \
+            while (__i < ((fd_set FAR *)(set))->fd_count-1) { \
+                ((fd_set FAR *)(set))->fd_array[__i] = \
+                    ((fd_set FAR *)(set))->fd_array[__i+1]; \
+                __i++; \
+            } \
+            ((fd_set FAR *)(set))->fd_count--; \
+            break; \
+        } \
+    } \
+} while(0)
+
+#undef FD_SET
+#define FD_SET(fd, set) do { \
+    u_int __i; \
+    SOCKET __sock = _get_osfhandle(fd); \
+    for (__i = 0; __i < ((fd_set FAR *)(set))->fd_count; __i++) { \
+        if (((fd_set FAR *)(set))->fd_array[__i] == (__sock)) { \
+            break; \
+        } \
+    } \
+    if (__i == ((fd_set FAR *)(set))->fd_count) { \
+        if (((fd_set FAR *)(set))->fd_count < FD_SETSIZE) { \
+            ((fd_set FAR *)(set))->fd_array[__i] = (__sock); \
+            ((fd_set FAR *)(set))->fd_count++; \
+        } \
+    } \
+} while(0)
+
+#undef FD_ISSET
+#define FD_ISSET(fd, set) __WSAFDIsSet((SOCKET)(_get_osfhandle(fd)), (fd_set FAR *)(set))
+
+extern THREADLOCAL int ws32_result;
+#define __poll(f,n,t) \
+       (SOCKET_ERROR == WSAPoll(f,n,t) ? \
+       (errno = WSAGetLastError()), -1 : 0)
+#define __socket(f,t,p) \
+       (INVALID_SOCKET == ((SOCKET)(ws32_result = (int)socket(f,t,p))) ? \
+       ((WSAEMFILE == (errno = WSAGetLastError()) ? errno = EMFILE : -1), -1) : \
+       (SOCKET)_open_osfhandle(ws32_result,0))
+#define __accept(s,a,l) \
+       (INVALID_SOCKET == ((SOCKET)(ws32_result = (int)accept(_get_osfhandle(s),a,l))) ? \
+       ((WSAEMFILE == (errno = WSAGetLastError()) ? errno = EMFILE : -1), -1) : \
+       (SOCKET)_open_osfhandle(ws32_result,0))
+#define __bind(s,n,l) \
+       (SOCKET_ERROR == bind(_get_osfhandle(s),n,l) ? \
+       (errno = WSAGetLastError()), -1 : 0)
+#define __connect(s,n,l) \
+       (SOCKET_ERROR == connect(_get_osfhandle(s),n,l) ? \
+       (WSAEMFILE == (errno = WSAGetLastError()) ? errno = EMFILE : -1, -1) : 0)
+#define __listen(s,b) \
+       (SOCKET_ERROR == listen(_get_osfhandle(s),b) ? \
+       (WSAEMFILE == (errno = WSAGetLastError()) ? errno = EMFILE : -1, -1) : 0)
+#define __shutdown(s,h) \
+       (SOCKET_ERROR == shutdown(_get_osfhandle(s),h) ? \
+       (errno = WSAGetLastError()), -1 : 0)
+#define __select(n,r,w,e,t) \
+       (SOCKET_ERROR == (ws32_result = select(n,r,w,e,t)) ? \
+       (errno = WSAGetLastError()), -1 : ws32_result)
+#define __recv(s,b,l,f) \
+       (SOCKET_ERROR == (ws32_result = recv(_get_osfhandle(s),b,l,f)) ? \
+  (errno = WSAGetLastError()), -1 : ws32_result)
+#define __recvfrom(s,b,l,f,fr,frl) \
+       (SOCKET_ERROR == (ws32_result = recvfrom(_get_osfhandle(s),b,l,f,fr,frl)) ? \
+       (errno = WSAGetLastError()), -1 : ws32_result)
+#define __send(s,b,l,f) \
+       (SOCKET_ERROR == (ws32_result = send(_get_osfhandle(s),b,l,f)) ? \
+       (errno = WSAGetLastError()), -1 : ws32_result)
+#define __sendto(s,b,l,f,t,tl) \
+       (SOCKET_ERROR == (ws32_result = sendto(_get_osfhandle(s),b,l,f,t,tl)) ? \
+       (errno = WSAGetLastError()), -1 : ws32_result)
+#define __getsockname(s,n,l) \
+       (SOCKET_ERROR == getsockname(_get_osfhandle(s),n,l) ? \
+       (errno = WSAGetLastError()), -1 : 0)
+#define __getpeername(s,n,l) \
+       (SOCKET_ERROR == getpeername(_get_osfhandle(s),n,l) ? \
+       (errno = WSAGetLastError()), -1 : 0)
+#define __getsockopt(s,l,o,v,n) \
+       (Sleep(1), SOCKET_ERROR == getsockopt(_get_osfhandle(s),l,o,(char*)v,n) ? \
+       (errno = WSAGetLastError()), -1 : 0)
+#define __setsockopt(s,l,o,v,n) \
+       (SOCKET_ERROR == setsockopt(_get_osfhandle(s),l,o,v,n) ? \
+       (errno = WSAGetLastError()), -1 : 0)
+#define __ioctlsocket(s,c,a) \
+       (SOCKET_ERROR == ioctlsocket(_get_osfhandle(s),c,a) ? \
+       (errno = WSAGetLastError()), -1 : 0)
+#define __gethostname(n,l) \
+       (SOCKET_ERROR == gethostname(n,l) ? \
+       (errno = WSAGetLastError()), -1 : 0)
+#define __gethostbyname(n) \
+       (NULL == ((HOSTENT FAR*)(ws32_result = (int)gethostbyname(n))) ? \
+       (errno = WSAGetLastError()), NULL : (HOSTENT FAR*)ws32_result)
+#define __getservbyname(n,p) \
+       (NULL == ((SERVENT FAR*)(ws32_result = (int)getservbyname(n,p))) ? \
+       (errno = WSAGetLastError()), NULL : (SERVENT FAR*)ws32_result)
+#define __gethostbyaddr(a,l,t) \
+       (NULL == ((HOSTENT FAR*)(ws32_result = (int)gethostbyaddr(a,l,t))) ? \
+       (errno = WSAGetLastError()), NULL : (HOSTENT FAR*)ws32_result)
+extern THREADLOCAL int _so_err;
+extern THREADLOCAL int _so_err_siz;
+#define __read(fd,buf,siz) \
+       (_so_err_siz = sizeof(_so_err), \
+       __getsockopt((fd),SOL_SOCKET,SO_ERROR,&_so_err,&_so_err_siz) \
+       == 0 ? __recv((fd),(char *)(buf),(siz),0) : _read((fd),(char *)(buf),(siz)))
+#define __write(fd,buf,siz) \
+       (_so_err_siz = sizeof(_so_err), \
+       __getsockopt((fd),SOL_SOCKET,SO_ERROR,&_so_err,&_so_err_siz) \
+       == 0 ? __send((fd),(const char *)(buf),(siz),0) : _write((fd),(const char *)(buf),(siz)))
+
+
+#if !defined(__MINGW32__)
+#define strtok_r( _s, _sep, _lasts ) \
+       ( *(_lasts) = strtok( (_s), (_sep) ) )
+#endif /* !__MINGW32__ */
+
+#define asctime_r( _tm, _buf ) \
+       ( strcpy( (_buf), asctime( (_tm) ) ), \
+         (_buf) )
+
+#define ctime_r( _clock, _buf ) \
+       ( strcpy( (_buf), ctime( (_clock) ) ),  \
+         (_buf) )
+
+#define gmtime_r( _clock, _result ) \
+       ( *(_result) = *gmtime( (_clock) ), \
+         (_result) )
+
+#define localtime_r( _clock, _result ) \
+       ( *(_result) = *localtime( (_clock) ), \
+         (_result) )
+
+#define rand_r( _seed ) \
+       ( _seed == _seed? rand() : rand() )
+
+#endif
diff --git a/xbmc/pvrclients/vdr-streamdev/Makefile.in b/xbmc/pvrclients/vdr-streamdev/Makefile.in
new file mode 100644 (file)
index 0000000..293f8cf
--- /dev/null
@@ -0,0 +1,24 @@
+#
+# Makefile for the XBMC Video Disk Recorder PVR AddOn
+#
+# See the README for copyright information and
+# how to reach the author.
+#
+
+LIBS   =-ldl
+LIBDIR = ../../../addons/pvr.vdr.streamdev
+LIB    = ../../../addons/pvr.vdr.streamdev/XBMC_VDR.pvr
+
+SRCS   = channelscan.cpp \
+       client.cpp \
+       vtptransceiver.cpp \
+       timers.cpp \
+       channels.cpp \
+       recordings.cpp \
+       epg.cpp \
+       tools.cpp \
+       thread.cpp \
+       ringbuffer.cpp \
+       select.cpp
+
+include ../Makefile.include
diff --git a/xbmc/pvrclients/vdr-streamdev/README b/xbmc/pvrclients/vdr-streamdev/README
new file mode 100644 (file)
index 0000000..52fb91b
--- /dev/null
@@ -0,0 +1,34 @@
+XBMC Video Disk Recorder ('VDR') PVR Add-on
+------------------------------------------
+
+THIS IS A PRELIMINARY README AND IS SUBJECT TO CHANGE!!!
+
+Written by:                  The XBMC Team
+
+Project's homepage:          http://code.google.com/p/xbmcpvr-vdr/
+
+Latest version available at: http://code.google.com/p/xbmcpvr-vdr/
+
+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 of the License, or
+any later version.
+See the file LICENSE.GPL for more information.
+
+------------------------------------------
+
+This is a PVR Add-on for XBMC to add VDR (http://www.cadsoft.de/vdr) as a TV/PVR Backend to
+XBMC.
+
+It want to support Live TV watching, replaying of Recordings, programming Timers and 
+EPG TV Guide to use on same computer or over the Network.
+
+The connection of this AddOn depend upon a installed Streamdev-Server-Plugin on the VDR.
+This is the only minimum requirement to VDR for a connection to this addon, but the
+functionality can be improved by some other types of VDR Plugins.
+
+
+Links:
+VDR:                     http://www.cadsoft.de/vdr
+Streamdev-Plugin:        http://streamdev.vdr-developer.org/
+Dummydevice-Plugin:      http://phivdr.dyndns.org/vdr/
diff --git a/xbmc/pvrclients/vdr-streamdev/StdString.h b/xbmc/pvrclients/vdr-streamdev/StdString.h
new file mode 100644 (file)
index 0000000..8ee3060
--- /dev/null
@@ -0,0 +1,4333 @@
+#pragma once
+#include <string>
+#include <stdint.h>
+#if !defined(_LINUX)
+#include <windows.h>
+#include "pvrclient-vdr_os.h"
+#endif
+
+// =============================================================================
+//  FILE:  StdString.h
+//  AUTHOR:  Joe O'Leary (with outside help noted in comments)
+//
+//    If you find any bugs in this code, please let me know:
+//
+//        jmoleary@earthlink.net
+//        http://www.joeo.net/stdstring.htm (a bit outdated)
+//
+//      The latest version of this code should always be available at the
+//      following link:
+//
+//              http://www.joeo.net/code/StdString.zip (Dec 6, 2003)
+//
+//
+//  REMARKS:
+//    This header file declares the CStdStr template.  This template derives
+//    the Standard C++ Library basic_string<> template and add to it the
+//    the following conveniences:
+//      - The full MFC CString set of functions (including implicit cast)
+//      - writing to/reading from COM IStream interfaces
+//      - Functional objects for use in STL algorithms
+//
+//    From this template, we intstantiate two classes:  CStdStringA and
+//    CStdStringW.  The name "CStdString" is just a #define of one of these,
+//    based upone the UNICODE macro setting
+//
+//    This header also declares our own version of the MFC/ATL UNICODE-MBCS
+//    conversion macros.  Our version looks exactly like the Microsoft's to
+//    facilitate portability.
+//
+//  NOTE:
+//    If you you use this in an MFC or ATL build, you should include either
+//    afx.h or atlbase.h first, as appropriate.
+//
+//  PEOPLE WHO HAVE CONTRIBUTED TO THIS CLASS:
+//
+//    Several people have helped me iron out problems and othewise improve
+//    this class.  OK, this is a long list but in my own defense, this code
+//    has undergone two major rewrites.  Many of the improvements became
+//    necessary after I rewrote the code as a template.  Others helped me
+//    improve the CString facade.
+//
+//    Anyway, these people are (in chronological order):
+//
+//      - Pete the Plumber (???)
+//      - Julian Selman
+//      - Chris (of Melbsys)
+//      - Dave Plummer
+//      - John C Sipos
+//      - Chris Sells
+//      - Nigel Nunn
+//      - Fan Xia
+//      - Matthew Williams
+//      - Carl Engman
+//      - Mark Zeren
+//      - Craig Watson
+//      - Rich Zuris
+//      - Karim Ratib
+//      - Chris Conti
+//      - Baptiste Lepilleur
+//      - Greg Pickles
+//      - Jim Cline
+//      - Jeff Kohn
+//      - Todd Heckel
+//      - Ullrich Poll�hne
+//      - Joe Vitaterna
+//      - Joe Woodbury
+//      - Aaron (no last name)
+//      - Joldakowski (???)
+//      - Scott Hathaway
+//      - Eric Nitzche
+//      - Pablo Presedo
+//      - Farrokh Nejadlotfi
+//      - Jason Mills
+//      - Igor Kholodov
+//      - Mike Crusader
+//      - John James
+//      - Wang Haifeng
+//      - Tim Dowty
+//          - Arnt Witteveen
+//          - Glen Maynard
+//          - Paul DeMarco
+//          - Bagira (full name?)
+//          - Ronny Schulz
+//          - Jakko Van Hunen
+//      - Charles Godwin
+//      - Henk Demper
+//      - Greg Marr
+//      - Bill Carducci
+//      - Brian Groose
+//      - MKingman
+//      - Don Beusee
+//
+//  REVISION HISTORY
+//
+//    2005-JAN-10 - Thanks to Don Beusee for pointing out the danger in mapping
+//          length-checked formatting functions to non-length-checked
+//          CRT equivalents.  Also thanks to him for motivating me to
+//          optimize my implementation of Replace()
+//
+//    2004-APR-22 - A big, big thank you to "MKingman" (whoever you are) for
+//          finally spotting a silly little error in StdCodeCvt that
+//          has been causing me (and users of CStdString) problems for
+//          years in some relatively rare conversions.  I had reversed
+//          two length arguments.
+//
+//    2003-NOV-24 - Thanks to a bunch of people for helping me clean up many
+//          compiler warnings (and yes, even a couple of actual compiler
+//          errors).  These include Henk Demper for figuring out how
+//          to make the Intellisense work on with CStdString on VC6,
+//          something I was never able to do.  Greg Marr pointed out
+//          a compiler warning about an unreferenced symbol and a
+//          problem with my version of Load in MFC builds.  Bill
+//          Carducci took a lot of time with me to help me figure out
+//          why some implementations of the Standard C++ Library were
+//          returning error codes for apparently successful conversions
+//          between ASCII and UNICODE.  Finally thanks to Brian Groose
+//          for helping me fix compiler signed unsigned warnings in
+//          several functions.
+//
+//    2003-JUL-10 - Thanks to Charles Godwin for making me realize my 'FmtArg'
+//          fixes had inadvertently broken the DLL-export code (which is
+//                  normally commented out.  I had to move it up higher.  Also
+//          this helped me catch a bug in ssicoll that would prevent
+//                  compilation, otherwise.
+//
+//    2003-MAR-14 - Thanks to Jakko Van Hunen for pointing out a copy-and-paste
+//                  bug in one of the overloads of FmtArg.
+//
+//    2003-MAR-10 - Thanks to Ronny Schulz for (twice!) sending me some changes
+//                  to help CStdString build on SGI and for pointing out an
+//                  error in placement of my preprocessor macros for ssfmtmsg.
+//
+//    2002-NOV-26 - Thanks to Bagira for pointing out that my implementation of
+//                  SpanExcluding was not properly handling the case in which
+//                  the string did NOT contain any of the given characters
+//
+//    2002-OCT-21 - Many thanks to Paul DeMarco who was invaluable in helping me
+//                  get this code working with Borland's free compiler as well
+//                  as the Dev-C++ compiler (available free at SourceForge).
+//
+//    2002-SEP-13 - Thanks to Glen Maynard who helped me get rid of some loud
+//                  but harmless warnings that were showing up on g++.  Glen
+//                  also pointed out that some pre-declarations of FmtArg<>
+//                  specializations were unnecessary (and no good on G++)
+//
+//    2002-JUN-26 - Thanks to Arnt Witteveen for pointing out that I was using
+//                  static_cast<> in a place in which I should have been using
+//                  reinterpret_cast<> (the ctor for unsigned char strings).
+//                  That's what happens when I don't unit-test properly!
+//                  Arnt also noticed that CString was silently correcting the
+//                  'nCount' argument to Left() and Right() where CStdString was
+//                  not (and crashing if it was bad).  That is also now fixed!
+//
+//    2002-FEB-25 - Thanks to Tim Dowty for pointing out (and giving me the fix
+//          for) a conversion problem with non-ASCII MBCS characters.
+//          CStdString is now used in my favorite commercial MP3 player!
+//
+//    2001-DEC-06 - Thanks to Wang Haifeng for spotting a problem in one of the
+//          assignment operators (for _bstr_t) that would cause compiler
+//          errors when refcounting protection was turned off.
+//
+//    2001-NOV-27 - Remove calls to operator!= which involve reverse_iterators
+//          due to a conflict with the rel_ops operator!=.  Thanks to
+//          John James for pointing this out.
+//
+//    2001-OCT-29 - Added a minor range checking fix for the Mid function to
+//          make it as forgiving as CString's version is.  Thanks to
+//          Igor Kholodov for noticing this.
+//          - Added a specialization of std::swap for CStdString.  Thanks
+//          to Mike Crusader for suggesting this!  It's commented out
+//          because you're not supposed to inject your own code into the
+//          'std' namespace.  But if you don't care about that, it's
+//          there if you want it
+//          - Thanks to Jason Mills for catching a case where CString was
+//          more forgiving in the Delete() function than I was.
+//
+//    2001-JUN-06 - I was violating the Standard name lookup rules stated
+//          in [14.6.2(3)].  None of the compilers I've tried so
+//          far apparently caught this but HP-UX aCC 3.30 did.  The
+//          fix was to add 'this->' prefixes in many places.
+//          Thanks to Farrokh Nejadlotfi for this!
+//
+//    2001-APR-27 - StreamLoad was calculating the number of BYTES in one
+//          case, not characters.  Thanks to Pablo Presedo for this.
+//
+//    2001-FEB-23 - Replace() had a bug which caused infinite loops if the
+//          source string was empty.  Fixed thanks to Eric Nitzsche.
+//
+//    2001-FEB-23 - Scott Hathaway was a huge help in providing me with the
+//          ability to build CStdString on Sun Unix systems.  He
+//          sent me detailed build reports about what works and what
+//          does not.  If CStdString compiles on your Unix box, you
+//          can thank Scott for it.
+//
+//    2000-DEC-29 - Joldakowski noticed one overload of Insert failed to do a
+//          range check as CString's does.  Now fixed -- thanks!
+//
+//    2000-NOV-07 - Aaron pointed out that I was calling static member
+//          functions of char_traits via a temporary.  This was not
+//          technically wrong, but it was unnecessary and caused
+//          problems for poor old buggy VC5.  Thanks Aaron!
+//
+//    2000-JUL-11 - Joe Woodbury noted that the CString::Find docs don't match
+//          what the CString::Find code really ends up doing.   I was
+//          trying to match the docs.  Now I match the CString code
+//          - Joe also caught me truncating strings for GetBuffer() calls
+//          when the supplied length was less than the current length.
+//
+//    2000-MAY-25 - Better support for STLPORT's Standard library distribution
+//          - Got rid of the NSP macro - it interfered with Koenig lookup
+//          - Thanks to Joe Woodbury for catching a TrimLeft() bug that
+//          I introduced in January.  Empty strings were not getting
+//          trimmed
+//
+//    2000-APR-17 - Thanks to Joe Vitaterna for pointing out that ReverseFind
+//          is supposed to be a const function.
+//
+//    2000-MAR-07 - Thanks to Ullrich Poll�hne for catching a range bug in one
+//          of the overloads of assign.
+//
+//    2000-FEB-01 - You can now use CStdString on the Mac with CodeWarrior!
+//          Thanks to Todd Heckel for helping out with this.
+//
+//    2000-JAN-23 - Thanks to Jim Cline for pointing out how I could make the
+//          Trim() function more efficient.
+//          - Thanks to Jeff Kohn for prompting me to find and fix a typo
+//          in one of the addition operators that takes _bstr_t.
+//          - Got rid of the .CPP file -  you only need StdString.h now!
+//
+//    1999-DEC-22 - Thanks to Greg Pickles for helping me identify a problem
+//          with my implementation of CStdString::FormatV in which
+//          resulting string might not be properly NULL terminated.
+//
+//    1999-DEC-06 - Chris Conti pointed yet another basic_string<> assignment
+//          bug that MS has not fixed.  CStdString did nothing to fix
+//          it either but it does now!  The bug was: create a string
+//          longer than 31 characters, get a pointer to it (via c_str())
+//          and then assign that pointer to the original string object.
+//          The resulting string would be empty.  Not with CStdString!
+//
+//    1999-OCT-06 - BufferSet was erasing the string even when it was merely
+//          supposed to shrink it.  Fixed.  Thanks to Chris Conti.
+//          - Some of the Q172398 fixes were not checking for assignment-
+//          to-self.  Fixed.  Thanks to Baptiste Lepilleur.
+//
+//    1999-AUG-20 - Improved Load() function to be more efficient by using
+//          SizeOfResource().  Thanks to Rich Zuris for this.
+//          - Corrected resource ID constructor, again thanks to Rich.
+//          - Fixed a bug that occurred with UNICODE characters above
+//          the first 255 ANSI ones.  Thanks to Craig Watson.
+//          - Added missing overloads of TrimLeft() and TrimRight().
+//          Thanks to Karim Ratib for pointing them out
+//
+//    1999-JUL-21 - Made all calls to GetBuf() with no args check length first.
+//
+//    1999-JUL-10 - Improved MFC/ATL independence of conversion macros
+//          - Added SS_NO_REFCOUNT macro to allow you to disable any
+//          reference-counting your basic_string<> impl. may do.
+//          - Improved ReleaseBuffer() to be as forgiving as CString.
+//          Thanks for Fan Xia for helping me find this and to
+//          Matthew Williams for pointing it out directly.
+//
+//    1999-JUL-06 - Thanks to Nigel Nunn for catching a very sneaky bug in
+//          ToLower/ToUpper.  They should call GetBuf() instead of
+//          data() in order to ensure the changed string buffer is not
+//          reference-counted (in those implementations that refcount).
+//
+//    1999-JUL-01 - Added a true CString facade.  Now you can use CStdString as
+//          a drop-in replacement for CString.  If you find this useful,
+//          you can thank Chris Sells for finally convincing me to give
+//          in and implement it.
+//          - Changed operators << and >> (for MFC CArchive) to serialize
+//          EXACTLY as CString's do.  So now you can send a CString out
+//          to a CArchive and later read it in as a CStdString.   I have
+//          no idea why you would want to do this but you can.
+//
+//    1999-JUN-21 - Changed the CStdString class into the CStdStr template.
+//          - Fixed FormatV() to correctly decrement the loop counter.
+//          This was harmless bug but a bug nevertheless.  Thanks to
+//          Chris (of Melbsys) for pointing it out
+//          - Changed Format() to try a normal stack-based array before
+//          using to _alloca().
+//          - Updated the text conversion macros to properly use code
+//          pages and to fit in better in MFC/ATL builds.  In other
+//          words, I copied Microsoft's conversion stuff again.
+//          - Added equivalents of CString::GetBuffer, GetBufferSetLength
+//          - new sscpy() replacement of CStdString::CopyString()
+//          - a Trim() function that combines TrimRight() and TrimLeft().
+//
+//    1999-MAR-13 - Corrected the "NotSpace" functional object to use _istpace()
+//          instead of _isspace()   Thanks to Dave Plummer for this.
+//
+//    1999-FEB-26 - Removed errant line (left over from testing) that #defined
+//          _MFC_VER.  Thanks to John C Sipos for noticing this.
+//
+//    1999-FEB-03 - Fixed a bug in a rarely-used overload of operator+() that
+//          caused infinite recursion and stack overflow
+//          - Added member functions to simplify the process of
+//          persisting CStdStrings to/from DCOM IStream interfaces
+//          - Added functional objects (e.g. StdStringLessNoCase) that
+//          allow CStdStrings to be used as keys STL map objects with
+//          case-insensitive comparison
+//          - Added array indexing operators (i.e. operator[]).  I
+//          originally assumed that these were unnecessary and would be
+//          inherited from basic_string.  However, without them, Visual
+//          C++ complains about ambiguous overloads when you try to use
+//          them.  Thanks to Julian Selman to pointing this out.
+//
+//    1998-FEB-?? - Added overloads of assign() function to completely account
+//          for Q172398 bug.  Thanks to "Pete the Plumber" for this
+//
+//    1998-FEB-?? - Initial submission
+//
+// COPYRIGHT:
+//    2002 Joseph M. O'Leary.  This code is 100% free.  Use it anywhere you
+//      want.  Rewrite it, restructure it, whatever.  If you can write software
+//      that makes money off of it, good for you.  I kinda like capitalism.
+//      Please don't blame me if it causes your $30 billion dollar satellite
+//      explode in orbit.  If you redistribute it in any form, I'd appreciate it
+//      if you would leave this notice here.
+// =============================================================================
+
+// Avoid multiple inclusion
+
+#ifndef STDSTRING_H
+#define STDSTRING_H
+
+// When using VC, turn off browser references
+// Turn off unavoidable compiler warnings
+
+#if defined(_MSC_VER) && (_MSC_VER > 1100)
+  #pragma component(browser, off, references, "CStdString")
+  #pragma warning (disable : 4290) // C++ Exception Specification ignored
+  #pragma warning (disable : 4127) // Conditional expression is constant
+  #pragma warning (disable : 4097) // typedef name used as synonym for class name
+#endif
+
+// Borland warnings to turn off
+
+#ifdef __BORLANDC__
+    #pragma option push -w-inl
+//  #pragma warn -inl   // Turn off inline function warnings
+#endif
+
+// SS_IS_INTRESOURCE
+// -----------------
+//    A copy of IS_INTRESOURCE from VC7.  Because old VC6 version of winuser.h
+//    doesn't have this.
+
+#define SS_IS_INTRESOURCE(_r) (false)
+
+#if !defined (SS_ANSI) && defined(_MSC_VER)
+  #undef SS_IS_INTRESOURCE
+  #if defined(_WIN64)
+    #define SS_IS_INTRESOURCE(_r) (((unsigned __int64)(_r) >> 16) == 0)
+  #else
+    #define SS_IS_INTRESOURCE(_r) (((unsigned long)(_r) >> 16) == 0)
+  #endif
+#endif
+
+
+// MACRO: SS_UNSIGNED
+// ------------------
+//      This macro causes the addition of a constructor and assignment operator
+//      which take unsigned characters.  CString has such functions and in order
+//      to provide maximum CString-compatability, this code needs them as well.
+//      In practice you will likely never need these functions...
+
+//#define SS_UNSIGNED
+
+#ifdef SS_ALLOW_UNSIGNED_CHARS
+  #define SS_UNSIGNED
+#endif
+
+// MACRO: SS_SAFE_FORMAT
+// ---------------------
+//      This macro provides limited compatability with a questionable CString
+//      "feature".  You can define it in order to avoid a common problem that
+//      people encounter when switching from CString to CStdString.
+//
+//      To illustrate the problem -- With CString, you can do this:
+//
+//          CString sName("Joe");
+//          CString sTmp;
+//          sTmp.Format("My name is %s", sName);                    // WORKS!
+//
+//      However if you were to try this with CStdString, your program would
+//      crash.
+//
+//          CStdString sName("Joe");
+//          CStdString sTmp;
+//          sTmp.Format("My name is %s", sName);                    // CRASHES!
+//
+//      You must explicitly call c_str() or cast the object to the proper type
+//
+//          sTmp.Format("My name is %s", sName.c_str());            // WORKS!
+//          sTmp.Format("My name is %s", static_cast<PCSTR>(sName));// WORKS!
+//          sTmp.Format("My name is %s", (PCSTR)sName);        // WORKS!
+//
+//      This is because it is illegal to pass anything but a POD type as a
+//      variadic argument to a variadic function (i.e. as one of the "..."
+//      arguments).  The type const char* is a POD type.  The type CStdString
+//      is not.  Of course, neither is the type CString, but CString lets you do
+//      it anyway due to the way they laid out the class in binary.  I have no
+//      control over this in CStdString since I derive from whatever
+//      implementation of basic_string is available.
+//
+//      However if you have legacy code (which does this) that you want to take
+//      out of the MFC world and you don't want to rewrite all your calls to
+//      Format(), then you can define this flag and it will no longer crash.
+//
+//      Note however that this ONLY works for Format(), not sprintf, fprintf,
+//      etc.  If you pass a CStdString object to one of those functions, your
+//      program will crash.  Not much I can do to get around this, short of
+//      writing substitutes for those functions as well.
+
+#define SS_SAFE_FORMAT  // use new template style Format() function
+
+
+// MACRO: SS_NO_IMPLICIT_CAST
+// --------------------------
+//      Some people don't like the implicit cast to const char* (or rather to
+//      const CT*) that CStdString (and MFC's CString) provide.  That was the
+//      whole reason I created this class in the first place, but hey, whatever
+//      bakes your cake.  Just #define this macro to get rid of the the implicit
+//      cast.
+
+//#define SS_NO_IMPLICIT_CAST // gets rid of operator const CT*()
+
+
+// MACRO: SS_NO_REFCOUNT
+// ---------------------
+//    turns off reference counting at the assignment level.  Only needed
+//    for the version of basic_string<> that comes with Visual C++ versions
+//    6.0 or earlier, and only then in some heavily multithreaded scenarios.
+//    Uncomment it if you feel you need it.
+
+//#define SS_NO_REFCOUNT
+
+// MACRO: SS_WIN32
+// ---------------
+//      When this flag is set, we are building code for the Win32 platform and
+//      may use Win32 specific functions (such as LoadString).  This gives us
+//      a couple of nice extras for the code.
+//
+//      Obviously, Microsoft's is not the only compiler available for Win32 out
+//      there.  So I can't just check to see if _MSC_VER is defined to detect
+//      if I'm building on Win32.  So for now, if you use MS Visual C++ or
+//      Borland's compiler, I turn this on.  Otherwise you may turn it on
+//      yourself, if you prefer
+
+#if defined(_MSC_VER) || defined(__BORLANDC__) || defined(_WIN32)
+ #define SS_WIN32
+#endif
+
+// MACRO: SS_ANSI
+// --------------
+//      When this macro is defined, the code attempts only to use ANSI/ISO
+//      standard library functions to do it's work.  It will NOT attempt to use
+//      any Win32 of Visual C++ specific functions -- even if they are
+//      available.  You may define this flag yourself to prevent any Win32
+//      of VC++ specific functions from being called.
+
+// If we're not on Win32, we MUST use an ANSI build
+
+#ifndef SS_WIN32
+    #if !defined(SS_NO_ANSI)
+        #define SS_ANSI
+    #endif
+#endif
+
+// MACRO: SS_ALLOCA
+// ----------------
+//      Some implementations of the Standard C Library have a non-standard
+//      function known as alloca().  This functions allows one to allocate a
+//      variable amount of memory on the stack.  It is needed to implement
+//      the ASCII/MBCS conversion macros.
+//
+//      I wanted to find some way to determine automatically if alloca() is
+//    available on this platform via compiler flags but that is asking for
+//    trouble.  The crude test presented here will likely need fixing on
+//    other platforms.  Therefore I'll leave it up to you to fiddle with
+//    this test to determine if it exists.  Just make sure SS_ALLOCA is or
+//    is not defined as appropriate and you control this feature.
+
+#if defined(_MSC_VER) && !defined(SS_ANSI)
+  #define SS_ALLOCA
+#endif
+
+
+// MACRO: SS_MBCS
+// --------------
+//    Setting this macro means you are using MBCS characters.  In MSVC builds,
+//    this macro gets set automatically by detection of the preprocessor flag
+//    _MBCS.  For other platforms you may set it manually if you wish.  The
+//    only effect it currently has is to cause the allocation of more space
+//    for wchar_t --> char conversions.
+//    Note that MBCS does not mean UNICODE.
+//
+//  #define SS_MBCS
+//
+
+#ifdef _MBCS
+  #define SS_MBCS
+#endif
+
+
+// MACRO SS_NO_LOCALE
+// ------------------
+// If your implementation of the Standard C++ Library lacks the <locale> header,
+// you can #define this macro to make your code build properly.  Note that this
+// is some of my newest code and frankly I'm not very sure of it, though it does
+// pass my unit tests.
+
+// #define SS_NO_LOCALE
+
+
+// Compiler Error regarding _UNICODE and UNICODE
+// -----------------------------------------------
+// Microsoft header files are screwy.  Sometimes they depend on a preprocessor
+// flag named "_UNICODE".  Other times they check "UNICODE" (note the lack of
+// leading underscore in the second version".  In several places, they silently
+// "synchronize" these two flags this by defining one of the other was defined.
+// In older version of this header, I used to try to do the same thing.
+//
+// However experience has taught me that this is a bad idea.  You get weird
+// compiler errors that seem to indicate things like LPWSTR and LPTSTR not being
+// equivalent in UNICODE builds, stuff like that (when they MUST be in a proper
+// UNICODE  build).  You end up scratching your head and saying, "But that HAS
+// to compile!".
+//
+// So what should you do if you get this error?
+//
+// Make sure that both macros (_UNICODE and UNICODE) are defined before this
+// file is included.  You can do that by either
+//
+//    a) defining both yourself before any files get included
+//    b) including the proper MS headers in the proper order
+//    c) including this file before any other file, uncommenting
+//       the #defines below, and commenting out the #errors
+//
+//  Personally I recommend solution a) but it's your call.
+
+#ifdef _MSC_VER
+  #if defined (_UNICODE) && !defined (UNICODE)
+    #error UNICODE defined  but not UNICODE
+  //  #define UNICODE  // no longer silently fix this
+  #endif
+  #if defined (UNICODE) && !defined (_UNICODE)
+    #error Warning, UNICODE defined  but not _UNICODE
+  //  #define _UNICODE  // no longer silently fix this
+  #endif
+#endif
+
+
+// -----------------------------------------------------------------------------
+// MIN and MAX.  The Standard C++ template versions go by so many names (at
+// at least in the MS implementation) that you never know what's available
+// -----------------------------------------------------------------------------
+template<class Type>
+inline const Type& SSMIN(const Type& arg1, const Type& arg2)
+{
+  return arg2 < arg1 ? arg2 : arg1;
+}
+template<class Type>
+inline const Type& SSMAX(const Type& arg1, const Type& arg2)
+{
+  return arg2 > arg1 ? arg2 : arg1;
+}
+
+// If they have not #included W32Base.h (part of my W32 utility library) then
+// we need to define some stuff.  Otherwise, this is all defined there.
+
+#if !defined(W32BASE_H)
+
+  // If they want us to use only standard C++ stuff (no Win32 stuff)
+
+  #ifdef SS_ANSI
+
+    // On Win32 we have TCHAR.H so just include it.  This is NOT violating
+        // the spirit of SS_ANSI as we are not calling any Win32 functions here.
+
+    #ifdef SS_WIN32
+
+      #include <TCHAR.H>
+      #include <WTYPES.H>
+      #ifndef STRICT
+        #define STRICT
+      #endif
+
+        // ... but on non-Win32 platforms, we must #define the types we need.
+
+    #else
+
+      typedef const char*    PCSTR;
+      typedef char*      PSTR;
+      typedef const wchar_t*  PCWSTR;
+      typedef wchar_t*    PWSTR;
+      #ifdef UNICODE
+        typedef wchar_t    TCHAR;
+      #else
+        typedef char    TCHAR;
+      #endif
+      typedef wchar_t      OLECHAR;
+
+    #endif  // #ifndef _WIN32
+
+
+    // Make sure ASSERT and verify are defined using only ANSI stuff
+
+    #ifndef ASSERT
+      #include <assert.h>
+      #define ASSERT(f) assert((f))
+    #endif
+    #ifndef VERIFY
+      #ifdef _DEBUG
+        #define VERIFY(x) ASSERT((x))
+      #else
+        #define VERIFY(x) x
+      #endif
+    #endif
+
+  #else // ...else SS_ANSI is NOT defined
+
+    #include <TCHAR.H>
+    #include <WTYPES.H>
+    #ifndef STRICT
+      #define STRICT
+    #endif
+
+    // Make sure ASSERT and verify are defined
+
+    #ifndef ASSERT
+      #include <crtdbg.h>
+      #define ASSERT(f) _ASSERTE((f))
+    #endif
+    #ifndef VERIFY
+      #ifdef _DEBUG
+        #define VERIFY(x) ASSERT((x))
+      #else
+        #define VERIFY(x) x
+      #endif
+    #endif
+
+  #endif // #ifdef SS_ANSI
+
+  #ifndef UNUSED
+    #define UNUSED(x) x
+  #endif
+
+#endif // #ifndef W32BASE_H
+
+// Standard headers needed
+
+#include <string>      // basic_string
+#include <algorithm>    // for_each, etc.
+#include <functional>    // for StdStringLessNoCase, et al
+#ifndef SS_NO_LOCALE
+  #include <locale>      // for various facets
+#endif
+
+// If this is a recent enough version of VC include comdef.h, so we can write
+// member functions to deal with COM types & compiler support classes e.g.
+// _bstr_t
+
+#if defined (_MSC_VER) && (_MSC_VER >= 1100)
+ #include <comdef.h>
+ #define SS_INC_COMDEF  // signal that we #included MS comdef.h file
+ #define STDSTRING_INC_COMDEF
+ #define SS_NOTHROW __declspec(nothrow)
+#else
+  #define SS_NOTHROW
+#endif
+
+#ifndef TRACE
+  #define TRACE_DEFINED_HERE
+  #define TRACE
+#endif
+
+// Microsoft defines PCSTR, PCWSTR, etc, but no PCTSTR.  I hate to use the
+// versions with the "L" in front of them because that's a leftover from Win 16
+// days, even though it evaluates to the same thing.  Therefore, Define a PCSTR
+// as an LPCTSTR.
+
+#if !defined(PCTSTR) && !defined(PCTSTR_DEFINED)
+  typedef const TCHAR*      PCTSTR;
+  #define PCTSTR_DEFINED
+#endif
+
+#if !defined(PCOLESTR) && !defined(PCOLESTR_DEFINED)
+  typedef const OLECHAR*      PCOLESTR;
+  #define PCOLESTR_DEFINED
+#endif
+
+#if !defined(POLESTR) && !defined(POLESTR_DEFINED)
+  typedef OLECHAR*        POLESTR;
+  #define POLESTR_DEFINED
+#endif
+
+#if !defined(PCUSTR) && !defined(PCUSTR_DEFINED)
+  typedef const unsigned char*  PCUSTR;
+  typedef unsigned char*      PUSTR;
+  #define PCUSTR_DEFINED
+#endif
+
+
+// SGI compiler 7.3 doesnt know these  types - oh and btw, remember to use
+// -LANG:std in the CXX Flags
+#if defined(__sgi)
+    typedef unsigned long           DWORD;
+    typedef void *                  LPCVOID;
+#endif
+
+
+// SS_USE_FACET macro and why we need it:
+//
+// Since I'm a good little Standard C++ programmer, I use locales.  Thus, I
+// need to make use of the use_facet<> template function here.   Unfortunately,
+// this need is complicated by the fact the MS' implementation of the Standard
+// C++ Library has a non-standard version of use_facet that takes more
+// arguments than the standard dictates.  Since I'm trying to write CStdString
+// to work with any version of the Standard library, this presents a problem.
+//
+// The upshot of this is that I can't do 'use_facet' directly.  The MS' docs
+// tell me that I have to use a macro, _USE() instead.  Since _USE obviously
+// won't be available in other implementations, this means that I have to write
+// my OWN macro -- SS_USE_FACET -- that evaluates either to _USE or to the
+// standard, use_facet.
+//
+// If you are having trouble with the SS_USE_FACET macro, in your implementation
+// of the Standard C++ Library, you can define your own version of SS_USE_FACET.
+
+#ifndef schMSG
+  #define schSTR(x)     #x
+  #define schSTR2(x)  schSTR(x)
+  #define schMSG(desc) message(__FILE__ "(" schSTR2(__LINE__) "):" #desc)
+#endif
+
+#ifndef SS_USE_FACET
+
+  // STLPort #defines a macro (__STL_NO_EXPLICIT_FUNCTION_TMPL_ARGS) for
+  // all MSVC builds, erroneously in my opinion.  It causes problems for
+  // my SS_ANSI builds.  In my code, I always comment out that line.  You'll
+  // find it in   \stlport\config\stl_msvc.h
+
+  #if defined(__SGI_STL_PORT) && (__SGI_STL_PORT >= 0x400 )
+
+    #if defined(__STL_NO_EXPLICIT_FUNCTION_TMPL_ARGS) && defined(_MSC_VER)
+      #ifdef SS_ANSI
+        #pragma schMSG(__STL_NO_EXPLICIT_FUNCTION_TMPL_ARGS defined!!)
+      #endif
+    #endif
+    #define SS_USE_FACET(loc, fac) std::use_facet<fac >(loc)
+
+  #elif defined(_MSC_VER )
+
+    #define SS_USE_FACET(loc, fac) std::_USE(loc, fac)
+
+  // ...and
+  #elif defined(_RWSTD_NO_TEMPLATE_ON_RETURN_TYPE)
+
+        #define SS_USE_FACET(loc, fac) std::use_facet(loc, (fac*)0)
+
+  #else
+
+    #define SS_USE_FACET(loc, fac) std::use_facet<fac >(loc)
+
+  #endif
+
+#endif
+
+// =============================================================================
+// UNICODE/MBCS conversion macros.  Made to work just like the MFC/ATL ones.
+// =============================================================================
+
+#include <wchar.h>      // Added to Std Library with Amendment #1.
+
+// First define the conversion helper functions.  We define these regardless of
+// any preprocessor macro settings since their names won't collide.
+
+// Not sure if we need all these headers.   I believe ANSI says we do.
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <wctype.h>
+#include <ctype.h>
+#include <stdlib.h>
+#ifndef va_start
+  #include <varargs.h>
+#endif
+
+
+#ifdef SS_NO_LOCALE
+
+  #if defined(_WIN32) || defined (_WIN32_WCE)
+
+    inline PWSTR StdCodeCvt(PWSTR pDstW, int nDst, PCSTR pSrcA, int nSrc,
+      UINT acp=CP_ACP)
+    {
+      ASSERT(0 != pSrcA);
+      ASSERT(0 != pDstW);
+      pDstW[0] = '\0';
+      MultiByteToWideChar(acp, 0, pSrcA, nSrc, pDstW, nDst);
+      return pDstW;
+    }
+    inline PWSTR StdCodeCvt(PWSTR pDstW, int nDst, PCUSTR pSrcA, int nSrc,
+      UINT acp=CP_ACP)
+    {
+      return StdCodeCvt(pDstW, nDst, (PCSTR)pSrcA, nSrc, acp);
+    }
+
+    inline PSTR StdCodeCvt(PSTR pDstA, int nDst, PCWSTR pSrcW, int nSrc,
+      UINT acp=CP_ACP)
+    {
+      ASSERT(0 != pDstA);
+      ASSERT(0 != pSrcW);
+      pDstA[0] = '\0';
+      WideCharToMultiByte(acp, 0, pSrcW, nSrc, pDstA, nDst, 0, 0);
+      return pDstA;
+    }
+    inline PUSTR StdCodeCvt(PUSTR pDstA, int nDst, PCWSTR pSrcW, int nSrc,
+      UINT acp=CP_ACP)
+    {
+      return (PUSTR)StdCodeCvt((PSTR)pDstA, nDst, pSrcW, nSrc, acp);
+    }
+  #else
+  #endif
+
+#else
+
+  // StdCodeCvt - made to look like Win32 functions WideCharToMultiByte
+  //        and MultiByteToWideChar but uses locales in SS_ANSI
+  //        builds.  There are a number of overloads.
+  //              First argument is the destination buffer.
+  //              Second argument is the source buffer
+  //#if defined (SS_ANSI) || !defined (SS_WIN32)
+
+  // 'SSCodeCvt' - shorthand name for the codecvt facet we use
+
+  typedef std::codecvt<wchar_t, char, mbstate_t> SSCodeCvt;
+
+  inline PWSTR StdCodeCvt(PWSTR pDstW, int nDst, PCSTR pSrcA, int nSrc,
+    const std::locale& loc=std::locale())
+  {
+    ASSERT(0 != pSrcA);
+    ASSERT(0 != pDstW);
+
+    pDstW[0]          = '\0';
+
+    if ( nSrc > 0 )
+    {
+      PCSTR pNextSrcA      = pSrcA;
+      PWSTR pNextDstW      = pDstW;
+      SSCodeCvt::result res  = SSCodeCvt::ok;
+      const SSCodeCvt& conv  = SS_USE_FACET(loc, SSCodeCvt);
+      SSCodeCvt::state_type st= { 0 };
+      res            = conv.in(st,
+                    pSrcA, pSrcA + nSrc, pNextSrcA,
+                    pDstW, pDstW + nDst, pNextDstW);
+#ifdef _LINUX
+#define ASSERT2(a) if (!(a)) {fprintf(stderr, "StdString: Assertion Failed on line %d\n", __LINE__);}
+#else
+#define ASSERT2 ASSERT
+#endif
+      ASSERT2(SSCodeCvt::ok == res);
+      ASSERT2(SSCodeCvt::error != res);
+      ASSERT2(pNextDstW >= pDstW);
+      ASSERT2(pNextSrcA >= pSrcA);
+#undef ASSERT2
+      // Null terminate the converted string
+
+      if ( pNextDstW - pDstW > nDst )
+        *(pDstW + nDst) = '\0';
+      else
+        *pNextDstW = '\0';
+    }
+    return pDstW;
+  }
+  inline PWSTR StdCodeCvt(PWSTR pDstW, int nDst, PCUSTR pSrcA, int nSrc,
+    const std::locale& loc=std::locale())
+  {
+    return StdCodeCvt(pDstW, nDst, (PCSTR)pSrcA, nSrc, loc);
+  }
+
+  inline PSTR StdCodeCvt(PSTR pDstA, int nDst, PCWSTR pSrcW, int nSrc,
+    const std::locale& loc=std::locale())
+  {
+    ASSERT(0 != pDstA);
+    ASSERT(0 != pSrcW);
+
+    pDstA[0]          = '\0';
+
+    if ( nSrc > 0 )
+    {
+      PSTR pNextDstA      = pDstA;
+      PCWSTR pNextSrcW    = pSrcW;
+      SSCodeCvt::result res  = SSCodeCvt::ok;
+      const SSCodeCvt& conv  = SS_USE_FACET(loc, SSCodeCvt);
+      SSCodeCvt::state_type st= { 0 };
+      res            = conv.out(st,
+                    pSrcW, pSrcW + nSrc, pNextSrcW,
+                    pDstA, pDstA + nDst, pNextDstA);
+#ifdef _LINUX
+#define ASSERT2(a) if (!(a)) {fprintf(stderr, "StdString: Assertion Failed on line %d\n", __LINE__);}
+#else
+#define ASSERT2 ASSERT
+#endif
+      ASSERT2(SSCodeCvt::error != res);
+      ASSERT2(SSCodeCvt::ok == res);  // strict, comment out for sanity
+      ASSERT2(pNextDstA >= pDstA);
+      ASSERT2(pNextSrcW >= pSrcW);
+#undef ASSERT2
+
+      // Null terminate the converted string
+
+      if ( pNextDstA - pDstA > nDst )
+        *(pDstA + nDst) = '\0';
+      else
+        *pNextDstA = '\0';
+    }
+    return pDstA;
+  }
+
+  inline PUSTR StdCodeCvt(PUSTR pDstA, int nDst, PCWSTR pSrcW, int nSrc,
+    const std::locale& loc=std::locale())
+  {
+    return (PUSTR)StdCodeCvt((PSTR)pDstA, nDst, pSrcW, nSrc, loc);
+  }
+
+#endif
+
+
+
+// Unicode/MBCS conversion macros are only available on implementations of
+// the "C" library that have the non-standard _alloca function.  As far as I
+// know that's only Microsoft's though I've heard that the function exists
+// elsewhere.
+
+#if defined(SS_ALLOCA) && !defined SS_NO_CONVERSION
+
+    #include <malloc.h>  // needed for _alloca
+
+    // Define our conversion macros to look exactly like Microsoft's to
+    // facilitate using this stuff both with and without MFC/ATL
+
+    #ifdef _CONVERSION_USES_THREAD_LOCALE
+
+      #ifndef _DEBUG
+        #define SSCVT int _cvt; _cvt; UINT _acp=GetACP(); \
+          _acp; PCWSTR _pw; _pw; PCSTR _pa; _pa
+      #else
+        #define SSCVT int _cvt = 0; _cvt; UINT _acp=GetACP();\
+           _acp; PCWSTR _pw=0; _pw; PCSTR _pa=0; _pa
+      #endif
+      #define SSA2W(pa) (\
+        ((_pa = pa) == 0) ? 0 : (\
+          _cvt = (sslen(_pa)),\
+          StdCodeCvt((PWSTR) _alloca((_cvt+1)*2), (_cvt+1)*2, \
+              _pa, _cvt, _acp)))
+      #define SSW2A(pw) (\
+        ((_pw = pw) == 0) ? 0 : (\
+          _cvt = sslen(_pw), \
+          StdCodeCvt((LPSTR) _alloca((_cvt+1)*2), (_cvt+1)*2, \
+          _pw, _cvt, _acp)))
+  #else
+
+      #ifndef _DEBUG
+        #define SSCVT int _cvt; _cvt; UINT _acp=CP_ACP; _acp;\
+           PCWSTR _pw; _pw; PCSTR _pa; _pa
+      #else
+        #define SSCVT int _cvt = 0; _cvt; UINT _acp=CP_ACP; \
+          _acp; PCWSTR _pw=0; _pw; PCSTR _pa=0; _pa
+      #endif
+      #define SSA2W(pa) (\
+        ((_pa = pa) == 0) ? 0 : (\
+          _cvt = (sslen(_pa)),\
+          StdCodeCvt((PWSTR) _alloca((_cvt+1)*2), (_cvt+1)*2, \
+          _pa, _cvt)))
+      #define SSW2A(pw) (\
+        ((_pw = pw) == 0) ? 0 : (\
+          _cvt = (sslen(_pw)),\
+          StdCodeCvt((LPSTR) _alloca((_cvt+1)*2), (_cvt+1)*2, \
+          _pw, _cvt)))
+    #endif
+
+    #define SSA2CW(pa) ((PCWSTR)SSA2W((pa)))
+    #define SSW2CA(pw) ((PCSTR)SSW2A((pw)))
+
+    #ifdef UNICODE
+      #define SST2A  SSW2A
+      #define SSA2T  SSA2W
+      #define SST2CA  SSW2CA
+      #define SSA2CT  SSA2CW
+    // (Did you get a compiler error here about not being able to convert
+    // PTSTR into PWSTR?  Then your _UNICODE and UNICODE flags are messed
+    // up.  Best bet: #define BOTH macros before including any MS headers.)
+      inline PWSTR  SST2W(PTSTR p)      { return p; }
+      inline PTSTR  SSW2T(PWSTR p)      { return p; }
+      inline PCWSTR  SST2CW(PCTSTR p)    { return p; }
+      inline PCTSTR  SSW2CT(PCWSTR p)    { return p; }
+    #else
+      #define SST2W  SSA2W
+      #define SSW2T  SSW2A
+      #define SST2CW  SSA2CW
+      #define SSW2CT  SSW2CA
+      inline PSTR    SST2A(PTSTR p)      { return p; }
+      inline PTSTR  SSA2T(PSTR p)      { return p; }
+      inline PCSTR  SST2CA(PCTSTR p)    { return p; }
+      inline PCTSTR  SSA2CT(PCSTR p)      { return p; }
+    #endif // #ifdef UNICODE
+
+    #if defined(UNICODE)
+    // in these cases the default (TCHAR) is the same as OLECHAR
+      inline PCOLESTR  SST2COLE(PCTSTR p)    { return p; }
+      inline PCTSTR  SSOLE2CT(PCOLESTR p)  { return p; }
+      inline POLESTR  SST2OLE(PTSTR p)    { return p; }
+      inline PTSTR  SSOLE2T(POLESTR p)    { return p; }
+    #elif defined(OLE2ANSI)
+    // in these cases the default (TCHAR) is the same as OLECHAR
+      inline PCOLESTR  SST2COLE(PCTSTR p)    { return p; }
+      inline PCTSTR  SSOLE2CT(PCOLESTR p)  { return p; }
+      inline POLESTR  SST2OLE(PTSTR p)    { return p; }
+      inline PTSTR  SSOLE2T(POLESTR p)    { return p; }
+    #else
+      //CharNextW doesn't work on Win95 so we use this
+      #define SST2COLE(pa)  SSA2CW((pa))
+      #define SST2OLE(pa)    SSA2W((pa))
+      #define SSOLE2CT(po)  SSW2CA((po))
+      #define SSOLE2T(po)    SSW2A((po))
+    #endif
+
+    #ifdef OLE2ANSI
+      #define SSW2OLE    SSW2A
+      #define SSOLE2W    SSA2W
+      #define SSW2COLE  SSW2CA
+      #define SSOLE2CW  SSA2CW
+      inline POLESTR    SSA2OLE(PSTR p)    { return p; }
+      inline PSTR      SSOLE2A(POLESTR p)  { return p; }
+      inline PCOLESTR    SSA2COLE(PCSTR p)  { return p; }
+      inline PCSTR    SSOLE2CA(PCOLESTR p){ return p; }
+    #else
+      #define SSA2OLE    SSA2W
+      #define SSOLE2A    SSW2A
+      #define SSA2COLE  SSA2CW
+      #define SSOLE2CA  SSW2CA
+      inline POLESTR    SSW2OLE(PWSTR p)  { return p; }
+      inline PWSTR    SSOLE2W(POLESTR p)  { return p; }
+      inline PCOLESTR    SSW2COLE(PCWSTR p)  { return p; }
+      inline PCWSTR    SSOLE2CW(PCOLESTR p){ return p; }
+    #endif
+
+    // Above we've defined macros that look like MS' but all have
+    // an 'SS' prefix.  Now we need the real macros.  We'll either
+    // get them from the macros above or from MFC/ATL.
+
+  #if defined (USES_CONVERSION)
+
+    #define _NO_STDCONVERSION  // just to be consistent
+
+  #else
+
+    #ifdef _MFC_VER
+
+      #include <afxconv.h>
+      #define _NO_STDCONVERSION // just to be consistent
+
+    #else
+
+      #define USES_CONVERSION SSCVT
+      #define A2CW      SSA2CW
+      #define W2CA      SSW2CA
+      #define T2A        SST2A
+      #define A2T        SSA2T
+      #define T2W        SST2W
+      #define W2T        SSW2T
+      #define T2CA      SST2CA
+      #define A2CT      SSA2CT
+      #define T2CW      SST2CW
+      #define W2CT      SSW2CT
+      #define ocslen      sslen
+      #define ocscpy      sscpy
+      #define T2COLE      SST2COLE
+      #define OLE2CT      SSOLE2CT
+      #define T2OLE      SST2COLE
+      #define OLE2T      SSOLE2CT
+      #define A2OLE      SSA2OLE
+      #define OLE2A      SSOLE2A
+      #define W2OLE      SSW2OLE
+      #define OLE2W      SSOLE2W
+      #define A2COLE      SSA2COLE
+      #define OLE2CA      SSOLE2CA
+      #define W2COLE      SSW2COLE
+      #define OLE2CW      SSOLE2CW
+
+    #endif // #ifdef _MFC_VER
+  #endif // #ifndef USES_CONVERSION
+#endif // #ifndef SS_NO_CONVERSION
+
+// Define ostring - generic name for std::basic_string<OLECHAR>
+
+#if !defined(ostring) && !defined(OSTRING_DEFINED)
+  typedef std::basic_string<OLECHAR> ostring;
+  #define OSTRING_DEFINED
+#endif
+
+// StdCodeCvt when there's no conversion to be done
+template <typename T>
+inline T* StdCodeCvt(T* pDst, int nDst, const T* pSrc, int nSrc)
+{
+  int nChars = SSMIN(nSrc, nDst);
+
+  if ( nChars > 0 )
+  {
+    pDst[0]        = '\0';
+    std::basic_string<T>::traits_type::copy(pDst, pSrc, nChars);
+//    std::char_traits<T>::copy(pDst, pSrc, nChars);
+    pDst[nChars]  = '\0';
+  }
+
+  return pDst;
+}
+inline PSTR StdCodeCvt(PSTR pDst, int nDst, PCUSTR pSrc, int nSrc)
+{
+  return StdCodeCvt(pDst, nDst, (PCSTR)pSrc, nSrc);
+}
+inline PUSTR StdCodeCvt(PUSTR pDst, int nDst, PCSTR pSrc, int nSrc)
+{
+  return (PUSTR)StdCodeCvt((PSTR)pDst, nDst, pSrc, nSrc);
+}
+
+// Define tstring -- generic name for std::basic_string<TCHAR>
+
+#if !defined(tstring) && !defined(TSTRING_DEFINED)
+  typedef std::basic_string<TCHAR> tstring;
+  #define TSTRING_DEFINED
+#endif
+
+// a very shorthand way of applying the fix for KB problem Q172398
+// (basic_string assignment bug)
+
+#if defined ( _MSC_VER ) && ( _MSC_VER < 1200 )
+  #define Q172398(x) (x).erase()
+#else
+  #define Q172398(x)
+#endif
+
+// =============================================================================
+// INLINE FUNCTIONS ON WHICH CSTDSTRING RELIES
+//
+// Usually for generic text mapping, we rely on preprocessor macro definitions
+// to map to string functions.  However the CStdStr<> template cannot use
+// macro-based generic text mappings because its character types do not get
+// resolved until template processing which comes AFTER macro processing.  In
+// other words, the preprocessor macro UNICODE is of little help to us in the
+// CStdStr template
+//
+// Therefore, to keep the CStdStr declaration simple, we have these inline
+// functions.  The template calls them often.  Since they are inline (and NOT
+// exported when this is built as a DLL), they will probably be resolved away
+// to nothing.
+//
+// Without these functions, the CStdStr<> template would probably have to broken
+// out into two, almost identical classes.  Either that or it would be a huge,
+// convoluted mess, with tons of "if" statements all over the place checking the
+// size of template parameter CT.
+// =============================================================================
+
+#ifdef SS_NO_LOCALE
+
+  // --------------------------------------------------------------------------
+  // Win32 GetStringTypeEx wrappers
+  // --------------------------------------------------------------------------
+  inline bool wsGetStringType(LCID lc, DWORD dwT, PCSTR pS, int nSize,
+    WORD* pWd)
+  {
+    return FALSE != GetStringTypeExA(lc, dwT, pS, nSize, pWd);
+  }
+  inline bool wsGetStringType(LCID lc, DWORD dwT, PCWSTR pS, int nSize,
+    WORD* pWd)
+  {
+    return FALSE != GetStringTypeExW(lc, dwT, pS, nSize, pWd);
+  }
+
+
+  template<typename CT>
+    inline bool ssisspace (CT t)
+  {
+    WORD toYourMother;
+    return  wsGetStringType(GetThreadLocale(), CT_CTYPE1, &t, 1, &toYourMother)
+      && 0 != (C1_BLANK & toYourMother);
+  }
+
+#endif
+
+// If they defined SS_NO_REFCOUNT, then we must convert all assignments
+
+#if defined (_MSC_VER) && (_MSC_VER < 1300)
+  #ifdef SS_NO_REFCOUNT
+    #define SSREF(x) (x).c_str()
+  #else
+    #define SSREF(x) (x)
+  #endif
+#else
+  #define SSREF(x) (x)
+#endif
+
+// -----------------------------------------------------------------------------
+// sslen: strlen/wcslen wrappers
+// -----------------------------------------------------------------------------
+template<typename CT> inline int sslen(const CT* pT)
+{
+  return 0 == pT ? 0 : (int)std::basic_string<CT>::traits_type::length(pT);
+//  return 0 == pT ? 0 : std::char_traits<CT>::length(pT);
+}
+inline SS_NOTHROW int sslen(const std::string& s)
+{
+  return static_cast<int>(s.length());
+}
+inline SS_NOTHROW int sslen(const std::wstring& s)
+{
+  return static_cast<int>(s.length());
+}
+
+// -----------------------------------------------------------------------------
+// sstolower/sstoupper -- convert characters to upper/lower case
+// -----------------------------------------------------------------------------
+
+#ifdef SS_NO_LOCALE
+  inline char sstoupper(char ch)    { return (char)::toupper(ch); }
+  inline wchar_t sstoupper(wchar_t ch){ return (wchar_t)::towupper(ch); }
+  inline char sstolower(char ch)    { return (char)::tolower(ch); }
+  inline wchar_t sstolower(wchar_t ch){ return (wchar_t)::tolower(ch); }
+#else
+  template<typename CT>
+  inline CT sstolower(const CT& t, const std::locale& loc = std::locale())
+  {
+    return std::tolower<CT>(t, loc);
+  }
+  template<typename CT>
+  inline CT sstoupper(const CT& t, const std::locale& loc = std::locale())
+  {
+    return std::toupper<CT>(t, loc);
+  }
+#endif
+
+// -----------------------------------------------------------------------------
+// ssasn: assignment functions -- assign "sSrc" to "sDst"
+// -----------------------------------------------------------------------------
+typedef std::string::size_type    SS_SIZETYPE; // just for shorthand, really
+typedef std::string::pointer    SS_PTRTYPE;
+typedef std::wstring::size_type    SW_SIZETYPE;
+typedef std::wstring::pointer    SW_PTRTYPE;
+
+
+template <typename T>
+inline void  ssasn(std::basic_string<T>& sDst, const std::basic_string<T>& sSrc)
+{
+  if ( sDst.c_str() != sSrc.c_str() )
+  {
+    sDst.erase();
+    sDst.assign(SSREF(sSrc));
+  }
+}
+template <typename T>
+inline void  ssasn(std::basic_string<T>& sDst, const T *pA)
+{
+  // Watch out for NULLs, as always.
+
+  if ( 0 == pA )
+  {
+    sDst.erase();
+  }
+
+  // If pA actually points to part of sDst, we must NOT erase(), but
+  // rather take a substring
+
+  else if ( pA >= sDst.c_str() && pA <= sDst.c_str() + sDst.size() )
+  {
+    sDst =sDst.substr(static_cast<typename std::basic_string<T>::size_type>(pA-sDst.c_str()));
+  }
+
+  // Otherwise (most cases) apply the assignment bug fix, if applicable
+  // and do the assignment
+
+  else
+  {
+    Q172398(sDst);
+    sDst.assign(pA);
+  }
+}
+inline void  ssasn(std::string& sDst, const std::wstring& sSrc)
+{
+  if ( sSrc.empty() )
+  {
+    sDst.erase();
+  }
+  else
+  {
+    int nDst  = static_cast<int>(sSrc.size());
+
+    // In MBCS builds, pad the buffer to account for the possibility of
+    // some 3 byte characters.  Not perfect but should get most cases.
+
+#ifdef SS_MBCS
+    // In MBCS builds, we don't know how long the destination string will be.
+    nDst  = static_cast<int>(static_cast<double>(nDst) * 1.3);
+    sDst.resize(nDst+1);
+    PCSTR szCvt = StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()), nDst,
+      sSrc.c_str(), static_cast<int>(sSrc.size()));
+    sDst.resize(sslen(szCvt));
+#else
+    sDst.resize(nDst+1);
+    StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()), nDst,
+      sSrc.c_str(), static_cast<int>(sSrc.size()));
+    sDst.resize(sSrc.size());
+#endif
+  }
+}
+inline void  ssasn(std::string& sDst, PCWSTR pW)
+{
+  int nSrc  = sslen(pW);
+  if ( nSrc > 0 )
+  {
+    int nSrc  = sslen(pW);
+    int nDst  = nSrc;
+
+    // In MBCS builds, pad the buffer to account for the possibility of
+    // some 3 byte characters.  Not perfect but should get most cases.
+
+#ifdef SS_MBCS
+    nDst  = static_cast<int>(static_cast<double>(nDst) * 1.3);
+    // In MBCS builds, we don't know how long the destination string will be.
+    sDst.resize(nDst + 1);
+    PCSTR szCvt = StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()), nDst,
+      pW, nSrc);
+    sDst.resize(sslen(szCvt));
+#else
+    sDst.resize(nDst + 1);
+    StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()), nDst, pW, nSrc);
+    sDst.resize(nDst);
+#endif
+  }
+  else
+  {
+    sDst.erase();
+  }
+}
+inline void ssasn(std::string& sDst, const int nNull)
+{
+  //UNUSED(nNull);
+  ASSERT(nNull==0);
+  sDst.assign("");
+}
+#undef StrSizeType
+inline void  ssasn(std::wstring& sDst, const std::string& sSrc)
+{
+  if ( sSrc.empty() )
+  {
+    sDst.erase();
+  }
+  else
+  {
+    int nSrc  = static_cast<int>(sSrc.size());
+    int nDst  = nSrc;
+
+    sDst.resize(nSrc+1);
+    PCWSTR szCvt = StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()), nDst,
+      sSrc.c_str(), nSrc);
+
+    sDst.resize(sslen(szCvt));
+  }
+}
+inline void  ssasn(std::wstring& sDst, PCSTR pA)
+{
+  int nSrc  = sslen(pA);
+
+  if ( 0 == nSrc )
+  {
+    sDst.erase();
+  }
+  else
+  {
+    int nDst  = nSrc;
+    sDst.resize(nDst+1);
+    PCWSTR szCvt = StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()), nDst, pA,
+      nSrc);
+
+    sDst.resize(sslen(szCvt));
+  }
+}
+inline void ssasn(std::wstring& sDst, const int nNull)
+{
+  //UNUSED(nNull);
+  ASSERT(nNull==0);
+  sDst.assign(L"");
+}
+
+// -----------------------------------------------------------------------------
+// ssadd: string object concatenation -- add second argument to first
+// -----------------------------------------------------------------------------
+inline void  ssadd(std::string& sDst, const std::wstring& sSrc)
+{
+  int nSrc  = static_cast<int>(sSrc.size());
+
+  if ( nSrc > 0 )
+  {
+    int nDst  = static_cast<int>(sDst.size());
+    int nAdd  = nSrc;
+
+    // In MBCS builds, pad the buffer to account for the possibility of
+    // some 3 byte characters.  Not perfect but should get most cases.
+
+#ifdef SS_MBCS
+    nAdd    = static_cast<int>(static_cast<double>(nAdd) * 1.3);
+    sDst.resize(nDst+nAdd+1);
+    PCSTR szCvt = StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()+nDst),
+      nAdd, sSrc.c_str(), nSrc);
+    sDst.resize(nDst + sslen(szCvt));
+#else
+    sDst.resize(nDst+nAdd+1);
+    StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()+nDst), nAdd, sSrc.c_str(), nSrc);
+    sDst.resize(nDst + nAdd);
+#endif
+  }
+}
+template <typename T>
+inline void  ssadd(typename std::basic_string<T>& sDst, const typename std::basic_string<T>& sSrc)
+{
+  sDst += sSrc;
+}
+inline void  ssadd(std::string& sDst, PCWSTR pW)
+{
+  int nSrc    = sslen(pW);
+  if ( nSrc > 0 )
+  {
+    int nDst  = static_cast<int>(sDst.size());
+    int nAdd  = nSrc;
+
+#ifdef SS_MBCS
+    nAdd  = static_cast<int>(static_cast<double>(nAdd) * 1.3);
+    sDst.resize(nDst + nAdd + 1);
+    PCSTR szCvt = StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()+nDst),
+      nAdd, pW, nSrc);
+    sDst.resize(nDst + sslen(szCvt));
+#else
+    sDst.resize(nDst + nAdd + 1);
+    StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()+nDst), nAdd, pW, nSrc);
+    sDst.resize(nDst + nSrc);
+#endif
+  }
+}
+template <typename T>
+inline void  ssadd(typename std::basic_string<T>& sDst, const T *pA)
+{
+  if ( pA )
+  {
+    // If the string being added is our internal string or a part of our
+    // internal string, then we must NOT do any reallocation without
+    // first copying that string to another object (since we're using a
+    // direct pointer)
+
+    if ( pA >= sDst.c_str() && pA <= sDst.c_str()+sDst.length())
+    {
+      if ( sDst.capacity() <= sDst.size()+sslen(pA) )
+        sDst.append(std::basic_string<T>(pA));
+      else
+        sDst.append(pA);
+    }
+    else
+    {
+      sDst.append(pA);
+    }
+  }
+}
+inline void  ssadd(std::wstring& sDst, const std::string& sSrc)
+{
+  if ( !sSrc.empty() )
+  {
+    int nSrc  = static_cast<int>(sSrc.size());
+    int nDst  = static_cast<int>(sDst.size());
+
+    sDst.resize(nDst + nSrc + 1);
+#ifdef SS_MBCS
+    PCWSTR szCvt = StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()+nDst),
+      nSrc, sSrc.c_str(), nSrc+1);
+    sDst.resize(nDst + sslen(szCvt));
+#else
+    StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()+nDst), nSrc, sSrc.c_str(), nSrc+1);
+    sDst.resize(nDst + nSrc);
+#endif
+  }
+}
+inline void  ssadd(std::wstring& sDst, PCSTR pA)
+{
+  int nSrc    = sslen(pA);
+
+  if ( nSrc > 0 )
+  {
+    int nDst  = static_cast<int>(sDst.size());
+
+    sDst.resize(nDst + nSrc + 1);
+#ifdef SS_MBCS
+    PCWSTR szCvt = StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()+nDst),
+      nSrc, pA, nSrc+1);
+    sDst.resize(nDst + sslen(szCvt));
+#else
+    StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()+nDst), nSrc, pA, nSrc+1);
+    sDst.resize(nDst + nSrc);
+#endif
+  }
+}
+
+// -----------------------------------------------------------------------------
+// sscmp: comparison (case sensitive, not affected by locale)
+// -----------------------------------------------------------------------------
+template<typename CT>
+inline int sscmp(const CT* pA1, const CT* pA2)
+{
+    CT f;
+    CT l;
+
+    do
+    {
+      f = *(pA1++);
+      l = *(pA2++);
+    } while ( (f) && (f == l) );
+
+    return (int)(f - l);
+}
+
+// -----------------------------------------------------------------------------
+// ssicmp: comparison (case INsensitive, not affected by locale)
+// -----------------------------------------------------------------------------
+template<typename CT>
+inline int ssicmp(const CT* pA1, const CT* pA2)
+{
+  // Using the "C" locale = "not affected by locale"
+
+  std::locale loc = std::locale::classic();
+    const std::ctype<CT>& ct = SS_USE_FACET(loc, std::ctype<CT>);
+    CT f;
+    CT l;
+
+    do
+    {
+      f = ct.tolower(*(pA1++));
+      l = ct.tolower(*(pA2++));
+    } while ( (f) && (f == l) );
+
+    return (int)(f - l);
+}
+
+// -----------------------------------------------------------------------------
+// ssupr/sslwr: Uppercase/Lowercase conversion functions
+// -----------------------------------------------------------------------------
+
+template<typename CT>
+inline void sslwr(CT* pT, size_t nLen, const std::locale& loc=std::locale())
+{
+  SS_USE_FACET(loc, std::ctype<CT>).tolower(pT, pT+nLen);
+}
+template<typename CT>
+inline void ssupr(CT* pT, size_t nLen, const std::locale& loc=std::locale())
+{
+  SS_USE_FACET(loc, std::ctype<CT>).toupper(pT, pT+nLen);
+}
+
+// -----------------------------------------------------------------------------
+// vsprintf/vswprintf or _vsnprintf/_vsnwprintf equivalents.  In standard
+// builds we can't use _vsnprintf/_vsnwsprintf because they're MS extensions.
+//
+// -----------------------------------------------------------------------------
+// Borland's headers put some ANSI "C" functions in the 'std' namespace.
+// Promote them to the global namespace so we can use them here.
+
+#if defined(__BORLANDC__)
+    using std::vsprintf;
+    using std::vswprintf;
+#endif
+
+  // GNU is supposed to have vsnprintf and vsnwprintf.  But only the newer
+  // distributions do.
+
+#if defined(__GNUC__)
+
+  inline int ssvsprintf(PSTR pA, size_t nCount, PCSTR pFmtA, va_list vl)
+  {
+    return vsnprintf(pA, nCount, pFmtA, vl);
+  }
+  inline int ssvsprintf(PWSTR pW, size_t nCount, PCWSTR pFmtW, va_list vl)
+  {
+    return vswprintf(pW, nCount, pFmtW, vl);
+  }
+
+  // Microsofties can use
+#elif defined(_MSC_VER) && !defined(SS_ANSI)
+
+  inline int  ssvsprintf(PSTR pA, size_t nCount, PCSTR pFmtA, va_list vl)
+  {
+    return _vsnprintf(pA, nCount, pFmtA, vl);
+  }
+  inline int  ssvsprintf(PWSTR pW, size_t nCount, PCWSTR pFmtW, va_list vl)
+  {
+    return _vsnwprintf(pW, nCount, pFmtW, vl);
+  }
+
+#elif defined (SS_DANGEROUS_FORMAT)  // ignore buffer size parameter if needed?
+
+  inline int ssvsprintf(PSTR pA, size_t /*nCount*/, PCSTR pFmtA, va_list vl)
+  {
+    return vsprintf(pA, pFmtA, vl);
+  }
+
+  inline int ssvsprintf(PWSTR pW, size_t nCount, PCWSTR pFmtW, va_list vl)
+  {
+    // JMO: Some distributions of the "C" have a version of vswprintf that
+        // takes 3 arguments (e.g. Microsoft, Borland, GNU).  Others have a
+        // version which takes 4 arguments (an extra "count" argument in the
+        // second position.  The best stab I can take at this so far is that if
+        // you are NOT running with MS, Borland, or GNU, then I'll assume you
+        // have the version that takes 4 arguments.
+        //
+        // I'm sure that these checks don't catch every platform correctly so if
+        // you get compiler errors on one of the lines immediately below, it's
+        // probably because your implemntation takes a different number of
+        // arguments.  You can comment out the offending line (and use the
+        // alternate version) or you can figure out what compiler flag to check
+        // and add that preprocessor check in.  Regardless, if you get an error
+        // on these lines, I'd sure like to hear from you about it.
+        //
+        // Thanks to Ronny Schulz for the SGI-specific checks here.
+
+//  #if !defined(__MWERKS__) && !defined(__SUNPRO_CC_COMPAT) && !defined(__SUNPRO_CC)
+    #if    !defined(_MSC_VER) \
+        && !defined (__BORLANDC__) \
+        && !defined(__GNUC__) \
+        && !defined(__sgi)
+
+        return vswprintf(pW, nCount, pFmtW, vl);
+
+    // suddenly with the current SGI 7.3 compiler there is no such function as
+    // vswprintf and the substitute needs explicit casts to compile
+
+    #elif defined(__sgi)
+
+        nCount;
+        return vsprintf( (char *)pW, (char *)pFmtW, vl);
+
+    #else
+
+        nCount;
+        return vswprintf(pW, pFmtW, vl);
+
+    #endif
+
+  }
+
+#endif
+
+  // GOT COMPILER PROBLEMS HERE?
+  // ---------------------------
+  // Does your compiler choke on one or more of the following 2 functions?  It
+  // probably means that you don't have have either vsnprintf or vsnwprintf in
+  // your version of the CRT.  This is understandable since neither is an ANSI
+  // "C" function.  However it still leaves you in a dilemma.  In order to make
+  // this code build, you're going to have to to use some non-length-checked
+  // formatting functions that every CRT has:  vsprintf and vswprintf.
+  //
+  // This is very dangerous.  With the proper erroneous (or malicious) code, it
+  // can lead to buffer overlows and crashing your PC.  Use at your own risk
+  // In order to use them, just #define SS_DANGEROUS_FORMAT at the top of
+  // this file.
+  //
+  // Even THEN you might not be all the way home due to some non-conforming
+  // distributions.  More on this in the comments below.
+
+  inline int  ssnprintf(PSTR pA, size_t nCount, PCSTR pFmtA, va_list vl)
+  {
+  #ifdef _MSC_VER
+      return _vsnprintf(pA, nCount, pFmtA, vl);
+  #else
+      return vsnprintf(pA, nCount, pFmtA, vl);
+  #endif
+  }
+  inline int  ssnprintf(PWSTR pW, size_t nCount, PCWSTR pFmtW, va_list vl)
+  {
+  #ifdef _MSC_VER
+      return _vsnwprintf(pW, nCount, pFmtW, vl);
+  #else
+      return vswprintf(pW, nCount, pFmtW, vl);
+  #endif
+  }
+
+
+
+
+// -----------------------------------------------------------------------------
+// ssload: Type safe, overloaded ::LoadString wrappers
+// There is no equivalent of these in non-Win32-specific builds.  However, I'm
+// thinking that with the message facet, there might eventually be one
+// -----------------------------------------------------------------------------
+#if defined (SS_WIN32) && !defined(SS_ANSI)
+  inline int ssload(HMODULE hInst, UINT uId, PSTR pBuf, int nMax)
+  {
+    return ::LoadStringA(hInst, uId, pBuf, nMax);
+  }
+  inline int ssload(HMODULE hInst, UINT uId, PWSTR pBuf, int nMax)
+  {
+    return ::LoadStringW(hInst, uId, pBuf, nMax);
+  }
+#if defined ( _MSC_VER ) && ( _MSC_VER >= 1500 )
+  inline int ssload(HMODULE hInst, UINT uId, uint16_t *pBuf, int nMax)
+  {
+    return 0;
+  }
+  inline int ssload(HMODULE hInst, UINT uId, uint32_t *pBuf, int nMax)
+  {
+    return 0;
+  }
+#endif
+#endif
+
+
+// -----------------------------------------------------------------------------
+// sscoll/ssicoll: Collation wrappers
+//    Note -- with MSVC I have reversed the arguments order here because the
+//    functions appear to return the opposite of what they should
+// -----------------------------------------------------------------------------
+#ifndef SS_NO_LOCALE
+template <typename CT>
+inline int sscoll(const CT* sz1, int nLen1, const CT* sz2, int nLen2)
+{
+  const std::collate<CT>& coll =
+    SS_USE_FACET(std::locale(), std::collate<CT>);
+
+  return coll.compare(sz2, sz2+nLen2, sz1, sz1+nLen1);
+}
+template <typename CT>
+inline int ssicoll(const CT* sz1, int nLen1, const CT* sz2, int nLen2)
+{
+  const std::locale loc;
+  const std::collate<CT>& coll = SS_USE_FACET(loc, std::collate<CT>);
+
+  // Some implementations seem to have trouble using the collate<>
+  // facet typedefs so we'll just default to basic_string and hope
+  // that's what the collate facet uses (which it generally should)
+
+//  std::collate<CT>::string_type s1(sz1);
+//  std::collate<CT>::string_type s2(sz2);
+  const std::basic_string<CT> sEmpty;
+    std::basic_string<CT> s1(sz1 ? sz1 : sEmpty.c_str());
+    std::basic_string<CT> s2(sz2 ? sz2 : sEmpty.c_str());
+
+  sslwr(const_cast<CT*>(s1.c_str()), nLen1, loc);
+  sslwr(const_cast<CT*>(s2.c_str()), nLen2, loc);
+  return coll.compare(s2.c_str(), s2.c_str()+nLen2,
+            s1.c_str(), s1.c_str()+nLen1);
+}
+#endif
+
+
+// -----------------------------------------------------------------------------
+// ssfmtmsg: FormatMessage equivalents.  Needed because I added a CString facade
+// Again -- no equivalent of these on non-Win32 builds but their might one day
+// be one if the message facet gets implemented
+// -----------------------------------------------------------------------------
+#if defined (SS_WIN32) && !defined(SS_ANSI)
+  inline DWORD ssfmtmsg(DWORD dwFlags, LPCVOID pSrc, DWORD dwMsgId,
+              DWORD dwLangId, PSTR pBuf, DWORD nSize,
+              va_list* vlArgs)
+  {
+    return FormatMessageA(dwFlags, pSrc, dwMsgId, dwLangId,
+                pBuf, nSize,vlArgs);
+  }
+  inline DWORD ssfmtmsg(DWORD dwFlags, LPCVOID pSrc, DWORD dwMsgId,
+              DWORD dwLangId, PWSTR pBuf, DWORD nSize,
+              va_list* vlArgs)
+  {
+    return FormatMessageW(dwFlags, pSrc, dwMsgId, dwLangId,
+                pBuf, nSize,vlArgs);
+  }
+#else
+#endif
+
+
+
+// FUNCTION: sscpy.  Copies up to 'nMax' characters from pSrc to pDst.
+// -----------------------------------------------------------------------------
+// FUNCTION:  sscpy
+//    inline int sscpy(PSTR pDst, PCSTR pSrc, int nMax=-1);
+//    inline int sscpy(PUSTR pDst,  PCSTR pSrc, int nMax=-1)
+//    inline int sscpy(PSTR pDst, PCWSTR pSrc, int nMax=-1);
+//    inline int sscpy(PWSTR pDst, PCWSTR pSrc, int nMax=-1);
+//    inline int sscpy(PWSTR pDst, PCSTR pSrc, int nMax=-1);
+//
+// DESCRIPTION:
+//    This function is very much (but not exactly) like strcpy.  These
+//    overloads simplify copying one C-style string into another by allowing
+//    the caller to specify two different types of strings if necessary.
+//
+//    The strings must NOT overlap
+//
+//    "Character" is expressed in terms of the destination string, not
+//    the source.  If no 'nMax' argument is supplied, then the number of
+//    characters copied will be sslen(pSrc).  A NULL terminator will
+//    also be added so pDst must actually be big enough to hold nMax+1
+//    characters.  The return value is the number of characters copied,
+//    not including the NULL terminator.
+//
+// PARAMETERS:
+//    pSrc - the string to be copied FROM.  May be a char based string, an
+//         MBCS string (in Win32 builds) or a wide string (wchar_t).
+//    pSrc - the string to be copied TO.  Also may be either MBCS or wide
+//    nMax - the maximum number of characters to be copied into szDest.  Note
+//         that this is expressed in whatever a "character" means to pDst.
+//         If pDst is a wchar_t type string than this will be the maximum
+//         number of wchar_ts that my be copied.  The pDst string must be
+//         large enough to hold least nMaxChars+1 characters.
+//         If the caller supplies no argument for nMax this is a signal to
+//         the routine to copy all the characters in pSrc, regardless of
+//         how long it is.
+//
+// RETURN VALUE: none
+// -----------------------------------------------------------------------------
+
+template<typename CT1, typename CT2>
+inline int sscpycvt(CT1* pDst, const CT2* pSrc, int nMax)
+{
+  // Note -- we assume pDst is big enough to hold pSrc.  If not, we're in
+  // big trouble.  No bounds checking.  Caveat emptor.
+
+  int nSrc = sslen(pSrc);
+
+  const CT1* szCvt = StdCodeCvt(pDst, nMax, pSrc, nSrc);
+
+  // If we're copying the same size characters, then all the "code convert"
+  // just did was basically memcpy so the #of characters copied is the same
+  // as the number requested.  I should probably specialize this function
+  // template to achieve this purpose as it is silly to do a runtime check
+  // of a fact known at compile time.  I'll get around to it.
+
+  return sslen(szCvt);
+}
+
+template<typename T>
+inline int sscpycvt(T* pDst, const T* pSrc, int nMax)
+{
+  int nCount = nMax;
+  for (; nCount > 0 && *pSrc; ++pSrc, ++pDst, --nCount)
+    std::basic_string<T>::traits_type::assign(*pDst, *pSrc);
+
+  *pDst = 0;
+  return nMax - nCount;
+}
+
+inline int sscpycvt(PWSTR pDst, PCSTR pSrc, int nMax)
+{
+  // Note -- we assume pDst is big enough to hold pSrc.  If not, we're in
+  // big trouble.  No bounds checking.  Caveat emptor.
+
+  const PWSTR szCvt = StdCodeCvt(pDst, nMax, pSrc, nMax);
+  return sslen(szCvt);
+}
+
+template<typename CT1, typename CT2>
+inline int sscpy(CT1* pDst, const CT2* pSrc, int nMax, int nLen)
+{
+  return sscpycvt(pDst, pSrc, SSMIN(nMax, nLen));
+}
+template<typename CT1, typename CT2>
+inline int sscpy(CT1* pDst, const CT2* pSrc, int nMax)
+{
+  return sscpycvt(pDst, pSrc, SSMIN(nMax, sslen(pSrc)));
+}
+template<typename CT1, typename CT2>
+inline int sscpy(CT1* pDst, const CT2* pSrc)
+{
+  return sscpycvt(pDst, pSrc, sslen(pSrc));
+}
+template<typename CT1, typename CT2>
+inline int sscpy(CT1* pDst, const std::basic_string<CT2>& sSrc, int nMax)
+{
+  return sscpycvt(pDst, sSrc.c_str(), SSMIN(nMax, (int)sSrc.length()));
+}
+template<typename CT1, typename CT2>
+inline int sscpy(CT1* pDst, const std::basic_string<CT2>& sSrc)
+{
+  return sscpycvt(pDst, sSrc.c_str(), (int)sSrc.length());
+}
+
+#ifdef SS_INC_COMDEF
+  template<typename CT1>
+  inline int sscpy(CT1* pDst, const _bstr_t& bs, int nMax)
+  {
+    return sscpycvt(pDst, static_cast<PCOLESTR>(bs),
+            SSMIN(nMax, static_cast<int>(bs.length())));
+  }
+  template<typename CT1>
+  inline int sscpy(CT1* pDst, const _bstr_t& bs)
+  {
+    return sscpy(pDst, bs, static_cast<int>(bs.length()));
+  }
+#endif
+
+
+// -----------------------------------------------------------------------------
+// Functional objects for changing case.  They also let you pass locales
+// -----------------------------------------------------------------------------
+
+#ifdef SS_NO_LOCALE
+  template<typename CT>
+  struct SSToUpper : public std::unary_function<CT, CT>
+  {
+    inline CT operator()(const CT& t) const
+    {
+      return sstoupper(t);
+    }
+  };
+  template<typename CT>
+  struct SSToLower : public std::unary_function<CT, CT>
+  {
+    inline CT operator()(const CT& t) const
+    {
+      return sstolower(t);
+    }
+  };
+#else
+  template<typename CT>
+  struct SSToUpper : public std::binary_function<CT, std::locale, CT>
+  {
+    inline CT operator()(const CT& t, const std::locale& loc) const
+    {
+      return sstoupper<CT>(t, loc);
+    }
+  };
+  template<typename CT>
+  struct SSToLower : public std::binary_function<CT, std::locale, CT>
+  {
+    inline CT operator()(const CT& t, const std::locale& loc) const
+    {
+      return sstolower<CT>(t, loc);
+    }
+  };
+#endif
+
+// This struct is used for TrimRight() and TrimLeft() function implementations.
+//template<typename CT>
+//struct NotSpace : public std::unary_function<CT, bool>
+//{
+//  const std::locale& loc;
+//  inline NotSpace(const std::locale& locArg) : loc(locArg) {}
+//  inline bool operator() (CT t) { return !std::isspace(t, loc); }
+//};
+template<typename CT>
+struct NotSpace : public std::unary_function<CT, bool>
+{
+  // DINKUMWARE BUG:
+  // Note -- using std::isspace in a COM DLL gives us access violations
+  // because it causes the dynamic addition of a function to be called
+  // when the library shuts down.  Unfortunately the list is maintained
+  // in DLL memory but the function is in static memory.  So the COM DLL
+  // goes away along with the function that was supposed to be called,
+  // and then later when the DLL CRT shuts down it unloads the list and
+  // tries to call the long-gone function.
+  // This is DinkumWare's implementation problem.  If you encounter this
+  // problem, you may replace the calls here with good old isspace() and
+  // iswspace() from the CRT unless they specify SS_ANSI
+
+#ifdef SS_NO_LOCALE
+
+  bool operator() (CT t) const { return !ssisspace(t); }
+
+#else
+  const std::locale loc;
+  NotSpace(const std::locale& locArg=std::locale()) : loc(locArg) {}
+  bool operator() (CT t) const { return !std::isspace(t, loc); }
+#endif
+};
+
+
+
+
+//      Now we can define the template (finally!)
+// =============================================================================
+// TEMPLATE: CStdStr
+//    template<typename CT> class CStdStr : public std::basic_string<CT>
+//
+// REMARKS:
+//    This template derives from basic_string<CT> and adds some MFC CString-
+//    like functionality
+//
+//    Basically, this is my attempt to make Standard C++ library strings as
+//    easy to use as the MFC CString class.
+//
+//    Note that although this is a template, it makes the assumption that the
+//    template argument (CT, the character type) is either char or wchar_t.
+// =============================================================================
+
+//#define CStdStr _SS  // avoid compiler warning 4786
+
+//    template<typename ARG> ARG& FmtArg(ARG& arg)  { return arg; }
+//    PCSTR  FmtArg(const std::string& arg)  { return arg.c_str(); }
+//    PCWSTR FmtArg(const std::wstring& arg) { return arg.c_str(); }
+
+template<typename ARG>
+struct FmtArg
+{
+    explicit FmtArg(const ARG& arg) : a_(arg) {}
+    const ARG& operator()() const { return a_; }
+    const ARG& a_;
+private:
+    FmtArg& operator=(const FmtArg&) { return *this; }
+};
+
+template<typename CT>
+class CStdStr : public std::basic_string<CT>
+{
+  // Typedefs for shorter names.  Using these names also appears to help
+  // us avoid some ambiguities that otherwise arise on some platforms
+
+  #define MYBASE std::basic_string<CT>         // my base class
+  //typedef typename std::basic_string<CT>    MYBASE;   // my base class
+  typedef CStdStr<CT>              MYTYPE;   // myself
+  typedef typename MYBASE::const_pointer    PCMYSTR; // PCSTR or PCWSTR
+  typedef typename MYBASE::pointer      PMYSTR;   // PSTR or PWSTR
+  typedef typename MYBASE::iterator      MYITER;  // my iterator type
+  typedef typename MYBASE::const_iterator    MYCITER; // you get the idea...
+  typedef typename MYBASE::reverse_iterator  MYRITER;
+  typedef typename MYBASE::size_type      MYSIZE;
+  typedef typename MYBASE::value_type      MYVAL;
+  typedef typename MYBASE::allocator_type    MYALLOC;
+
+public:
+  // shorthand conversion from PCTSTR to string resource ID
+  #define SSRES(pctstr)  LOWORD(reinterpret_cast<unsigned long>(pctstr))
+
+  bool TryLoad(const void* pT)
+  {
+    bool bLoaded = false;
+
+#if defined(SS_WIN32) && !defined(SS_ANSI)
+    if ( ( pT != NULL ) && SS_IS_INTRESOURCE(pT) )
+    {
+      UINT nId = LOWORD(reinterpret_cast<unsigned long>(pT));
+      if ( !LoadString(nId) )
+      {
+        TRACE(_T("Can't load string %u\n"), SSRES(pT));
+      }
+      bLoaded = true;
+    }
+#endif
+
+    return bLoaded;
+  }
+
+
+  // CStdStr inline constructors
+  CStdStr()
+  {
+  }
+
+  CStdStr(const MYTYPE& str) : MYBASE(SSREF(str))
+  {
+  }
+
+  CStdStr(const std::string& str)
+  {
+    ssasn(*this, SSREF(str));
+  }
+
+  CStdStr(const std::wstring& str)
+  {
+    ssasn(*this, SSREF(str));
+  }
+
+  CStdStr(PCMYSTR pT, MYSIZE n) : MYBASE(pT, n)
+  {
+  }
+
+#ifdef SS_UNSIGNED
+  CStdStr(PCUSTR pU)
+  {
+    *this = reinterpret_cast<PCSTR>(pU);
+  }
+#endif
+
+  CStdStr(PCSTR pA)
+  {
+  #ifdef SS_ANSI
+    *this = pA;
+  #else
+    if ( !TryLoad(pA) )
+      *this = pA;
+  #endif
+  }
+
+  CStdStr(PCWSTR pW)
+  {
+  #ifdef SS_ANSI
+    *this = pW;
+  #else
+    if ( !TryLoad(pW) )
+      *this = pW;
+  #endif
+  }
+
+  CStdStr(uint16_t* pW)
+  {
+  #ifdef SS_ANSI
+    *this = pW;
+  #else
+    if ( !TryLoad(pW) )
+      *this = pW;
+  #endif
+  }
+
+  CStdStr(uint32_t* pW)
+  {
+  #ifdef SS_ANSI
+    *this = pW;
+  #else
+    if ( !TryLoad(pW) )
+      *this = pW;
+  #endif
+  }
+
+  CStdStr(MYCITER first, MYCITER last)
+    : MYBASE(first, last)
+  {
+  }
+
+  CStdStr(MYSIZE nSize, MYVAL ch, const MYALLOC& al=MYALLOC())
+    : MYBASE(nSize, ch, al)
+  {
+  }
+
+  #ifdef SS_INC_COMDEF
+    CStdStr(const _bstr_t& bstr)
+    {
+      if ( bstr.length() > 0 )
+        this->append(static_cast<PCMYSTR>(bstr), bstr.length());
+    }
+  #endif
+
+  // CStdStr inline assignment operators -- the ssasn function now takes care
+  // of fixing  the MSVC assignment bug (see knowledge base article Q172398).
+  MYTYPE& operator=(const MYTYPE& str)
+  {
+    ssasn(*this, str);
+    return *this;
+  }
+
+  MYTYPE& operator=(const std::string& str)
+  {
+    ssasn(*this, str);
+    return *this;
+  }
+
+  MYTYPE& operator=(const std::wstring& str)
+  {
+    ssasn(*this, str);
+    return *this;
+  }
+
+  MYTYPE& operator=(PCSTR pA)
+  {
+    ssasn(*this, pA);
+    return *this;
+  }
+
+  MYTYPE& operator=(PCWSTR pW)
+  {
+    ssasn(*this, pW);
+    return *this;
+  }
+
+#ifdef SS_UNSIGNED
+  MYTYPE& operator=(PCUSTR pU)
+  {
+    ssasn(*this, reinterpret_cast<PCSTR>(pU));
+    return *this;
+  }
+#endif
+
+  MYTYPE& operator=(uint16_t* pA)
+  {
+    ssasn(*this, pA);
+    return *this;
+  }
+
+  MYTYPE& operator=(uint32_t* pA)
+  {
+    ssasn(*this, pA);
+    return *this;
+  }
+
+  MYTYPE& operator=(CT t)
+  {
+    Q172398(*this);
+    this->assign(1, t);
+    return *this;
+  }
+
+  #ifdef SS_INC_COMDEF
+    MYTYPE& operator=(const _bstr_t& bstr)
+    {
+      if ( bstr.length() > 0 )
+      {
+        this->assign(static_cast<PCMYSTR>(bstr), bstr.length());
+        return *this;
+      }
+      else
+      {
+        this->erase();
+        return *this;
+      }
+    }
+  #endif
+
+
+  // Overloads  also needed to fix the MSVC assignment bug (KB: Q172398)
+  //  *** Thanks to Pete The Plumber for catching this one ***
+  // They also are compiled if you have explicitly turned off refcounting
+  #if ( defined(_MSC_VER) && ( _MSC_VER < 1200 ) ) || defined(SS_NO_REFCOUNT)
+
+    MYTYPE& assign(const MYTYPE& str)
+    {
+      Q172398(*this);
+      sscpy(GetBuffer(str.size()+1), SSREF(str));
+      this->ReleaseBuffer(str.size());
+      return *this;
+    }
+
+    MYTYPE& assign(const MYTYPE& str, MYSIZE nStart, MYSIZE nChars)
+    {
+      // This overload of basic_string::assign is supposed to assign up to
+      // <nChars> or the NULL terminator, whichever comes first.  Since we
+      // are about to call a less forgiving overload (in which <nChars>
+      // must be a valid length), we must adjust the length here to a safe
+      // value.  Thanks to Ullrich Poll�hne for catching this bug
+
+      nChars    = SSMIN(nChars, str.length() - nStart);
+      MYTYPE strTemp(str.c_str()+nStart, nChars);
+      Q172398(*this);
+      this->assign(strTemp);
+      return *this;
+    }
+
+    MYTYPE& assign(const MYBASE& str)
+    {
+      ssasn(*this, str);
+      return *this;
+    }
+
+    MYTYPE& assign(const MYBASE& str, MYSIZE nStart, MYSIZE nChars)
+    {
+      // This overload of basic_string::assign is supposed to assign up to
+      // <nChars> or the NULL terminator, whichever comes first.  Since we
+      // are about to call a less forgiving overload (in which <nChars>
+      // must be a valid length), we must adjust the length here to a safe
+      // value. Thanks to Ullrich Poll�hne for catching this bug
+
+      nChars    = SSMIN(nChars, str.length() - nStart);
+
+      // Watch out for assignment to self
+
+      if ( this == &str )
+      {
+        MYTYPE strTemp(str.c_str() + nStart, nChars);
+        static_cast<MYBASE*>(this)->assign(strTemp);
+      }
+      else
+      {
+        Q172398(*this);
+        static_cast<MYBASE*>(this)->assign(str.c_str()+nStart, nChars);
+      }
+      return *this;
+    }
+
+    MYTYPE& assign(const CT* pC, MYSIZE nChars)
+    {
+      // Q172398 only fix -- erase before assigning, but not if we're
+      // assigning from our own buffer
+
+  #if defined ( _MSC_VER ) && ( _MSC_VER < 1200 )
+      if ( !this->empty() &&
+        ( pC < this->data() || pC > this->data() + this->capacity() ) )
+      {
+        this->erase();
+      }
+  #endif
+      Q172398(*this);
+      static_cast<MYBASE*>(this)->assign(pC, nChars);
+      return *this;
+    }
+
+    MYTYPE& assign(MYSIZE nChars, MYVAL val)
+    {
+      Q172398(*this);
+      static_cast<MYBASE*>(this)->assign(nChars, val);
+      return *this;
+    }
+
+    MYTYPE& assign(const CT* pT)
+    {
+      return this->assign(pT, MYBASE::traits_type::length(pT));
+    }
+
+    MYTYPE& assign(MYCITER iterFirst, MYCITER iterLast)
+    {
+  #if defined ( _MSC_VER ) && ( _MSC_VER < 1200 )
+      // Q172398 fix.  don't call erase() if we're assigning from ourself
+      if ( iterFirst < this->begin() ||
+                 iterFirst > this->begin() + this->size() )
+            {
+        this->erase()
+            }
+  #endif
+      this->replace(this->begin(), this->end(), iterFirst, iterLast);
+      return *this;
+    }
+  #endif
+
+
+  // -------------------------------------------------------------------------
+  // CStdStr inline concatenation.
+  // -------------------------------------------------------------------------
+  MYTYPE& operator+=(const MYTYPE& str)
+  {
+    ssadd(*this, str);
+    return *this;
+  }
+
+  MYTYPE& operator+=(const std::string& str)
+  {
+    ssadd(*this, str);
+    return *this;
+  }
+
+  MYTYPE& operator+=(const std::wstring& str)
+  {
+    ssadd(*this, str);
+    return *this;
+  }
+
+  MYTYPE& operator+=(PCSTR pA)
+  {
+    ssadd(*this, pA);
+    return *this;
+  }
+
+  MYTYPE& operator+=(PCWSTR pW)
+  {
+    ssadd(*this, pW);
+    return *this;
+  }
+
+  MYTYPE& operator+=(uint16_t* pW)
+  {
+    ssadd(*this, pW);
+    return *this;
+  }
+
+  MYTYPE& operator+=(uint32_t* pW)
+  {
+    ssadd(*this, pW);
+    return *this;
+  }
+
+  MYTYPE& operator+=(CT t)
+  {
+    this->append(1, t);
+    return *this;
+  }
+  #ifdef SS_INC_COMDEF  // if we have _bstr_t, define a += for it too.
+    MYTYPE& operator+=(const _bstr_t& bstr)
+    {
+      return this->operator+=(static_cast<PCMYSTR>(bstr));
+    }
+  #endif
+
+
+  // -------------------------------------------------------------------------
+  // Case changing functions
+  // -------------------------------------------------------------------------
+
+    MYTYPE& ToUpper(const std::locale& loc=std::locale())
+  {
+    // Note -- if there are any MBCS character sets in which the lowercase
+    // form a character takes up a different number of bytes than the
+    // uppercase form, this would probably not work...
+
+    std::transform(this->begin(),
+             this->end(),
+             this->begin(),
+#ifdef SS_NO_LOCALE
+             SSToUpper<CT>());
+#else
+             std::bind2nd(SSToUpper<CT>(), loc));
+#endif
+
+    // ...but if it were, this would probably work better.  Also, this way
+    // seems to be a bit faster when anything other then the "C" locale is
+    // used...
+
+//    if ( !empty() )
+//    {
+//      ssupr(this->GetBuf(), this->size(), loc);
+//      this->RelBuf();
+//    }
+
+    return *this;
+  }
+
+  MYTYPE& ToLower(const std::locale& loc=std::locale())
+  {
+    // Note -- if there are any MBCS character sets in which the lowercase
+    // form a character takes up a different number of bytes than the
+    // uppercase form, this would probably not work...
+
+    std::transform(this->begin(),
+             this->end(),
+             this->begin(),
+#ifdef SS_NO_LOCALE
+             SSToLower<CT>());
+#else
+             std::bind2nd(SSToLower<CT>(), loc));
+#endif
+
+    // ...but if it were, this would probably work better.  Also, this way
+    // seems to be a bit faster when anything other then the "C" locale is
+    // used...
+
+//    if ( !empty() )
+//    {
+//      sslwr(this->GetBuf(), this->size(), loc);
+//      this->RelBuf();
+//    }
+    return *this;
+  }
+
+
+  MYTYPE& Normalize()
+  {
+    return Trim().ToLower();
+  }
+
+
+  // -------------------------------------------------------------------------
+  // CStdStr -- Direct access to character buffer.  In the MS' implementation,
+  // the at() function that we use here also calls _Freeze() providing us some
+  // protection from multithreading problems associated with ref-counting.
+    // In VC 7 and later, of course, the ref-counting stuff is gone.
+  // -------------------------------------------------------------------------
+
+  CT* GetBuf(int nMinLen=-1)
+  {
+    if ( static_cast<int>(this->size()) < nMinLen )
+      this->resize(static_cast<MYSIZE>(nMinLen));
+
+    return this->empty() ? const_cast<CT*>(this->data()) : &(this->at(0));
+  }
+
+  CT* SetBuf(int nLen)
+  {
+    nLen = ( nLen > 0 ? nLen : 0 );
+    if ( this->capacity() < 1 && nLen == 0 )
+      this->resize(1);
+
+    this->resize(static_cast<MYSIZE>(nLen));
+    return const_cast<CT*>(this->data());
+  }
+  void RelBuf(int nNewLen=-1)
+  {
+    this->resize(static_cast<MYSIZE>(nNewLen > -1 ? nNewLen :
+                                                        sslen(this->c_str())));
+  }
+
+  void BufferRel()     { RelBuf(); }      // backwards compatability
+  CT*  Buffer()       { return GetBuf(); }  // backwards compatability
+  CT*  BufferSet(int nLen) { return SetBuf(nLen);}// backwards compatability
+
+  bool Equals(const CT* pT, bool bUseCase=false) const
+  {
+    return  0 == (bUseCase ? this->compare(pT) : ssicmp(this->c_str(), pT));
+  }
+
+  // -------------------------------------------------------------------------
+  // FUNCTION:  CStdStr::Load
+  // REMARKS:
+  //    Loads string from resource specified by nID
+  //
+  // PARAMETERS:
+  //    nID - resource Identifier.  Purely a Win32 thing in this case
+  //
+  // RETURN VALUE:
+  //    true if successful, false otherwise
+  // -------------------------------------------------------------------------
+
+#ifndef SS_ANSI
+
+  bool Load(UINT nId, HMODULE hModule=NULL)
+  {
+    bool bLoaded    = false;  // set to true of we succeed.
+
+  #ifdef _MFC_VER    // When in Rome (or MFC land)...
+
+    // If they gave a resource handle, use it.  Note - this is archaic
+    // and not really what I would recommend.  But then again, in MFC
+    // land, you ought to be using CString for resources anyway since
+    // it walks the resource chain for you.
+
+    HMODULE hModuleOld = NULL;
+
+    if ( NULL != hModule )
+    {
+      hModuleOld = AfxGetResourceHandle();
+      AfxSetResourceHandle(hModule);
+    }
+
+    // ...load the string
+
+    CString strRes;
+    bLoaded        = FALSE != strRes.LoadString(nId);
+
+    // ...and if we set the resource handle, restore it.
+
+    if ( NULL != hModuleOld )
+      AfxSetResourceHandle(hModule);
+
+    if ( bLoaded )
+      *this      = strRes;
+
+  #else // otherwise make our own hackneyed version of CString's Load
+
+    // Get the resource name and module handle
+
+    if ( NULL == hModule )
+      hModule      = GetResourceHandle();
+
+    PCTSTR szName    = MAKEINTRESOURCE((nId>>4)+1); // lifted
+    DWORD dwSize    = 0;
+
+    // No sense continuing if we can't find the resource
+
+    HRSRC hrsrc      = ::FindResource(hModule, szName, RT_STRING);
+
+    if ( NULL == hrsrc )
+    {
+      TRACE(_T("Cannot find resource %d: 0x%X"), nId, ::GetLastError());
+    }
+    else if ( 0 == (dwSize = ::SizeofResource(hModule, hrsrc) / sizeof(CT)))
+    {
+      TRACE(_T("Cant get size of resource %d 0x%X\n"),nId,GetLastError());
+    }
+    else
+    {
+      bLoaded      = 0 != ssload(hModule, nId, GetBuf(dwSize), dwSize);
+      ReleaseBuffer();
+    }
+
+  #endif  // #ifdef _MFC_VER
+
+    if ( !bLoaded )
+      TRACE(_T("String not loaded 0x%X\n"), ::GetLastError());
+
+    return bLoaded;
+  }
+
+#endif  // #ifdef SS_ANSI
+
+  // -------------------------------------------------------------------------
+  // FUNCTION:  CStdStr::Format
+  //    void _cdecl Formst(CStdStringA& PCSTR szFormat, ...)
+  //    void _cdecl Format(PCSTR szFormat);
+  //
+  // DESCRIPTION:
+  //    This function does sprintf/wsprintf style formatting on CStdStringA
+  //    objects.  It looks a lot like MFC's CString::Format.  Some people
+  //    might even call this identical.  Fortunately, these people are now
+  //    dead... heh heh.
+  //
+  // PARAMETERS:
+  //    nId - ID of string resource holding the format string
+  //    szFormat - a PCSTR holding the format specifiers
+  //    argList - a va_list holding the arguments for the format specifiers.
+  //
+  // RETURN VALUE:  None.
+  // -------------------------------------------------------------------------
+  // formatting (using wsprintf style formatting)
+
+    // If they want a Format() function that safely handles string objects
+    // without casting
+
+#ifdef SS_SAFE_FORMAT
+
+    // Question:  Joe, you wacky coder you, why do you have so many overloads
+    //      of the Format() function
+    // Answer:  One reason only - CString compatability.  In short, by making
+    //      the Format() function a template this way, I can do strong typing
+    //      and allow people to pass CStdString arguments as fillers for
+    //      "%s" format specifiers without crashing their program!  The downside
+    //      is that I need to overload on the number of arguments.   If you are
+    //      passing more arguments than I have listed below in any of my
+    //      overloads, just add another one.
+    //
+    //      Yes, yes, this is really ugly.  In essence what I am doing here is
+    //      protecting people from a bad (and incorrect) programming practice
+    //      that they should not be doing anyway.  I am protecting them from
+    //      themselves.  Why am I doing this?  Well, if you had any idea the
+    //      number of times I've been emailed by people about this
+    //      "incompatability" in my code, you wouldn't ask.
+
+  void Fmt(const CT* szFmt, ...)
+  {
+    va_list argList;
+    va_start(argList, szFmt);
+    FormatV(szFmt, argList);
+    va_end(argList);
+  }
+
+#ifndef SS_ANSI
+
+    void Format(UINT nId)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+            this->swap(strFmt);
+    }
+    template<class A1>
+    void Format(UINT nId, const A1& v)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+            Fmt(strFmt, FmtArg<A1>(v)());
+    }
+    template<class A1, class A2>
+    void Format(UINT nId, const A1& v1, const A2& v2)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+           Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)());
+    }
+    template<class A1, class A2, class A3>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(),FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(),FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+           Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)(), FmtArg<A10>(v10)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)(),FmtArg<A10>(v10)(),FmtArg<A11>(v11)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+                FmtArg<A12>(v12)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+                FmtArg<A12>(v12)(), FmtArg<A13>(v13)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13, class A14>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13, const A14& v14)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+                FmtArg<A12>(v12)(), FmtArg<A13>(v13)(),FmtArg<A14>(v14)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13, class A14, class A15>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13, const A14& v14, const A15& v15)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+                FmtArg<A12>(v12)(),FmtArg<A13>(v13)(),FmtArg<A14>(v14)(),
+                FmtArg<A15>(v15)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13, class A14, class A15, class A16>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13, const A14& v14, const A15& v15,
+                const A16& v16)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+                FmtArg<A12>(v12)(),FmtArg<A13>(v13)(),FmtArg<A14>(v14)(),
+                FmtArg<A15>(v15)(), FmtArg<A16>(v16)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13, class A14, class A15, class A16, class A17>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13, const A14& v14, const A15& v15,
+                const A16& v16, const A17& v17)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+                FmtArg<A12>(v12)(),FmtArg<A13>(v13)(),FmtArg<A14>(v14)(),
+                FmtArg<A15>(v15)(),FmtArg<A16>(v16)(),FmtArg<A17>(v17)());
+        }
+    }
+
+#endif // #ifndef SS_ANSI
+
+    // ...now the other overload of Format: the one that takes a string literal
+
+    void Format(const CT* szFmt)
+    {
+        *this = szFmt;
+    }
+    template<class A1>
+    void Format(const CT* szFmt, const A1& v)
+    {
+        Fmt(szFmt, FmtArg<A1>(v)());
+    }
+    template<class A1, class A2>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)());
+    }
+    template<class A1, class A2, class A3>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)());
+    }
+    template<class A1, class A2, class A3, class A4>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)(), FmtArg<A10>(v10)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)(),FmtArg<A10>(v10)(),FmtArg<A11>(v11)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+            FmtArg<A12>(v12)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+            FmtArg<A12>(v12)(), FmtArg<A13>(v13)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13, class A14>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13, const A14& v14)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+            FmtArg<A12>(v12)(), FmtArg<A13>(v13)(),FmtArg<A14>(v14)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13, class A14, class A15>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13, const A14& v14, const A15& v15)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+            FmtArg<A12>(v12)(),FmtArg<A13>(v13)(),FmtArg<A14>(v14)(),
+            FmtArg<A15>(v15)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13, class A14, class A15, class A16>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13, const A14& v14, const A15& v15,
+                const A16& v16)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+            FmtArg<A12>(v12)(),FmtArg<A13>(v13)(),FmtArg<A14>(v14)(),
+            FmtArg<A15>(v15)(), FmtArg<A16>(v16)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13, class A14, class A15, class A16, class A17>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13, const A14& v14, const A15& v15,
+                const A16& v16, const A17& v17)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+            FmtArg<A12>(v12)(),FmtArg<A13>(v13)(),FmtArg<A14>(v14)(),
+            FmtArg<A15>(v15)(),FmtArg<A16>(v16)(),FmtArg<A17>(v17)());
+    }
+
+#else  // #ifdef SS_SAFE_FORMAT
+
+
+#ifndef SS_ANSI
+
+  void Format(UINT nId, ...)
+  {
+    va_list argList;
+    va_start(argList, nId);
+
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+      FormatV(strFmt, argList);
+
+    va_end(argList);
+  }
+
+#endif  // #ifdef SS_ANSI
+
+  void Format(const CT* szFmt, ...)
+  {
+    va_list argList;
+    va_start(argList, szFmt);
+    FormatV(szFmt, argList);
+    va_end(argList);
+  }
+
+#endif // #ifdef SS_SAFE_FORMAT
+
+  void AppendFormat(const CT* szFmt, ...)
+  {
+    va_list argList;
+    va_start(argList, szFmt);
+    AppendFormatV(szFmt, argList);
+    va_end(argList);
+  }
+
+  #define MAX_FMT_TRIES    5   // #of times we try
+  #define FMT_BLOCK_SIZE    2048 // # of bytes to increment per try
+  #define BUFSIZE_1ST  256
+  #define BUFSIZE_2ND 512
+  #define STD_BUF_SIZE    1024
+
+  // an efficient way to add formatted characters to the string.  You may only
+  // add up to STD_BUF_SIZE characters at a time, though
+  void AppendFormatV(const CT* szFmt, va_list argList)
+  {
+    CT szBuf[STD_BUF_SIZE];
+    int nLen = ssnprintf(szBuf, STD_BUF_SIZE-1, szFmt, argList);
+
+    if ( 0 < nLen )
+      this->append(szBuf, nLen);
+  }
+
+  // -------------------------------------------------------------------------
+  // FUNCTION:  FormatV
+  //    void FormatV(PCSTR szFormat, va_list, argList);
+  //
+  // DESCRIPTION:
+  //    This function formats the string with sprintf style format-specs.
+  //    It makes a general guess at required buffer size and then tries
+  //    successively larger buffers until it finds one big enough or a
+  //    threshold (MAX_FMT_TRIES) is exceeded.
+  //
+  // PARAMETERS:
+  //    szFormat - a PCSTR holding the format of the output
+  //    argList - a Microsoft specific va_list for variable argument lists
+  //
+  // RETURN VALUE:
+  // -------------------------------------------------------------------------
+
+  // NOTE: Changed by JM to actually function under non-win32,
+  //       and to remove the upper limit on size.
+  void FormatV(const CT* szFormat, va_list argList)
+  {
+    // try and grab a sufficient buffersize
+    int nChars = FMT_BLOCK_SIZE;
+    va_list argCopy;
+
+    CT *p = reinterpret_cast<CT*>(malloc(sizeof(CT)*nChars));
+    if (!p) return;
+
+    while (1)
+    {
+      va_copy(argCopy, argList);
+
+      int nActual = ssvsprintf(p, nChars, szFormat, argCopy);
+      /* If that worked, return the string. */
+      if (nActual > -1 && nActual < nChars)
+      { /* make sure it's NULL terminated */
+        p[nActual] = '\0';
+        this->assign(p, nActual);
+        free(p);
+        va_end(argCopy);
+        return;
+      }
+      /* Else try again with more space. */
+      if (nActual > -1)        /* glibc 2.1 */
+        nChars = nActual + 1;  /* precisely what is needed */
+      else                     /* glibc 2.0 */
+        nChars *= 2;           /* twice the old size */
+
+      CT *np = reinterpret_cast<CT*>(realloc(p, sizeof(CT)*nChars));
+      if (np == NULL)
+      {
+        free(p);
+        va_end(argCopy);
+        return;   // failed :(
+      }
+      p = np;
+      va_end(argCopy);
+    }
+  }
+
+  // -------------------------------------------------------------------------
+  // CString Facade Functions:
+  //
+  // The following methods are intended to allow you to use this class as a
+  // near drop-in replacement for CString.
+  // -------------------------------------------------------------------------
+  #ifdef SS_WIN32
+    BSTR AllocSysString() const
+    {
+      ostring os;
+      ssasn(os, *this);
+      return ::SysAllocString(os.c_str());
+    }
+  #endif
+
+#ifndef SS_NO_LOCALE
+  int Collate(PCMYSTR szThat) const
+  {
+    return sscoll(this->c_str(), this->length(), szThat, sslen(szThat));
+  }
+
+  int CollateNoCase(PCMYSTR szThat) const
+  {
+    return ssicoll(this->c_str(), this->length(), szThat, sslen(szThat));
+  }
+#endif
+  int Compare(PCMYSTR szThat) const
+  {
+    return this->compare(szThat);
+  }
+
+  int CompareNoCase(PCMYSTR szThat)  const
+  {
+    return ssicmp(this->c_str(), szThat);
+  }
+
+  int Delete(int nIdx, int nCount=1)
+  {
+        if ( nIdx < 0 )
+      nIdx = 0;
+
+    if ( nIdx < this->GetLength() )
+      this->erase(static_cast<MYSIZE>(nIdx), static_cast<MYSIZE>(nCount));
+
+    return GetLength();
+  }
+
+  void Empty()
+  {
+    this->erase();
+  }
+
+  int Find(CT ch) const
+  {
+    MYSIZE nIdx  = this->find_first_of(ch);
+    return static_cast<int>(MYBASE::npos == nIdx  ? -1 : nIdx);
+  }
+
+  int Find(PCMYSTR szSub) const
+  {
+    MYSIZE nIdx  = this->find(szSub);
+    return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);
+  }
+
+  int Find(CT ch, int nStart) const
+  {
+    // CString::Find docs say add 1 to nStart when it's not zero
+    // CString::Find code doesn't do that however.  We'll stick
+    // with what the code does
+
+    MYSIZE nIdx  = this->find_first_of(ch, static_cast<MYSIZE>(nStart));
+    return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);
+  }
+
+  int Find(PCMYSTR szSub, int nStart) const
+  {
+    // CString::Find docs say add 1 to nStart when it's not zero
+    // CString::Find code doesn't do that however.  We'll stick
+    // with what the code does
+
+    MYSIZE nIdx  = this->find(szSub, static_cast<MYSIZE>(nStart));
+    return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);
+  }
+
+  int FindOneOf(PCMYSTR szCharSet) const
+  {
+    MYSIZE nIdx = this->find_first_of(szCharSet);
+    return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);
+  }
+
+#ifndef SS_ANSI
+  void FormatMessage(PCMYSTR szFormat, ...) throw(std::exception)
+  {
+    va_list argList;
+    va_start(argList, szFormat);
+    PMYSTR szTemp;
+    if ( ssfmtmsg(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER,
+             szFormat, 0, 0,
+             reinterpret_cast<PMYSTR>(&szTemp), 0, &argList) == 0 ||
+       szTemp == 0 )
+    {
+      throw std::runtime_error("out of memory");
+    }
+    *this = szTemp;
+    LocalFree(szTemp);
+    va_end(argList);
+  }
+
+  void FormatMessage(UINT nFormatId, ...) throw(std::exception)
+  {
+    MYTYPE sFormat;
+    VERIFY(sFormat.LoadString(nFormatId));
+    va_list argList;
+    va_start(argList, nFormatId);
+    PMYSTR szTemp;
+    if ( ssfmtmsg(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER,
+             sFormat, 0, 0,
+             reinterpret_cast<PMYSTR>(&szTemp), 0, &argList) == 0 ||
+      szTemp == 0)
+    {
+      throw std::runtime_error("out of memory");
+    }
+    *this = szTemp;
+    LocalFree(szTemp);
+    va_end(argList);
+  }
+#endif
+
+  // GetAllocLength -- an MSVC7 function but it costs us nothing to add it.
+
+  int GetAllocLength()
+  {
+    return static_cast<int>(this->capacity());
+  }
+
+  // -------------------------------------------------------------------------
+  // GetXXXX -- Direct access to character buffer
+  // -------------------------------------------------------------------------
+  CT GetAt(int nIdx) const
+  {
+    return this->at(static_cast<MYSIZE>(nIdx));
+  }
+
+  CT* GetBuffer(int nMinLen=-1)
+  {
+    return GetBuf(nMinLen);
+  }
+
+  CT* GetBufferSetLength(int nLen)
+  {
+    return BufferSet(nLen);
+  }
+
+  // GetLength() -- MFC docs say this is the # of BYTES but
+  // in truth it is the number of CHARACTERs (chars or wchar_ts)
+  int GetLength() const
+  {
+    return static_cast<int>(this->length());
+  }
+
+  int Insert(int nIdx, CT ch)
+  {
+    if ( static_cast<MYSIZE>(nIdx) > this->size()-1 )
+      this->append(1, ch);
+    else
+      this->insert(static_cast<MYSIZE>(nIdx), 1, ch);
+
+    return GetLength();
+  }
+  int Insert(int nIdx, PCMYSTR sz)
+  {
+    if ( static_cast<MYSIZE>(nIdx) >= this->size() )
+      this->append(sz, static_cast<MYSIZE>(sslen(sz)));
+    else
+      this->insert(static_cast<MYSIZE>(nIdx), sz);
+
+    return GetLength();
+  }
+
+  bool IsEmpty() const
+  {
+    return this->empty();
+  }
+
+  MYTYPE Left(int nCount) const
+  {
+        // Range check the count.
+
+    nCount = SSMAX(0, SSMIN(nCount, static_cast<int>(this->size())));
+    return this->substr(0, static_cast<MYSIZE>(nCount));
+  }
+
+#ifndef SS_ANSI
+  bool LoadString(UINT nId)
+  {
+    return this->Load(nId);
+  }
+#endif
+
+  void MakeLower()
+  {
+    ToLower();
+  }
+
+  void MakeReverse()
+  {
+    std::reverse(this->begin(), this->end());
+  }
+
+  void MakeUpper()
+  {
+    ToUpper();
+  }
+
+  MYTYPE Mid(int nFirst) const
+  {
+    return Mid(nFirst, this->GetLength()-nFirst);
+  }
+
+  MYTYPE Mid(int nFirst, int nCount) const
+  {
+    // CString does range checking here.  Since we're trying to emulate it,
+    // we must check too.
+
+    if ( nFirst < 0 )
+      nFirst = 0;
+    if ( nCount < 0 )
+      nCount = 0;
+
+    int nSize = static_cast<int>(this->size());
+
+    if ( nFirst + nCount > nSize )
+      nCount = nSize - nFirst;
+
+    if ( nFirst > nSize )
+      return MYTYPE();
+
+    ASSERT(nFirst >= 0);
+    ASSERT(nFirst + nCount <= nSize);
+
+    return this->substr(static_cast<MYSIZE>(nFirst),
+              static_cast<MYSIZE>(nCount));
+  }
+
+  void ReleaseBuffer(int nNewLen=-1)
+  {
+    RelBuf(nNewLen);
+  }
+
+  int Remove(CT ch)
+  {
+    MYSIZE nIdx    = 0;
+    int nRemoved  = 0;
+    while ( (nIdx=this->find_first_of(ch)) != MYBASE::npos )
+    {
+      this->erase(nIdx, 1);
+      nRemoved++;
+    }
+    return nRemoved;
+  }
+
+  int Replace(CT chOld, CT chNew)
+  {
+    int nReplaced  = 0;
+
+    for ( MYITER iter=this->begin(); iter != this->end(); iter++ )
+    {
+      if ( *iter == chOld )
+      {
+        *iter = chNew;
+        nReplaced++;
+      }
+    }
+
+    return nReplaced;
+  }
+
+  int Replace(PCMYSTR szOld, PCMYSTR szNew)
+  {
+    int nReplaced    = 0;
+    MYSIZE nIdx      = 0;
+    MYSIZE nOldLen    = sslen(szOld);
+
+    if ( 0 != nOldLen )
+    {
+      // If the replacement string is longer than the one it replaces, this
+      // string is going to have to grow in size,  Figure out how much
+      // and grow it all the way now, rather than incrementally
+
+      MYSIZE nNewLen    = sslen(szNew);
+      if ( nNewLen > nOldLen )
+      {
+        int nFound      = 0;
+        while ( nIdx < this->length() &&
+          (nIdx=this->find(szOld, nIdx)) != MYBASE::npos )
+        {
+          nFound++;
+          nIdx += nOldLen;
+        }
+        this->reserve(this->size() + nFound * (nNewLen - nOldLen));
+      }
+
+
+      static const CT ch  = CT(0);
+      PCMYSTR szRealNew  = szNew == 0 ? &ch : szNew;
+      nIdx        = 0;
+
+      while ( nIdx < this->length() &&
+        (nIdx=this->find(szOld, nIdx)) != MYBASE::npos )
+      {
+        this->replace(this->begin()+nIdx, this->begin()+nIdx+nOldLen,
+          szRealNew);
+
+        nReplaced++;
+        nIdx += nNewLen;
+      }
+    }
+
+    return nReplaced;
+  }
+
+  int ReverseFind(CT ch) const
+  {
+    MYSIZE nIdx  = this->find_last_of(ch);
+    return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);
+  }
+
+  // ReverseFind overload that's not in CString but might be useful
+  int ReverseFind(PCMYSTR szFind, MYSIZE pos=MYBASE::npos) const
+  {
+    //yuvalt - this does not compile with g++ since MYTTYPE() is different type
+    //MYSIZE nIdx  = this->rfind(0 == szFind ? MYTYPE() : szFind, pos);
+    MYSIZE nIdx  = this->rfind(0 == szFind ? "" : szFind, pos);
+    return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);
+  }
+
+  MYTYPE Right(int nCount) const
+  {
+        // Range check the count.
+
+    nCount = SSMAX(0, SSMIN(nCount, static_cast<int>(this->size())));
+    return this->substr(this->size()-static_cast<MYSIZE>(nCount));
+  }
+
+  void SetAt(int nIndex, CT ch)
+  {
+    ASSERT(this->size() > static_cast<MYSIZE>(nIndex));
+    this->at(static_cast<MYSIZE>(nIndex))    = ch;
+  }
+
+#ifndef SS_ANSI
+  BSTR SetSysString(BSTR* pbstr) const
+  {
+    ostring os;
+    ssasn(os, *this);
+    if ( !::SysReAllocStringLen(pbstr, os.c_str(), os.length()) )
+      throw std::runtime_error("out of memory");
+
+    ASSERT(*pbstr != 0);
+    return *pbstr;
+  }
+#endif
+
+  MYTYPE SpanExcluding(PCMYSTR szCharSet) const
+  {
+        MYSIZE pos = this->find_first_of(szCharSet);
+        return pos == MYBASE::npos ? *this : Left(pos);
+  }
+
+  MYTYPE SpanIncluding(PCMYSTR szCharSet) const
+  {
+        MYSIZE pos = this->find_first_not_of(szCharSet);
+        return pos == MYBASE::npos ? *this : Left(pos);
+  }
+
+#if defined SS_WIN32 && !defined(UNICODE) && !defined(SS_ANSI)
+
+  // CString's OemToAnsi and AnsiToOem functions are available only in
+  // Unicode builds.  However since we're a template we also need a
+  // runtime check of CT and a reinterpret_cast to account for the fact
+  // that CStdStringW gets instantiated even in non-Unicode builds.
+
+  void AnsiToOem()
+  {
+    if ( sizeof(CT) == sizeof(char) && !empty() )
+    {
+      ::CharToOem(reinterpret_cast<PCSTR>(this->c_str()),
+            reinterpret_cast<PSTR>(GetBuf()));
+    }
+    else
+    {
+      ASSERT(false);
+    }
+  }
+
+  void OemToAnsi()
+  {
+    if ( sizeof(CT) == sizeof(char) && !empty() )
+    {
+      ::OemToChar(reinterpret_cast<PCSTR>(this->c_str()),
+            reinterpret_cast<PSTR>(GetBuf()));
+    }
+    else
+    {
+      ASSERT(false);
+    }
+  }
+
+#endif
+
+
+  // -------------------------------------------------------------------------
+  // Trim and its variants
+  // -------------------------------------------------------------------------
+  MYTYPE& Trim()
+  {
+    return TrimLeft().TrimRight();
+  }
+
+  MYTYPE& TrimLeft()
+  {
+    this->erase(this->begin(),
+      std::find_if(this->begin(), this->end(), NotSpace<CT>()));
+
+    return *this;
+  }
+
+  MYTYPE&  TrimLeft(CT tTrim)
+  {
+    this->erase(0, this->find_first_not_of(tTrim));
+    return *this;
+  }
+
+  MYTYPE&  TrimLeft(PCMYSTR szTrimChars)
+  {
+    this->erase(0, this->find_first_not_of(szTrimChars));
+    return *this;
+  }
+
+  MYTYPE& TrimRight()
+  {
+    // NOTE:  When comparing reverse_iterators here (MYRITER), I avoid using
+    // operator!=.  This is because namespace rel_ops also has a template
+    // operator!= which conflicts with the global operator!= already defined
+    // for reverse_iterator in the header <utility>.
+    // Thanks to John James for alerting me to this.
+
+    MYRITER it = std::find_if(this->rbegin(), this->rend(), NotSpace<CT>());
+    if ( !(this->rend() == it) )
+      this->erase(this->rend() - it);
+
+    this->erase(!(it == this->rend()) ? this->find_last_of(*it) + 1 : 0);
+    return *this;
+  }
+
+  MYTYPE&  TrimRight(CT tTrim)
+  {
+    MYSIZE nIdx  = this->find_last_not_of(tTrim);
+    this->erase(MYBASE::npos == nIdx ? 0 : ++nIdx);
+    return *this;
+  }
+
+  MYTYPE&  TrimRight(PCMYSTR szTrimChars)
+  {
+    MYSIZE nIdx  = this->find_last_not_of(szTrimChars);
+    this->erase(MYBASE::npos == nIdx ? 0 : ++nIdx);
+    return *this;
+  }
+
+  void      FreeExtra()
+  {
+    MYTYPE mt;
+    this->swap(mt);
+    if ( !mt.empty() )
+      this->assign(mt.c_str(), mt.size());
+  }
+
+  // I have intentionally not implemented the following CString
+  // functions.   You cannot make them work without taking advantage
+  // of implementation specific behavior.  However if you absolutely
+  // MUST have them, uncomment out these lines for "sort-of-like"
+  // their behavior.  You're on your own.
+
+//  CT*        LockBuffer()  { return GetBuf(); }// won't really lock
+//  void      UnlockBuffer(); { }  // why have UnlockBuffer w/o LockBuffer?
+
+  // Array-indexing operators.  Required because we defined an implicit cast
+  // to operator const CT* (Thanks to Julian Selman for pointing this out)
+
+  CT& operator[](int nIdx)
+  {
+    return static_cast<MYBASE*>(this)->operator[](static_cast<MYSIZE>(nIdx));
+  }
+
+  const CT& operator[](int nIdx) const
+  {
+    return static_cast<const MYBASE*>(this)->operator[](static_cast<MYSIZE>(nIdx));
+  }
+
+  CT& operator[](unsigned int nIdx)
+  {
+    return static_cast<MYBASE*>(this)->operator[](static_cast<MYSIZE>(nIdx));
+  }
+
+  const CT& operator[](unsigned int nIdx) const
+  {
+    return static_cast<const MYBASE*>(this)->operator[](static_cast<MYSIZE>(nIdx));
+  }
+
+  CT& operator[](unsigned long nIdx)
+  {
+    return static_cast<MYBASE*>(this)->operator[](static_cast<MYSIZE>(nIdx));
+  }
+
+  const CT& operator[](unsigned long nIdx) const
+  {
+    return static_cast<const MYBASE*>(this)->operator[](static_cast<MYSIZE>(nIdx));
+  }
+
+#ifndef SS_NO_IMPLICIT_CAST
+  operator const CT*() const
+  {
+    return this->c_str();
+  }
+#endif
+
+  // IStream related functions.  Useful in IPersistStream implementations
+
+#ifdef SS_INC_COMDEF
+
+  // struct SSSHDR - useful for non Std C++ persistence schemes.
+  typedef struct SSSHDR
+  {
+    BYTE  byCtrl;
+    ULONG  nChars;
+  } SSSHDR;  // as in "Standard String Stream Header"
+
+  #define SSSO_UNICODE  0x01  // the string is a wide string
+  #define SSSO_COMPRESS  0x02  // the string is compressed
+
+  // -------------------------------------------------------------------------
+  // FUNCTION: StreamSize
+  // REMARKS:
+  //    Returns how many bytes it will take to StreamSave() this CStdString
+  //    object to an IStream.
+  // -------------------------------------------------------------------------
+  ULONG StreamSize() const
+  {
+    // Control header plus string
+    ASSERT(this->size()*sizeof(CT) < 0xffffffffUL - sizeof(SSSHDR));
+    return (this->size() * sizeof(CT)) + sizeof(SSSHDR);
+  }
+
+  // -------------------------------------------------------------------------
+  // FUNCTION: StreamSave
+  // REMARKS:
+  //    Saves this CStdString object to a COM IStream.
+  // -------------------------------------------------------------------------
+  HRESULT StreamSave(IStream* pStream) const
+  {
+    ASSERT(this->size()*sizeof(CT) < 0xffffffffUL - sizeof(SSSHDR));
+    HRESULT hr    = E_FAIL;
+    ASSERT(pStream != 0);
+    SSSHDR hdr;
+    hdr.byCtrl    = sizeof(CT) == 2 ? SSSO_UNICODE : 0;
+    hdr.nChars    = this->size();
+
+
+    if ( FAILED(hr=pStream->Write(&hdr, sizeof(SSSHDR), 0)) )
+    {
+      TRACE(_T("StreamSave: Cannot write control header, ERR=0x%X\n"),hr);
+    }
+    else if ( empty() )
+    {
+      ;    // nothing to write
+    }
+    else if ( FAILED(hr=pStream->Write(this->c_str(),
+      this->size()*sizeof(CT), 0)) )
+    {
+      TRACE(_T("StreamSave: Cannot write string to stream 0x%X\n"), hr);
+    }
+
+    return hr;
+  }
+
+
+  // -------------------------------------------------------------------------
+  // FUNCTION: StreamLoad
+  // REMARKS:
+  //    This method loads the object from an IStream.
+  // -------------------------------------------------------------------------
+  HRESULT StreamLoad(IStream* pStream)
+  {
+    ASSERT(pStream != 0);
+    SSSHDR hdr;
+    HRESULT hr      = E_FAIL;
+
+    if ( FAILED(hr=pStream->Read(&hdr, sizeof(SSSHDR), 0)) )
+    {
+      TRACE(_T("StreamLoad: Cant read control header, ERR=0x%X\n"), hr);
+    }
+    else if ( hdr.nChars > 0 )
+    {
+      ULONG nRead    = 0;
+      PMYSTR pMyBuf  = BufferSet(hdr.nChars);
+
+      // If our character size matches the character size of the string
+      // we're trying to read, then we can read it directly into our
+      // buffer. Otherwise, we have to read into an intermediate buffer
+      // and convert.
+
+      if ( (hdr.byCtrl & SSSO_UNICODE) != 0 )
+      {
+        ULONG nBytes  = hdr.nChars * sizeof(wchar_t);
+        if ( sizeof(CT) == sizeof(wchar_t) )
+        {
+          if ( FAILED(hr=pStream->Read(pMyBuf, nBytes, &nRead)) )
+            TRACE(_T("StreamLoad: Cannot read string: 0x%X\n"), hr);
+        }
+        else
+        {
+          PWSTR pBufW = reinterpret_cast<PWSTR>(_alloca((nBytes)+1));
+          if ( FAILED(hr=pStream->Read(pBufW, nBytes, &nRead)) )
+            TRACE(_T("StreamLoad: Cannot read string: 0x%X\n"), hr);
+          else
+            sscpy(pMyBuf, pBufW, hdr.nChars);
+        }
+      }
+      else
+      {
+        ULONG nBytes  = hdr.nChars * sizeof(char);
+        if ( sizeof(CT) == sizeof(char) )
+        {
+          if ( FAILED(hr=pStream->Read(pMyBuf, nBytes, &nRead)) )
+            TRACE(_T("StreamLoad: Cannot read string: 0x%X\n"), hr);
+        }
+        else
+        {
+          PSTR pBufA = reinterpret_cast<PSTR>(_alloca(nBytes));
+          if ( FAILED(hr=pStream->Read(pBufA, hdr.nChars, &nRead)) )
+            TRACE(_T("StreamLoad: Cannot read string: 0x%X\n"), hr);
+          else
+            sscpy(pMyBuf, pBufA, hdr.nChars);
+        }
+      }
+    }
+    else
+    {
+      this->erase();
+    }
+    return hr;
+  }
+#endif // #ifdef SS_INC_COMDEF
+
+#ifndef SS_ANSI
+
+  // SetResourceHandle/GetResourceHandle.  In MFC builds, these map directly
+  // to AfxSetResourceHandle and AfxGetResourceHandle.  In non-MFC builds they
+  // point to a single static HINST so that those who call the member
+  // functions that take resource IDs can provide an alternate HINST of a DLL
+  // to search.  This is not exactly the list of HMODULES that MFC provides
+  // but it's better than nothing.
+
+  #ifdef _MFC_VER
+    static void SetResourceHandle(HMODULE hNew)
+    {
+      AfxSetResourceHandle(hNew);
+    }
+    static HMODULE GetResourceHandle()
+    {
+      return AfxGetResourceHandle();
+    }
+  #else
+    static void SetResourceHandle(HMODULE hNew)
+    {
+      SSResourceHandle() = hNew;
+    }
+    static HMODULE GetResourceHandle()
+    {
+      return SSResourceHandle();
+    }
+  #endif
+
+#endif
+};
+
+// -----------------------------------------------------------------------------
+// MSVC USERS: HOW TO EXPORT CSTDSTRING FROM A DLL
+//
+// If you are using MS Visual C++ and you want to export CStdStringA and
+// CStdStringW from a DLL, then all you need to
+//
+//    1.  make sure that all components link to the same DLL version
+//      of the CRT (not the static one).
+//    2.  Uncomment the 3 lines of code below
+//    3.  #define 2 macros per the instructions in MS KnowledgeBase
+//      article Q168958.  The macros are:
+//
+//    MACRO    DEFINTION WHEN EXPORTING    DEFINITION WHEN IMPORTING
+//    -----    ------------------------    -------------------------
+//    SSDLLEXP  (nothing, just #define it)    extern
+//    SSDLLSPEC  __declspec(dllexport)      __declspec(dllimport)
+//
+//    Note that these macros must be available to ALL clients who want to
+//    link to the DLL and use the class.  If they
+//
+// A word of advice: Don't bother.
+//
+// Really, it is not necessary to export CStdString functions from a DLL.  I
+// never do.  In my projects, I do generally link to the DLL version of the
+// Standard C++ Library, but I do NOT attempt to export CStdString functions.
+// I simply include the header where it is needed and allow for the code
+// redundancy.
+//
+// That redundancy is a lot less than you think.  This class does most of its
+// work via the Standard C++ Library, particularly the base_class basic_string<>
+// member functions.  Most of the functions here are small enough to be inlined
+// anyway.  Besides, you'll find that in actual practice you use less than 1/2
+// of the code here, even in big projects and different modules will use as
+// little as 10% of it.  That means a lot less functions actually get linked
+// your binaries.  If you export this code from a DLL, it ALL gets linked in.
+//
+// I've compared the size of the binaries from exporting vs NOT exporting.  Take
+// my word for it -- exporting this code is not worth the hassle.
+//
+// -----------------------------------------------------------------------------
+//#pragma warning(disable:4231) // non-standard extension ("extern template")
+//  SSDLLEXP template class SSDLLSPEC CStdStr<char>;
+//  SSDLLEXP template class SSDLLSPEC CStdStr<wchar_t>;
+
+
+// =============================================================================
+//            END OF CStdStr INLINE FUNCTION DEFINITIONS
+// =============================================================================
+
+//  Now typedef our class names based upon this humongous template
+
+typedef CStdStr<char>    CStdStringA;  // a better std::string
+typedef CStdStr<wchar_t>  CStdStringW;  // a better std::wstring
+typedef CStdStr<uint16_t>  CStdString16;  // a 16bit char string
+typedef CStdStr<uint32_t>  CStdString32;  // a 32bit char string
+typedef CStdStr<OLECHAR>  CStdStringO;  // almost always CStdStringW
+
+// -----------------------------------------------------------------------------
+// CStdStr addition functions defined as inline
+// -----------------------------------------------------------------------------
+
+
+inline CStdStringA operator+(const CStdStringA& s1, const CStdStringA& s2)
+{
+  CStdStringA sRet(SSREF(s1));
+  sRet.append(s2);
+  return sRet;
+}
+inline CStdStringA operator+(const CStdStringA& s1, CStdStringA::value_type t)
+{
+  CStdStringA sRet(SSREF(s1));
+  sRet.append(1, t);
+  return sRet;
+}
+inline CStdStringA operator+(const CStdStringA& s1, PCSTR pA)
+{
+  CStdStringA sRet(SSREF(s1));
+  sRet.append(pA);
+  return sRet;
+}
+inline CStdStringA operator+(PCSTR pA, const CStdStringA& sA)
+{
+  CStdStringA sRet;
+  CStdStringA::size_type nObjSize = sA.size();
+  CStdStringA::size_type nLitSize =
+    static_cast<CStdStringA::size_type>(sslen(pA));
+
+  sRet.reserve(nLitSize + nObjSize);
+  sRet.assign(pA);
+  sRet.append(sA);
+  return sRet;
+}
+
+
+inline CStdStringA operator+(const CStdStringA& s1, const CStdStringW& s2)
+{
+  return s1 + CStdStringA(s2);
+}
+inline CStdStringW operator+(const CStdStringW& s1, const CStdStringW& s2)
+{
+  CStdStringW sRet(SSREF(s1));
+  sRet.append(s2);
+  return sRet;
+}
+inline CStdStringA operator+(const CStdStringA& s1, PCWSTR pW)
+{
+  return s1 + CStdStringA(pW);
+}
+
+#ifdef UNICODE
+  inline CStdStringW operator+(PCWSTR pW, const CStdStringA& sA)
+  {
+    return CStdStringW(pW) + CStdStringW(SSREF(sA));
+  }
+  inline CStdStringW operator+(PCSTR pA, const CStdStringW& sW)
+  {
+    return CStdStringW(pA) + sW;
+  }
+#else
+  inline CStdStringA operator+(PCWSTR pW, const CStdStringA& sA)
+  {
+    return CStdStringA(pW) + sA;
+  }
+  inline CStdStringA operator+(PCSTR pA, const CStdStringW& sW)
+  {
+    return pA + CStdStringA(sW);
+  }
+#endif
+
+// ...Now the wide string versions.
+inline CStdStringW operator+(const CStdStringW& s1, CStdStringW::value_type t)
+{
+  CStdStringW sRet(SSREF(s1));
+  sRet.append(1, t);
+  return sRet;
+}
+inline CStdStringW operator+(const CStdStringW& s1, PCWSTR pW)
+{
+  CStdStringW sRet(SSREF(s1));
+  sRet.append(pW);
+  return sRet;
+}
+inline CStdStringW operator+(PCWSTR pW, const CStdStringW& sW)
+{
+  CStdStringW sRet;
+  CStdStringW::size_type nObjSize = sW.size();
+  CStdStringA::size_type nLitSize =
+    static_cast<CStdStringW::size_type>(sslen(pW));
+
+  sRet.reserve(nLitSize + nObjSize);
+  sRet.assign(pW);
+  sRet.append(sW);
+  return sRet;
+}
+
+inline CStdStringW operator+(const CStdStringW& s1, const CStdStringA& s2)
+{
+  return s1 + CStdStringW(s2);
+}
+inline CStdStringW operator+(const CStdStringW& s1, PCSTR pA)
+{
+  return s1 + CStdStringW(pA);
+}
+
+
+// New-style format function is a template
+
+#ifdef SS_SAFE_FORMAT
+
+template<>
+struct FmtArg<CStdStringA>
+{
+    explicit FmtArg(const CStdStringA& arg) : a_(arg) {}
+    PCSTR operator()() const { return a_.c_str(); }
+    const CStdStringA& a_;
+private:
+    FmtArg<CStdStringA>& operator=(const FmtArg<CStdStringA>&) { return *this; }
+};
+template<>
+struct FmtArg<CStdStringW>
+{
+    explicit FmtArg(const CStdStringW& arg) : a_(arg) {}
+    PCWSTR operator()() const { return a_.c_str(); }
+    const CStdStringW& a_;
+private:
+    FmtArg<CStdStringW>& operator=(const FmtArg<CStdStringW>&) { return *this; }
+};
+
+template<>
+struct FmtArg<std::string>
+{
+    explicit FmtArg(const std::string& arg) : a_(arg) {}
+    PCSTR operator()() const { return a_.c_str(); }
+    const std::string& a_;
+private:
+    FmtArg<std::string>& operator=(const FmtArg<std::string>&) { return *this; }
+};
+template<>
+struct FmtArg<std::wstring>
+{
+    explicit FmtArg(const std::wstring& arg) : a_(arg) {}
+    PCWSTR operator()() const { return a_.c_str(); }
+    const std::wstring& a_;
+private:
+    FmtArg<std::wstring>& operator=(const FmtArg<std::wstring>&) {return *this;}
+};
+#endif // #ifdef SS_SAFEFORMAT
+
+#ifndef SS_ANSI
+  // SSResourceHandle: our MFC-like resource handle
+  inline HMODULE& SSResourceHandle()
+  {
+    static HMODULE hModuleSS  = GetModuleHandle(0);
+    return hModuleSS;
+  }
+#endif
+
+
+// In MFC builds, define some global serialization operators
+// Special operators that allow us to serialize CStdStrings to CArchives.
+// Note that we use an intermediate CString object in order to ensure that
+// we use the exact same format.
+
+#ifdef _MFC_VER
+  inline CArchive& AFXAPI operator<<(CArchive& ar, const CStdStringA& strA)
+  {
+    CString strTemp  = strA;
+    return ar << strTemp;
+  }
+  inline CArchive& AFXAPI operator<<(CArchive& ar, const CStdStringW& strW)
+  {
+    CString strTemp  = strW;
+    return ar << strTemp;
+  }
+
+  inline CArchive& AFXAPI operator>>(CArchive& ar, CStdStringA& strA)
+  {
+    CString strTemp;
+    ar >> strTemp;
+    strA = strTemp;
+    return ar;
+  }
+  inline CArchive& AFXAPI operator>>(CArchive& ar, CStdStringW& strW)
+  {
+    CString strTemp;
+    ar >> strTemp;
+    strW = strTemp;
+    return ar;
+  }
+#endif  // #ifdef _MFC_VER -- (i.e. is this MFC?)
+
+
+
+// -----------------------------------------------------------------------------
+// GLOBAL FUNCTION:  WUFormat
+//    CStdStringA WUFormat(UINT nId, ...);
+//    CStdStringA WUFormat(PCSTR szFormat, ...);
+//
+// REMARKS:
+//    This function allows the caller for format and return a CStdStringA
+//    object with a single line of code.
+// -----------------------------------------------------------------------------
+#ifdef SS_ANSI
+#else
+  inline CStdStringA WUFormatA(UINT nId, ...)
+  {
+    va_list argList;
+    va_start(argList, nId);
+
+    CStdStringA strFmt;
+    CStdStringA strOut;
+    if ( strFmt.Load(nId) )
+      strOut.FormatV(strFmt, argList);
+
+    va_end(argList);
+    return strOut;
+  }
+  inline CStdStringA WUFormatA(PCSTR szFormat, ...)
+  {
+    va_list argList;
+    va_start(argList, szFormat);
+    CStdStringA strOut;
+    strOut.FormatV(szFormat, argList);
+    va_end(argList);
+    return strOut;
+  }
+  inline CStdStringW WUFormatW(UINT nId, ...)
+  {
+    va_list argList;
+    va_start(argList, nId);
+
+    CStdStringW strFmt;
+    CStdStringW strOut;
+    if ( strFmt.Load(nId) )
+      strOut.FormatV(strFmt, argList);
+
+    va_end(argList);
+    return strOut;
+  }
+  inline CStdStringW WUFormatW(PCWSTR szwFormat, ...)
+  {
+    va_list argList;
+    va_start(argList, szwFormat);
+    CStdStringW strOut;
+    strOut.FormatV(szwFormat, argList);
+    va_end(argList);
+    return strOut;
+  }
+#endif // #ifdef SS_ANSI
+
+
+
+#if defined(SS_WIN32) && !defined (SS_ANSI)
+  // -------------------------------------------------------------------------
+  // FUNCTION: WUSysMessage
+  //   CStdStringA WUSysMessageA(DWORD dwError, DWORD dwLangId=SS_DEFLANGID);
+  //   CStdStringW WUSysMessageW(DWORD dwError, DWORD dwLangId=SS_DEFLANGID);
+  //
+  // DESCRIPTION:
+  //   This function simplifies the process of obtaining a string equivalent
+  //   of a system error code returned from GetLastError().  You simply
+  //   supply the value returned by GetLastError() to this function and the
+  //   corresponding system string is returned in the form of a CStdStringA.
+  //
+  // PARAMETERS:
+  //   dwError - a DWORD value representing the error code to be translated
+  //   dwLangId - the language id to use.  defaults to english.
+  //
+  // RETURN VALUE:
+  //   a CStdStringA equivalent of the error code.  Currently, this function
+  //   only returns either English of the system default language strings.
+  // -------------------------------------------------------------------------
+  #define SS_DEFLANGID MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT)
+  inline CStdStringA WUSysMessageA(DWORD dwError, DWORD dwLangId=SS_DEFLANGID)
+  {
+    CHAR szBuf[512];
+
+    if ( 0 != ::FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwError,
+                   dwLangId, szBuf, 511, NULL) )
+      return WUFormatA("%s (0x%X)", szBuf, dwError);
+    else
+       return WUFormatA("Unknown error (0x%X)", dwError);
+  }
+  inline CStdStringW WUSysMessageW(DWORD dwError, DWORD dwLangId=SS_DEFLANGID)
+  {
+    WCHAR szBuf[512];
+
+    if ( 0 != ::FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwError,
+                   dwLangId, szBuf, 511, NULL) )
+      return WUFormatW(L"%s (0x%X)", szBuf, dwError);
+    else
+       return WUFormatW(L"Unknown error (0x%X)", dwError);
+  }
+#endif
+
+// Define TCHAR based friendly names for some of these functions
+
+#ifdef UNICODE
+  //#define CStdString        CStdStringW
+  typedef CStdStringW        CStdString;
+  #define WUSysMessage      WUSysMessageW
+  #define WUFormat        WUFormatW
+#else
+  //#define CStdString        CStdStringA
+  typedef CStdStringA        CStdString;
+  #define WUSysMessage      WUSysMessageA
+  #define WUFormat        WUFormatA
+#endif
+
+// ...and some shorter names for the space-efficient
+
+#define WUSysMsg          WUSysMessage
+#define WUSysMsgA          WUSysMessageA
+#define WUSysMsgW          WUSysMessageW
+#define WUFmtA            WUFormatA
+#define  WUFmtW            WUFormatW
+#define WUFmt            WUFormat
+#define WULastErrMsg()        WUSysMessage(::GetLastError())
+#define WULastErrMsgA()        WUSysMessageA(::GetLastError())
+#define WULastErrMsgW()        WUSysMessageW(::GetLastError())
+
+
+// -----------------------------------------------------------------------------
+// FUNCTIONAL COMPARATORS:
+// REMARKS:
+//    These structs are derived from the std::binary_function template.  They
+//    give us functional classes (which may be used in Standard C++ Library
+//    collections and algorithms) that perform case-insensitive comparisons of
+//    CStdString objects.  This is useful for maps in which the key may be the
+//     proper string but in the wrong case.
+// -----------------------------------------------------------------------------
+#define StdStringLessNoCaseW    SSLNCW  // avoid VC compiler warning 4786
+#define StdStringEqualsNoCaseW    SSENCW
+#define StdStringLessNoCaseA    SSLNCA
+#define StdStringEqualsNoCaseA    SSENCA
+
+#ifdef UNICODE
+  #define StdStringLessNoCase    SSLNCW
+  #define StdStringEqualsNoCase  SSENCW
+#else
+  #define StdStringLessNoCase    SSLNCA
+  #define StdStringEqualsNoCase  SSENCA
+#endif
+
+struct StdStringLessNoCaseW
+  : std::binary_function<CStdStringW, CStdStringW, bool>
+{
+  inline
+  bool operator()(const CStdStringW& sLeft, const CStdStringW& sRight) const
+  { return ssicmp(sLeft.c_str(), sRight.c_str()) < 0; }
+};
+struct StdStringEqualsNoCaseW
+  : std::binary_function<CStdStringW, CStdStringW, bool>
+{
+  inline
+  bool operator()(const CStdStringW& sLeft, const CStdStringW& sRight) const
+  { return ssicmp(sLeft.c_str(), sRight.c_str()) == 0; }
+};
+struct StdStringLessNoCaseA
+  : std::binary_function<CStdStringA, CStdStringA, bool>
+{
+  inline
+  bool operator()(const CStdStringA& sLeft, const CStdStringA& sRight) const
+  { return ssicmp(sLeft.c_str(), sRight.c_str()) < 0; }
+};
+struct StdStringEqualsNoCaseA
+  : std::binary_function<CStdStringA, CStdStringA, bool>
+{
+  inline
+  bool operator()(const CStdStringA& sLeft, const CStdStringA& sRight) const
+  { return ssicmp(sLeft.c_str(), sRight.c_str()) == 0; }
+};
+
+// If we had to define our own version of TRACE above, get rid of it now
+
+#ifdef TRACE_DEFINED_HERE
+  #undef TRACE
+  #undef TRACE_DEFINED_HERE
+#endif
+
+
+// These std::swap specializations come courtesy of Mike Crusader.
+
+//namespace std
+//{
+//  inline void swap(CStdStringA& s1, CStdStringA& s2) throw()
+//  {
+//    s1.swap(s2);
+//  }
+//  template<>
+//  inline void swap(CStdStringW& s1, CStdStringW& s2) throw()
+//  {
+//    s1.swap(s2);
+//  }
+//}
+
+// Turn back on any Borland warnings we turned off.
+
+#ifdef __BORLANDC__
+    #pragma option pop  // Turn back on inline function warnings
+//  #pragma warn +inl   // Turn back on inline function warnings
+#endif
+
+#endif  // #ifndef STDSTRING_H
diff --git a/xbmc/pvrclients/vdr-streamdev/channels.cpp b/xbmc/pvrclients/vdr-streamdev/channels.cpp
new file mode 100644 (file)
index 0000000..9c8b122
--- /dev/null
@@ -0,0 +1,391 @@
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+/*
+ * This code is taken from channels.c in the Video Disk Recorder ('VDR')
+ */
+
+#include <string>
+#include <vector>
+#include "vtptransceiver.h"
+#include "pvrclient-vdr_os.h"
+#include "channels.h"
+#include "client.h"
+
+using namespace std;
+
+const tChannelParameterMap InversionValues[] = {
+  {   0, INVERSION_OFF,  "off" },
+  {   1, INVERSION_ON,   "on" },
+  { 999, INVERSION_AUTO, "auto" },
+  { -1 }
+  };
+
+const tChannelParameterMap BandwidthValues[] = {
+  {   6, 6000000, "6 MHz" },
+  {   7, 7000000, "7 MHz" },
+  {   8, 8000000, "8 MHz" },
+  { -1 }
+  };
+
+const tChannelParameterMap CoderateValues[] = {
+  {   0, FEC_NONE, "none" },
+  {  12, FEC_1_2,  "1/2" },
+  {  23, FEC_2_3,  "2/3" },
+  {  34, FEC_3_4,  "3/4" },
+  {  35, FEC_3_5,  "3/5" },
+  {  45, FEC_4_5,  "4/5" },
+  {  56, FEC_5_6,  "5/6" },
+  {  67, FEC_6_7,  "6/7" },
+  {  78, FEC_7_8,  "7/8" },
+  {  89, FEC_8_9,  "8/9" },
+  { 910, FEC_9_10, "9/10" },
+  { 999, FEC_AUTO, "auto" },
+  { -1 }
+  };
+
+const tChannelParameterMap ModulationValues[] = {
+  {  16, QAM_16,   "QAM16" },
+  {  32, QAM_32,   "QAM32" },
+  {  64, QAM_64,   "QAM64" },
+  { 128, QAM_128,  "QAM128" },
+  { 256, QAM_256,  "QAM256" },
+  {   2, QPSK,     "QPSK" },
+  {   5, PSK_8,    "8PSK" },
+  {   6, APSK_16,  "16APSK" },
+  {  10, VSB_8,    "VSB8" },
+  {  11, VSB_16,   "VSB16" },
+  { 998, QAM_AUTO, "QAMAUTO" },
+  { -1 }
+  };
+
+const tChannelParameterMap SystemValues[] = {
+  {   0, SYS_DVBS,  "DVB-S" },
+  {   1, SYS_DVBS2, "DVB-S2" },
+  { -1 }
+  };
+
+const tChannelParameterMap TransmissionValues[] = {
+  {   2, TRANSMISSION_MODE_2K,   "2K" },
+  {   8, TRANSMISSION_MODE_8K,   "8K" },
+  { 999, TRANSMISSION_MODE_AUTO, "auto" },
+  { -1 }
+  };
+
+const tChannelParameterMap GuardValues[] = {
+  {   4, GUARD_INTERVAL_1_4,  "1/4" },
+  {   8, GUARD_INTERVAL_1_8,  "1/8" },
+  {  16, GUARD_INTERVAL_1_16, "1/16" },
+  {  32, GUARD_INTERVAL_1_32, "1/32" },
+  { 999, GUARD_INTERVAL_AUTO, "auto" },
+  { -1 }
+  };
+
+const tChannelParameterMap HierarchyValues[] = {
+  {   0, HIERARCHY_NONE, "none" },
+  {   1, HIERARCHY_1,    "1" },
+  {   2, HIERARCHY_2,    "2" },
+  {   4, HIERARCHY_4,    "4" },
+  { 999, HIERARCHY_AUTO, "auto" },
+  { -1 }
+  };
+
+const tChannelParameterMap RollOffValues[] = {
+  {   0, ROLLOFF_AUTO, "auto" },
+  {  20, ROLLOFF_20, "0.20" },
+  {  25, ROLLOFF_25, "0.25" },
+  {  35, ROLLOFF_35, "0.35" },
+  { -1 }
+  };
+
+
+cChannel::cChannel(const PVR_CHANNEL *Channel)
+{
+
+}
+
+cChannel::cChannel()
+{
+  name = strdup("");
+  shortName = strdup("");
+  provider = strdup("");
+  memset(&__BeginData__, 0, (char *)&__EndData__ - (char *)&__BeginData__);
+  inversion    = INVERSION_AUTO;
+  bandwidth    = 8000000;
+  coderateH    = FEC_AUTO;
+  coderateL    = FEC_AUTO;
+  modulation   = QPSK;
+  system       = SYS_DVBS;
+  transmission = TRANSMISSION_MODE_AUTO;
+  guard        = GUARD_INTERVAL_AUTO;
+  hierarchy    = HIERARCHY_AUTO;
+  rollOff      = ROLLOFF_AUTO;
+  modification = CHANNELMOD_NONE;
+
+}
+
+cChannel::~cChannel()
+{
+  free(name);
+  free(shortName);
+  free(provider);
+}
+
+bool cChannel::ReadFromVTP(int channel)
+{
+  vector<string> lines;
+  int            code;
+  char           buffer[64];
+
+  if (!VTPTransceiver.CheckConnection())
+    return false;
+
+  sprintf(buffer, "LSTC %i", channel);
+  while (!VTPTransceiver.SendCommand(buffer, code, lines))
+  {
+    if (code != 451)
+      return false;
+
+    Sleep(100);
+  }
+
+  vector<string>::iterator it = lines.begin();
+  string& data(*it);
+  CStdString str_result = data;
+
+  if (g_bCharsetConv)
+    XBMC->UnknownToUTF8(str_result);
+
+  Parse(str_result.c_str());
+  return true;
+}
+
+int UserIndex(int Value, const tChannelParameterMap *Map)
+{
+  const tChannelParameterMap *map = Map;
+  while (map && map->userValue != -1)
+  {
+    if (map->userValue == Value)
+      return map - Map;
+    map++;
+  }
+  return -1;
+}
+
+int MapToDriver(int Value, const tChannelParameterMap *Map)
+{
+  int n = UserIndex(Value, Map);
+  if (n >= 0)
+     return Map[n].driverValue;
+  return -1;
+}
+
+static const char *ParseParameter(const char *s, int &Value, const tChannelParameterMap *Map)
+{
+  if (*++s)
+  {
+    char *p = NULL;
+    errno = 0;
+    int n = strtol(s, &p, 10);
+    if (!errno && p != s)
+    {
+      Value = MapToDriver(n, Map);
+      if (Value >= 0)
+        return p;
+    }
+  }
+  XBMC->Log(LOG_ERROR, "PCRClient-vdr: invalid value for channelparameter '%c'", *(s - 1));
+  return NULL;
+}
+
+static const char *SkipDigits(const char *s)
+{
+  while (*++s && isdigit(*s))
+        ;
+  return s;
+}
+
+bool cChannel::StringToParameters(const char *s)
+{
+  while (s && *s)
+  {
+    switch (toupper(*s))
+    {
+      case 'A': s = SkipDigits(s); break; // for compatibility with the "multiproto" approach - may be removed in future versions
+      case 'B': s = ParseParameter(s, bandwidth, BandwidthValues); break;
+      case 'C': s = ParseParameter(s, coderateH, CoderateValues); break;
+      case 'D': s = ParseParameter(s, coderateL, CoderateValues); break;
+      case 'G': s = ParseParameter(s, guard, GuardValues); break;
+      case 'H': polarization = *s++; break;
+      case 'I': s = ParseParameter(s, inversion, InversionValues); break;
+      case 'L': polarization = *s++; break;
+      case 'M': s = ParseParameter(s, modulation, ModulationValues); break;
+      case 'O': s = ParseParameter(s, rollOff, RollOffValues); break;
+      case 'P': s = SkipDigits(s); break; // for compatibility with the "multiproto" approach - may be removed in future versions
+      case 'R': polarization = *s++; break;
+      case 'S': s = ParseParameter(s, system, SystemValues); break;
+      case 'T': s = ParseParameter(s, transmission, TransmissionValues); break;
+      case 'V': polarization = *s++; break;
+      case 'Y': s = ParseParameter(s, hierarchy, HierarchyValues); break;
+      case 'Z': s = SkipDigits(s); break; // for compatibility with the original DVB-S2 patch - may be removed in future versions
+      default: XBMC->Log(LOG_ERROR, "PCRClient-vdr: unknown parameter key '%c'", *s);
+          return false;
+    }
+  }
+  return true;
+}
+
+bool cChannel::Parse(const char *s)
+{
+  bool ok = true;
+
+  char namebuf[256];
+  char sourcebuf[256];
+  char parambuf[256];
+  char vpidbuf[128];
+  char apidbuf[128];
+  char caidbuf[128];
+  int fields = sscanf(s, "%d %[^:]:%d:%[^:]:%[^:]:%d :%[^:]:%[^:]:%d :%[^:]:%d :%d :%d :%d ", &number, namebuf, &frequency, parambuf, sourcebuf, &srate, vpidbuf, apidbuf, &tpid, caidbuf, &sid, &nid, &tid, &rid);
+  if (fields >= 9)
+  {
+    if (fields == 9)
+    {
+      // allow reading of old format
+      sid = atoi(caidbuf);
+      caids[0] = tpid;
+      caids[1] = 0;
+      tpid = 0;
+    }
+    vpid = ppid = 0;
+    vtype = 2; // default is MPEG-2
+    apids[0] = 0;
+    dpids[0] = 0;
+
+    ok = StringToParameters(parambuf);
+
+    char *p;
+    if ((p = strchr(vpidbuf, '=')) != NULL)
+    {
+      *p++ = 0;
+      if (sscanf(p, "%d", &vtype) != 1)
+      return false;
+    }
+    if ((p = strchr(vpidbuf, '+')) != NULL)
+    {
+      *p++ = 0;
+      if (sscanf(p, "%d", &ppid) != 1)
+      return false;
+    }
+    if (sscanf(vpidbuf, "%d", &vpid) != 1)
+      return false;
+    if (!ppid)
+      ppid = vpid;
+
+    char *dpidbuf = strchr(apidbuf, ';');
+    if (dpidbuf)
+      *dpidbuf++ = 0;
+    p = apidbuf;
+    char *q;
+    int NumApids = 0;
+    char *strtok_next;
+    while ((q = strtok_r(p, ",", &strtok_next)) != NULL)
+    {
+      if (NumApids < MAXAPIDS) {
+      char *l = strchr(q, '=');
+      if (l)
+      {
+        *l++ = 0;
+        strn0cpy(alangs[NumApids], l, MAXLANGCODE2);
+      }
+      else
+        *alangs[NumApids] = 0;
+      apids[NumApids++] = strtol(q, NULL, 10);
+      }
+      else
+        XBMC->Log(LOG_ERROR, "PCRClient-vdr: too many APIDs!"); // no need to set ok to 'false'
+      p = NULL;
+    }
+    apids[NumApids] = 0;
+
+    if (dpidbuf)
+    {
+      char *p = dpidbuf;
+      char *q;
+      char *strtok_next;
+
+      int NumDpids = 0;
+      while ((q = strtok_r(p, ",", &strtok_next)) != NULL)
+      {
+        if (NumDpids < MAXDPIDS)
+        {
+          char *l = strchr(q, '=');
+          if (l)
+          {
+            *l++ = 0;
+            strn0cpy(dlangs[NumDpids], l, MAXLANGCODE2);
+          }
+          else
+            *dlangs[NumDpids] = 0;
+          dpids[NumDpids++] = strtol(q, NULL, 10);
+        }
+        else
+          XBMC->Log(LOG_ERROR, "PCRClient-vdr: too many DPIDs!"); // no need to set ok to 'false'
+        p = NULL;
+      }
+      dpids[NumDpids] = 0;
+    }
+
+    p = caidbuf;
+    int NumCaIds = 0;
+    while ((q = strtok_r(p, ",", &strtok_next)) != NULL)
+    {
+      if (NumCaIds < MAXCAIDS)
+      {
+        caids[NumCaIds++] = strtol(q, NULL, 16) & 0xFFFF;
+        if (NumCaIds == 1 && caids[0] <= CA_USER_MAX)
+          break;
+      }
+      else
+        XBMC->Log(LOG_ERROR, "PCRClient-vdr: too many CA ids!"); // no need to set ok to 'false'
+      p = NULL;
+    }
+    caids[NumCaIds] = 0;
+    strreplace(namebuf, '|', ':');
+
+    p = strchr(namebuf, ';');
+    if (p)
+    {
+      *p++ = 0;
+      provider = strcpyrealloc(provider, p);
+    }
+    p = strchr(namebuf, ',');
+    if (p)
+    {
+      *p++ = 0;
+      shortName = strcpyrealloc(shortName, p);
+    }
+    name = strcpyrealloc(name, namebuf);
+  }
+  else
+    return false;
+
+  return ok;
+}
diff --git a/xbmc/pvrclients/vdr-streamdev/channels.h b/xbmc/pvrclients/vdr-streamdev/channels.h
new file mode 100644 (file)
index 0000000..13a43ff
--- /dev/null
@@ -0,0 +1,326 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef __CHANNELS_H
+#define __CHANNELS_H
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "tools.h"
+
+#define MAXAPIDS 32 // audio
+#define MAXDPIDS 16 // dolby (AC3 + DTS)
+#define MAXSPIDS 32 // subtitles
+#define MAXCAIDS  8 // conditional access
+
+#define MAXLANGCODE1 4 // a 3 letter language code, zero terminated
+#define MAXLANGCODE2 8 // up to two 3 letter language codes, separated by '+' and zero terminated
+
+#define CHANNELMOD_NONE     0x00
+#define CHANNELMOD_ALL      0xFF
+#define CHANNELMOD_NAME     0x01
+#define CHANNELMOD_PIDS     0x02
+#define CHANNELMOD_ID       0x04
+#define CHANNELMOD_CA       0x10
+#define CHANNELMOD_TRANSP   0x20
+#define CHANNELMOD_LANGS    0x40
+#define CHANNELMOD_RETUNE   (CHANNELMOD_PIDS | CHANNELMOD_CA | CHANNELMOD_TRANSP)
+
+#define CA_FTA           0x0000
+#define CA_DVB_MIN       0x0001
+#define CA_DVB_MAX       0x000F
+#define CA_USER_MIN      0x0010
+#define CA_USER_MAX      0x00FF
+#define CA_ENCRYPTED_MIN 0x0100
+#define CA_ENCRYPTED_MAX 0xFFFF
+
+typedef enum fe_type {
+        FE_QPSK,
+        FE_QAM,
+        FE_OFDM,
+        FE_ATSC
+} fe_type_t;
+
+
+typedef enum fe_caps {
+        FE_IS_STUPID                    = 0,
+        FE_CAN_INVERSION_AUTO           = 0x1,
+        FE_CAN_FEC_1_2                  = 0x2,
+        FE_CAN_FEC_2_3                  = 0x4,
+        FE_CAN_FEC_3_4                  = 0x8,
+        FE_CAN_FEC_4_5                  = 0x10,
+        FE_CAN_FEC_5_6                  = 0x20,
+        FE_CAN_FEC_6_7                  = 0x40,
+        FE_CAN_FEC_7_8                  = 0x80,
+        FE_CAN_FEC_8_9                  = 0x100,
+        FE_CAN_FEC_AUTO                 = 0x200,
+        FE_CAN_QPSK                     = 0x400,
+        FE_CAN_QAM_16                   = 0x800,
+        FE_CAN_QAM_32                   = 0x1000,
+        FE_CAN_QAM_64                   = 0x2000,
+        FE_CAN_QAM_128                  = 0x4000,
+        FE_CAN_QAM_256                  = 0x8000,
+        FE_CAN_QAM_AUTO                 = 0x10000,
+        FE_CAN_TRANSMISSION_MODE_AUTO   = 0x20000,
+        FE_CAN_BANDWIDTH_AUTO           = 0x40000,
+        FE_CAN_GUARD_INTERVAL_AUTO      = 0x80000,
+        FE_CAN_HIERARCHY_AUTO           = 0x100000,
+        FE_CAN_8VSB                     = 0x200000,
+        FE_CAN_16VSB                    = 0x400000,
+        FE_HAS_EXTENDED_CAPS            = 0x800000,   /* We need more bitspace for newer APIs, indicate this. */
+        FE_CAN_2G_MODULATION            = 0x10000000, /* frontend supports "2nd generation modulation" (DVB-S2) */
+        FE_NEEDS_BENDING                = 0x20000000, /* not supported anymore, don't use (frontend requires frequency bending) */
+        FE_CAN_RECOVER                  = 0x40000000, /* frontend can recover from a cable unplug automatically */
+        FE_CAN_MUTE_TS                  = 0x80000000  /* frontend can stop spurious TS data output */
+} fe_caps_t;
+
+typedef enum fe_sec_voltage {
+        SEC_VOLTAGE_13,
+        SEC_VOLTAGE_18,
+        SEC_VOLTAGE_OFF
+} fe_sec_voltage_t;
+
+
+typedef enum fe_sec_tone_mode {
+        SEC_TONE_ON,
+        SEC_TONE_OFF
+} fe_sec_tone_mode_t;
+
+
+typedef enum fe_sec_mini_cmd {
+        SEC_MINI_A,
+        SEC_MINI_B
+} fe_sec_mini_cmd_t;
+
+
+typedef enum fe_status {
+        FE_HAS_SIGNAL   = 0x01,   /* found something above the noise level */
+        FE_HAS_CARRIER  = 0x02,   /* found a DVB signal  */
+        FE_HAS_VITERBI  = 0x04,   /* FEC is stable  */
+        FE_HAS_SYNC     = 0x08,   /* found sync bytes  */
+        FE_HAS_LOCK     = 0x10,   /* everything's working... */
+        FE_TIMEDOUT     = 0x20,   /* no lock within the last ~2 seconds */
+        FE_REINIT       = 0x40    /* frontend was reinitialized,  */
+} fe_status_t;                    /* application is recommended to reset */
+                                  /* DiSEqC, tone and parameters */
+
+typedef enum fe_spectral_inversion {
+        INVERSION_OFF,
+        INVERSION_ON,
+        INVERSION_AUTO
+} fe_spectral_inversion_t;
+
+
+typedef enum fe_code_rate {
+        FEC_NONE = 0,
+        FEC_1_2,
+        FEC_2_3,
+        FEC_3_4,
+        FEC_4_5,
+        FEC_5_6,
+        FEC_6_7,
+        FEC_7_8,
+        FEC_8_9,
+        FEC_AUTO,
+        FEC_3_5,
+        FEC_9_10,
+} fe_code_rate_t;
+
+typedef enum fe_modulation {
+        QPSK,
+        QAM_16,
+        QAM_32,
+        QAM_64,
+        QAM_128,
+        QAM_256,
+        QAM_AUTO,
+        VSB_8,
+        VSB_16,
+        PSK_8,
+        APSK_16,
+        APSK_32,
+        DQPSK,
+} fe_modulation_t;
+
+typedef enum fe_transmit_mode {
+        TRANSMISSION_MODE_2K,
+        TRANSMISSION_MODE_8K,
+        TRANSMISSION_MODE_AUTO
+} fe_transmit_mode_t;
+
+typedef enum fe_bandwidth {
+        BANDWIDTH_8_MHZ,
+        BANDWIDTH_7_MHZ,
+        BANDWIDTH_6_MHZ,
+        BANDWIDTH_AUTO
+} fe_bandwidth_t;
+
+
+typedef enum fe_guard_interval {
+        GUARD_INTERVAL_1_32,
+        GUARD_INTERVAL_1_16,
+        GUARD_INTERVAL_1_8,
+        GUARD_INTERVAL_1_4,
+        GUARD_INTERVAL_AUTO
+} fe_guard_interval_t;
+
+
+typedef enum fe_hierarchy {
+        HIERARCHY_NONE,
+        HIERARCHY_1,
+        HIERARCHY_2,
+        HIERARCHY_4,
+        HIERARCHY_AUTO
+} fe_hierarchy_t;
+
+typedef enum fe_pilot {
+        PILOT_ON,
+        PILOT_OFF,
+        PILOT_AUTO,
+} fe_pilot_t;
+
+typedef enum fe_rolloff {
+        ROLLOFF_35, /* Implied value in DVB-S, default for DVB-S2 */
+        ROLLOFF_20,
+        ROLLOFF_25,
+        ROLLOFF_AUTO,
+} fe_rolloff_t;
+
+typedef enum fe_delivery_system {
+        SYS_UNDEFINED,
+        SYS_DVBC_ANNEX_AC,
+        SYS_DVBC_ANNEX_B,
+        SYS_DVBT,
+        SYS_DSS,
+        SYS_DVBS,
+        SYS_DVBS2,
+        SYS_DVBH,
+        SYS_ISDBT,
+        SYS_ISDBS,
+        SYS_ISDBC,
+        SYS_ATSC,
+        SYS_ATSCMH,
+        SYS_DMBTH,
+        SYS_CMMB,
+        SYS_DAB,
+} fe_delivery_system_t;
+
+struct tChannelParameterMap {
+  int userValue;
+  int driverValue;
+  const char *userString;
+};
+
+class cChannel
+{
+private:
+  char *name;
+  char *shortName;
+  char *provider;
+  char *portalName;
+  int __BeginData__;
+  int frequency; // MHz
+  int source;
+  int srate;
+  int vpid;
+  int ppid;
+  int vtype;
+  int apids[MAXAPIDS + 1]; // list is zero-terminated
+  char alangs[MAXAPIDS][MAXLANGCODE2];
+  int dpids[MAXDPIDS + 1]; // list is zero-terminated
+  char dlangs[MAXDPIDS][MAXLANGCODE2];
+  int spids[MAXSPIDS + 1]; // list is zero-terminated
+  char slangs[MAXSPIDS][MAXLANGCODE2];
+  int tpid;
+  int caids[MAXCAIDS + 1]; // list is zero-terminated
+  int nid;
+  int tid;
+  int sid;
+  int rid;
+  int number;    // Sequence number assigned on load
+  char polarization;
+  int inversion;
+  int bandwidth;
+  int coderateH;
+  int coderateL;
+  int modulation;
+  int system;
+  int transmission;
+  int guard;
+  int hierarchy;
+  int rollOff;
+  int __EndData__;
+  int modification;
+  
+  bool StringToParameters(const char *s);
+
+public:
+  cChannel(const PVR_CHANNEL *Channel);
+  cChannel();
+  virtual ~cChannel();
+  
+  bool ReadFromVTP(int channel);
+  bool Parse(const char *s);
+  const char *Name(void) const { return name; }
+  const char *ShortName(bool OrName = false) const { return (OrName && isempty(shortName)) ? name : shortName; }
+  const char *Provider(void) const { return provider; }
+  const char *PortalName(void) const { return portalName; }
+  int Frequency(void) const { return frequency; } ///< Returns the actual frequency, as given in 'channels.conf'
+  int Source(void) const { return source; }
+  int Srate(void) const { return srate; }
+  int Vpid(void) const { return vpid; }
+  int Ppid(void) const { return ppid; }
+  int Vtype(void) const { return vtype; }
+  const int *Apids(void) const { return apids; }
+  const int *Dpids(void) const { return dpids; }
+  const int *Spids(void) const { return spids; }
+  int Apid(int i) const { return (0 <= i && i < MAXAPIDS) ? apids[i] : 0; }
+  int Dpid(int i) const { return (0 <= i && i < MAXDPIDS) ? dpids[i] : 0; }
+  int Spid(int i) const { return (0 <= i && i < MAXSPIDS) ? spids[i] : 0; }
+  const char *Alang(int i) const { return (0 <= i && i < MAXAPIDS) ? alangs[i] : ""; }
+  const char *Dlang(int i) const { return (0 <= i && i < MAXDPIDS) ? dlangs[i] : ""; }
+  const char *Slang(int i) const { return (0 <= i && i < MAXSPIDS) ? slangs[i] : ""; }
+  int Tpid(void) const { return tpid; }
+  const int *Caids(void) const { return caids; }
+  int Ca(int Index = 0) const { return Index < MAXCAIDS ? caids[Index] : 0; }
+  int Nid(void) const { return nid; }
+  int Tid(void) const { return tid; }
+  int Sid(void) const { return sid; }
+  int Rid(void) const { return rid; }
+  int Number(void) const { return number; }
+  void SetNumber(int Number) { number = Number; }
+  char Polarization(void) const { return polarization; }
+  int Inversion(void) const { return inversion; }
+  int Bandwidth(void) const { return bandwidth; }
+  int CoderateH(void) const { return coderateH; }
+  int CoderateL(void) const { return coderateL; }
+  int Modulation(void) const { return modulation; }
+  int System(void) const { return system; }
+  int Transmission(void) const { return transmission; }
+  int Guard(void) const { return guard; }
+  int Hierarchy(void) const { return hierarchy; }
+  int RollOff(void) const { return rollOff; }
+
+};
+
+#endif //__TIMERS_H
diff --git a/xbmc/pvrclients/vdr-streamdev/channelscan.cpp b/xbmc/pvrclients/vdr-streamdev/channelscan.cpp
new file mode 100644 (file)
index 0000000..2711379
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "channelscan.h"
+
+cVDRChannelScan::cVDRChannelScan()
+{
+
+}
+
+cVDRChannelScan::~cVDRChannelScan()
+{
+
+}
+
+void cVDRChannelScan::Start()
+{
+
+}
diff --git a/xbmc/pvrclients/vdr-streamdev/channelscan.h b/xbmc/pvrclients/vdr-streamdev/channelscan.h
new file mode 100644 (file)
index 0000000..7439195
--- /dev/null
@@ -0,0 +1,33 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+class cVDRChannelScan
+{
+public:
+  cVDRChannelScan();
+  ~cVDRChannelScan();
+
+  void Start();
+  bool Possible() { return true; }
+
+
+};
diff --git a/xbmc/pvrclients/vdr-streamdev/client.cpp b/xbmc/pvrclients/vdr-streamdev/client.cpp
new file mode 100644 (file)
index 0000000..ad93106
--- /dev/null
@@ -0,0 +1,1225 @@
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "client.h"
+#include "tools.h"
+#include "vtptransceiver.h"
+#include "channelscan.h"
+#include "ringbuffer.h"
+#include "xbmc_pvr_dll.h"
+
+using namespace std;
+
+class cTSBuffer;
+class cDataResp;
+
+#define SEEK_POSSIBLE 0x10 // flag used to check if protocol allows seeks
+
+/* User adjustable settings are saved here.
+ * Default values are defined inside client.h
+ * and exported to the other source files.
+ */
+CStdString   g_szHostname             = DEFAULT_HOST;         ///< The Host name or IP of the VDR
+int          g_iPort                  = DEFAULT_PORT;         ///< The VTP Port of the VDR (default is 2004)
+int          g_iPriority              = DEFAULT_PRIORITY;     ///< The Priority this client have in response to other clients
+int          g_iConnectTimeout        = DEFAULT_TIMEOUT;      ///< The Socket connection timeout
+bool         g_bOnlyFTA               = DEFAULT_FTA_ONLY;     ///< Send only Free-To-Air Channels inside Channel list to XBMC
+bool         g_bRadioEnabled          = DEFAULT_RADIO;        ///< Send also Radio channels list to XBMC
+bool         g_bCharsetConv           = DEFAULT_CHARCONV;     ///< Convert VDR's incoming strings to UTF8 character set
+bool         g_bNoBadChannels         = DEFAULT_BADCHANNELS;  ///< Ignore channels without a PID, APID and DPID
+bool         g_bHandleMessages        = DEFAULT_HANDLE_MSG;   ///< Send VDR's OSD status messages to XBMC OSD
+bool         g_bUseRecordingsDir      = DEFAULT_USE_REC_DIR;  ///< Use a normal directory if true for recordings
+CStdString   g_szRecordingsDir        = DEFAULT_REC_DIR;      ///< The path to the recordings directory
+//
+///* Client member variables */
+uint64_t     m_currentPlayingRecordBytes;
+uint32_t     m_currentPlayingRecordFrames;
+uint64_t     m_currentPlayingRecordPosition;
+uint64_t     m_recordingTotalBytesReaded;
+bool         m_recordingFirstRead;
+cPoller     *m_recordingPoller        = NULL;
+char         m_noSignalStreamData[ 6 + 0xffff ];
+long         m_noSignalStreamSize     = 0;
+long         m_noSignalStreamReadPos  = 0;
+bool         m_bPlayingNoSignal       = false;
+int          m_iCurrentChannel        = 1;
+ADDON_STATUS m_CurStatus              = STATUS_UNKNOWN;
+cTSBuffer   *m_TSBuffer               = NULL;
+cDataResp   *m_pDataResponse          = NULL;
+bool         g_bCreated               = false;
+int          g_iClientID              = -1;
+CStdString   g_szUserPath             = "";
+CStdString   g_szClientPath           = "";
+cHelper_libXBMC_addon *XBMC           = NULL;
+cHelper_libXBMC_pvr   *PVR            = NULL;
+
+
+
+/***********************************************************
+ * Internal functions
+ ***********************************************************/
+
+/// Derived cDevice classes that can receive channels will have to provide
+/// Transport Stream (TS) packets one at a time. cTSBuffer implements a
+/// simple buffer that allows the device to read a larger amount of data
+/// from the driver with each call to Read(), thus avoiding the overhead
+/// of getting each TS packet separately from the driver. It also makes
+/// sure the returned data points to a TS packet and automatically
+/// re-synchronizes after broken packets.
+
+class cTSBuffer : public cThread
+{
+private:
+  int f;
+  bool delivered;
+  cRingBufferLinear *ringBuffer;
+  virtual void Action(void);
+public:
+  cTSBuffer(int File, int Size);
+  ~cTSBuffer();
+  unsigned char *Get(void);
+};
+
+cTSBuffer::cTSBuffer(int File, int Size)
+{
+  SetDescription("TS Live buffer");
+  f           = File;
+  delivered   = false;
+  ringBuffer  = new cRingBufferLinear(Size, TS_SIZE, true, "TS");
+  ringBuffer->SetTimeouts(100, 100);
+  Start();
+}
+
+cTSBuffer::~cTSBuffer()
+{
+  Cancel(3);
+  delete ringBuffer;
+}
+
+void cTSBuffer::Action(void)
+{
+  if (ringBuffer)
+  {
+    bool firstRead = true;
+    cPoller Poller(f);
+    while (Running())
+    {
+      if (firstRead || Poller.Poll(100))
+      {
+        firstRead = false;
+        int r = ringBuffer->Read(f);
+        if (r < 0 && FATALERRNO)
+        {
+          if (errno == EOVERFLOW)
+            XBMC->Log(LOG_ERROR, "driver buffer overflow");
+          else
+          {
+            XBMC->Log(LOG_ERROR, "ERROR (%s,%d): %m", __FILE__, __LINE__);
+            break;
+          }
+        }
+      }
+    }
+  }
+}
+
+unsigned char *cTSBuffer::Get(void)
+{
+  int Count = 0;
+  if (delivered)
+  {
+    ringBuffer->Del(TS_SIZE);
+    delivered = false;
+  }
+  unsigned char *p = ringBuffer->Get(Count);
+  if (p && Count >= TS_SIZE)
+  {
+    if (*p != TS_SYNC_BYTE)
+    {
+      for (int i = 1; i < Count; i++)
+      {
+        if (p[i] == TS_SYNC_BYTE)
+        {
+          Count = i;
+          break;
+        }
+      }
+      ringBuffer->Del(Count);
+      XBMC->Log(LOG_ERROR, "skipped %d bytes to sync on TS packet", Count);
+      return NULL;
+    }
+    delivered = true;
+    return p;
+  }
+  return NULL;
+}
+
+class cDataResp : public cThread
+{
+private:
+  virtual void Action(void);
+  bool VDRToXBMCCommand(char *Cmd);
+  bool CallBackMODT(const char *Option);
+  bool CallBackDELT(const char *Option);
+  bool CallBackADDT(const char *Option);
+  bool CallBackSMSG(const char *Option);
+  bool CallBackIMSG(const char *Option);
+  bool CallBackWMSG(const char *Option);
+  bool CallBackEMSG(const char *Option);
+
+public:
+  cDataResp();
+  ~cDataResp();
+};
+
+
+cDataResp::cDataResp()
+{
+  /* Open Streamdev-Server VTP-Connection to VDR Backend Server */
+  if (!VTPTransceiver.CheckConnection())
+    return;
+
+  if (!VTPTransceiver.CreateDataConnection(siDataRespond))
+  {
+    XBMC->Log(LOG_ERROR, "Couldn't create socket for data response");
+    return;
+  }
+
+  SetDescription("VDR Data Response");
+  Start();
+}
+
+cDataResp::~cDataResp()
+{
+  Cancel(3);
+  VTPTransceiver.CloseDataConnection(siDataRespond);
+}
+
+void cDataResp::Action(void)
+{
+  char                       data[1024];
+  fd_set          set_r, set_e;
+  struct timeval  tv;
+  int             ret;
+  memset(data,0,1024);
+
+  while (Running())
+  {
+    tv.tv_sec = 5;
+    tv.tv_usec = 0;
+
+    FD_ZERO(&set_r);
+    FD_ZERO(&set_e);
+    FD_SET(VTPTransceiver.DataSocket(siDataRespond), &set_r);
+    FD_SET(VTPTransceiver.DataSocket(siDataRespond), &set_e);
+    ret = __select(FD_SETSIZE, &set_r, NULL, &set_e, &tv);
+    if (ret < 0)
+    {
+      XBMC->Log(LOG_ERROR, "CallbackRcvThread - select failed");
+      continue;
+    }
+    else if (ret == 0)
+      continue;
+
+    ret = __recv(VTPTransceiver.DataSocket(siDataRespond), (char*)data, sizeof(data), 0);
+    if (ret < 0)
+    {
+      XBMC->Log(LOG_ERROR, "CallbackRcvThread - receive failed");
+      continue;
+    }
+    else if (ret == 0)
+      continue;
+
+    /* Check the received command and perform associated action*/
+    VDRToXBMCCommand(data);
+    memset(data,0,1024);
+  }
+  return;
+}
+
+bool cDataResp::VDRToXBMCCommand(char *Cmd)
+{
+       char *param = NULL;
+
+       if (Cmd != NULL)
+       {
+               if ((param = strchr(Cmd, ' ')) != NULL)
+                       *(param++) = '\0';
+               else
+                       param = Cmd + strlen(Cmd);
+       }
+       else
+       {
+    XBMC->Log(LOG_ERROR, "VDRToXBMCCommand - called without command from %s:%d", g_szHostname.c_str(), g_iPort);
+               return false;
+       }
+
+/* !!! DISABLED UNTIL STREAMDEV HAVE SUPPORT FOR IT INSIDE CVS !!!
+       if      (strcasecmp(Cmd, "MODT") == 0) return CallBackMODT(param);
+       else if (strcasecmp(Cmd, "DELT") == 0) return CallBackDELT(param);
+       else if (strcasecmp(Cmd, "ADDT") == 0) return CallBackADDT(param);
+*/
+
+       if (strcasecmp(Cmd, "IMSG") == 0) return CallBackIMSG(param);
+       else if (strcasecmp(Cmd, "WMSG") == 0) return CallBackWMSG(param);
+       else if (strcasecmp(Cmd, "EMSG") == 0) return CallBackEMSG(param);
+       else
+       {
+    XBMC->Log(LOG_ERROR, "VDRToXBMCCommand - Unkown respond command %s", Cmd);
+               return false;
+       }
+}
+
+bool cDataResp::CallBackMODT(const char *Option)
+{
+//  PVR_event_callback(PVR_EVENT_TIMERS_CHANGE, "");
+  return true;
+}
+
+bool cDataResp::CallBackDELT(const char *Option)
+{
+//  PVR_event_callback(PVR_EVENT_TIMERS_CHANGE, "");
+  return true;
+}
+
+bool cDataResp::CallBackADDT(const char *Option)
+{
+//  PVR_event_callback(PVR_EVENT_TIMERS_CHANGE, "");
+  return true;
+}
+
+bool cDataResp::CallBackIMSG(const char *Option)
+{
+  if (*Option)
+  {
+    CStdString text = Option;
+    if (g_bCharsetConv)
+      XBMC->UnknownToUTF8(text);
+    XBMC->QueueNotification(QUEUE_INFO, text.c_str());
+    return true;
+  }
+  else
+  {
+    XBMC->Log(LOG_ERROR, "CallBackIMSG - missing option");
+    return false;
+  }
+}
+
+bool cDataResp::CallBackWMSG(const char *Option)
+{
+  if (*Option)
+  {
+    CStdString text = Option;
+    if (g_bCharsetConv)
+      XBMC->UnknownToUTF8(text);
+    XBMC->QueueNotification(QUEUE_WARNING, text.c_str());
+    return true;
+  }
+  else
+  {
+    XBMC->Log(LOG_ERROR, "CallBackWMSG - missing option");
+    return false;
+  }
+}
+
+bool cDataResp::CallBackEMSG(const char *Option)
+{
+  if (*Option)
+  {
+    CStdString text = Option;
+    if (g_bCharsetConv)
+      XBMC->UnknownToUTF8(text);
+    XBMC->QueueNotification(QUEUE_ERROR, text.c_str());
+    return true;
+  }
+  else
+  {
+    XBMC->Log(LOG_ERROR, "CallBackEMSG - missing option");
+    return false;
+  }
+}
+
+
+bool readNoSignalStream()
+{
+  CStdString noSignalFileName = g_szClientPath + "/resources/data/noSignal.mpg";
+
+  FILE *const f = fopen(noSignalFileName.c_str(), "rb");
+  if (f)
+  {
+    m_noSignalStreamSize = fread(&m_noSignalStreamData[0] + 9, 1, sizeof (m_noSignalStreamData) - 9 - 9 - 4, f);
+    if (m_noSignalStreamSize == sizeof (m_noSignalStreamData) - 9 - 9 - 4)
+    {
+      XBMC->Log(LOG_ERROR, "readNoSignalStream - '%s' exeeds limit of %ld bytes!", noSignalFileName.c_str(), (long)(sizeof (m_noSignalStreamData) - 9 - 9 - 4 - 1));
+    }
+    else if (m_noSignalStreamSize > 0)
+    {
+      m_noSignalStreamData[ 0 ] = 0x00;
+      m_noSignalStreamData[ 1 ] = 0x00;
+      m_noSignalStreamData[ 2 ] = 0x01;
+      m_noSignalStreamData[ 3 ] = 0xe0;
+      m_noSignalStreamData[ 4 ] = (m_noSignalStreamSize + 3) >> 8;
+      m_noSignalStreamData[ 5 ] = (m_noSignalStreamSize + 3) & 0xff;
+      m_noSignalStreamData[ 6 ] = 0x80;
+      m_noSignalStreamData[ 7 ] = 0x00;
+      m_noSignalStreamData[ 8 ] = 0x00;
+      m_noSignalStreamSize += 9;
+      m_noSignalStreamData[ m_noSignalStreamSize++ ] = 0x00;
+      m_noSignalStreamData[ m_noSignalStreamSize++ ] = 0x00;
+      m_noSignalStreamData[ m_noSignalStreamSize++ ] = 0x01;
+      m_noSignalStreamData[ m_noSignalStreamSize++ ] = 0xe0;
+      m_noSignalStreamData[ m_noSignalStreamSize++ ] = 0x00;
+      m_noSignalStreamData[ m_noSignalStreamSize++ ] = 0x07;
+      m_noSignalStreamData[ m_noSignalStreamSize++ ] = 0x80;
+      m_noSignalStreamData[ m_noSignalStreamSize++ ] = 0x00;
+      m_noSignalStreamData[ m_noSignalStreamSize++ ] = 0x00;
+      m_noSignalStreamData[ m_noSignalStreamSize++ ] = 0x00;
+      m_noSignalStreamData[ m_noSignalStreamSize++ ] = 0x00;
+      m_noSignalStreamData[ m_noSignalStreamSize++ ] = 0x01;
+      m_noSignalStreamData[ m_noSignalStreamSize++ ] = 0xb7;
+    }
+    fclose(f);
+    return true;
+  }
+  else
+  {
+    XBMC->Log(LOG_ERROR, "readNoSignalStream - couldn't open '%s'!", noSignalFileName.c_str());
+  }
+
+  return false;
+}
+
+int writeNoSignalStream(unsigned char* buf, int buf_size)
+{
+  int sizeToWrite = m_noSignalStreamSize-m_noSignalStreamReadPos;
+  m_bPlayingNoSignal = true;
+  if (buf_size > sizeToWrite)
+  {
+    memcpy(buf, m_noSignalStreamData+m_noSignalStreamReadPos, sizeToWrite);
+    m_noSignalStreamReadPos = 0;
+    return sizeToWrite;
+  }
+  else
+  {
+    memcpy(buf, m_noSignalStreamData+m_noSignalStreamReadPos, buf_size);
+    m_noSignalStreamReadPos += buf_size;
+    return buf_size;
+  }
+}
+
+extern "C" {
+
+/***********************************************************
+ * Standart AddOn related public library functions
+ ***********************************************************/
+
+ADDON_STATUS Create(void* hdl, void* props)
+{
+  if (!props)
+    return STATUS_UNKNOWN;
+
+  PVR_PROPS* pvrprops = (PVR_PROPS*)props;
+
+  XBMC = new cHelper_libXBMC_addon;
+  if (!XBMC->RegisterMe(hdl))
+    return STATUS_UNKNOWN;
+
+  PVR = new cHelper_libXBMC_pvr;
+  if (!PVR->RegisterMe(hdl))
+    return STATUS_UNKNOWN;
+
+  XBMC->Log(LOG_DEBUG, "Creating VDR PVR-Client");
+
+  m_CurStatus    = STATUS_UNKNOWN;
+  g_iClientID    = pvrprops->clientID;
+  g_szUserPath   = pvrprops->userpath;
+  g_szClientPath = pvrprops->clientpath;
+
+  /* Read setting "host" from settings.xml */
+  char * buffer;
+  buffer = (char*) malloc (1024);
+  buffer[0] = 0; /* Set the end of string */
+
+  if (XBMC->GetSetting("host", buffer))
+    g_szHostname = buffer;
+  else
+  {
+    /* If setting is unknown fallback to defaults */
+    XBMC->Log(LOG_ERROR, "Couldn't get 'host' setting, falling back to '127.0.0.1' as default");
+    g_szHostname = DEFAULT_HOST;
+  }
+  free (buffer);
+
+  /* Read setting "port" from settings.xml */
+  if (!XBMC->GetSetting("port", &g_iPort))
+  {
+    /* If setting is unknown fallback to defaults */
+    XBMC->Log(LOG_ERROR, "Couldn't get 'port' setting, falling back to '2004' as default");
+    g_iPort = DEFAULT_PORT;
+  }
+
+  /* Read setting "priority" from settings.xml */
+  if (!XBMC->GetSetting("priority", &g_iPriority))
+  {
+    /* If setting is unknown fallback to defaults */
+    XBMC->Log(LOG_ERROR, "Couldn't get 'priority' setting, falling back to %i as default", DEFAULT_PRIORITY);
+    g_iPriority = DEFAULT_PRIORITY;
+  }
+
+  /* Read setting "ftaonly" from settings.xml */
+  if (!XBMC->GetSetting("ftaonly", &g_bOnlyFTA))
+  {
+    /* If setting is unknown fallback to defaults */
+    XBMC->Log(LOG_ERROR, "Couldn't get 'ftaonly' setting, falling back to 'false' as default");
+    g_bOnlyFTA = DEFAULT_FTA_ONLY;
+  }
+
+  /* Read setting "useradio" from settings.xml */
+  if (!XBMC->GetSetting("useradio", &g_bRadioEnabled))
+  {
+    /* If setting is unknown fallback to defaults */
+    XBMC->Log(LOG_ERROR, "Couldn't get 'useradio' setting, falling back to 'true' as default");
+    g_bRadioEnabled = DEFAULT_RADIO;
+  }
+
+  /* Read setting "convertchar" from settings.xml */
+  if (!XBMC->GetSetting("convertchar", &g_bCharsetConv))
+  {
+    /* If setting is unknown fallback to defaults */
+    XBMC->Log(LOG_ERROR, "Couldn't get 'convertchar' setting, falling back to 'false' as default");
+    g_bCharsetConv = DEFAULT_CHARCONV;
+  }
+
+  /* Read setting "timeout" from settings.xml */
+  if (!XBMC->GetSetting("timeout", &g_iConnectTimeout))
+  {
+    /* If setting is unknown fallback to defaults */
+    XBMC->Log(LOG_ERROR, "Couldn't get 'timeout' setting, falling back to %i seconds as default", DEFAULT_TIMEOUT);
+    g_iConnectTimeout = DEFAULT_TIMEOUT;
+  }
+
+  /* Read setting "ignorechannels" from settings.xml */
+  if (!XBMC->GetSetting("ignorechannels", &g_bNoBadChannels))
+  {
+    /* If setting is unknown fallback to defaults */
+    XBMC->Log(LOG_ERROR, "Couldn't get 'ignorechannels' setting, falling back to 'true' as default");
+    g_bNoBadChannels = DEFAULT_BADCHANNELS;
+  }
+
+  /* Read setting "ignorechannels" from settings.xml */
+  if (!XBMC->GetSetting("handlemessages", &g_bHandleMessages))
+  {
+    /* If setting is unknown fallback to defaults */
+    XBMC->Log(LOG_ERROR, "Couldn't get 'handlemessages' setting, falling back to 'true' as default");
+    g_bHandleMessages = DEFAULT_HANDLE_MSG;
+  }
+
+  /* Read setting "ignorechannels" from settings.xml */
+  if (!XBMC->GetSetting("usedirectory", &g_bUseRecordingsDir))
+  {
+    /* If setting is unknown fallback to defaults */
+    XBMC->Log(LOG_ERROR, "Couldn't get 'usedirectory' setting, falling back to 'false' as default");
+    g_bUseRecordingsDir = DEFAULT_USE_REC_DIR;
+  }
+
+  if (g_bUseRecordingsDir)
+  {
+    /* Read setting "recordingdir" from settings.xml */
+    buffer = (char*) malloc (2048);
+    buffer[0] = 0; /* Set the end of string */
+
+    if (XBMC->GetSetting("recordingdir", buffer))
+      g_szRecordingsDir = buffer;
+    else
+    {
+      /* If setting is unknown fallback to defaults */
+      XBMC->Log(LOG_ERROR, "Couldn't get 'recordingdir' setting, directory not set");
+      g_szRecordingsDir = DEFAULT_REC_DIR;
+      g_bUseRecordingsDir = false;
+    }
+    free (buffer);
+  }
+
+  /* Create connection to streamdev-server */
+  if (!VTPTransceiver.CheckConnection())
+  {
+    m_CurStatus = STATUS_LOST_CONNECTION;
+    return m_CurStatus;
+  }
+
+  /* Check VDR streamdev is patched by calling a newly added command */
+  if (VTPTransceiver.GetNumChannels() == -1)
+  {
+    XBMC->Log(LOG_ERROR, "PCRClient-vdr: Detected unsupported Streamdev-Version");
+    m_CurStatus = STATUS_UNKNOWN;
+    return STATUS_UNKNOWN;
+  }
+  m_CurStatus = STATUS_OK;
+
+  readNoSignalStream();
+       if (g_bHandleMessages)
+    m_pDataResponse = new cDataResp();
+
+  g_bCreated = true;
+  return m_CurStatus;
+}
+
+void Destroy()
+{
+  if (g_bCreated)
+  {
+    DELETENULL(m_TSBuffer);
+    DELETENULL(m_pDataResponse);
+    VTPTransceiver.Quit();
+    VTPTransceiver.Reset();
+    g_bCreated = false;
+  }
+  m_CurStatus = STATUS_UNKNOWN;
+}
+
+ADDON_STATUS GetStatus()
+{
+  return m_CurStatus;
+}
+
+bool HasSettings()
+{
+  return true;
+}
+
+unsigned int GetSettings(StructSetting ***sSet)
+{
+  return 0;
+}
+
+ADDON_STATUS SetSetting(const char *settingName, const void *settingValue)
+{
+  string str = settingName;
+  if (str == "host")
+  {
+    string tmp_sHostname;
+    XBMC->Log(LOG_INFO, "Changed Setting 'host' from %s to %s", g_szHostname.c_str(), (const char*) settingValue);
+    tmp_sHostname = g_szHostname;
+    g_szHostname = (const char*) settingValue;
+    if (tmp_sHostname != g_szHostname)
+      return STATUS_NEED_RESTART;
+  }
+  else if (str == "port")
+  {
+    XBMC->Log(LOG_INFO, "Changed Setting 'port' from %u to %u", g_iPort, *(int*) settingValue);
+    if (g_iPort != *(int*) settingValue)
+    {
+      g_iPort = *(int*) settingValue;
+      return STATUS_NEED_RESTART;
+    }
+  }
+  else if (str == "priority")
+  {
+    XBMC->Log(LOG_INFO, "Changed Setting 'priority' from %u to %u", g_iPriority, *(int*) settingValue);
+    g_iPriority = *(int*) settingValue;
+  }
+  else if (str == "ftaonly")
+  {
+    XBMC->Log(LOG_INFO, "Changed Setting 'ftaonly' from %u to %u", g_bOnlyFTA, *(bool*) settingValue);
+    g_bOnlyFTA = *(bool*) settingValue;
+  }
+  else if (str == "useradio")
+  {
+    XBMC->Log(LOG_INFO, "Changed Setting 'useradio' from %u to %u", g_bRadioEnabled, *(bool*) settingValue);
+    g_bRadioEnabled = *(bool*) settingValue;
+  }
+  else if (str == "convertchar")
+  {
+    XBMC->Log(LOG_INFO, "Changed Setting 'convertchar' from %u to %u", g_bCharsetConv, *(bool*) settingValue);
+    g_bCharsetConv = *(bool*) settingValue;
+  }
+  else if (str == "timeout")
+  {
+    XBMC->Log(LOG_INFO, "Changed Setting 'timeout' from %u to %u", g_iConnectTimeout, *(int*) settingValue);
+    g_iConnectTimeout = *(int*) settingValue;
+  }
+  else if (str == "ignorechannels")
+  {
+    XBMC->Log(LOG_INFO, "Changed Setting 'ignorechannels' from %u to %u", g_bNoBadChannels, *(bool*) settingValue);
+    g_bNoBadChannels = *(bool*) settingValue;
+  }
+  else if (str == "handlemessages")
+  {
+    XBMC->Log(LOG_INFO, "Changed Setting 'handlemessages' from %u to %u", g_bHandleMessages, *(bool*) settingValue);
+    g_bHandleMessages = *(bool*) settingValue;
+  }
+  else if (str == "usedirectory")
+  {
+    XBMC->Log(LOG_INFO, "Changed Setting 'usedirectory' from %u to %u", g_bUseRecordingsDir, *(bool*) settingValue);
+    g_bUseRecordingsDir = *(bool*) settingValue;
+  }
+  else if (str == "recordingdir")
+  {
+    XBMC->Log(LOG_INFO, "Changed Setting 'recordingdir' from %s to %s", g_szRecordingsDir.c_str(), (const char*) settingValue);
+    g_szRecordingsDir = (const char*) settingValue;
+  }
+
+  return STATUS_OK;
+}
+
+void Stop()
+{
+}
+
+void FreeSettings()
+{
+}
+
+
+/***********************************************************
+ * PVR Client AddOn specific public library functions
+ ***********************************************************/
+
+PVR_ERROR GetProperties(PVR_SERVERPROPS* props)
+{
+  props->SupportChannelLogo        = false;
+  props->SupportTimeShift          = false;
+  props->SupportEPG                = true;
+  props->SupportRecordings         = true;
+  props->SupportTimers             = true;
+  props->SupportTV                 = true;
+  props->SupportRadio              = g_bRadioEnabled;
+  props->SupportChannelSettings    = false;
+  props->SupportDirector           = false;
+  props->SupportBouquets           = false;
+  props->HandleInputStream         = true;
+  props->HandleDemuxing            = false;
+  props->SupportChannelScan        = false;
+
+  return PVR_ERROR_NO_ERROR;
+}
+
+PVR_ERROR GetStreamProperties(PVR_STREAMPROPS* props)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+const char * GetBackendName()
+{
+  static CStdString BackendName = VTPTransceiver.GetBackendName();
+  return BackendName.c_str();
+}
+
+const char * GetBackendVersion()
+{
+  static CStdString BackendName = VTPTransceiver.GetBackendVersion();
+  return BackendName.c_str();
+}
+
+const char * GetConnectionString()
+{
+  static CStdString ConnectionString;
+  ConnectionString.Format("%s:%i%s", g_szHostname.c_str(), g_iPort, VTPTransceiver.CheckConnection() ? "" : " (Not connected!)");
+  return ConnectionString.c_str();
+}
+
+PVR_ERROR GetDriveSpace(long long *total, long long *used)
+{
+  return VTPTransceiver.GetDriveSpace(total, used);
+}
+
+PVR_ERROR GetBackendTime(time_t *localTime, int *gmtOffset)
+{
+  return VTPTransceiver.GetBackendTime(localTime, gmtOffset);
+}
+
+PVR_ERROR DialogChannelScan()
+{
+  cVDRChannelScan *VDRChannelScan = new cVDRChannelScan;
+  if (VDRChannelScan->Possible())
+  {
+
+    return PVR_ERROR_NO_ERROR;
+  }
+  return PVR_ERROR_NOT_POSSIBLE;
+}
+
+PVR_ERROR MenuHook(const PVR_MENUHOOK &menuhook)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+/*******************************************/
+/** PVR EPG Functions                     **/
+
+PVR_ERROR RequestEPGForChannel(PVRHANDLE handle, const PVR_CHANNEL &channel, time_t start, time_t end)
+{
+  return VTPTransceiver.RequestEPGForChannel(channel, handle, start, end);
+}
+
+
+/*******************************************/
+/** PVR Bouquets Functions                **/
+
+int GetNumBouquets()
+{
+  return 0;
+}
+
+PVR_ERROR RequestBouquetsList(PVRHANDLE handle, int radio)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+
+/*******************************************/
+/** PVR Channel Functions                 **/
+
+int GetNumChannels()
+{
+  return VTPTransceiver.GetNumChannels();
+}
+
+PVR_ERROR RequestChannelList(PVRHANDLE handle, int radio)
+{
+  return VTPTransceiver.RequestChannelList(handle, radio);
+}
+
+PVR_ERROR DeleteChannel(unsigned int number)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+PVR_ERROR RenameChannel(unsigned int number, const char *newname)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+PVR_ERROR MoveChannel(unsigned int number, unsigned int newnumber)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+PVR_ERROR DialogChannelSettings(const PVR_CHANNEL &channelinfo)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+PVR_ERROR DialogAddChannel(const PVR_CHANNEL &channelinfo)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+
+/*******************************************/
+/** PVR Recording Functions               **/
+
+int GetNumRecordings(void)
+{
+  return VTPTransceiver.GetNumRecordings();
+}
+
+PVR_ERROR RequestRecordingsList(PVRHANDLE handle)
+{
+  return VTPTransceiver.RequestRecordingsList(handle);
+}
+
+PVR_ERROR DeleteRecording(const PVR_RECORDINGINFO &recinfo)
+{
+  return VTPTransceiver.DeleteRecording(recinfo);
+}
+
+PVR_ERROR RenameRecording(const PVR_RECORDINGINFO &recinfo, const char *newname)
+{
+  return VTPTransceiver.RenameRecording(recinfo, newname);
+}
+
+
+/*******************************************/
+/** PVR Recording cut marks Functions     **/
+
+bool HaveCutmarks()
+{
+  return false;
+}
+
+PVR_ERROR RequestCutMarksList(PVRHANDLE handle)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+PVR_ERROR AddCutMark(const PVR_CUT_MARK &cutmark)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+PVR_ERROR DeleteCutMark(const PVR_CUT_MARK &cutmark)
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+PVR_ERROR StartCut()
+{
+  return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+
+/*******************************************/
+/** PVR Timer Functions                   **/
+
+int GetNumTimers(void)
+{
+  return VTPTransceiver.GetNumTimers();
+}
+
+PVR_ERROR RequestTimerList(PVRHANDLE handle)
+{
+  return VTPTransceiver.RequestTimerList(handle);
+}
+
+PVR_ERROR AddTimer(const PVR_TIMERINFO &timerinfo)
+{
+  return VTPTransceiver.AddTimer(timerinfo);
+}
+
+PVR_ERROR DeleteTimer(const PVR_TIMERINFO &timerinfo, bool force)
+{
+  return VTPTransceiver.DeleteTimer(timerinfo, force);
+}
+
+PVR_ERROR RenameTimer(const PVR_TIMERINFO &timerinfo, const char *newname)
+{
+  return VTPTransceiver.RenameTimer(timerinfo, newname);
+}
+
+PVR_ERROR UpdateTimer(const PVR_TIMERINFO &timerinfo)
+{
+  return VTPTransceiver.UpdateTimer(timerinfo);
+}
+
+
+/*******************************************/
+/** PVR Live Stream Functions             **/
+
+bool OpenLiveStream(const PVR_CHANNEL &channelinfo)
+{
+  if (!VTPTransceiver.CheckConnection())
+    return false;
+
+  if (!VTPTransceiver.ProvidesChannel(channelinfo.number, g_iPriority))
+  {
+    XBMC->Log(LOG_ERROR, "VDR does not provide channel %i", channelinfo.number);
+    return false;
+  }
+
+  if (!VTPTransceiver.SetChannelDevice(channelinfo.number))
+  {
+    XBMC->Log(LOG_ERROR, "Could't tune to channel %i", channelinfo.number);
+    return false;
+  }
+
+  if (!VTPTransceiver.CreateDataConnection(siLive))
+  {
+    XBMC->Log(LOG_ERROR, "Could't create connection to VDR for Live streaming");
+    return false;
+  }
+
+  m_iCurrentChannel       = channelinfo.number;
+  m_noSignalStreamReadPos = 0;
+  m_bPlayingNoSignal      = false;
+  m_TSBuffer              = new cTSBuffer(VTPTransceiver.DataSocket(siLive), MEGABYTE(2));
+  return true;
+}
+
+void CloseLiveStream()
+{
+  if (!VTPTransceiver.CheckConnection())
+    XBMC->Log(LOG_DEBUG, "CloseLiveStream(): Control connection gone !");
+
+  VTPTransceiver.CloseDataConnection(siLive);
+  m_iCurrentChannel = 1;
+  DELETENULL(m_TSBuffer);
+  return;
+}
+
+int ReadLiveStream(unsigned char* buf, int buf_size)
+{
+  bool tryReconnect         = true;
+  int TSReadNeeded          = buf_size / TS_SIZE;
+  int TSReadDone            = 0;
+  static int read_timeouts  = 0;
+
+  while (TSReadDone < TSReadNeeded)
+  {
+    if (!m_TSBuffer)
+      return writeNoSignalStream(buf, buf_size);
+
+    unsigned char *Data = m_TSBuffer->Get();
+    if (!Data)
+    {
+      if (m_bPlayingNoSignal)
+        return writeNoSignalStream(buf, buf_size);
+
+      if (VTPTransceiver.DataSocket(siLive) == INVALID_SOCKET)
+      {
+        if (tryReconnect)
+        {
+          tryReconnect = false;
+          XBMC->Log(LOG_INFO, "Streaming connections lost during ReadLiveStream, trying reconnect");
+
+          if (!VTPTransceiver.ProvidesChannel(m_iCurrentChannel, g_iPriority))
+            return -1;
+
+          DELETENULL(m_TSBuffer);
+          VTPTransceiver.CloseDataConnection(siLive);
+
+          if (!VTPTransceiver.SetChannelDevice(m_iCurrentChannel))
+            continue; /* Continue here to read NoSignal stream */
+
+          if (!VTPTransceiver.CreateDataConnection(siLive))
+            continue; /* Continue here to read NoSignal stream */
+
+          m_noSignalStreamReadPos = 0;
+          m_bPlayingNoSignal      = false;
+          m_TSBuffer              = new cTSBuffer(VTPTransceiver.DataSocket(siLive), MEGABYTE(2));
+          continue;
+        }
+        XBMC->Log(LOG_ERROR, "Reconnect in ReadLiveStream not possible");
+        continue; /* Continue here to read NoSignal stream */
+      }
+
+      if (read_timeouts > 20)
+      {
+        XBMC->Log(LOG_INFO, "No data in 2 seconds, queuing no signal image");
+        read_timeouts = 0;
+        return writeNoSignalStream(buf, buf_size);
+      }
+      read_timeouts++;
+      usleep(10*1000);
+
+      continue;
+    }
+    memcpy(buf+TSReadDone*TS_SIZE, Data, TS_SIZE);
+    TSReadDone++;
+  }
+  read_timeouts = 0;
+  m_bPlayingNoSignal = false;
+  return TSReadDone*TS_SIZE;
+}
+
+int GetCurrentClientChannel()
+{
+  return m_iCurrentChannel;
+}
+
+bool SwitchChannel(const PVR_CHANNEL &channelinfo)
+{
+  if (!VTPTransceiver.CheckConnection())
+    return false;
+
+  if (!VTPTransceiver.ProvidesChannel(channelinfo.number, g_iPriority))
+  {
+    XBMC->Log(LOG_ERROR, "VDR does not provide channel %i", channelinfo.number);
+    return false;
+  }
+
+  DELETENULL(m_TSBuffer);
+  VTPTransceiver.CloseDataConnection(siLive);
+
+  if (!VTPTransceiver.SetChannelDevice(channelinfo.number))
+  {
+    XBMC->Log(LOG_ERROR, "Could't tune to channel %i", channelinfo.number);
+    return false;
+  }
+
+  if (!VTPTransceiver.CreateDataConnection(siLive))
+  {
+    XBMC->Log(LOG_ERROR, "Could't create connection to VDR for Live streaming");
+    return false;
+  }
+
+  m_iCurrentChannel       = channelinfo.number;
+  m_noSignalStreamReadPos = 0;
+  m_bPlayingNoSignal      = false;
+  m_TSBuffer              = new cTSBuffer(VTPTransceiver.DataSocket(siLive), MEGABYTE(2));
+  return true;
+}
+
+PVR_ERROR SignalQuality(PVR_SIGNALQUALITY &qualityinfo)
+{
+  return VTPTransceiver.SignalQuality(qualityinfo, m_iCurrentChannel);
+}
+
+
+/*******************************************/
+/** PVR Secondary Stream Functions        **/
+
+bool SwapLiveTVSecondaryStream()
+{
+  return false;
+}
+
+bool OpenSecondaryStream(const PVR_CHANNEL &channelinfo)
+{
+  return false;
+}
+
+void CloseSecondaryStream()
+{
+
+}
+
+int ReadSecondaryStream(unsigned char* buf, int buf_size)
+{
+  return 0;
+}
+
+
+/*******************************************/
+/** PVR Recording Stream Functions        **/
+
+bool OpenRecordedStream(const PVR_RECORDINGINFO &recinfo)
+{
+  if (!VTPTransceiver.CheckConnection())
+    return false;
+
+  if (!VTPTransceiver.SetRecordingIndex(recinfo.index))
+  {
+    XBMC->Log(LOG_ERROR, "Could't open recording %i", recinfo.index);
+    return false;
+  }
+
+  if (!VTPTransceiver.CreateDataConnection(siReplay))
+  {
+    XBMC->Log(LOG_ERROR, "Could't create connection to VDR for recording streaming");
+    return false;
+  }
+
+  m_currentPlayingRecordPosition = 0;
+  m_recordingTotalBytesReaded    = 0;
+  m_recordingPoller              = new cPoller(VTPTransceiver.DataSocket(siReplay));
+  m_recordingFirstRead           = true;
+  return true;
+}
+
+void CloseRecordedStream(void)
+{
+  if (!VTPTransceiver.CheckConnection())
+    XBMC->Log(LOG_DEBUG, "CloseRecordedStream(): Control connection gone !");
+
+  VTPTransceiver.CloseDataConnection(siReplay);
+
+  DELETENULL(m_recordingPoller);
+}
+
+int ReadRecordedStream(unsigned char* buf, int buf_size)
+{
+  if (VTPTransceiver.DataSocket(siReplay) == INVALID_SOCKET)
+    return 0;
+
+  int res = 0;
+  if (m_recordingFirstRead || m_recordingPoller->Poll(100))
+  {
+    m_recordingFirstRead = false;
+    res = safe_read(VTPTransceiver.DataSocket(siReplay), buf, buf_size);
+    if (res < 0 && FATALERRNO)
+    {
+      if (errno == EOVERFLOW)
+        XBMC->Log(LOG_ERROR, "driver buffer overflow");
+      else
+        XBMC->Log(LOG_ERROR, "ERROR (%s,%d): %m", __FILE__, __LINE__);
+      return 0;
+    }
+  }
+
+  m_currentPlayingRecordPosition += res;
+  m_recordingTotalBytesReaded    += res;
+  return res;
+}
+
+long long SeekRecordedStream(long long pos, int whence)
+{
+  if (!VTPTransceiver.CheckConnection())
+    return -1;
+
+  long long nextPos = m_currentPlayingRecordPosition;
+
+  switch (whence)
+  {
+    case SEEK_SET:
+      nextPos = pos;
+      break;
+
+    case SEEK_CUR:
+      nextPos += pos;
+      break;
+
+    case SEEK_END:
+      if (m_currentPlayingRecordBytes)
+        nextPos = m_currentPlayingRecordBytes - pos;
+      else
+        return -1;
+      break;
+
+    case SEEK_POSSIBLE:
+      return 1;
+
+    default:
+      return -1;
+  }
+
+  if (nextPos > (long long) m_currentPlayingRecordBytes)
+    return 0;
+
+  m_currentPlayingRecordPosition = nextPos;
+
+  /* The VDR streamdev seek command returns the amount which is sendet into the
+     network. */
+  uint64_t bytesToFlush = VTPTransceiver.SeekRecordingPosition(nextPos);
+  /* I don't like the following code, but have no better idea todo this.
+     It flush the already by VDR transmitted data from the TCP stack, so if XBMC
+     perform the next Data read the right stream position is on top of the Socket. */
+  while (m_recordingTotalBytesReaded < bytesToFlush)
+  {
+    unsigned char buffer[32768];
+    ReadRecordedStream(&*buffer, sizeof(buffer));
+  }
+  return nextPos;
+}
+
+long long PositionRecordedStream(void)
+{
+  return m_currentPlayingRecordPosition;
+}
+
+long long LengthRecordedStream(void)
+{
+  VTPTransceiver.GetPlayingRecordingSize(&m_currentPlayingRecordBytes, &m_currentPlayingRecordFrames);
+  return m_currentPlayingRecordBytes;
+}
+
+
+/** UNUSED API FUNCTIONS */
+DemuxPacket* DemuxRead() { return NULL; }
+void DemuxAbort() {}
+void DemuxReset() {}
+void DemuxFlush() {}
+long long SeekLiveStream(long long pos, int whence) { return -1; }
+long long PositionLiveStream(void) { return -1; }
+long long LengthLiveStream(void) { return -1; }
+const char * GetLiveStreamURL(const PVR_CHANNEL &channelinfo) { return ""; }
+
+} //end extern "C"
diff --git a/xbmc/pvrclients/vdr-streamdev/client.h b/xbmc/pvrclients/vdr-streamdev/client.h
new file mode 100644 (file)
index 0000000..76bc3a8
--- /dev/null
@@ -0,0 +1,62 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef CLIENT_H
+#define CLIENT_H
+
+#include "StdString.h"
+#include "../../../addons/library.xbmc.addon/libXBMC_addon.h"
+#include "../../../addons/library.xbmc.pvr/libXBMC_pvr.h"
+
+#define DEFAULT_HOST          "127.0.0.1"
+#define DEFAULT_PORT          2004
+#define DEFAULT_FTA_ONLY      false
+#define DEFAULT_RADIO         true
+#define DEFAULT_CHARCONV      false
+#define DEFAULT_BADCHANNELS   true
+#define DEFAULT_HANDLE_MSG    true
+#define DEFAULT_PRIORITY      99
+#define DEFAULT_TIMEOUT       3
+#define DEFAULT_USE_REC_DIR   false
+#define DEFAULT_REC_DIR       ""
+
+extern bool         g_bCreated;           ///< Shows that the Create function was successfully called
+extern int          g_iClientID;          ///< The PVR client ID used by XBMC for this driver
+extern CStdString   g_szUserPath;         ///< The Path to the user directory inside user profile
+extern CStdString   g_szClientPath;       ///< The Path where this driver is located
+
+/* Client Settings */
+extern CStdString   g_szHostname;         ///< The Host name or IP of the VDR
+extern int          g_iPort;              ///< The VTP Port of the VDR (default is 2004)
+extern int          g_iPriority;          ///< The Priority this client have in response to other clients
+extern int          g_iConnectTimeout;    ///< The Socket connection timeout
+extern bool         g_bOnlyFTA;           ///< Send only Free-To-Air Channels inside Channel list to XBMC
+extern bool         g_bRadioEnabled;      ///< Send also Radio channels list to XBMC
+extern bool         g_bCharsetConv;       ///< Convert VDR's incoming strings to UTF8 character set
+extern bool         g_bNoBadChannels;     ///< Ignore channels without a PID, APID and DPID
+extern bool         g_bHandleMessages;    ///< Send VDR's OSD status messages to XBMC OSD
+extern bool         g_bUseRecordingsDir;  ///< Use a normal directory if true for recordings
+extern CStdString   g_szRecordingsDir;    ///< The path to the recordings directory
+extern cHelper_libXBMC_addon *XBMC;
+extern cHelper_libXBMC_pvr   *PVR;
+
+#endif /* CLIENT_H */
diff --git a/xbmc/pvrclients/vdr-streamdev/epg.cpp b/xbmc/pvrclients/vdr-streamdev/epg.cpp
new file mode 100644 (file)
index 0000000..80108c2
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+/*
+ * This code is taken from epg.c in the Video Disk Recorder ('VDR')
+ */
+
+#include "epg.h"
+
+cEpg::cEpg()
+{
+  m_uid             = 0;
+  m_title           = NULL;
+  m_shortText       = NULL;
+  m_description     = NULL;
+  m_aux             = NULL;
+  m_StartTime       = 0;
+  m_EndTime         = 0;
+  m_Duration        = 0;
+  m_genre           = NULL;
+  m_genre_type      = 0;
+  m_genre_sub_type  = 0;
+  m_parental_rating = -1;
+  m_vps             = 0;
+
+}
+
+cEpg::~cEpg()
+{
+  free(m_aux);
+  free(m_genre);
+  free(m_title);
+  free(m_shortText);
+  free(m_description);
+}
+
+void cEpg::Reset()
+{
+  free(m_aux);
+  free(m_genre);
+  free(m_title);
+  free(m_shortText);
+  free(m_description);
+
+  m_StartTime       = 0;
+  m_EndTime         = 0;
+  m_Duration        = 0;
+  m_genre_type      = 0;
+  m_genre_sub_type  = 0;
+  m_parental_rating = -1;
+  m_vps             = 0;
+  m_uid             = 0;
+  m_title           = NULL;
+  m_shortText       = NULL;
+  m_description     = NULL;
+  m_aux             = NULL;
+  m_genre           = NULL;
+}
+
+bool cEpg::ParseLine(const char *s)
+{
+  char *t = skipspace(s + 1);
+  switch (*s)
+  {
+    case 'T': SetTitle(t);
+              break;
+    case 'S': SetShortText(t);
+              break;
+    case 'D': strreplace(t, '|', '\n');
+              SetDescription(t);
+              break;
+    case 'E':
+      {
+        unsigned int EventID;
+        time_t StartTime;
+        int Duration;
+        unsigned int TableID = 0;
+        unsigned int Version = 0xFF; // actual value is ignored
+        int n = sscanf(t, "%u %ld %d %X %X", &EventID, &StartTime, &Duration, &TableID, &Version);
+        if (n >= 3 && n <= 5)
+        {
+          m_StartTime = StartTime;
+          m_EndTime   = StartTime+Duration;
+          m_Duration  = Duration;
+          m_uid       = EventID;
+        }
+      }
+      break;
+    case 'G':
+      {
+        char genre[1024];
+        int genreType;
+        int genreSubType;
+        int n = sscanf(t, "%u %u %[^\n]", &genreType, &genreSubType, genre);
+        if (n == 3)
+        {
+          SetGenre(genre, genreType, genreSubType);
+        }
+      }
+      break;
+    case 'X': break;
+    case 'C': break;
+    case 'c': break;
+    case 'V': SetVps(atoi(t));
+              break;
+    case 'R': SetParentalRating(atoi(t));
+              break;
+    case 'e': return true;
+    default:  XBMC->Log(LOG_ERROR, "cEpg::ParseLine - unexpected tag while reading EPG data: %s", s);
+              return true;
+  }
+  return false;
+}
+
+void cEpg::SetTitle(const char *Title)
+{
+  m_title = strcpyrealloc(m_title, Title);
+}
+
+void cEpg::SetShortText(const char *ShortText)
+{
+  m_shortText = strcpyrealloc(m_shortText, ShortText);
+}
+
+void cEpg::SetDescription(const char *Description)
+{
+  m_description = strcpyrealloc(m_description, Description);
+}
+
+void cEpg::SetGenre(const char *Genre, int genreType, int genreSubType)
+{
+  m_genre_type      = genreType;
+  m_genre_sub_type  = genreSubType;
+  if (Genre)
+    m_genre = strcpyrealloc(m_genre, Genre);
+}
+
+void cEpg::SetVps(time_t Vps)
+{
+  m_vps = Vps;
+}
+
+void cEpg::SetParentalRating(int Rating)
+{
+  m_parental_rating = Rating;
+}
diff --git a/xbmc/pvrclients/vdr-streamdev/epg.h b/xbmc/pvrclients/vdr-streamdev/epg.h
new file mode 100644 (file)
index 0000000..15f6bc6
--- /dev/null
@@ -0,0 +1,76 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef __EPG_H
+#define __EPG_H
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "tools.h"
+
+class cEpg
+{
+private:
+  unsigned int m_uid;
+  char *m_title;
+  char *m_shortText;
+  char *m_description;
+  char *m_aux;
+  time_t m_StartTime;
+  time_t m_EndTime;
+  int m_Duration;
+  char *m_genre;
+  int m_genre_type;
+  int m_genre_sub_type;
+  int m_parental_rating;
+  time_t m_vps;              // Video Programming Service timestamp (VPS, aka "Programme Identification Label", PIL)
+
+public:
+  cEpg();
+  virtual ~cEpg();
+  void Reset();
+
+  bool ParseLine(const char *s);
+  bool ParseEntryLine(const char *s);
+  const char *Aux(void) const { return m_aux; }
+  int UniqueId(void) const { return m_uid; }
+  time_t StartTime(void) const { return m_StartTime; }
+  time_t EndTime(void) const { return m_EndTime; }
+  time_t Duration(void) const { return m_Duration; }
+  time_t Vps(void) const { return m_vps; }
+  void SetVps(time_t Vps);
+  const char *Title(void) const { return m_title; }
+  const char *ShortText(void) const { return m_shortText; }
+  const char *Description(void) const { return m_description; }
+  const char *Genre(void) const { return m_genre; }
+  int GenreType(void) const { return m_genre_type; }
+  int GenreSubType(void) const { return m_genre_sub_type; }
+  int ParentalRating(void) const { return m_parental_rating; }
+  void SetParentalRating(int Rating);
+  void SetTitle(const char *Title);
+  void SetShortText(const char *ShortText);
+  void SetDescription(const char *Description);
+  void SetGenre(const char *Genre, int genreType, int genreSubType);
+};
+
+#endif //__EPG_H
diff --git a/xbmc/pvrclients/vdr-streamdev/linux/pvrclient-vdr_os_posix.h b/xbmc/pvrclients/vdr-streamdev/linux/pvrclient-vdr_os_posix.h
new file mode 100644 (file)
index 0000000..7f6a56e
--- /dev/null
@@ -0,0 +1,108 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef PVRCLIENT_VDR_OS_POSIX_H
+#define PVRCLIENT_VDR_OS_POSIX_H
+
+#define _FILE_OFFSET_BITS 64
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/syscall.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/timeb.h>
+#include <sys/wait.h>
+#include <sys/signal.h>
+#include <sys/resource.h>
+#ifndef __APPLE__
+#include <sys/prctl.h> 
+#endif
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <pthread.h>
+#include <poll.h>
+
+typedef int bool_t;
+typedef int SOCKET;
+
+#define __close(a) close(a)
+#define __select select
+#define __recv recv
+#define __shutdown shutdown
+#define __socket socket
+#define __bind bind
+#define __getsockname getsockname
+#define __connect connect
+#define __getpeername getpeername
+#define __send send
+#define __getsockopt getsockopt
+#define __listen listen
+#define __accept accept
+#define __setsockopt setsockopt
+#define __fcntl fcntl
+#define __gethostbyname gethostbyname
+#define __read read
+#define __write write
+#define __poll poll
+#define SOCKET_ERROR   (-1)
+#define INVALID_SOCKET (-1)
+#define SD_BOTH SHUT_RDWR
+
+#define LIBTYPE
+#define sock_getlasterror errno
+#define sock_getlasterror_socktimeout (errno == EAGAIN)
+#define console_vprintf vprintf
+#define console_printf printf
+#define THREAD_FUNC_PREFIX void *
+
+#ifndef __STL_CONFIG_H
+template<class T> inline T min(T a, T b) { return a <= b ? a : b; }
+template<class T> inline T max(T a, T b) { return a >= b ? a : b; }
+template<class T> inline int sgn(T a) { return a < 0 ? -1 : a > 0 ? 1 : 0; }
+template<class T> inline void swap(T &a, T &b) { T t = a; a = b; b = t; }
+#endif
+
+#define Sleep(t) usleep(t*1000)
+
+static inline uint64_t getcurrenttime(void)
+{
+       struct timeval t;
+       gettimeofday(&t, NULL);
+       return ((uint64_t)t.tv_sec * 1000) + (t.tv_usec / 1000);
+}
+
+static inline int setsocktimeout(int s, int level, int optname, uint64_t timeout)
+{
+       struct timeval t;
+       t.tv_sec = timeout / 1000;
+       t.tv_usec = (timeout % 1000) * 1000;
+       return setsockopt(s, level, optname, (char *)&t, sizeof(t));
+}
+
+#endif
diff --git a/xbmc/pvrclients/vdr-streamdev/patches/streamdev-cvs100210-ReplaceRecordingStreaming.patch b/xbmc/pvrclients/vdr-streamdev/patches/streamdev-cvs100210-ReplaceRecordingStreaming.patch
new file mode 100644 (file)
index 0000000..a5cc5d6
--- /dev/null
@@ -0,0 +1,581 @@
+diff -NaurwB streamdev/Makefile streamdev-patched/Makefile
+--- streamdev/Makefile 2009-11-04 12:12:20.000000000 +0100
++++ streamdev-patched/Makefile 2010-02-09 15:58:13.000000000 +0100
+@@ -65,7 +65,7 @@
+       server/server.o server/component.o server/connection.o \
+       server/componentVTP.o server/componentHTTP.o server/componentIGMP.o \
+       server/connectionVTP.o server/connectionHTTP.o server/connectionIGMP.o \
+-      server/streamer.o server/livestreamer.o server/livefilter.o \
++      server/streamer.o server/livestreamer.o server/livefilter.o server/recordingstreamer.o \
+       server/suspend.o server/setup.o server/menuHTTP.o server/recplayer.o \
+       remux/tsremux.o remux/ts2pes.o remux/ts2ps.o remux/ts2es.o remux/extern.o
+       
+diff -NaurwB streamdev/server/connectionVTP.c streamdev-patched/server/connectionVTP.c
+--- streamdev/server/connectionVTP.c   2010-01-29 13:03:02.000000000 +0100
++++ streamdev-patched/server/connectionVTP.c   2010-02-10 08:56:22.000000000 +0100
+@@ -4,6 +4,7 @@
+  
+ #include "server/connectionVTP.h"
+ #include "server/livestreamer.h"
++#include "server/recordingstreamer.h"
+ #include "server/suspend.h"
+ #include "setup.h"
+@@ -741,11 +742,11 @@
+               m_FilterSocket(NULL),
+               m_FilterStreamer(NULL),
+               m_RecSocket(NULL),
++              m_RecStreamer(NULL),
+               m_DataSocket(NULL),
+               m_LastCommand(NULL),
+               m_StreamType(stTSPIDS),
+               m_FiltersSupport(false),
+-              m_RecPlayer(NULL),
+               m_LSTEHandler(NULL),
+               m_LSTCHandler(NULL),
+               m_LSTTHandler(NULL),
+@@ -759,6 +760,7 @@
+               free(m_LastCommand);
+       delete m_LiveStreamer;
+       delete m_LiveSocket;
++      delete m_RecStreamer;
+       delete m_RecSocket;
+       delete m_FilterStreamer;
+       delete m_FilterSocket;
+@@ -767,7 +769,6 @@
+       delete m_LSTCHandler;
+       delete m_LSTEHandler;
+       delete m_LSTRHandler;
+-      delete m_RecPlayer;
+ }
+ inline bool cConnectionVTP::Abort(void) const
+@@ -833,9 +834,10 @@
+       if      (strcasecmp(Cmd, "CAPS") == 0) return CmdCAPS(param);
+       else if (strcasecmp(Cmd, "PROV") == 0) return CmdPROV(param);
+       else if (strcasecmp(Cmd, "PORT") == 0) return CmdPORT(param);
+-      else if (strcasecmp(Cmd, "READ") == 0) return CmdREAD(param);
+       else if (strcasecmp(Cmd, "TUNE") == 0) return CmdTUNE(param);
+       else if (strcasecmp(Cmd, "PLAY") == 0) return CmdPLAY(param);
++      else if (strcasecmp(Cmd, "SEEK") == 0) return CmdSEEK(param);
++      else if (strcasecmp(Cmd, "SIZE") == 0) return CmdSIZE(param);
+       else if (strcasecmp(Cmd, "ADDP") == 0) return CmdADDP(param);
+       else if (strcasecmp(Cmd, "DELP") == 0) return CmdDELP(param);
+       else if (strcasecmp(Cmd, "ADDF") == 0) return CmdADDF(param);
+@@ -1012,6 +1014,8 @@
+               if (!m_RecSocket->SetDSCP())
+                       LOG_ERROR_STR("unable to set DSCP sockopt");
++              if(m_RecSocket)
++                      m_RecStreamer->Start(m_RecSocket);
+               return Respond(220, "Port command ok, data connection opened");
+               break;
+@@ -1034,35 +1038,6 @@
+       }
+ }
+-bool cConnectionVTP::CmdREAD(char *Opts)
+-{
+-      if (*Opts) {
+-              char *tail;
+-              uint64_t position = strtoll(Opts, &tail, 10);
+-              if (tail && tail != Opts) {
+-                      tail = skipspace(tail);
+-                      if (tail && tail != Opts) {
+-                              int size = strtol(tail, NULL, 10);
+-                              uint8_t* data = (uint8_t*)malloc(size+4);
+-                              unsigned long count_readed = m_RecPlayer->getBlock(data, position, size);
+-                              unsigned long count_written = m_RecSocket->SysWrite(data, count_readed);
+-
+-                              free(data);
+-                              return Respond(220, "%lu Bytes submitted", count_written);
+-                      }
+-                      else {
+-                              return Respond(501, "Missing position");
+-                      }
+-              }
+-              else {
+-                      return Respond(501, "Missing size");
+-              }
+-      }
+-      else {
+-              return Respond(501, "Missing position");
+-      }
+-}
+-
+ bool cConnectionVTP::CmdTUNE(char *Opts) 
+ {
+       const cChannel *chan;
+@@ -1096,27 +1071,40 @@
+ bool cConnectionVTP::CmdPLAY(char *Opts)
+ {
+-      if (*Opts) {
+-              if (isnumber(Opts)) {
+-                      cRecording *recording = Recordings.Get(strtol(Opts, NULL, 10) - 1);
+-                      if (recording) {
+-                              if (m_RecPlayer) {
+-                                      delete m_RecPlayer;
+-                              }
+-                              m_RecPlayer = new RecPlayer(recording);
+-                              return Respond(220, "%llu (Bytes), %u (Frames)", (long long unsigned int) m_RecPlayer->getLengthBytes(), (unsigned int) m_RecPlayer->getLengthFrames());
+-                      }
+-                      else {
+-                              return Respond(550, "Recording \"%s\" not found", Opts);
+-                      }
+-              }
+-              else {
+-                      return Respond(500, "Use: PLAY record");
++      const cRecording *recording;
++      cDevice *dev;
++
++      if ((recording = Recordings.Get(strtol(Opts, NULL, 10) - 1)) == NULL)
++              return Respond(550, "Undefined recording \"%s\"", Opts);
++
++      delete m_RecStreamer;
++      m_RecStreamer = new cStreamdevRecStreamer(recording);
++      if(m_RecSocket)
++              m_RecStreamer->Start(m_RecSocket);
++
++      return Respond(220, "Recording opened");
+               }
++
++bool cConnectionVTP::CmdSEEK(char *Opts)
++{
++      if (m_RecStreamer)
++      {
++              uint64_t TotalBytesWritten = m_RecStreamer->getTotalBytesWritten();
++              uint64_t newPosition = atoll(Opts);
++              m_RecStreamer->seekPosition(newPosition);
++
++              return Respond(220, "%llu (TCP Stack Position) %llu (New Position)", TotalBytesWritten, newPosition);
+       }
+-      else {
+-              return Respond(500, "Use: PLAY record");
++
++      return Respond(550, "Curretly no record playing");
+       }
++
++bool cConnectionVTP::CmdSIZE(char *Opts)
++{
++      if (m_RecStreamer)
++              return Respond(220, "%llu (Bytes), %u (Frames)", (long long unsigned int) m_RecStreamer->getLengthBytes(), (unsigned int) m_RecStreamer->getLengthFrames());
++
++      return Respond(550, "Curretly no record playing");
+ }
+ bool cConnectionVTP::CmdADDP(char *Opts) 
+@@ -1215,7 +1203,7 @@
+               DELETENULL(m_FilterSocket);
+               break;
+       case siReplay:
+-              DELETENULL(m_RecPlayer);
++              DELETENULL(m_RecStreamer);
+               DELETENULL(m_RecSocket);
+               break;
+       case siDataRespond:
+diff -NaurwB streamdev/server/connectionVTP.h streamdev-patched/server/connectionVTP.h
+--- streamdev/server/connectionVTP.h   2009-07-01 12:46:16.000000000 +0200
++++ streamdev-patched/server/connectionVTP.h   2010-02-09 18:08:35.000000000 +0100
+@@ -7,6 +7,7 @@
+ class cTBSocket;
+ class cStreamdevLiveStreamer;
+ class cStreamdevFilterStreamer;
++class cStreamdevRecStreamer;
+ class cLSTEHandler;
+ class cLSTCHandler;
+ class cLSTTHandler;
+@@ -24,12 +25,12 @@
+       cTBSocket                *m_FilterSocket;
+       cStreamdevFilterStreamer *m_FilterStreamer;
+       cTBSocket                *m_RecSocket;
++      cStreamdevRecStreamer    *m_RecStreamer;
+       cTBSocket                *m_DataSocket;
+       char                   *m_LastCommand;
+       eStreamType             m_StreamType;
+       bool                    m_FiltersSupport;
+-      RecPlayer              *m_RecPlayer;
+       // Members adopted for SVDRP
+       cLSTEHandler *m_LSTEHandler;
+@@ -56,9 +57,10 @@
+       bool CmdCAPS(char *Opts);
+       bool CmdPROV(char *Opts);
+       bool CmdPORT(char *Opts);
+-      bool CmdREAD(char *Opts);
+       bool CmdTUNE(char *Opts);
+       bool CmdPLAY(char *Opts);
++      bool CmdSEEK(char *Opts);
++      bool CmdSIZE(char *Opts);
+       bool CmdADDP(char *Opts);
+       bool CmdDELP(char *Opts);
+       bool CmdADDF(char *Opts);
+diff -NaurwB streamdev/server/recordingstreamer.c streamdev-patched/server/recordingstreamer.c
+--- streamdev/server/recordingstreamer.c       1970-01-01 01:00:00.000000000 +0100
++++ streamdev-patched/server/recordingstreamer.c       2010-02-10 08:58:01.000000000 +0100
+@@ -0,0 +1,89 @@
++#include <vdr/recording.h>
++
++#include "tools/socket.h"
++#include "tools/select.h"
++
++#include "server/recordingstreamer.h"
++#include "recplayer.h"
++#include "common.h"
++
++// --- cStreamdevRecStreamer -------------------------------------------------
++
++cStreamdevRecStreamer::cStreamdevRecStreamer(const cRecording *Recording):
++              cThread("streamdev-recordingstreaming"),
++              m_Recording(Recording),
++              m_RecPlayer(new cRecPlayer(Recording)),
++              m_Position(0),
++              m_TotalBytesWritten(0)
++{
++}
++
++cStreamdevRecStreamer::~cStreamdevRecStreamer()
++{
++      Dprintf("Desctructing Recording streamer\n");
++      Stop();
++
++      DELETENULL(m_RecPlayer);
++}
++
++void cStreamdevRecStreamer::Start(cTBSocket *Socket)
++{
++      Dprintf("start streamer\n");
++      m_Socket = Socket;
++      cThread::Start();
++}
++
++void cStreamdevRecStreamer::Stop(void)
++{
++      if (Running()) {
++              Dprintf("stopping streamer\n");
++              Cancel(3);
++      }
++}
++
++uint64_t cStreamdevRecStreamer::getLengthBytes()
++{
++      return m_RecPlayer->getLengthBytes();
++}
++
++uint32_t cStreamdevRecStreamer::getLengthFrames()
++{
++      return m_RecPlayer->getLengthFrames();
++}
++
++void cStreamdevRecStreamer::seekPosition(uint64_t position)
++{
++      m_Position = position;
++}
++
++void cStreamdevRecStreamer::Action(void)
++{
++      cTBSelect sel;
++      uint8_t data[32768];
++      Dprintf("Writer start\n");
++
++      sel.Clear();
++      sel.Add(*m_Socket, true);
++      while (Running()) {
++
++              if (sel.Select(15000) == -1) {
++                      esyslog("ERROR: streamdev-server: couldn't send recording data: %m");
++                      continue; /* Continue here instead of break, the recording playback can be paused */
++              }
++
++              if (sel.CanWrite(*m_Socket)) {
++                      int written;
++                      unsigned long readed = m_RecPlayer->getBlock(&*data, m_Position, sizeof(data));
++                      if (readed <= 0)
++                              continue;
++
++                      if ((written = m_Socket->Write(&*data, readed)) == -1) {
++                              esyslog("ERROR: streamdev-server: couldn't send %d bytes: %m", written);
++                              break;
++                      }
++
++                      m_Position += written;
++                      m_TotalBytesWritten += written;
++              }
++      }
++}
+diff -NaurwB streamdev/server/recordingstreamer.h streamdev-patched/server/recordingstreamer.h
+--- streamdev/server/recordingstreamer.h       1970-01-01 01:00:00.000000000 +0100
++++ streamdev-patched/server/recordingstreamer.h       2010-02-10 08:58:13.000000000 +0100
+@@ -0,0 +1,38 @@
++#ifndef VDR_STREAMDEV_RECORDINGSTREAMER_H
++#define VDR_STREAMDEV_RECORDINGSTREAMER_H
++
++#include <vdr/thread.h>
++#include <vdr/config.h>
++#include <vdr/receiver.h>
++
++#include "common.h"
++
++class cRecording;
++class cRecPlayer;
++
++// --- cStreamdevRecStreamer -------------------------------------------------
++
++class cStreamdevRecStreamer: public cThread  {
++private:
++      const cRecording       *m_Recording;
++      cRecPlayer             *m_RecPlayer;
++      cTBSocket              *m_Socket;
++      uint64_t                m_Position;
++      uint64_t                m_TotalBytesWritten;
++
++protected:
++      virtual void Action(void);
++
++public:
++      cStreamdevRecStreamer(const cRecording *Recording);
++      virtual ~cStreamdevRecStreamer();
++
++      void Start(cTBSocket *Socket);
++      void Stop(void);
++      uint64_t getTotalBytesWritten() { return m_TotalBytesWritten; }
++      uint64_t getLengthBytes();
++      uint32_t getLengthFrames();
++      void seekPosition(uint64_t position);
++};
++
++#endif // VDR_STREAMDEV_RECORDINGSTREAMER_H
+diff -NaurwB streamdev/server/recplayer.c streamdev-patched/server/recplayer.c
+--- streamdev/server/recplayer.c       2009-07-01 13:00:49.000000000 +0200
++++ streamdev-patched/server/recplayer.c       2010-02-10 08:24:01.000000000 +0100
+@@ -24,7 +24,7 @@
+ #define _XOPEN_SOURCE 600
+ #include <fcntl.h>
+-RecPlayer::RecPlayer(cRecording* rec)
++cRecPlayer::cRecPlayer(const cRecording* rec)
+ {
+   file = NULL;
+   fileOpen = 0;
+@@ -44,7 +44,7 @@
+   scan();
+ }
+-void RecPlayer::scan()
++void cRecPlayer::scan()
+ {
+   if (file) fclose(file);
+   totalLength = 0;
+@@ -60,7 +60,7 @@
+ #if APIVERSNUM < 10703
+     snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), i);
+-    //log->log("RecPlayer", Log::DEBUG, "FILENAME: %s", fileName);
++    //log->log("cRecPlayer", Log::DEBUG, "FILENAME: %s", fileName);
+     file = fopen(fileName, "r");
+ #else
+     snprintf(fileName, 2047, "%s/%05i.ts", recording->FileName(), i);
+@@ -77,7 +77,7 @@
+     fseek(file, 0, SEEK_END);
+     totalLength += ftell(file);
+     totalFrames = indexFile->Last();
+-    //log->log("RecPlayer", Log::DEBUG, "File %i found, totalLength now %llu, numFrames = %lu", i, totalLength, totalFrames);
++    //log->log("cRecPlayer", Log::DEBUG, "File %i found, totalLength now %llu, numFrames = %lu", i, totalLength, totalFrames);
+     segments[i]->end = totalLength;
+     fclose(file);
+   }
+@@ -85,15 +85,15 @@
+   file = NULL;
+ }
+-RecPlayer::~RecPlayer()
++cRecPlayer::~cRecPlayer()
+ {
+-  //log->log("RecPlayer", Log::DEBUG, "destructor");
++  //log->log("cRecPlayer", Log::DEBUG, "destructor");
+   int i = 1;
+   while(segments[i++]) delete segments[i];
+   if (file) fclose(file);
+ }
+-int RecPlayer::openFile(int index)
++int cRecPlayer::openFile(int index)
+ {
+   if (file) fclose(file);
+@@ -113,7 +113,7 @@
+   snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), index);
+   isyslog("openFile called for index %i string:%s", index, fileName);
+-  //log->log("RecPlayer", Log::DEBUG, "openFile called for index %i string:%s", index, fileName);
++  //log->log("cRecPlayer", Log::DEBUG, "openFile called for index %i string:%s", index, fileName);
+   file = fopen(fileName, "r");
+   if (file)
+@@ -122,38 +122,58 @@
+     return 1;
+   }
+-  //log->log("RecPlayer", Log::DEBUG, "file failed to open");
++  //log->log("cRecPlayer", Log::DEBUG, "file failed to open");
+   fileOpen = 0;
+   return 0;
+ }
+-uint64_t RecPlayer::getLengthBytes()
++uint64_t cRecPlayer::getLengthBytes()
+ {
++  int totalLength = 0;
++  char fileName[2048];
++  struct stat st;
++
++  for(int i = 1; i < 1000; i++)
++  {
++#if APIVERSNUM < 10703
++    snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), i);
++#else
++    snprintf(fileName, 2047, "%s/%05i.ts", recording->FileName(), i);
++    if (stat(fileName, &st) < 0) {
++      snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), i);
++    }
++#endif
++
++    if (stat(fileName, &st) == 0) {
++      totalLength += st.st_size;
++    }
++  }
++
+   return totalLength;
+ }
+-uint32_t RecPlayer::getLengthFrames()
++uint32_t cRecPlayer::getLengthFrames()
+ {
+   return totalFrames;
+ }
+-unsigned long RecPlayer::getBlock(unsigned char* buffer, uint64_t position, unsigned long amount)
++unsigned long cRecPlayer::getBlock(uint8_t* buffer, uint64_t position, unsigned long amount)
+ {
+   if ((amount > totalLength) || (amount > 500000))
+   {
+-    //log->log("RecPlayer", Log::DEBUG, "Amount %lu requested and rejected", amount);
++    //log->log("cRecPlayer", Log::DEBUG, "Amount %lu requested and rejected", amount);
+     return 0;
+   }
+   if (position >= totalLength)
+   {
+-    //log->log("RecPlayer", Log::DEBUG, "Client asked for data starting past end of recording!");
++    //log->log("cRecPlayer", Log::DEBUG, "Client asked for data starting past end of recording!");
+     return 0;
+   }
+   if ((position + amount) > totalLength)
+   {
+-    //log->log("RecPlayer", Log::DEBUG, "Client asked for some data past the end of recording, adjusting amount");
++    //log->log("cRecPlayer", Log::DEBUG, "Client asked for some data past the end of recording, adjusting amount");
+     amount = totalLength - position;
+   }
+@@ -208,17 +228,17 @@
+   return got;
+ }
+-uint64_t RecPlayer::getLastPosition()
++uint64_t cRecPlayer::getLastPosition()
+ {
+   return lastPosition;
+ }
+-cRecording* RecPlayer::getCurrentRecording()
++const cRecording* cRecPlayer::getCurrentRecording()
+ {
+   return recording;
+ }
+-uint64_t RecPlayer::positionFromFrameNumber(uint32_t frameNumber)
++uint64_t cRecPlayer::positionFromFrameNumber(uint32_t frameNumber)
+ {
+   if (!indexFile) return 0;
+@@ -235,21 +255,21 @@
+     return 0;
+   }
+-//  log->log("RecPlayer", Log::DEBUG, "FN: %u FO: %i", retFileNumber, retFileOffset);
++//  log->log("cRecPlayer", Log::DEBUG, "FN: %u FO: %i", retFileNumber, retFileOffset);
+   if (!segments[retFileNumber]) return 0;
+   uint64_t position = segments[retFileNumber]->start + retFileOffset;
+-//  log->log("RecPlayer", Log::DEBUG, "Pos: %llu", position);
++//  log->log("cRecPlayer", Log::DEBUG, "Pos: %llu", position);
+   return position;
+ }
+-uint32_t RecPlayer::frameNumberFromPosition(uint64_t position)
++uint32_t cRecPlayer::frameNumberFromPosition(uint64_t position)
+ {
+   if (!indexFile) return 0;
+   if (position >= totalLength)
+   {
+-    //log->log("RecPlayer", Log::DEBUG, "Client asked for data starting past end of recording!");
++    //log->log("cRecPlayer", Log::DEBUG, "Client asked for data starting past end of recording!");
+     return 0;
+   }
+@@ -265,7 +285,7 @@
+ }
+-bool RecPlayer::getNextIFrame(uint32_t frameNumber, uint32_t direction, uint64_t* rfilePosition, uint32_t* rframeNumber, uint32_t* rframeLength)
++bool cRecPlayer::getNextIFrame(uint32_t frameNumber, uint32_t direction, uint64_t* rfilePosition, uint32_t* rframeNumber, uint32_t* rframeLength)
+ {
+   // 0 = backwards
+   // 1 = forwards
+@@ -276,7 +296,7 @@
+   int indexReturnFrameNumber;
+   indexReturnFrameNumber = (uint32_t)indexFile->GetNextIFrame(frameNumber, (direction==1 ? true : false), NULL, NULL, &iframeLength);
+-  //log->log("RecPlayer", Log::DEBUG, "GNIF input framenumber:%lu, direction=%lu, output:framenumber=%i, framelength=%i", frameNumber, direction, indexReturnFrameNumber, iframeLength);
++  //log->log("cRecPlayer", Log::DEBUG, "GNIF input framenumber:%lu, direction=%lu, output:framenumber=%i, framelength=%i", frameNumber, direction, indexReturnFrameNumber, iframeLength);
+   if (indexReturnFrameNumber == -1) return false;
+diff -NaurwB streamdev/server/recplayer.h streamdev-patched/server/recplayer.h
+--- streamdev/server/recplayer.h       2009-07-01 13:00:49.000000000 +0200
++++ streamdev-patched/server/recplayer.h       2010-02-09 17:18:23.000000000 +0100
+@@ -33,24 +33,24 @@
+     uint64_t end;
+ };
+-class RecPlayer
++class cRecPlayer
+ {
+   public:
+-    RecPlayer(cRecording* rec);
+-    ~RecPlayer();
++    cRecPlayer(const cRecording* rec);
++    ~cRecPlayer();
+     uint64_t getLengthBytes();
+     uint32_t getLengthFrames();
+-    unsigned long getBlock(unsigned char* buffer, uint64_t position, unsigned long amount);
++    unsigned long getBlock(uint8_t* buffer, uint64_t position, unsigned long amount);
+     int openFile(int index);
+     uint64_t getLastPosition();
+-    cRecording* getCurrentRecording();
++    const cRecording* getCurrentRecording();
+     void scan();
+     uint64_t positionFromFrameNumber(uint32_t frameNumber);
+     uint32_t frameNumberFromPosition(uint64_t position);
+     bool getNextIFrame(uint32_t frameNumber, uint32_t direction, uint64_t* rfilePosition, uint32_t* rframeNumber, uint32_t* rframeLength);
+   private:
+-    cRecording* recording;
++    const cRecording* recording;
+     cIndexFile* indexFile;
+     FILE* file;
+     int fileOpen;
diff --git a/xbmc/pvrclients/vdr-streamdev/patches/streamdev-cvs221109-AddCallbackMsg.diff b/xbmc/pvrclients/vdr-streamdev/patches/streamdev-cvs221109-AddCallbackMsg.diff
new file mode 100644 (file)
index 0000000..7facc61
--- /dev/null
@@ -0,0 +1,120 @@
+diff -NaurwB streamdev-unpatched/common.h streamdev/common.h
+--- streamdev-unpatched/common.h       2009-09-18 12:41:41.000000000 +0200
++++ streamdev/common.h 2009-11-23 04:54:04.000000000 +0100
+@@ -57,6 +57,8 @@
+       si_Count
+ };
++#define MAX_RESPONSE_SIZE   1024
++
+ extern const char *VERSION;
+ class cMenuEditIpItem: public cMenuEditItem {
+diff -NaurwB streamdev-unpatched/server/connectionVTP.c streamdev/server/connectionVTP.c
+--- streamdev-unpatched/server/connectionVTP.c 2009-10-13 08:38:47.000000000 +0200
++++ streamdev/server/connectionVTP.c   2009-11-23 14:23:33.000000000 +0100
+@@ -1714,3 +1714,69 @@
+                               Code < 0 ? -Code : Code,
+                               Code < 0 ? '-' : ' ', *str);
+ }
++
++void cConnectionVTP::TimerChange(const cTimer *Timer, eTimerChange Change)
++{
++  if (m_DataSocket) {
++    char buf[MAX_RESPONSE_SIZE];
++    if (Change == tcMod) {
++      snprintf(buf, MAX_RESPONSE_SIZE, "MODT %s\0", Timer ? *Timer->ToText(true) : "-");
++    }
++    if (Change == tcAdd) {
++      snprintf(buf, MAX_RESPONSE_SIZE, "ADDT %s\0", Timer ? *Timer->ToText(true) : "-");
++    }
++    if (Change == tcDel) {
++      snprintf(buf, MAX_RESPONSE_SIZE, "DELT %s\0", Timer ? *Timer->ToText(true) : "-");
++    }
++
++    m_DataSocket->SysWrite(buf, strlen(buf));
++  }
++}
++
++#ifdef USE_STATUS_EXTENSION
++void cConnectionVTP::OsdStatusMessage(eMessageType type, const char *Message)
++#else
++void cConnectionVTP::OsdStatusMessage(const char *Message)
++#endif
++{
++  if (m_DataSocket && Message) {
++    char buf[MAX_RESPONSE_SIZE];
++
++    /* Ignore this messages */
++    if (strcasecmp(Message, trVDR("Channel not available!")) == 0) return;
++    else if (strcasecmp(Message, trVDR("Delete timer?")) == 0) return;
++    else if (strcasecmp(Message, trVDR("Delete recording?")) == 0) return;
++    else if (strcasecmp(Message, trVDR("Press any key to cancel shutdown")) == 0) return;
++    else if (strcasecmp(Message, trVDR("Press any key to cancel restart")) == 0) return;
++    else if (strcasecmp(Message, trVDR("Editing - shut down anyway?")) == 0) return;
++    else if (strcasecmp(Message, trVDR("Recording - shut down anyway?")) == 0) return;
++    else if (strcasecmp(Message, trVDR("shut down anyway?")) == 0) return;
++    else if (strcasecmp(Message, trVDR("Recording - restart anyway?")) == 0) return;
++    else if (strcasecmp(Message, trVDR("Editing - restart anyway?")) == 0) return;
++    else if (strcasecmp(Message, trVDR("Delete channel?")) == 0) return;
++    else if (strcasecmp(Message, trVDR("Timer still recording - really delete?")) == 0) return;
++    else if (strcasecmp(Message, trVDR("Delete marks information?")) == 0) return;
++    else if (strcasecmp(Message, trVDR("Delete resume information?")) == 0) return;
++    else if (strcasecmp(Message, trVDR("CAM is in use - really reset?")) == 0) return;
++    else if (strcasecmp(Message, trVDR("Really restart?")) == 0) return;
++    else if (strcasecmp(Message, trVDR("Stop recording?")) == 0) return;
++    else if (strcasecmp(Message, trVDR("Cancel editing?")) == 0) return;
++    else if (strcasecmp(Message, trVDR("Cutter already running - Add to cutting queue?")) == 0) return;
++    else if (strcasecmp(Message, trVDR("No index-file found. Creating may take minutes. Create one?")) == 0) return;
++
++#ifdef USE_STATUS_EXTENSION
++    if (type == mtStatus)
++      snprintf(buf, MAX_RESPONSE_SIZE, "SMSG %s\0", Message);
++    else if (type == mtInfo)
++      snprintf(buf, MAX_RESPONSE_SIZE, "IMSG %s\0", Message);
++    else if (type == mtWarning)
++      snprintf(buf, MAX_RESPONSE_SIZE, "WMSG %s\0", Message);
++    else if (type == mtError)
++      snprintf(buf, MAX_RESPONSE_SIZE, "EMSG %s\0", Message);
++    else
++#endif
++      snprintf(buf, MAX_RESPONSE_SIZE, "IMSG %s\0", Message);
++
++    m_DataSocket->SysWrite(buf, strlen(buf));
++  }
++}
+diff -NaurwB streamdev-unpatched/server/connectionVTP.h streamdev/server/connectionVTP.h
+--- streamdev-unpatched/server/connectionVTP.h 2009-07-01 12:46:16.000000000 +0200
++++ streamdev/server/connectionVTP.h   2009-11-23 14:23:33.000000000 +0100
+@@ -1,6 +1,7 @@
+ #ifndef VDR_STREAMDEV_SERVERS_CONNECTIONVTP_H
+ #define VDR_STREAMDEV_SERVERS_CONNECTIONVTP_H
++#include <vdr/status.h>
+ #include "server/connection.h"
+ #include "server/recplayer.h"
+@@ -12,7 +13,8 @@
+ class cLSTTHandler;
+ class cLSTRHandler;
+-class cConnectionVTP: public cServerConnection {
++class cConnectionVTP: public cServerConnection
++                    , public cStatus  {
+       friend class cLSTEHandler;
+ #if !defined __GNUC__ || __GNUC__ >= 3
+       using cServerConnection::Respond;
+@@ -41,6 +43,13 @@
+       template<class cHandler>
+       bool CmdLSTX(cHandler *&Handler, char *Option);
++      virtual void TimerChange(const cTimer *Timer, eTimerChange Change);
++#ifdef USE_STATUS_EXTENSION
++  virtual void OsdStatusMessage(eMessageType type, const char *Message);
++#else
++  virtual void OsdStatusMessage(const char *Message);
++#endif
++
+ public:
+       cConnectionVTP(void);
+       virtual ~cConnectionVTP();
diff --git a/xbmc/pvrclients/vdr-streamdev/patches/streamdev-cvs221109-AddFemonV1.diff b/xbmc/pvrclients/vdr-streamdev/patches/streamdev-cvs221109-AddFemonV1.diff
new file mode 100644 (file)
index 0000000..39872e4
--- /dev/null
@@ -0,0 +1,210 @@
+diff -NaurwB streamdev-unpatched/server/connectionVTP.c streamdev/server/connectionVTP.c
+--- streamdev-unpatched/server/connectionVTP.c 2009-10-13 08:38:47.000000000 +0200
++++ streamdev/server/connectionVTP.c   2009-11-22 20:04:07.000000000 +0100
+@@ -7,6 +7,8 @@
+ #include "server/suspend.h"
+ #include "setup.h"
++#include "../services/femonservice.h"
++
+ #include <vdr/tools.h>
+ #include <vdr/videodir.h>
+ #include <vdr/menu.h>
+@@ -710,6 +712,102 @@
+       return false;
+ }
++
++// --- cLSTQHandler -----------------------------------------------------------
++
++class cLSTQHandler
++{
++private:
++      enum eStates { Device, Status, Signal, SNR, BER, UNC, Video,
++                     Audio, Dolby, EndQuality };
++      cConnectionVTP    *m_Client;
++      FemonService_v1_0  m_femon;
++      int                m_Errno;
++      int                m_Channel;
++      cString            m_Error;
++      eStates            m_State;
++public:
++      cLSTQHandler(cConnectionVTP *Client, const char *Option);
++      ~cLSTQHandler();
++      bool Next(bool &Last);
++};
++
++cLSTQHandler::cLSTQHandler(cConnectionVTP *Client, const char *Option):
++              m_Client(Client),
++              m_Errno(0),
++              m_State(Device),
++              m_Channel(-1)
++{
++//    if (*Option) {
++//            if (isnumber(Option)) {
++//                    m_Channel = atoi(Option);
++//            }
++//    }
++
++  cPlugin *p;
++  p = cPluginManager::CallFirstService("FemonService-v1.0", &m_femon);
++  if (!p) {
++    m_Errno = 550;
++    m_Error = cString::sprintf("No support for Signal Quality found");
++  }
++}
++
++cLSTQHandler::~cLSTQHandler()
++{
++}
++
++bool cLSTQHandler::Next(bool &Last)
++{
++      if (*m_Error != NULL) {
++              Last = true;
++              cString str(m_Error);
++              m_Error = NULL;
++              return m_Client->Respond(m_Errno, "%s", *str);
++      }
++
++  Last = false;
++  switch (m_State) {
++  case Device:
++    m_State = Status;
++    if (*m_femon.fe_name != NULL)
++      return m_Client->Respond(-215, "Device : %s", *m_femon.fe_name);
++    else
++      return m_Client->Respond(-215, "Device : ");
++  case Status:
++    m_State = Signal;
++    if (*m_femon.fe_status != NULL)
++      return m_Client->Respond(-215, "Status : %s", *m_femon.fe_status);
++    else
++      return m_Client->Respond(-215, "Status : ");
++  case Signal:
++    m_State = SNR;
++    return m_Client->Respond(-215, "Signal : %04X (%2d%%)", m_femon.fe_signal, m_femon.fe_signal / 655);
++  case SNR:
++    m_State = BER;
++    return m_Client->Respond(-215, "SNR    : %04X (%2d%%)", m_femon.fe_snr, m_femon.fe_snr / 655);
++  case BER:
++    m_State = UNC;
++    return m_Client->Respond(-215, "BER    : %08X", m_femon.fe_ber);
++  case UNC:
++    m_State = Video;
++    return m_Client->Respond(-215, "UNC    : %08X", m_femon.fe_unc);
++  case Video:
++    m_State = Audio;
++    return m_Client->Respond(-215, "Video  : %.2f Mbit/s", m_femon.video_bitrate);
++  case Audio:
++    m_State = Dolby;
++    return m_Client->Respond(-215, "Audio  : %.0f kbit/s", m_femon.audio_bitrate);
++  case Dolby:
++    m_State = EndQuality;
++    return m_Client->Respond(-215, "Dolby  : %.0f kbit/s", m_femon.dolby_bitrate);
++  case EndQuality:
++    Last = true;
++    return m_Client->Respond(215, "End of quality information");
++      }
++  return false;
++}
++
++
+ // --- cConnectionVTP ---------------------------------------------------------
+ cConnectionVTP::cConnectionVTP(void): 
+@@ -727,7 +825,8 @@
+               m_LSTEHandler(NULL),
+               m_LSTCHandler(NULL),
+               m_LSTTHandler(NULL),
+-              m_LSTRHandler(NULL)
++              m_LSTRHandler(NULL),
++              m_LSTQHandler(NULL)
+ {
+ }
+@@ -745,6 +844,7 @@
+       delete m_LSTCHandler;
+       delete m_LSTEHandler;
+       delete m_LSTRHandler;
++      delete m_LSTQHandler;
+       delete m_RecPlayer;
+ }
+@@ -801,6 +901,7 @@
+       else if (strcasecmp(Cmd, "LSTR") == 0) return CmdLSTR(param);
+       else if (strcasecmp(Cmd, "LSTT") == 0) return CmdLSTT(param);
+       else if (strcasecmp(Cmd, "LSTC") == 0) return CmdLSTC(param);
++      else if (strcasecmp(Cmd, "LSTQ") == 0) return CmdLSTQ(param);
+       if (param == NULL) {
+               esyslog("ERROR: streamdev: this seriously shouldn't happen at %s:%d",
+@@ -1268,6 +1369,11 @@
+       return CmdLSTX(m_LSTRHandler, Option);
+ }
++bool cConnectionVTP::CmdLSTQ(char *Option)
++{
++      return CmdLSTX(m_LSTQHandler, Option);
++}
++
+ // Functions adopted from SVDRP
+ #define INIT_WRAPPER() bool _res
+ #define Reply(c,m...) _res = Respond(c,m)
+diff -NaurwB streamdev-unpatched/server/connectionVTP.h streamdev/server/connectionVTP.h
+--- streamdev-unpatched/server/connectionVTP.h 2009-07-01 12:46:16.000000000 +0200
++++ streamdev/server/connectionVTP.h   2009-11-22 16:08:51.000000000 +0100
+@@ -11,6 +11,7 @@
+ class cLSTCHandler;
+ class cLSTTHandler;
+ class cLSTRHandler;
++class cLSTQHandler;
+ class cConnectionVTP: public cServerConnection {
+       friend class cLSTEHandler;
+@@ -36,6 +37,7 @@
+       cLSTCHandler *m_LSTCHandler;
+       cLSTTHandler *m_LSTTHandler;
+       cLSTRHandler *m_LSTRHandler;
++      cLSTQHandler *m_LSTQHandler;
+ protected:
+       template<class cHandler>
+@@ -72,6 +74,7 @@
+       bool CmdLSTC(char *Opts);
+       bool CmdLSTT(char *Opts);
+       bool CmdLSTR(char *Opts);
++      bool CmdLSTQ(char *Opts);
+       // Commands adopted from SVDRP
+       bool CmdSTAT(const char *Option);
+diff -NaurwB streamdev-unpatched/services/femonservice.h streamdev/services/femonservice.h
+--- streamdev-unpatched/services/femonservice.h        1970-01-01 01:00:00.000000000 +0100
++++ streamdev/services/femonservice.h  2009-10-01 03:20:00.000000000 +0200
+@@ -0,0 +1,26 @@
++/*
++ * Frontend Status Monitor plugin for the Video Disk Recorder
++ *
++ * See the README file for copyright information and how to reach the author.
++ *
++ */
++
++#ifndef __FEMONSERVICE_H
++#define __FEMONSERVICE_H
++
++#include <linux/dvb/frontend.h>
++
++struct FemonService_v1_0 {
++  cString fe_name;
++  cString fe_status;
++  uint16_t fe_snr;
++  uint16_t fe_signal;
++  uint32_t fe_ber;
++  uint32_t fe_unc;
++  double video_bitrate;
++  double audio_bitrate;
++  double dolby_bitrate;
++  };
++
++#endif //__FEMONSERVICE_H
++
diff --git a/xbmc/pvrclients/vdr-streamdev/patches/vdr-1.7.10_extensionsAndXBMC.diff b/xbmc/pvrclients/vdr-streamdev/patches/vdr-1.7.10_extensionsAndXBMC.diff
new file mode 100644 (file)
index 0000000..622c464
--- /dev/null
@@ -0,0 +1,19905 @@
+diff -NaurwB vdr-1.7.10/channels.c vdr-1.7.10-patched/channels.c
+--- vdr-1.7.10/channels.c      2009-08-30 13:25:50.000000000 +0200
++++ vdr-1.7.10-patched/channels.c      2009-12-18 06:29:02.000000000 +0100
+@@ -188,6 +188,9 @@
+   shortName = strdup("");
+   provider = strdup("");
+   portalName = strdup("");
++#ifdef USE_PLUGINPARAM
++  pluginParam = strdup("");
++#endif /* PLUGINPARAM */
+   memset(&__BeginData__, 0, (char *)&__EndData__ - (char *)&__BeginData__);
+   inversion    = INVERSION_AUTO;
+   bandwidth    = 8000000;
+@@ -211,6 +214,9 @@
+   shortName = NULL;
+   provider = NULL;
+   portalName = NULL;
++#ifdef USE_PLUGINPARAM
++  pluginParam = NULL;
++#endif /* PLUGINPARAM */
+   schedule     = NULL;
+   linkChannels = NULL;
+   refChannel   = NULL;
+@@ -239,6 +245,9 @@
+   free(shortName);
+   free(provider);
+   free(portalName);
++#ifdef USE_PLUGINPARAM
++  free(pluginParam);
++#endif /* PLUGINPARAM */
+ }
+ cChannel& cChannel::operator= (const cChannel &Channel)
+@@ -247,6 +256,9 @@
+   shortName = strcpyrealloc(shortName, Channel.shortName);
+   provider = strcpyrealloc(provider, Channel.provider);
+   portalName = strcpyrealloc(portalName, Channel.portalName);
++#ifdef USE_PLUGINPARAM
++  pluginParam = strcpyrealloc(pluginParam, Channel.pluginParam);
++#endif /* PLUGINPARAM */
+   memcpy(&__BeginData__, &Channel.__BeginData__, (char *)&Channel.__EndData__ - (char *)&Channel.__BeginData__);
+   return *this;
+ }
+@@ -306,6 +318,9 @@
+      guard        = Channel->guard;
+      hierarchy    = Channel->hierarchy;
+      rollOff      = Channel->rollOff;
++#ifdef USE_PLUGINPARAM
++     if (IsPlug()) pluginParam = strcpyrealloc(pluginParam, Channel->pluginParam);
++#endif /* PLUGINPARAM */
+      }
+ }
+@@ -343,6 +358,24 @@
+   return true;
+ }
++#ifdef USE_PLUGINPARAM
++bool cChannel::SetPlugTransponderData(int Source, int Frequency, const char *PluginParam)
++{
++  if (source != Source || frequency != Frequency || (strcmp(pluginParam, PluginParam) != 0)) {
++     if (Number()) {
++        dsyslog("changing transponder data of channel %d from %s:%d:%s to %s:%d:%s", Number(), *cSource::ToString(source), frequency, pluginParam, *cSource::ToString(Source), Frequency, PluginParam);
++        modification |= CHANNELMOD_TRANSP;
++        Channels.SetModified();
++        }
++     source = Source;
++     frequency = Frequency;
++     pluginParam = strcpyrealloc(pluginParam, PluginParam);
++     schedule = NULL;
++     }
++  return true;
++}
++#endif /* PLUGINPARAM */
++
+ bool cChannel::SetCableTransponderData(int Source, int Frequency, int Modulation, int Srate, int CoderateH)
+ {
+   if (source != Source || frequency != Frequency || modulation != Modulation || srate != Srate || coderateH != CoderateH) {
+@@ -438,6 +471,20 @@
+      }
+ }
++#ifdef USE_PLUGINPARAM
++void cChannel::SetPluginParam(const char *PluginParam)
++{
++  if (!isempty(PluginParam) && strcmp(pluginParam, PluginParam) != 0) {
++     if (Number()) {
++        dsyslog("changing plugin parameters of channel %d from '%s' to '%s'", Number(), pluginParam, PluginParam);
++        modification |= CHANNELMOD_TRANSP;
++        Channels.SetModified();
++        }
++     pluginParam = strcpyrealloc(pluginParam, PluginParam);
++     }
++}
++#endif /* PLUGINPARAM */
++
+ #define STRDIFF 0x01
+ #define VALDIFF 0x02
+@@ -550,6 +597,17 @@
+      }
+ }
++#ifdef USE_TTXTSUBS
++void cChannel::SetTPidData(char TLangs[][MAXLANGCODE2], int TPages[])
++{
++  for (int i = 0; i < MAXTPAGES; i++) {
++      tpages[i] = TPages[i];
++      strn0cpy(tlangs[i], TLangs[i], MAXLANGCODE2);
++      }
++  tpages[MAXTPAGES] = 0;
++}
++#endif /* TTXTSUBS */
++
+ void cChannel::SetCaIds(const int *CaIds)
+ {
+   if (caids[0] && caids[0] <= CA_USER_MAX)
+@@ -651,7 +709,11 @@
+   if (isdigit(type))
+      type = 'S';
+ #define ST(s) if (strchr(s, type))
++#ifdef USE_PLUGINPARAM
++  char buffer[256];
++#else
+   char buffer[64];
++#endif /* PLUGINPARAM */
+   char *q = buffer;
+   *q = 0;
+   ST(" S ")  q += sprintf(q, "%c", polarization);
+@@ -665,6 +727,9 @@
+   ST(" S ")  q += PrintParameter(q, 'S', MapToUser(system, SystemValues));
+   ST("  T")  q += PrintParameter(q, 'T', MapToUser(transmission, TransmissionValues));
+   ST("  T")  q += PrintParameter(q, 'Y', MapToUser(hierarchy, HierarchyValues));
++#ifdef USE_PLUGINPARAM
++  ST("P  ")  snprintf(buffer, sizeof(buffer), "%s", pluginParam);
++#endif /* PLUGINPARAM */
+   return buffer;
+ }
+@@ -693,7 +758,11 @@
+ bool cChannel::StringToParameters(const char *s)
+ {
++#ifdef USE_PLUGINPARAM
++  while (s && *s && !IsPlug()) {
++#else
+   while (s && *s) {
++#endif /* PLUGINPARAM */
+         switch (toupper(*s)) {
+           case 'A': s = SkipDigits(s); break; // for compatibility with the "multiproto" approach - may be removed in future versions
+           case 'B': s = ParseParameter(s, bandwidth, BandwidthValues); break;
+@@ -811,7 +880,11 @@
+         dpids[0] = 0;
+         ok = false;
+         if (parambuf && sourcebuf && vpidbuf && apidbuf) {
++#ifdef USE_PLUGINPARAM
++           ok = ((source = cSource::FromString(sourcebuf)) >= 0) && StringToParameters(parambuf);
++#else
+            ok = StringToParameters(parambuf) && (source = cSource::FromString(sourcebuf)) >= 0;
++#endif /* PLUGINPARAM */
+            char *p;
+            if ((p = strchr(vpidbuf, '=')) != NULL) {
+@@ -906,6 +979,9 @@
+            shortName = strcpyrealloc(shortName, p);
+            }
+         name = strcpyrealloc(name, namebuf);
++#ifdef USE_PLUGINPARAM
++        if (IsPlug()) pluginParam = strcpyrealloc(pluginParam, parambuf);
++#endif /* PLUGINPARAM */
+         free(parambuf);
+         free(sourcebuf);
+diff -NaurwB vdr-1.7.10/channels.h vdr-1.7.10-patched/channels.h
+--- vdr-1.7.10/channels.h      2009-08-30 13:05:54.000000000 +0200
++++ vdr-1.7.10-patched/channels.h      2009-12-18 06:25:25.000000000 +0100
+@@ -35,6 +35,9 @@
+ #define MAXDPIDS 16 // dolby (AC3 + DTS)
+ #define MAXSPIDS 32 // subtitles
+ #define MAXCAIDS  8 // conditional access
++#ifdef USE_TTXTSUBS
++#define MAXTPAGES 8 // teletext pages
++#endif /* TTXTSUBS */
+ #define MAXLANGCODE1 4 // a 3 letter language code, zero terminated
+ #define MAXLANGCODE2 8 // up to two 3 letter language codes, separated by '+' and zero terminated
+@@ -116,6 +119,9 @@
+   char *shortName;
+   char *provider;
+   char *portalName;
++#ifdef USE_PLUGINPARAM
++  char *pluginParam;
++#endif /* PLUGINPARAM */
+   int __BeginData__;
+   int frequency; // MHz
+   int source;
+@@ -133,6 +139,10 @@
+   uint16_t compositionPageIds[MAXSPIDS];
+   uint16_t ancillaryPageIds[MAXSPIDS];
+   int tpid;
++#ifdef USE_TTXTSUBS
++  char tlangs[MAXTPAGES][MAXLANGCODE2];
++  int tpages[MAXTPAGES + 1]; // list is zero-terminated
++#endif /* TTXTSUBS */
+   int caids[MAXCAIDS + 1]; // list is zero-terminated
+   int nid;
+   int tid;
+@@ -174,6 +184,9 @@
+   int Frequency(void) const { return frequency; } ///< Returns the actual frequency, as given in 'channels.conf'
+   int Transponder(void) const;                    ///< Returns the transponder frequency in MHz, plus the polarization in case of sat
+   static int Transponder(int Frequency, char Polarization); ///< builds the transponder from the given Frequency and Polarization
++#ifdef USE_PLUGINPARAM
++  const char *PluginParam(void) const { return pluginParam; }
++#endif /* PLUGINPARAM */
+   int Source(void) const { return source; }
+   int Srate(void) const { return srate; }
+   int Vpid(void) const { return vpid; }
+@@ -192,6 +205,10 @@
+   uint16_t CompositionPageId(int i) const { return (0 <= i && i < MAXSPIDS) ? compositionPageIds[i] : 0; }
+   uint16_t AncillaryPageId(int i) const { return (0 <= i && i < MAXSPIDS) ? ancillaryPageIds[i] : 0; }
+   int Tpid(void) const { return tpid; }
++#ifdef USE_TTXTSUBS
++  const char *Tlang(int i) const { return (0 <= i && i < MAXTPAGES) ? tlangs[i] : ""; }
++  const int TPages(int i) const { return (0 <= i && i < MAXTPAGES) ? tpages[i] : 0; }
++#endif /* TTXTSUBS */
+   const int *Caids(void) const { return caids; }
+   int Ca(int Index = 0) const { return Index < MAXCAIDS ? caids[Index] : 0; }
+   int Nid(void) const { return nid; }
+@@ -214,6 +231,9 @@
+   int RollOff(void) const { return rollOff; }
+   const cLinkChannels* LinkChannels(void) const { return linkChannels; }
+   const cChannel *RefChannel(void) const { return refChannel; }
++#ifdef USE_PLUGINPARAM
++  bool IsPlug(void) const { return cSource::IsPlug(source); }
++#endif /* PLUGINPARAM */
+   bool IsCable(void) const { return cSource::IsCable(source); }
+   bool IsSat(void) const { return cSource::IsSat(source); }
+   bool IsTerr(void) const { return cSource::IsTerr(source); }
+@@ -221,13 +241,22 @@
+   bool HasTimer(void) const;
+   int Modification(int Mask = CHANNELMOD_ALL);
+   void CopyTransponderData(const cChannel *Channel);
++#ifdef USE_PLUGINPARAM
++  bool SetPlugTransponderData(int Source, int Frequency, const char *PluginParam);
++#endif /* PLUGINPARAM */
+   bool SetSatTransponderData(int Source, int Frequency, char Polarization, int Srate, int CoderateH, int Modulation, int System, int RollOff);
+   bool SetCableTransponderData(int Source, int Frequency, int Modulation, int Srate, int CoderateH);
+   bool SetTerrTransponderData(int Source, int Frequency, int Bandwidth, int Modulation, int Hierarchy, int CodeRateH, int CodeRateL, int Guard, int Transmission);
+   void SetId(int Nid, int Tid, int Sid, int Rid = 0);
+   void SetName(const char *Name, const char *ShortName, const char *Provider);
+   void SetPortalName(const char *PortalName);
++#ifdef USE_PLUGINPARAM
++  void SetPluginParam(const char *PluginParam);
++#endif /* PLUGINPARAM */
+   void SetPids(int Vpid, int Ppid, int Vtype, int *Apids, char ALangs[][MAXLANGCODE2], int *Dpids, char DLangs[][MAXLANGCODE2], int *Spids, char SLangs[][MAXLANGCODE2], int Tpid);
++#ifdef USE_TTXTSUBS
++  void SetTPidData(char TLangs[][MAXLANGCODE2], int TPages[]);
++#endif /* TTXTSUBS */
+   void SetCaIds(const int *CaIds); // list must be zero-terminated
+   void SetCaDescriptors(int Level);
+   void SetLinkChannels(cLinkChannels *LinkChannels);
+diff -NaurwB vdr-1.7.10/config.c vdr-1.7.10-patched/config.c
+--- vdr-1.7.10/config.c        2009-06-13 12:25:05.000000000 +0200
++++ vdr-1.7.10-patched/config.c        2009-12-18 06:25:25.000000000 +0100
+@@ -15,6 +15,9 @@
+ #include "interface.h"
+ #include "plugin.h"
+ #include "recording.h"
++#ifdef USE_SOURCECAPS
++#include "sources.h"
++#endif /* SOURCECAPS */
+ // IMPORTANT NOTE: in the 'sscanf()' calls there is a blank after the '%d'
+ // format characters in order to allow any number of blanks after a numeric
+@@ -30,18 +33,32 @@
+ {
+   title = command = NULL;
+   confirm = false;
++#ifdef USE_CMDSUBMENU
++  nIndent = 0;
++  childs = NULL;
++#endif /* CMDSUBMENU */
+ }
+ cCommand::~cCommand()
+ {
+   free(title);
+   free(command);
++#ifdef USE_CMDSUBMENU
++  delete childs;
++#endif /* CMDSUBMENU */
+ }
+ bool cCommand::Parse(const char *s)
+ {
+   const char *p = strchr(s, ':');
+   if (p) {
++#ifdef USE_CMDSUBMENU
++     nIndent = 0;
++     while (*s == '-') {
++           nIndent++;
++           s++;
++           }
++#endif /* CMDSUBMENU */
+      int l = p - s;
+      if (l > 0) {
+         title = MALLOC(char, l + 1);
+@@ -87,6 +104,20 @@
+   return result;
+ }
++#ifdef USE_CMDSUBMENU
++int cCommand::getChildCount(void)
++{
++  return childs ? childs->Count() : 0;
++}
++
++void cCommand::addChild(cCommand *newChild)
++{
++  if (!childs)
++     childs = new cCommands();
++  childs->AddConfig(newChild);
++}
++#endif /* CMDSUBMENU */
++
+ // --- cSVDRPhost ------------------------------------------------------------
+ cSVDRPhost::cSVDRPhost(void)
+@@ -127,6 +158,26 @@
+ cCommands Commands;
+ cCommands RecordingCommands;
++#ifdef USE_TIMERCMD
++cCommands TimerCommands;
++#endif /* TIMERCMD */
++
++#ifdef USE_CMDSUBMENU
++void cCommands::AddConfig(cCommand *Object)
++{
++  if (!Object)
++     return;
++  //isyslog ("Indent %d %s\n", Object->getIndent(), Object->Title());
++  for (int index = Count() - 1; index >= 0; index--) {
++      cCommand *parent = Get(index);
++      if (parent->getIndent() < Object->getIndent()) {
++         parent->addChild(Object);
++         return;
++         }
++      }
++  cConfig<cCommand>::Add(Object);
++}
++#endif /* CMDSUBMENU */
+ // --- cSVDRPhosts -----------------------------------------------------------
+@@ -218,6 +269,12 @@
+   strcpy(OSDLanguage, ""); // default is taken from environment
+   strcpy(OSDSkin, "sttng");
+   strcpy(OSDTheme, "default");
++#ifdef USE_WAREAGLEICON
++  WarEagleIcons = 1;
++#endif /* WAREAGLEICON */
++#ifdef USE_VALIDINPUT
++  ShowValidInput = 0;
++#endif /* VALIDINPUT */
+   PrimaryDVB = 1;
+   ShowInfoOnChSwitch = 1;
+   TimeoutRequChInfo = 1;
+@@ -263,6 +320,15 @@
+   VideoFormat = 0;
+   UpdateChannels = 5;
+   UseDolbyDigital = 1;
++#ifdef USE_DOLBYINREC
++  UseDolbyInRecordings = 1;
++#endif /* DOLBYINREC */
++#ifdef USE_DVBSETUP
++  DolbyTransferFix = 1;
++  ChannelBlocker = 0;
++  ChannelBlockerMode = 0;
++  ChannelBlockerList = strdup("");
++#endif /* DVBSETUP */
+   ChannelInfoPos = 0;
+   ChannelInfoTime = 5;
+   OSDLeftP = 0.08;
+@@ -287,24 +353,135 @@
+   FontSmlSize = 18;
+   FontFixSize = 20;
+   MaxVideoFileSize = MAXVIDEOFILESIZEDEFAULT;
++#ifdef USE_HARDLINKCUTTER
++  MaxRecordingSize = DEFAULTRECORDINGSIZE;
++#endif /* HARDLINKCUTTER */
+   SplitEditedFiles = 0;
++#ifdef USE_HARDLINKCUTTER
++  HardLinkCutter = 0;
++#endif /* HARDLINKCUTTER */
++#ifdef USE_DELTIMESHIFTREC
++  DelTimeshiftRec = 0;
++#endif /* DELTIMESHIFTREC */
+   MinEventTimeout = 30;
+   MinUserInactivity = 300;
+   NextWakeupTime = 0;
+   MultiSpeedMode = 0;
+   ShowReplayMode = 0;
++#ifdef USE_DDEPGENTRY
++  DoubleEpgTimeDelta = 15;
++  DoubleEpgAction = 0;
++  MixEpgAction = 0;
++  DisableVPS = 0;
++#endif /* DDEPGENTRY */
+   ResumeID = 0;
++#ifdef USE_JUMPPLAY
++  JumpPlay = 0;
++  PlayJump = 0;
++  PauseLastMark = 0;
++  ReloadMarks = 0;
++#endif /* JUMPPLAY */
++#ifdef USE_SOURCECAPS
++  memset(SourceCaps, 0, sizeof SourceCaps);
++  SourceCapsSet = false;
++#endif /* SOURCECAPS */
+   CurrentChannel = -1;
+   CurrentVolume = MAXVOLUME;
+   CurrentDolby = 0;
+   InitialChannel = 0;
+   InitialVolume = -1;
++#ifdef USE_VOLCTRL
++  LRVolumeControl = 0;
++  LRChannelGroups = 1;
++  LRForwardRewind = 1;
++#endif /* VOLCTRL */
+   EmergencyExit = 1;
++#ifdef USE_NOEPG
++  noEPGMode = 0;
++  noEPGList = strdup("");
++#endif /* NOEPG */
++#ifdef USE_LIRCSETTINGS
++  LircRepeatDelay = 350;
++  LircRepeatFreq = 100;
++  LircRepeatTimeout = 500;
++#endif /* LIRCSETTINGS */
++#ifdef USE_LIEMIEXT
++  ShowRecDate = 1;
++  ShowRecTime = 1;
++  ShowRecLength = 0;
++  ShowProgressBar = 0;
++  MenuCmdPosition = 0;
++  JumpSeconds = 60;
++  JumpSecondsSlow = 10;
++  ShowTimerStop = 1;
++  MainMenuTitle = 0;
++  strcpy(CustomMainMenuTitle, "Video Disk Recorder");
++#endif /* LIEMIEXT */
++#ifdef USE_SORTRECORDS
++  RecordingsSortMode = 0;
++  RecordingsSortDirsFirst = 0;
++#endif /* SORTRECORDS */
++#ifdef USE_CUTTERQUEUE
++  CutterAutoDelete = 0;
++#endif /* CUTTERQUEUE */
++#ifdef USE_CUTTIME
++  CutTime = 1;
++#endif /* CUTTIME */
++#ifdef USE_DVDARCHIVE
++  DvdDisplayMode = 1;
++  DvdDisplayZeros = 1;
++  DvdTrayMode = 0;
++  DvdSpeedLimit = 0;
++#endif /* DVDARCHIVE */
++#ifdef USE_SOFTOSD
++  UseSoftOsd = 0;
++  SoftOsdRate = 50;
++  SoftOsdFadeinSteps = 6;
++  SoftOsdFadeoutSteps = 16;
++  SoftOsdPaletteOnly = 0;
++#endif /* SOFTOSD */
++#ifdef USE_LNBSHARE
++  VerboseLNBlog = 0;
++  for (int i = 0; i < MAXDEVICES; i++) CardUsesLNBnr[i] = i + 1;
++#endif /* LNBSHARE */
++#ifdef USE_DVLVIDPREFER
++  UseVidPrefer = 0; // default = disabled
++  nVidPrefer = 1;
++  for (int zz = 1; zz < DVLVIDPREFER_MAX; zz++) {
++      VidPreferPrio[ zz ] = 50;
++      VidPreferSize[ zz ] = 100;
++      }
++  VidPreferSize[ 0 ] = 800;
++  VidPreferPrio[ 0 ] = 50;
++#endif /* DVLVIDPREFER */
++#ifdef USE_DVLFRIENDLYFNAMES
++  UseFriendlyFNames = 0; // default = disabled
++#endif /* DVLFRIENDLYFNAMES */
++}
++
++#if defined (USE_DVBSETUP) || defined (USE_NOEPG)
++cSetup::~cSetup()
++{
++#ifdef USE_DVBSETUP
++  free(ChannelBlockerList);
++#endif /* DVBSETUP */
++#ifdef USE_NOEPG
++  free(noEPGList);
++#endif /* NOEPG */
+ }
++#endif /* DVBSETUP + NOEPG */
+ cSetup& cSetup::operator= (const cSetup &s)
+ {
+   memcpy(&__BeginData__, &s.__BeginData__, (char *)&s.__EndData__ - (char *)&s.__BeginData__);
++#ifdef USE_DVBSETUP
++  free(ChannelBlockerList);
++  ChannelBlockerList = strdup(s.ChannelBlockerList);
++#endif /* DVBSETUP */
++#ifdef USE_NOEPG
++  free(noEPGList);
++  noEPGList = strdup(s.noEPGList);
++#endif /* NOEPG */
+   return *this;
+ }
+@@ -400,11 +577,62 @@
+   return true;
+ }
++#ifdef USE_SOURCECAPS
++void cSetup::StoreSourceCaps(const char *Name)
++{
++  cSetupLine *l;
++  while ((l = Get(Name)) != NULL)
++     Del(l);
++
++  for (int i = 0; i < MAXDEVICES; i++) {
++      char buffer[MAXSOURCECAPS*8]={0,}, *q = buffer;
++      int j = 0;
++      while (SourceCaps[i][j] && j < MAXSOURCECAPS) {
++         if (j==0)
++            q += snprintf(buffer, sizeof(buffer), "%i ", i+1);
++         q += snprintf(q, sizeof(buffer) - (q-buffer), "%s ", *cSource::ToString(SourceCaps[i][j++]));
++         }
++      if (*buffer)
++         Store(Name, buffer, NULL, true);
++      }
++}
++
++bool cSetup::ParseSourceCaps(const char *Value)
++{
++  char *p;
++  int d = strtol(Value, &p, 10)-1, i = 0;
++  while (p < Value+strlen(Value)) {
++     if (*p==0) return true;
++     if (isblank(*p)) ++p;
++     if (isalpha(*p)) {
++        int source = cSource::FromString(p);
++        if (source != cSource::stNone) {
++           SourceCaps[d][i++] = source;
++           SourceCapsSet = true;
++           }
++        else
++           return false;
++        while (!isblank(*p) && *p)
++           ++p;
++        if (i>MAXSOURCECAPS)
++           return false;
++        }
++     }
++  return true;
++}
++#endif /* SOURCECAPS */
++
+ bool cSetup::Parse(const char *Name, const char *Value)
+ {
+   if      (!strcasecmp(Name, "OSDLanguage"))       { strn0cpy(OSDLanguage, Value, sizeof(OSDLanguage)); I18nSetLocale(OSDLanguage); }
+   else if (!strcasecmp(Name, "OSDSkin"))             Utf8Strn0Cpy(OSDSkin, Value, MaxSkinName);
+   else if (!strcasecmp(Name, "OSDTheme"))            Utf8Strn0Cpy(OSDTheme, Value, MaxThemeName);
++#ifdef USE_WAREAGLEICON
++  else if (!strcasecmp(Name, "WarEagleIcons"))       WarEagleIcons      = atoi(Value);
++#endif /* WAREAGLEICON */
++#ifdef USE_VALIDINPUT
++  else if (!strcasecmp(Name, "ShowValidInput"))      ShowValidInput     = atoi(Value);
++#endif /* VALIDINPUT */
+   else if (!strcasecmp(Name, "PrimaryDVB"))          PrimaryDVB         = atoi(Value);
+   else if (!strcasecmp(Name, "ShowInfoOnChSwitch"))  ShowInfoOnChSwitch = atoi(Value);
+   else if (!strcasecmp(Name, "TimeoutRequChInfo"))   TimeoutRequChInfo  = atoi(Value);
+@@ -450,6 +678,18 @@
+   else if (!strcasecmp(Name, "VideoFormat"))         VideoFormat        = atoi(Value);
+   else if (!strcasecmp(Name, "UpdateChannels"))      UpdateChannels     = atoi(Value);
+   else if (!strcasecmp(Name, "UseDolbyDigital"))     UseDolbyDigital    = atoi(Value);
++#ifdef USE_DOLBYINREC
++  else if (!strcasecmp(Name, "UseDolbyInRecordings")) UseDolbyInRecordings = atoi(Value);
++#endif /* DOLBYINREC */
++#ifdef USE_DVBSETUP
++  else if (!strcasecmp(Name, "DolbyTransferFix"))    DolbyTransferFix   = atoi(Value);
++  else if (!strcasecmp(Name, "ChannelBlocker"))      ChannelBlocker     = atoi(Value);
++  else if (!strcasecmp(Name, "ChannelBlockerMode"))  ChannelBlockerMode = atoi(Value);
++  else if (!strcasecmp(Name, "ChannelBlockerList")) {
++     free(ChannelBlockerList);
++     ChannelBlockerList = strdup(Value ? Value : "");
++     }
++#endif /* DVBSETUP */
+   else if (!strcasecmp(Name, "ChannelInfoPos"))      ChannelInfoPos     = atoi(Value);
+   else if (!strcasecmp(Name, "ChannelInfoTime"))     ChannelInfoTime    = atoi(Value);
+   else if (!strcasecmp(Name, "OSDLeftP"))            OSDLeftP           = atof(Value);
+@@ -474,21 +714,144 @@
+   else if (!strcasecmp(Name, "FontSmlSize"))         FontSmlSize        = atoi(Value);
+   else if (!strcasecmp(Name, "FontFixSize"))         FontFixSize        = atoi(Value);
+   else if (!strcasecmp(Name, "MaxVideoFileSize"))    MaxVideoFileSize   = atoi(Value);
++#ifdef USE_HARDLINKCUTTER
++  else if (!strcasecmp(Name, "MaxRecordingSize"))    MaxRecordingSize   = atoi(Value);
++#endif /* HARDLINKCUTTER */
+   else if (!strcasecmp(Name, "SplitEditedFiles"))    SplitEditedFiles   = atoi(Value);
++#ifdef USE_HARDLINKCUTTER
++  else if (!strcasecmp(Name, "HardLinkCutter"))      HardLinkCutter     = atoi(Value);
++#endif /* HARDLINKCUTTER */
++#ifdef USE_DELTIMESHIFTREC
++  else if (!strcasecmp(Name, "DelTimeshiftRec"))     DelTimeshiftRec    = atoi(Value);
++#endif /* DELTIMESHIFTREC */
+   else if (!strcasecmp(Name, "MinEventTimeout"))     MinEventTimeout    = atoi(Value);
+   else if (!strcasecmp(Name, "MinUserInactivity"))   MinUserInactivity  = atoi(Value);
+   else if (!strcasecmp(Name, "NextWakeupTime"))      NextWakeupTime     = atoi(Value);
+   else if (!strcasecmp(Name, "MultiSpeedMode"))      MultiSpeedMode     = atoi(Value);
+   else if (!strcasecmp(Name, "ShowReplayMode"))      ShowReplayMode     = atoi(Value);
++#ifdef USE_DDEPGENTRY
++  else if (!strcasecmp(Name, "DoubleEpgTimeDelta"))  DoubleEpgTimeDelta = atoi(Value);
++  else if (!strcasecmp(Name, "DoubleEpgAction"))     DoubleEpgAction    = atoi(Value);
++  else if (!strcasecmp(Name, "MixEpgAction"))        MixEpgAction       = atoi(Value);
++  else if (!strcasecmp(Name, "DisableVPS"))          DisableVPS         = atoi(Value);
++#endif /* DDEPGENTRY */
+   else if (!strcasecmp(Name, "ResumeID"))            ResumeID           = atoi(Value);
++#ifdef USE_JUMPPLAY
++  else if (!strcasecmp(Name, "JumpPlay"))            JumpPlay           = atoi(Value);
++  else if (!strcasecmp(Name, "PlayJump"))            PlayJump           = atoi(Value);
++  else if (!strcasecmp(Name, "PauseLastMark"))       PauseLastMark      = atoi(Value);
++  else if (!strcasecmp(Name, "ReloadMarks"))         ReloadMarks        = atoi(Value);
++#endif /* JUMPPLAY */
++#ifdef USE_SOURCECAPS
++  else if (!strcasecmp(Name, "SourceCaps"))          return ParseSourceCaps(Value);
++#endif /* SOURCECAPS */
+   else if (!strcasecmp(Name, "CurrentChannel"))      CurrentChannel     = atoi(Value);
+   else if (!strcasecmp(Name, "CurrentVolume"))       CurrentVolume      = atoi(Value);
+   else if (!strcasecmp(Name, "CurrentDolby"))        CurrentDolby       = atoi(Value);
+   else if (!strcasecmp(Name, "InitialChannel"))      InitialChannel     = atoi(Value);
+   else if (!strcasecmp(Name, "InitialVolume"))       InitialVolume      = atoi(Value);
++#ifdef USE_VOLCTRL
++  else if (!strcasecmp(Name, "LRVolumeControl"))     LRVolumeControl    = atoi(Value);
++  else if (!strcasecmp(Name, "LRChannelGroups"))     LRChannelGroups    = atoi(Value);
++  else if (!strcasecmp(Name, "LRForwardRewind"))     LRForwardRewind    = atoi(Value);
++#endif /* VOLCTRL */
+   else if (!strcasecmp(Name, "EmergencyExit"))       EmergencyExit      = atoi(Value);
++#ifdef USE_NOEPG
++  else if (!strcasecmp(Name, "noEPGMode"))           noEPGMode          = atoi(Value);
++  else if (!strcasecmp(Name, "noEPGList")) {
++     free(noEPGList);
++     noEPGList = strdup(Value ? Value : "");
++     }
++#endif /* NOEPG */
++#ifdef USE_LIRCSETTINGS
++  else if (!strcasecmp(Name, "LircRepeatDelay"))     LircRepeatDelay    = atoi(Value);
++  else if (!strcasecmp(Name, "LircRepeatFreq"))      LircRepeatFreq     = atoi(Value);
++  else if (!strcasecmp(Name, "LircRepeatTimeout"))   LircRepeatTimeout  = atoi(Value);
++#endif /* LIRCSETTINGS */
++#ifdef USE_LIEMIEXT
++  else if (!strcasecmp(Name, "ShowRecDate"))         ShowRecDate        = atoi(Value);
++  else if (!strcasecmp(Name, "ShowRecTime"))         ShowRecTime        = atoi(Value);
++  else if (!strcasecmp(Name, "ShowRecLength"))       ShowRecLength      = atoi(Value);
++  else if (!strcasecmp(Name, "ShowProgressBar"))     ShowProgressBar    = atoi(Value);
++  else if (!strcasecmp(Name, "MenuCmdPosition"))     MenuCmdPosition    = atoi(Value);
++  else if (!strcasecmp(Name, "JumpSeconds"))         JumpSeconds        = atoi(Value);
++  else if (!strcasecmp(Name, "JumpSecondsSlow"))     JumpSecondsSlow    = atoi(Value);
++  else if (!strcasecmp(Name, "ShowTimerStop"))       ShowTimerStop      = atoi(Value);
++  else if (!strcasecmp(Name, "MainMenuTitle"))       MainMenuTitle      = atoi(Value);
++  else if (!strcasecmp(Name, "CustomMainMenuTitle")) Utf8Strn0Cpy(CustomMainMenuTitle, Value, MaxTitleName);
++#endif /* LIEMIEXT */
++#ifdef USE_SORTRECORDS
++  else if (!strcasecmp(Name, "RecordingsSortMode"))  RecordingsSortMode = atoi(Value);
++  else if (!strcasecmp(Name, "RecordingsSortDirsFirst")) RecordingsSortDirsFirst = atoi(Value);
++#endif /* SORTRECORDS */
++#ifdef USE_CUTTERQUEUE
++  else if (!strcasecmp(Name, "CutterAutoDelete"))    CutterAutoDelete   = atoi(Value);
++#endif /* CUTTERQUEUE */
++#ifdef USE_CUTTIME
++  else if (!strcasecmp(Name, "CutTime"))             CutTime            = atoi(Value);
++#endif /* CUTTIME */
++#ifdef USE_DVDARCHIVE
++  else if (!strcasecmp(Name, "DvdDisplayMode"))      DvdDisplayMode     = atoi(Value);
++  else if (!strcasecmp(Name, "DvdDisplayZeros"))     DvdDisplayZeros    = atoi(Value);
++  else if (!strcasecmp(Name, "DvdTrayMode"))         DvdTrayMode        = atoi(Value);
++  else if (!strcasecmp(Name, "DvdSpeedLimit"))       DvdSpeedLimit      = atoi(Value);
++#endif /* DVDARCHIVE */
++#ifdef USE_SOFTOSD
++  else if (!strcasecmp(Name, "UseSoftOsd"))          UseSoftOsd          = atoi(Value);
++  else if (!strcasecmp(Name, "SoftOsdRate"))         SoftOsdRate         = atoi(Value);
++  else if (!strcasecmp(Name, "SoftOsdFadeinSteps"))  SoftOsdFadeinSteps  = atoi(Value);
++  else if (!strcasecmp(Name, "SoftOsdFadeoutSteps")) SoftOsdFadeoutSteps = atoi(Value);
++  else if (!strcasecmp(Name, "SoftOsdPaletteOnly"))  SoftOsdPaletteOnly  = atoi(Value);
++#endif /* SOFTOSD */
++#ifdef USE_DVLVIDPREFER
++  else if (strcasecmp(Name, "UseVidPrefer") == 0)    UseVidPrefer       = atoi(Value);
++  else if (strcasecmp(Name, "nVidPrefer") == 0)      nVidPrefer         = atoi(Value);
++  else if (strstr(Name, "VidPrefer") == Name) {
++     char *x = (char *)&Name[ strlen(Name) - 1 ];
++     int vN;
++
++     if (isdigit(*x) != 0) {
++        while (isdigit(*x) != 0)
++              x--;
++        x++;
++        }
++
++     vN = atoi(x);
++     if (vN < DVLVIDPREFER_MAX) {
++        if (strstr(Name, "VidPreferPrio") == Name) {
++           VidPreferPrio[ vN ] = atoi(Value);
++           if (VidPreferPrio[ vN ] > 99)
++              VidPreferPrio[ vN ] = 99;
++           }
++        else if (strstr(Name, "VidPreferSize") == Name) {
++           VidPreferSize[ vN ] = atoi(Value);
++           }
+   else
+      return false;
++        }
++     }
++#endif /* DVLVIDPREFER */
++#ifdef USE_DVLFRIENDLYFNAMES
++  else if (strcasecmp(Name, "UseFriendlyFNames") == 0) UseFriendlyFNames = atoi(Value);
++#endif /* DVLFRIENDLYFNAMES */
++  else
++#ifdef USE_LNBSHARE
++  if (!strcasecmp(Name, "VerboseLNBlog")) VerboseLNBlog = atoi(Value);
++  else {
++     char tmp[20];
++     bool result = false;
++     for (int i = 1; i <= MAXDEVICES; i++) {
++         sprintf(tmp, "Card%dusesLNBnr", i);
++         if (!strcasecmp(Name, tmp)) {
++            CardUsesLNBnr[i - 1] = atoi(Value);
++            result = true;
++            }
++         }
++     return result;
++  }
++#else
++     return false;
++#endif /* LNBSHARE */
+   return true;
+ }
+@@ -497,6 +860,12 @@
+   Store("OSDLanguage",        OSDLanguage);
+   Store("OSDSkin",            OSDSkin);
+   Store("OSDTheme",           OSDTheme);
++#ifdef USE_WAREAGLEICON
++  Store("WarEagleIcons",      WarEagleIcons);
++#endif /* WAREAGLEICON */
++#ifdef USE_VALIDINPUT
++  Store("ShowValidInput",     ShowValidInput);
++#endif /* VALIDINPUT */
+   Store("PrimaryDVB",         PrimaryDVB);
+   Store("ShowInfoOnChSwitch", ShowInfoOnChSwitch);
+   Store("TimeoutRequChInfo",  TimeoutRequChInfo);
+@@ -542,6 +911,15 @@
+   Store("VideoFormat",        VideoFormat);
+   Store("UpdateChannels",     UpdateChannels);
+   Store("UseDolbyDigital",    UseDolbyDigital);
++#ifdef USE_DOLBYINREC
++  Store("UseDolbyInRecordings", UseDolbyInRecordings);
++#endif /* DOLBYINREC */
++#ifdef USE_DVBSETUP
++  Store("DolbyTransferFix",   DolbyTransferFix);
++  Store("ChannelBlocker",     ChannelBlocker);
++  Store("ChannelBlockerMode", ChannelBlockerMode);
++  Store("ChannelBlockerList", ChannelBlockerList);
++#endif /* DVBSETUP */
+   Store("ChannelInfoPos",     ChannelInfoPos);
+   Store("ChannelInfoTime",    ChannelInfoTime);
+   Store("OSDLeftP",           OSDLeftP);
+@@ -566,25 +944,152 @@
+   Store("FontSmlSize",        FontSmlSize);
+   Store("FontFixSize",        FontFixSize);
+   Store("MaxVideoFileSize",   MaxVideoFileSize);
++#ifdef USE_HARDLINKCUTTER
++  Store("MaxRecordingSize",   MaxRecordingSize);
++#endif /* HARDLINKCUTTER */
+   Store("SplitEditedFiles",   SplitEditedFiles);
++#ifdef USE_HARDLINKCUTTER
++  Store("HardLinkCutter",     HardLinkCutter);
++#endif /* HARDLINKCUTTER */
++#ifdef USE_DELTIMESHIFTREC
++  Store("DelTimeshiftRec",    DelTimeshiftRec);
++#endif /* DELTIMESHIFTREC */
+   Store("MinEventTimeout",    MinEventTimeout);
+   Store("MinUserInactivity",  MinUserInactivity);
+   Store("NextWakeupTime",     NextWakeupTime);
++#ifdef USE_DDEPGENTRY
++  Store("DoubleEpgAction",    DoubleEpgAction);
++  Store("MixEpgAction",       MixEpgAction);
++  Store("DisableVPS",         DisableVPS);
++  Store("DoubleEpgTimeDelta", DoubleEpgTimeDelta);
++#endif /* DDEPGENTRY */
+   Store("MultiSpeedMode",     MultiSpeedMode);
+   Store("ShowReplayMode",     ShowReplayMode);
+   Store("ResumeID",           ResumeID);
++#ifdef USE_JUMPPLAY
++  Store("JumpPlay",           JumpPlay);
++  Store("PlayJump",           PlayJump);
++  Store("PauseLastMark",      PauseLastMark);
++  Store("ReloadMarks",        ReloadMarks);
++#endif /* JUMPPLAY */
++#ifdef USE_SOURCECAPS
++  if (SourceCapsSet) StoreSourceCaps("SourceCaps");
++#endif /* SOURCECAPS */
+   Store("CurrentChannel",     CurrentChannel);
+   Store("CurrentVolume",      CurrentVolume);
+   Store("CurrentDolby",       CurrentDolby);
+   Store("InitialChannel",     InitialChannel);
+   Store("InitialVolume",      InitialVolume);
++#ifdef USE_VOLCTRL
++  Store("LRVolumeControl",    LRVolumeControl);
++  Store("LRChannelGroups",    LRChannelGroups);
++  Store("LRForwardRewind",    LRForwardRewind);
++#endif /* VOLCTRL */
+   Store("EmergencyExit",      EmergencyExit);
++#ifdef USE_NOEPG
++  Store("noEPGMode",          noEPGMode);
++  Store("noEPGList",          noEPGList);
++#endif /* NOEPG */
++#ifdef USE_LIRCSETTINGS
++  Store("LircRepeatDelay",    LircRepeatDelay);
++  Store("LircRepeatFreq",     LircRepeatFreq);
++  Store("LircRepeatTimeout",  LircRepeatTimeout);
++#endif /* LIRCSETTINGS */
++#ifdef USE_LIEMIEXT
++  Store("ShowRecDate",        ShowRecDate);
++  Store("ShowRecTime",        ShowRecTime);
++  Store("ShowRecLength",      ShowRecLength);
++  Store("ShowProgressBar",    ShowProgressBar);
++  Store("MenuCmdPosition",    MenuCmdPosition);
++  Store("JumpSeconds",        JumpSeconds);
++  Store("JumpSecondsSlow",    JumpSecondsSlow);
++  Store("ShowTimerStop",      ShowTimerStop);
++  Store("MainMenuTitle",      MainMenuTitle);
++  Store("CustomMainMenuTitle",CustomMainMenuTitle);
++#endif /* LIEMIEXT */
++#ifdef USE_SORTRECORDS
++  Store("RecordingsSortMode", RecordingsSortMode);
++  Store("RecordingsSortDirsFirst", RecordingsSortDirsFirst);
++#endif /* SORTRECORDS */
++#ifdef USE_CUTTERQUEUE
++  Store("CutterAutoDelete",   CutterAutoDelete);
++#endif /* CUTTERQUEUE */
++#ifdef USE_CUTTIME
++  Store("CutTime",            CutTime);
++#endif /* CUTTIME */
++#ifdef USE_DVDARCHIVE
++  Store("DvdDisplayMode",     DvdDisplayMode);
++  Store("DvdDisplayZeros",    DvdDisplayZeros);
++  Store("DvdTrayMode",        DvdTrayMode);
++  Store("DvdSpeedLimit",      DvdSpeedLimit);
++#endif /* DVDARCHIVE */
++#ifdef USE_SOFTOSD
++  Store("UseSoftOsd",          UseSoftOsd);
++  Store("SoftOsdRate",         SoftOsdRate);
++  Store("SoftOsdFadeinSteps",  SoftOsdFadeinSteps);
++  Store("SoftOsdFadeoutSteps", SoftOsdFadeoutSteps);
++  Store("SoftOsdPaletteOnly",  SoftOsdPaletteOnly);
++#endif /* SOFTOSD */
++#ifdef USE_LNBSHARE
++  Store("VerboseLNBlog",      VerboseLNBlog);
++  char tmp[20];
++  if (cDevice::NumDevices() > 1) {
++     for (int i = 1; i <= cDevice::NumDevices(); i++) {
++         sprintf(tmp, "Card%dusesLNBnr", i);
++         Store(tmp, CardUsesLNBnr[i - 1]);
++         }
++     }
++#endif /* LNBSHARE */
++#ifdef USE_DVLVIDPREFER
++  Store ("UseVidPrefer",      UseVidPrefer);
++  Store ("nVidPrefer",        nVidPrefer);
++
++  char vidBuf[32];
++  for (int zz = 0; zz < nVidPrefer; zz++) {
++      sprintf(vidBuf, "VidPreferPrio%d", zz);
++      Store (vidBuf, VidPreferPrio[zz]);
++      sprintf(vidBuf, "VidPreferSize%d", zz);
++      Store (vidBuf, VidPreferSize[zz]);
++      }
++#endif /* DVLVIDPREFER */
++#ifdef USE_DVLFRIENDLYFNAMES
++  Store ("UseFriendlyFNames", UseFriendlyFNames);
++#endif /* DVLFRIENDLYFNAMES */
+   Sort();
+   if (cConfig<cSetupLine>::Save()) {
+      isyslog("saved setup to %s", FileName());
++#ifdef USE_DVDARCHIVE
++     if (DvdDisplayMode >= 1) ::Recordings.Load();
++#endif /* DVDARCHIVE */
+      return true;
+      }
+   return false;
+ }
++
++#ifdef USE_CMDRECCMDI18N
++bool LoadCommandsI18n(cCommands & cmds, const char *FileName, bool AllowComments, bool MustExist)
++{
++    bool bRet = true;
++    bool bLoadDefault = (bool)strcmp(Setup.OSDLanguage, "en_US");
++    if (bLoadDefault) {
++       // attempt to load a translated file
++       cString FullPath = cString::sprintf("%s.%s", FileName, Setup.OSDLanguage);
++       if (!cmds.Load((FullPath), AllowComments, true)) {
++          // require to exist, just to be able to log
++          // fallback
++          bLoadDefault = false;
++          esyslog("Failed to load translated '%s' for language (%s)", *FullPath, Setup.OSDLanguage);
++          esyslog("Falling back to default '%s' (if any)", FileName);
++          }
++       }
++    if (!bLoadDefault) {
++       // let's do it the normal way
++       bRet = cmds.Load(FileName, AllowComments, MustExist);
++       }
++    // return status only for the default commands file
++    return bRet;
++}
++#endif /* CMDRECCMDI18N */
++
+diff -NaurwB vdr-1.7.10/config.h vdr-1.7.10-patched/config.h
+--- vdr-1.7.10/config.h        2009-08-29 14:47:03.000000000 +0200
++++ vdr-1.7.10-patched/config.h        2009-12-18 06:25:25.000000000 +0100
+@@ -36,23 +36,74 @@
+ // plugins to work with newer versions of the core VDR as long as no
+ // VDR header files have changed.
++#define VDREXTENSIONS 72
++
++#ifdef USE_JUMPPLAY
++#define JUMPPLAYVERSNUM 100
++#endif /* JUMPPLAY */
++
++#ifdef USE_LIEMIEXT
++#define LIEMIKUUTIO  127
++#define MAXMAINMENUTITLE 4
++#define MaxTitleName 64
++#endif /* LIEMIEXT */
++
++#ifdef USE_CMDSUBMENU
++#define CMDSUBMENUVERSNUM 7
++#endif /* CMDSUBMENU */
++
++#ifdef USE_MAINMENUHOOKS
++#define MAINMENUHOOKSVERSNUM 1.0
++#endif /* MAINMENUHOOKS */
++
++#ifdef USE_PINPLUGIN
++#define PIN_PLUGIN_PATCH 120
++#endif /* PINPLUGIN */
++
++#ifdef USE_PLUGINPARAM
++#define PLUGINPARAMPATCHVERSNUM 1
++#endif /* PLUGINPARAM */
++
+ #define MAXPRIORITY 99
+ #define MAXLIFETIME 99
++#ifdef USE_DVLVIDPREFER
++#define DVLVIDPREFER_MAX 12
++#endif /* DVLVIDPREFER */
++
+ #define MINOSDWIDTH   480
+ #define MAXOSDWIDTH  1920
+ #define MINOSDHEIGHT  324
+ #define MAXOSDHEIGHT 1200
++#ifdef USE_SOURCECAPS
++#define MAXDEVICES         16 // the maximum number of devices in the system
++#define MAXSOURCECAPS     128 // the maximum number of different sources per device
++#endif /* SOURCECAPS */
++
++#ifdef USE_LNBSHARE
++#ifndef MAXDEVICES
++#define MAXDEVICES         16 // the maximum number of devices in the system
++#endif
++#endif /* LNBSHARE */
++
+ #define MaxFileName 256
+ #define MaxSkinName 16
+ #define MaxThemeName 16
++#ifdef USE_CMDSUBMENU
++class cCommands;
++#endif /* CMDSUBMENU */
++
+ class cCommand : public cListObject {
+ private:
+   char *title;
+   char *command;
+   bool confirm;
++#ifdef USE_CMDSUBMENU
++  int nIndent;
++  cCommands *childs;
++#endif /* CMDSUBMENU */
+   static char *result;
+ public:
+   cCommand(void);
+@@ -61,6 +112,14 @@
+   const char *Title(void) { return title; }
+   bool Confirm(void) { return confirm; }
+   const char *Execute(const char *Parameters = NULL);
++#ifdef USE_CMDSUBMENU
++  int getIndent(void) { return nIndent; }
++  void setIndent(int nNewIndent) { nIndent = nNewIndent; }
++  cCommands *getChilds(void) { return childs; }
++  int getChildCount(void);
++  bool hasChilds(void) { return getChildCount() > 0; }
++  void addChild(cCommand *newChild);
++#endif /* CMDSUBMENU */
+   };
+ typedef uint32_t in_addr_t; //XXX from /usr/include/netinet/in.h (apparently this is not defined on systems with glibc < 2.2)
+@@ -88,6 +147,9 @@
+ public:
+   cConfig(void) { fileName = NULL; }
+   virtual ~cConfig() { free(fileName); }
++#ifdef USE_CMDSUBMENU
++  virtual void AddConfig(T *Object) { cList<T>::Add(Object); }
++#endif /* CMDSUBMENU */
+   const char *FileName(void) { return fileName; }
+   bool Load(const char *FileName = NULL, bool AllowComments = false, bool MustExist = false)
+   {
+@@ -117,7 +179,11 @@
+                 if (!isempty(s)) {
+                    T *l = new T;
+                    if (l->Parse(s))
++#ifdef USE_CMDSUBMENU
++                      AddConfig(l);
++#else
+                       Add(l);
++#endif /* CMDSUBMENU */
+                    else {
+                       esyslog("ERROR: error in %s, line %d", fileName, line);
+                       delete l;
+@@ -158,7 +224,14 @@
+   }
+   };
++#ifdef USE_CMDSUBMENU
++class cCommands : public cConfig<cCommand> {
++public:
++  virtual void AddConfig(cCommand *Object);
++  };
++#else
+ class cCommands : public cConfig<cCommand> {};
++#endif /* CMDSUBMENU */
+ class cSVDRPhosts : public cConfig<cSVDRPhost> {
+ public:
+@@ -167,6 +240,9 @@
+ extern cCommands Commands;
+ extern cCommands RecordingCommands;
++#ifdef USE_TIMERCMD
++extern cCommands TimerCommands;
++#endif /* TIMERCMD */
+ extern cSVDRPhosts SVDRPhosts;
+ class cSetupLine : public cListObject {
+@@ -192,6 +268,10 @@
+   void StoreLanguages(const char *Name, int *Values);
+   bool ParseLanguages(const char *Value, int *Values);
+   bool Parse(const char *Name, const char *Value);
++#ifdef USE_SOURCECAPS
++  void StoreSourceCaps(const char *Name);
++  bool ParseSourceCaps(const char *Value);
++#endif /* SOURCECAPS */
+   cSetupLine *Get(const char *Name, const char *Plugin = NULL);
+   void Store(const char *Name, const char *Value, const char *Plugin = NULL, bool AllowMultiple = false);
+   void Store(const char *Name, int Value, const char *Plugin = NULL);
+@@ -202,6 +282,12 @@
+   char OSDLanguage[I18N_MAX_LOCALE_LEN];
+   char OSDSkin[MaxSkinName];
+   char OSDTheme[MaxThemeName];
++#ifdef USE_WAREAGLEICON
++  int WarEagleIcons;
++#endif /* WAREAGLEICON */
++#ifdef USE_VALIDINPUT
++  int ShowValidInput;
++#endif /* VALIDINPUT */
+   int PrimaryDVB;
+   int ShowInfoOnChSwitch;
+   int TimeoutRequChInfo;
+@@ -243,6 +329,14 @@
+   int VideoFormat;
+   int UpdateChannels;
+   int UseDolbyDigital;
++#ifdef USE_DOLBYINREC
++  int UseDolbyInRecordings;
++#endif /* DOLBYINREC */
++#ifdef USE_DVBSETUP
++  int DolbyTransferFix;
++  int ChannelBlocker;
++  int ChannelBlockerMode;
++#endif /* DVBSETUP */
+   int ChannelInfoPos;
+   int ChannelInfoTime;
+   double OSDLeftP, OSDTopP, OSDWidthP, OSDHeightP;
+@@ -261,20 +355,111 @@
+   int FontSmlSize;
+   int FontFixSize;
+   int MaxVideoFileSize;
++#ifdef USE_HARDLINKCUTTER
++  int MaxRecordingSize;
++#endif /* HARDLINKCUTTER */
+   int SplitEditedFiles;
++#ifdef USE_HARDLINKCUTTER
++  int HardLinkCutter;
++#endif /* HARDLINKCUTTER */
++#ifdef USE_DELTIMESHIFTREC
++  int DelTimeshiftRec;
++#endif /* DELTIMESHIFTREC */
+   int MinEventTimeout, MinUserInactivity;
+   time_t NextWakeupTime;
+   int MultiSpeedMode;
+   int ShowReplayMode;
++#ifdef USE_DDEPGENTRY
++  int DoubleEpgTimeDelta;
++  int DoubleEpgAction;
++  int MixEpgAction;
++  int DisableVPS;
++#endif /* DDEPGENTRY */
+   int ResumeID;
++#ifdef USE_JUMPPLAY
++  int JumpPlay;
++  int PlayJump;
++  int PauseLastMark;
++  int ReloadMarks;
++#endif /* JUMPPLAY */
++#ifdef USE_SOURCECAPS
++  int SourceCaps[MAXDEVICES][MAXSOURCECAPS];
++  bool SourceCapsSet;
++#endif /* SOURCECAPS */
+   int CurrentChannel;
+   int CurrentVolume;
+   int CurrentDolby;
+   int InitialChannel;
+   int InitialVolume;
++#ifdef USE_VOLCTRL
++  int LRVolumeControl;
++  int LRChannelGroups;
++  int LRForwardRewind;
++#endif /* VOLCTRL */
+   int EmergencyExit;
++#ifdef USE_NOEPG
++  int noEPGMode;
++#endif /* NOEPG */
++#ifdef USE_LIRCSETTINGS
++  int LircRepeatDelay;
++  int LircRepeatFreq;
++  int LircRepeatTimeout;
++#endif /* LIRCSETTINGS */
++#ifdef USE_LIEMIEXT
++  int ShowRecDate, ShowRecTime, ShowRecLength, ShowProgressBar, MenuCmdPosition;
++  int JumpSeconds;
++  int JumpSecondsSlow;
++  int ShowTimerStop;
++  int MainMenuTitle;
++  char CustomMainMenuTitle[MaxTitleName];
++#endif /* LIEMIEXT */
++#ifdef USE_SORTRECORDS
++  int RecordingsSortMode;
++  int RecordingsSortDirsFirst;
++#endif /* SORTRECORDS */
++#ifdef USE_CUTTERQUEUE
++  int CutterAutoDelete;
++#endif /* CUTTERQUEUE */
++#ifdef USE_CUTTIME
++  int CutTime;
++#endif /* CUTTIME */
++#ifdef USE_DVDARCHIVE
++  int DvdDisplayMode;
++  int DvdDisplayZeros;
++  int DvdTrayMode;
++  int DvdSpeedLimit;
++#endif /* DVDARCHIVE */
++#ifdef USE_SOFTOSD
++  int UseSoftOsd;
++  int SoftOsdRate;
++  int SoftOsdFadeinSteps;
++  int SoftOsdFadeoutSteps;
++  int SoftOsdPaletteOnly;
++#endif /* SOFTOSD */
++#ifdef USE_LNBSHARE
++  int VerboseLNBlog;
++  int CardUsesLNBnr[MAXDEVICES];
++#endif /* LNBSHARE */
++#ifdef USE_DVLVIDPREFER
++  int UseVidPrefer;  // 0 = VDR's default, 1 = use
++  int nVidPrefer;
++  int VidPreferPrio[DVLVIDPREFER_MAX];
++  int VidPreferSize[DVLVIDPREFER_MAX];
++#endif /* DVLVIDPREFER */
++#ifdef USE_DVLFRIENDLYFNAMES
++  int UseFriendlyFNames;
++#endif /* DVLFRIENDLYFNAMES */
+   int __EndData__;
++#ifdef USE_DVBSETUP
++  char *ChannelBlockerList;
++#endif /* DVBSETUP */
++#ifdef USE_NOEPG
++  char *noEPGList; // pointer not to be flat-copied
++#endif /* NOEPG */
+   cSetup(void);
++#if defined (USE_DVBSETUP) || defined (USE_NOEPG)
++  ~cSetup();
++#endif /* DVBSETUP + NOEPG */
+   cSetup& operator= (const cSetup &s);
+   bool Load(const char *FileName);
+   bool Save(void);
+@@ -282,4 +467,8 @@
+ extern cSetup Setup;
++#ifdef USE_CMDRECCMDI18N
++bool LoadCommandsI18n(cCommands & cmds, const char *FileName = NULL, bool AllowComments = false, bool MustExist = false);
++#endif /* CMDRECCMDI18N */
++
+ #endif //__CONFIG_H
+diff -NaurwB vdr-1.7.10/cutter.c vdr-1.7.10-patched/cutter.c
+--- vdr-1.7.10/cutter.c        2009-04-19 12:56:33.000000000 +0200
++++ vdr-1.7.10-patched/cutter.c        2009-12-18 06:25:25.000000000 +0100
+@@ -15,6 +15,19 @@
+ // --- cCuttingThread --------------------------------------------------------
++#ifdef USE_CUTTERLIMIT
++#ifndef CUTTER_MAX_BANDWIDTH
++#  define CUTTER_MAX_BANDWIDTH MEGABYTE(10) // 10 MB/s
++#endif
++#ifndef CUTTER_REL_BANDWIDTH
++#  define CUTTER_REL_BANDWIDTH 75 // %
++#endif
++#ifndef CUTTER_PRIORITY
++#  define CUTTER_PRIORITY sched_get_priority_min(SCHED_OTHER)
++#endif
++#define CUTTER_TIMESLICE   100   // ms
++#endif /* CUTTERLIMIT */
++
+ class cCuttingThread : public cThread {
+ private:
+   const char *error;
+@@ -67,6 +80,22 @@
+ void cCuttingThread::Action(void)
+ {
++#ifdef USE_CUTTERLIMIT
++#ifdef USE_HARDLINKCUTTER
++  if (!Setup.HardLinkCutter)
++#endif /* HARDLINKCUTTER */
++  {
++    sched_param tmp;
++    tmp.sched_priority = CUTTER_PRIORITY;
++    if (!pthread_setschedparam(pthread_self(), SCHED_OTHER, &tmp))
++       printf("cCuttingThread::Action: cant set priority\n");
++  }
++
++  int bytes = 0;
++  int __attribute__((unused)) burst_size = CUTTER_MAX_BANDWIDTH * CUTTER_TIMESLICE / 1000; // max bytes/timeslice
++  cTimeMs __attribute__((unused)) t;
++#endif /* CUTTERLIMIT */
++
+   cMark *Mark = fromMarks.First();
+   if (Mark) {
+      fromFile = fromFileName->Open();
+@@ -78,6 +107,9 @@
+      Mark = fromMarks.Next(Mark);
+      off_t FileSize = 0;
+      int CurrentFileNumber = 0;
++#ifdef USE_HARDLINKCUTTER
++     bool SkipThisSourceFile = false;
++#endif /* HARDLINKCUTTER */
+      int LastIFrame = 0;
+      toMarks.Add(0);
+      toMarks.Save();
+@@ -96,12 +128,101 @@
+            // Read one frame:
++#ifndef USE_HARDLINKCUTTER
+            if (fromIndex->Get(Index++, &FileNumber, &FileOffset, &Independent, &Length)) {
+               if (FileNumber != CurrentFileNumber) {
+                  fromFile = fromFileName->SetOffset(FileNumber, FileOffset);
+                  fromFile->SetReadAhead(MEGABYTE(20));
+                  CurrentFileNumber = FileNumber;
+                  }
++#else
++           if (!fromIndex->Get(Index++, &FileNumber, &FileOffset, &Independent, &Length)) {
++              // Error, unless we're past last cut-in and there's no cut-out
++              if (Mark || LastMark)
++                 error = "index";
++              break;
++              }
++
++           if (FileNumber != CurrentFileNumber) {
++              fromFile = fromFileName->SetOffset(FileNumber, FileOffset);
++              fromFile->SetReadAhead(MEGABYTE(20));
++              CurrentFileNumber = FileNumber;
++              if (SkipThisSourceFile) {
++                 // At end of fast forward: Always skip to next file
++                 toFile = toFileName->NextFile();
++                 if (!toFile) {
++                    error = "toFile 4";
++                    break;
++                    }
++                 FileSize = 0;
++                 SkipThisSourceFile = false;
++                 }
++
++
++              if (Setup.HardLinkCutter && FileOffset == 0) {
++                 // We are at the beginning of a new source file.
++                 // Do we need to copy the whole file?
++
++                 // if !Mark && LastMark, then we're past the last cut-out and continue to next I-frame
++                 // if !Mark && !LastMark, then there's just a cut-in, but no cut-out
++                 // if Mark, then we're between a cut-in and a cut-out
++
++                 uint16_t MarkFileNumber;
++                 off_t MarkFileOffset;
++                 // Get file number of next cut mark
++                 if (!Mark && !LastMark
++                     || Mark
++                        && fromIndex->Get(Mark->position, &MarkFileNumber, &MarkFileOffset)
++                        && (MarkFileNumber != CurrentFileNumber)) {
++                    // The current source file will be copied completely.
++                    // Start new output file unless we did that already
++                    if (FileSize != 0) {
++                       toFile = toFileName->NextFile();
++                       if (!toFile) {
++                          error = "toFile 3";
++                          break;
++                          }
++                       FileSize = 0;
++                       }
++
++                    // Safety check that file has zero size
++                    struct stat buf;
++                    if (stat(toFileName->Name(), &buf) == 0) {
++                       if (buf.st_size != 0) {
++                          esyslog("cCuttingThread: File %s exists and has nonzero size", toFileName->Name());
++                          error = "nonzero file exist";
++                          break;
++                          }
++                       }
++                    else if (errno != ENOENT) {
++                       esyslog("cCuttingThread: stat failed on %s", toFileName->Name());
++                       error = "stat";
++                       break;
++                       }
++
++                    // Clean the existing 0-byte file
++                    toFileName->Close();
++                    cString ActualToFileName(ReadLink(toFileName->Name()), true);
++                    unlink(ActualToFileName);
++                    unlink(toFileName->Name());
++
++                    // Try to create a hard link
++                    if (HardLinkVideoFile(fromFileName->Name(), toFileName->Name())) {
++                       // Success. Skip all data transfer for this file
++                       SkipThisSourceFile = true;
++                       cutIn = false;
++                       toFile = NULL; // was deleted by toFileName->Close()
++                       } 
++                    else {
++                       // Fallback: Re-open the file if necessary
++                       toFile = toFileName->Open();
++                       }
++                    }
++                 }
++              }
++
++           if (!SkipThisSourceFile) {
++#endif /* HARDLINKCUTTER */
+               if (fromFile) {
+                  int len = ReadFrame(fromFile, buffer,  Length, sizeof(buffer));
+                  if (len < 0) {
+@@ -118,6 +239,7 @@
+                  break;
+                  }
+               }
++#ifndef USE_HARDLINKCUTTER
+            else {
+               // Error, unless we're past the last cut-in and there's no cut-out
+               if (Mark || LastMark)
+@@ -125,12 +247,17 @@
+               break;
+               }
++#endif /* HARDLINKCUTTER */
+            // Write one frame:
+            if (Independent) { // every file shall start with an independent frame
+               if (LastMark) // edited version shall end before next I-frame
+                  break;
++#ifndef USE_HARDLINKCUTTER
+               if (FileSize > maxVideoFileSize) {
++#else
++              if (!SkipThisSourceFile && FileSize > toFileName->MaxFileSize()) {
++#endif /* HARDLINKCUTTER */
+                  toFile = toFileName->NextFile();
+                  if (!toFile) {
+                     error = "toFile 1";
+@@ -140,7 +267,11 @@
+                  }
+               LastIFrame = 0;
++#ifndef USE_HARDLINKCUTTER
+               if (cutIn) {
++#else
++              if (!SkipThisSourceFile && cutIn) {
++#endif /* HARDLINKCUTTER */
+                  if (isPesRecording)
+                     cRemux::SetBrokenLink(buffer, Length);
+                  else
+@@ -148,7 +279,11 @@
+                  cutIn = false;
+                  }
+               }
++#ifndef USE_HARDLINKCUTTER
+            if (toFile->Write(buffer, Length) < 0) {
++#else
++           if (!SkipThisSourceFile && toFile->Write(buffer, Length) < 0) {
++#endif /* HARDLINKCUTTER */
+               error = "safe_write";
+               break;
+               }
+@@ -183,8 +318,44 @@
+                     }
+                  }
+               else
++#ifndef USE_HARDLINKCUTTER
+                  LastMark = true;
++#else
++                 LastMark = true; // After last cut-out: Write on until next I-frame, then exit
++#endif /* HARDLINKCUTTER */
++              }
++#ifdef USE_CUTTERLIMIT
++#ifdef USE_HARDLINKCUTTER
++           if (!Setup.HardLinkCutter) {
++#endif /* HARDLINKCUTTER */
++           bytes += Length;
++           if (bytes >= burst_size) {
++              int elapsed = t.Elapsed();
++              int sleep = 0;
++
++#if CUTTER_REL_BANDWIDTH > 0 && CUTTER_REL_BANDWIDTH < 100
++              // stay under max. relative bandwidth
++
++              sleep = (elapsed * 100 / CUTTER_REL_BANDWIDTH) - elapsed;
++              //if (sleep<=0 && elapsed<=2) sleep = 1; 
++              //if (sleep) esyslog("cutter: relative bandwidth limit, sleep %d ms (chunk %dk / %dms)", sleep, burst_size/1024, CUTTER_TIMESLICE);
++#endif
++              // stay under max. absolute bandwidth
++              if (elapsed < CUTTER_TIMESLICE) {
++                 sleep = max(CUTTER_TIMESLICE - elapsed, sleep);
++                 //if (sleep) esyslog("cutter: absolute bandwidth limit, sleep %d ms (chunk %dk / %dms)", sleep, burst_size/1024, CUTTER_TIMESLICE);
++                 }
++
++              if (sleep>0)
++                 cCondWait::SleepMs(sleep);
++                 t.Set();
++                 bytes = 0;
++              }
++#ifdef USE_HARDLINKCUTTER
+               }
++#endif /* HARDLINKCUTTER */
++#endif /* CUTTERLIMIT */
++
+            }
+      Recordings.TouchUpdate();
+      }
+@@ -194,18 +365,74 @@
+ // --- cCutter ---------------------------------------------------------------
++#ifdef USE_CUTTERQUEUE
++#define WAIT_BEFORE_NEXT_CUT   (10*1000)  // 10 seconds
++
++class cStringListObject : public cListObject {
++  public:
++    cStringListObject(const char *s) { str = strdup(s); }
++    ~cStringListObject() { free(str); }
++
++    const char *Value() { return str; }
++    operator const char * () { return str; }
++
++  private:
++    char *str;
++};
++#endif /* CUTTERQUEUE */
++
+ char *cCutter::editedVersionName = NULL;
+ cCuttingThread *cCutter::cuttingThread = NULL;
+ bool cCutter::error = false;
+ bool cCutter::ended = false;
++#ifdef USE_CUTTERQUEUE
++cMutex *cCutter::cutterLock = new cMutex();
++static uint64_t /*cCutter::*/lastCuttingEndTime = 0;
++static cList<cStringListObject> /**cCutter::*/cutterQueue /*= new cList<cStringListObject>*/;
++#endif /* CUTTERQUEUE */
+ bool cCutter::Start(const char *FileName)
+ {
++#ifdef USE_CUTTERQUEUE
++  cMutexLock(cutterLock);
++  if (FileName) {
++     /* Add file to queue.
++      * If cutter is still active, next cutting will be started 
++      * when vdr.c:main calls cCutter::Active and previous cutting has 
++      * been stopped > 10 s before 
++      */
++     cutterQueue.Add(new cStringListObject(FileName));
++     }
++  if (cuttingThread)
++     return true;
++  /* cut next file from queue */
++  if (!(cutterQueue.First()))
++     return false;
++  FileName = cutterQueue.First()->Value();
++#endif /* CUTTERQUEUE */
+   if (!cuttingThread) {
+      error = false;
+      ended = false;
+      cRecording Recording(FileName);
++#ifdef USE_CUTTIME
++     if (Setup.CutTime) {
++        cMarks FromMarks;
++        FromMarks.Load(FileName);
++        cMark *First = FromMarks.First();
++        if (First) Recording.SetStartTime(Recording.start + (int(First->position / Recording.FramesPerSecond() +30) / 60) * 60);
++        }
++#endif /* CUTTIME */
+      const char *evn = Recording.PrefixFileName('%');
++#ifdef USE_CUTTERQUEUE
++     if (!(Recordings.GetByName(FileName))) {
++        // Should _not_ remove any cutted recordings
++        // (original recording already deleted ?)
++        // so, just pop item from queue and return.
++        esyslog("can't cut non-existing recording %s", FileName);
++        cutterQueue.Del(cutterQueue.First());
++        return true; // might be already queued recording
++        }
++#endif /* CUTTERQUEUE */
+      if (evn && RemoveVideoFile(evn) && MakeDirs(evn, true)) {
+         // XXX this can be removed once RenameVideoFile() follows symlinks (see videodir.c)
+         // remove a possible deleted recording with the same name to avoid symlink mixups:
+@@ -231,6 +458,9 @@
+ void cCutter::Stop(void)
+ {
++#ifdef USE_CUTTERQUEUE
++  cMutexLock(cutterLock);
++#endif /* CUTTERQUEUE */
+   bool Interrupted = cuttingThread && cuttingThread->Active();
+   const char *Error = cuttingThread ? cuttingThread->Error() : NULL;
+   delete cuttingThread;
+@@ -242,11 +472,20 @@
+         esyslog("ERROR: '%s' during editing process", Error);
+      RemoveVideoFile(editedVersionName); //XXX what if this file is currently being replayed?
+      Recordings.DelByName(editedVersionName);
+-     }
++#ifdef USE_CUTTERQUEUE
++     cutterQueue.Del(cutterQueue.First());
++#endif /* CUTTERQUEUE */
++     }
++#ifdef USE_CUTTERQUEUE
++  lastCuttingEndTime = cTimeMs::Now();
++#endif /* CUTTERQUEUE */
+ }
+ bool cCutter::Active(void)
+ {
++#ifdef USE_CUTTERQUEUE
++  cMutexLock(cutterLock);
++#endif /* CUTTERQUEUE */
+   if (cuttingThread) {
+      if (cuttingThread->Active())
+         return true;
+@@ -257,12 +496,40 @@
+      free(editedVersionName);
+      editedVersionName = NULL;
+      ended = true;
++#ifdef USE_CUTTERQUEUE
++     if (Setup.CutterAutoDelete) {
++        /* Remove original (if cutting was successful) */
++        if (!error) {
++           cRecording *recording = Recordings.GetByName(*cutterQueue.First());
++           if (!recording)
++              esyslog("ERROR: Can't found '%s' after editing process", cutterQueue.First()->Value());
++           else {
++              if (recording->Delete())
++                 Recordings.DelByName(recording->FileName());
++              else
++                 esyslog("ERROR: Can't delete '%s' after editing process", cutterQueue.First()->Value());
++              }
++           }
++        lastCuttingEndTime = cTimeMs::Now();
++        }
++     cutterQueue.Del(cutterQueue.First());
++#endif /* CUTTERQUEUE */
++     }
++#ifdef USE_CUTTERQUEUE
++  if (!cuttingThread && cutterQueue.First()) {
++     /* start next cutting from queue*/
++     if (cTimeMs::Now() > lastCuttingEndTime + WAIT_BEFORE_NEXT_CUT)
++        Start(NULL);
+      }
++#endif /* CUTTERQUEUE */
+   return false;
+ }
+ bool cCutter::Error(void)
+ {
++#ifdef USE_CUTTERQUEUE
++  cMutexLock(cutterLock);
++#endif /* CUTTERQUEUE */
+   bool result = error;
+   error = false;
+   return result;
+@@ -270,6 +537,9 @@
+ bool cCutter::Ended(void)
+ {
++#ifdef USE_CUTTERQUEUE
++  cMutexLock(cutterLock);
++#endif /* CUTTERQUEUE */
+   bool result = ended;
+   ended = false;
+   return result;
+diff -NaurwB vdr-1.7.10/cutter.h vdr-1.7.10-patched/cutter.h
+--- vdr-1.7.10/cutter.h        2002-06-22 12:03:15.000000000 +0200
++++ vdr-1.7.10-patched/cutter.h        2009-12-18 06:25:25.000000000 +0100
+@@ -11,6 +11,9 @@
+ #define __CUTTER_H
+ class cCuttingThread;
++#ifdef USE_CUTTERQUEUE
++class cMutex;
++#endif /* CUTTERQUEUE */
+ class cCutter {
+ private:
+@@ -18,6 +21,9 @@
+   static cCuttingThread *cuttingThread;
+   static bool error;
+   static bool ended;
++#ifdef USE_CUTTERQUEUE
++  static cMutex *cutterLock;
++#endif /* CUTTERQUEUE */
+ public:
+   static bool Start(const char *FileName);
+   static void Stop(void);
+diff -NaurwB vdr-1.7.10/device.c vdr-1.7.10-patched/device.c
+--- vdr-1.7.10/device.c        2009-11-22 14:19:03.000000000 +0100
++++ vdr-1.7.10-patched/device.c        2009-12-18 06:25:25.000000000 +0100
+@@ -18,6 +18,17 @@
+ #include "receiver.h"
+ #include "status.h"
+ #include "transfer.h"
++#ifdef USE_TTXTSUBS
++#include "vdrttxtsubshooks.h"
++#endif /* TTXTSUBS */
++
++#ifdef USE_LNBSHARE
++#include "diseqc.h"
++#endif /* LNBSHARE */
++
++#ifdef USE_CHANNELSCAN
++bool scanning_on_receiving_device = false;
++#endif /* CHANNELSCAN */
+ // --- cLiveSubtitle ---------------------------------------------------------
+@@ -69,6 +80,12 @@
+   SetVideoFormat(Setup.VideoFormat);
++#ifdef USE_LNBSHARE
++  LNBstate = -1;
++  LNBnr = Setup.CardUsesLNBnr[cardIndex];
++  LNBsource = NULL;
++#endif /* LNBSHARE */
++
+   mute = false;
+   volume = Setup.CurrentVolume;
+@@ -94,8 +111,15 @@
+   for (int i = 0; i < MAXRECEIVERS; i++)
+       receiver[i] = NULL;
++#ifdef USE_SOURCECAPS
++  if (numDevices < MAXDEVICES) {
++     device[numDevices++] = this;
++     SetSourceCaps(cardIndex);
++     }
++#else
+   if (numDevices < MAXDEVICES)
+      device[numDevices++] = this;
++#endif /* SOURCECAPS */
+   else
+      esyslog("ERROR: too many devices!");
+ }
+@@ -130,6 +154,16 @@
+      useDevice |= (1 << n);
+ }
++#ifdef USE_LNBSHARE
++void cDevice::SetLNBnr(void)
++{
++  for (int i = 0; i < numDevices; i++) {
++      device[i]->LNBnr = Setup.CardUsesLNBnr[i];
++      isyslog("LNB-sharing: setting device %d to use LNB %d", i, device[i]->LNBnr);
++      }
++}
++#endif /* LNBSHARE */
++
+ int cDevice::NextCardIndex(int n)
+ {
+   if (n > 0) {
+@@ -190,6 +224,98 @@
+   return d;
+ }
++#ifdef USE_LNBSHARE
++cDevice *cDevice::GetBadDevice(const cChannel *Channel)
++{
++  if (!cSource::IsSat(Channel->Source()))
++     return NULL;
++  if (Setup.DiSEqC) {
++     cDiseqc *diseqc;
++     diseqc = Diseqcs.Get(Channel->Source(), Channel->Frequency(), Channel->Polarization());
++
++     for (int i = 0; i < numDevices; i++) {
++         if (this != device[i] && device[i]->GetLNBnr() == LNBnr && device[i]->GetLNBsource() != (int*) diseqc) {
++            if (Setup.VerboseLNBlog)
++               isyslog("LNB %d: Device check for channel %d on device %d. LNB or DiSEq conflict with device %d", LNBnr, Channel->Number(), this->DeviceNumber(), i);
++            return device[i];
++            }
++         }
++     if (Setup.VerboseLNBlog)
++        isyslog("LNB %d: Device check for for channel %d on device %d. OK", LNBnr, Channel->Number(), this->DeviceNumber());
++     }
++  else {
++     char requiredState;
++
++     if (Channel->Frequency() >= Setup.LnbSLOF)
++        requiredState = 1 ;
++     else
++        requiredState = 0;
++
++     if (Channel->Polarization() == 'v' || Channel->Polarization() == 'V')
++        requiredState += 2;
++
++     for (int i = 0; i < numDevices; i++) {
++         if (this != device[i] && device[i]->GetLNBnr() == LNBnr && device[i]->GetLNBconf() != requiredState) {
++            if (Setup.VerboseLNBlog)
++               isyslog("LNB %d: Device check for channel %d, LNBstate %d on device %d, current LNBstate %d. Conflict with device %d, LNBstate %d", LNBnr, Channel->Number(), requiredState, this->DeviceNumber(), LNBstate, i, device[i]->GetLNBconf());
++            return device[i];
++            }
++         }
++     if (Setup.VerboseLNBlog)
++        isyslog("LNB %d: Device check for channel %d, LNBstate %d on device %d, current LNBstate %d. No other devices affected", LNBnr, Channel->Number(), requiredState, this->DeviceNumber(), LNBstate);
++     }
++  return NULL;
++}
++
++int cDevice::GetMaxBadPriority(const cChannel *Channel)
++{
++  if (!cSource::IsSat(Channel->Source())) return -2;
++  bool PrimaryIsBad = false;
++  int maxBadPriority = -2;
++
++  if (Setup.DiSEqC) {
++     cDiseqc *diseqc;
++     diseqc = Diseqcs.Get(Channel->Source(), Channel->Frequency(), Channel->Polarization());
++
++     for (int i = 0; i < numDevices; i++) {
++         if (this != device[i] && device[i]->GetLNBnr() == LNBnr && device[i]->GetLNBsource() != (int*) diseqc) {
++            if (device[i]->Receiving() && device[i]->Priority() > maxBadPriority)
++               maxBadPriority = device[i]->Priority();
++            if (i == ActualDevice()->CardIndex())
++               PrimaryIsBad = true;
++            }
++         }
++     }
++  else {
++     char requiredState;
++     if (Channel->Frequency() >= Setup.LnbSLOF)
++        requiredState = 1 ;
++     else
++        requiredState = 0;
++
++     if (Channel->Polarization() == 'v' || Channel->Polarization() == 'V')
++        requiredState += 2;
++
++     for (int i = 0; i < numDevices; i++) {
++         if (this != device[i] && device[i]->GetLNBnr() == LNBnr && device[i]->GetLNBconf() != requiredState) {
++            if (device[i]->Receiving() && device[i]->Priority() > maxBadPriority)
++               maxBadPriority = device[i]->Priority();
++            if (i == ActualDevice()->CardIndex())
++               PrimaryIsBad = true;
++            }
++         }
++     }
++
++  if (PrimaryIsBad && maxBadPriority == -2)
++     maxBadPriority = -1;
++
++  if (Setup.VerboseLNBlog)
++     isyslog("LNB %d: Request for channel %d on device %d. MaxBadPriority is %d", LNBnr, Channel->Number(), this->DeviceNumber(), maxBadPriority);
++
++  return maxBadPriority;
++}
++#endif /* LNBSHARE */
++
+ cDevice *cDevice::GetDevice(int Index)
+ {
+   return (0 <= Index && Index < numDevices) ? device[Index] : NULL;
+@@ -239,6 +365,10 @@
+   cCamSlot *s = NULL;
+   uint32_t Impact = 0xFFFFFFFF; // we're looking for a device with the least impact
++#ifdef USE_LNBSHARE
++  int badPriority;
++  uint imp2;
++#endif /* LNBSHARE */
+   for (int j = 0; j < NumCamSlots || !NumUsableSlots; j++) {
+       if (NumUsableSlots && SlotPriority[j] > MAXPRIORITY)
+          continue; // there is no CAM available in this slot
+@@ -272,7 +402,31 @@
+              imp <<= 1; imp |= NumUsableSlots ? 0 : device[i]->HasCi();                                              // avoid cards with Common Interface for FTA channels
+              imp <<= 1; imp |= device[i]->HasDecoder();                                                              // avoid full featured cards
+              imp <<= 1; imp |= NumUsableSlots ? !ChannelCamRelations.CamDecrypt(Channel->GetChannelID(), j + 1) : 0; // prefer CAMs that are known to decrypt this channel
++#ifdef USE_LNBSHARE
++             badPriority = device[i]->GetMaxBadPriority(Channel);
++             if (badPriority >= Priority || (badPriority == -1 && Priority < Setup.PrimaryLimit)) {
++                // channel is not available for the requested prioity
++                imp = 0xFFFFFFFF;
++                }
++             else {
++                switch (badPriority) {
++                   case -2: // not affected by LNB-sharing
++                            imp2 = 0;
++                            break;
++                   case -1: // the primary device would need a channel switch
++                            imp += 1 <<  17;
++                            imp2 = 0xFFFFFFFF | 1 << 17;
++                            break;
++                   default: // a device receiving with lower priority would need to be stopped
++                            imp += badPriority << 8;
++                            imp2 = 0xFFFFFFFF | badPriority << 8;
++                            break;
++                   }
++                }
++             if (imp < Impact && imp2 < Impact) {
++#else
+              if (imp < Impact) {
++#endif /* LNBSHARE */
+                 // This device has less impact than any previous one, so we take it.
+                 Impact = imp;
+                 d = device[i];
+@@ -313,6 +467,18 @@
+   camSlot = CamSlot;
+ }
++#ifdef USE_SOURCECAPS
++void cDevice::SetSourceCaps(int Index)
++{
++  for (int d = 0; d < numDevices; d++) {
++      if (Index < 0 || Index == device[d]->CardIndex()) {
++         for (int i = 0; i < MAXSOURCECAPS; i++)
++             device[d]->sourceCaps[i] = Setup.SourceCaps[device[d]->CardIndex()][i];
++         }
++      }
++}
++#endif /* SOURCECAPS */
++
+ void cDevice::Shutdown(void)
+ {
+   primaryDevice = NULL;
+@@ -571,7 +737,11 @@
+ bool cDevice::ProvidesTransponderExclusively(const cChannel *Channel) const
+ {
+   for (int i = 0; i < numDevices; i++) {
++#ifdef USE_LNBSHARE
++      if (device[i] && device[i] != this && device[i]->ProvidesTransponder(Channel) && device[i]->GetLNBnr() != LNBnr)
++#else
+       if (device[i] && device[i] != this && device[i]->ProvidesTransponder(Channel))
++#endif /* LNBSHARE */
+          return false;
+       }
+   return true;
+@@ -599,6 +769,24 @@
+ bool cDevice::SwitchChannel(const cChannel *Channel, bool LiveView)
+ {
++#ifdef USE_LNBSHARE
++  cDevice *tmpDevice;
++  if (this->GetMaxBadPriority(Channel) >= 0) {
++     Skins.Message(mtInfo, tr("Channel locked by LNB!"));
++     return false;
++     }
++  while ((tmpDevice = GetBadDevice(Channel)) != NULL) {
++        if (tmpDevice->IsPrimaryDevice() && LiveView)
++           tmpDevice->SwitchChannelForced(Channel, true);
++        else
++           tmpDevice->SwitchChannelForced(Channel, false);
++        }
++  return SwitchChannelForced(Channel, LiveView);
++}
++
++bool cDevice::SwitchChannelForced(const cChannel *Channel, bool LiveView)
++{
++#endif /* LNBSHARE */
+   if (LiveView) {
+      isyslog("switching to channel %d", Channel->Number());
+      cControl::Shutdown(); // prevents old channel from being shown too long if GetDevice() takes longer
+@@ -628,7 +816,14 @@
+      cChannel *channel;
+      while ((channel = Channels.GetByNumber(n, Direction)) != NULL) {
+            // try only channels which are currently available
++#ifdef USE_PINPLUGIN
++           if (cStatus::MsgChannelProtected(0, channel) == false)
++#endif /* PINPLUGIN */
++#ifdef USE_LNBSHARE
++           if (PrimaryDevice()->GetMaxBadPriority(channel) < 0 && (GetDevice(channel, 0, true)))
++#else
+            if (GetDevice(channel, 0, true))
++#endif /* LNBSHARE */
+               break;
+            n = channel->Number() + Direction;
+            }
+@@ -649,6 +844,13 @@
+ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
+ {
++#ifdef USE_PINPLUGIN
++  // I hope 'LiveView = false' indicates a channel switch for recording,
++  // I really don't know, but it works ...
++  if (LiveView && cStatus::MsgChannelProtected(this, Channel) == true)
++     return scrNotAvailable;
++#endif /* PINPLUGIN */
++
+   if (LiveView) {
+      StopReplay();
+      DELETENULL(liveSubtitle);
+@@ -661,11 +863,37 @@
+   eSetChannelResult Result = scrOk;
++#ifdef USE_LNBSHARE
++  char requiredState;
++  if (Channel->Frequency() >= Setup.LnbSLOF)
++     requiredState = 1;
++  else
++     requiredState = 0;
++
++  if (Channel->Polarization() == 'v' || Channel->Polarization() == 'V')
++     requiredState += 2;
++
++  if (Setup.VerboseLNBlog)
++     isyslog("LNB %d: Switching device %d to channel %d", LNBnr, this->DeviceNumber(), Channel->Number());
++#endif /* LNBSHARE */
++
+   // If this DVB card can't receive this channel, let's see if we can
+   // use the card that actually can receive it and transfer data from there:
+   if (NeedsTransferMode) {
+      if (Device && CanReplay()) {
++#ifdef USE_LNBSHARE
++        if (Device->GetLNBnr() == LNBnr) {
++           if (LNBstate != requiredState || (Setup.DiSEqC && LNBsource != (int*) Diseqcs.Get(Channel->Source(), Channel->Frequency(), Channel->Polarization())) ) {
++              if (IsPrimaryDevice())
++                 SetChannelDevice(Channel, true);
++              else
++                 SetChannelDevice(Channel, false);
++              LNBstate = requiredState;
++              LNBsource = (int*) Diseqcs.Get(Channel->Source(), Channel->Frequency(), Channel->Polarization());
++              }
++           }
++#endif /* LNBSHARE */
+         cStatus::MsgChannelSwitch(this, 0); // only report status if we are actually going to switch the channel
+         if (Device->SetChannel(Channel, false) == scrOk) // calling SetChannel() directly, not SwitchChannel()!
+            cControl::Launch(new cTransferControl(Device, Channel->GetChannelID(), Channel->Vpid(), Channel->Apids(), Channel->Dpids(), Channel->Spids()));
+@@ -683,6 +911,10 @@
+         sectionHandler->SetStatus(false);
+         sectionHandler->SetChannel(NULL);
+         }
++#ifdef USE_LNBSHARE
++     LNBstate = requiredState;
++     LNBsource = (int*) Diseqcs.Get(Channel->Source(), Channel->Frequency(), Channel->Polarization());
++#endif /* LNBSHARE */
+      // Tell the camSlot about the channel switch and add all PIDs of this
+      // channel to it, for possible later decryption:
+      if (camSlot)
+@@ -975,7 +1207,12 @@
+      int LanguagePreference = INT_MAX; // higher than the maximum possible value
+      for (int i = ttSubtitleFirst; i <= ttSubtitleLast; i++) {
+          const tTrackId *TrackId = GetTrack(eTrackType(i));
++#ifdef USE_LIEMIEXT
++         if (TrackId && TrackId->id && (I18nIsPreferredLanguage(Setup.SubtitleLanguages, TrackId->language, LanguagePreference) ||
++             ((i == ttSubtitleFirst + 8) && !(*TrackId->language) && (LanguagePreference == INT_MAX))))
++#else
+          if (TrackId && TrackId->id && I18nIsPreferredLanguage(Setup.SubtitleLanguages, TrackId->language, LanguagePreference))
++#endif /* LIEMIEXT */
+             PreferredTrack = eTrackType(i);
+          }
+      // Make sure we're set to an available subtitle track:
+@@ -1182,6 +1419,15 @@
+                   }
+                break;
+           case 0xBD: { // private stream 1
++#ifdef USE_TTXTSUBS
++               // EBU Teletext data, ETSI EN 300 472
++               // if PES data header length = 24 and data_identifier = 0x10..0x1F (EBU Data)
++               if (Data[8] == 0x24 && Data[45] >= 0x10 && Data[45] < 0x20) {
++                  cVDRTtxtsubsHookListener::Hook()->PlayerTeletextData((uint8_t*)Data, Length);
++                  break;
++                  }
++#endif /* TTXTSUBS */
++
+                int PayloadOffset = Data[8] + 9;
+                // Compatibility mode for old subtitles plugin:
+@@ -1209,7 +1455,11 @@
+                          w = PlaySubtitle(Start, d);
+                       break;
+                  case 0x80: // AC3 & DTS
++#ifdef USE_DOLBYINREC
++                      if (Setup.UseDolbyInRecordings) {
++#else
+                       if (Setup.UseDolbyDigital) {
++#endif /* DOLBYINREC */
+                          SetAvailableTrack(ttDolby, SubStreamIndex, SubStreamId);
+                          if ((!VideoOnly || HasIBPTrickSpeed()) && SubStreamId == availableTracks[currentAudioTrack].id) {
+                             w = PlayAudio(Start, d, SubStreamId);
+@@ -1341,6 +1591,9 @@
+      tsToPesVideo.Reset();
+      tsToPesAudio.Reset();
+      tsToPesSubtitle.Reset();
++#ifdef USE_TTXTSUBS
++     tsToPesTeletext.Reset();
++#endif /* TTXTSUBS */
+      }
+   else if (Length < TS_SIZE) {
+      esyslog("ERROR: skipped %d bytes of TS fragment", Length);
+@@ -1386,6 +1639,19 @@
+                     if (!VideoOnly || HasIBPTrickSpeed())
+                        PlayTsSubtitle(Data, TS_SIZE);
+                     }
++#ifdef USE_TTXTSUBS
++                 else if (Pid == patPmtParser.Tpid()) {
++                    if (!VideoOnly || HasIBPTrickSpeed()) {
++                       int l;
++                       tsToPesTeletext.PutTs(Data, Length);
++                       if (const uchar *p = tsToPesTeletext.GetPes(l)) {
++                          if ((l > 45) && (p[0] == 0x00) && (p[1] == 0x00) && (p[2] == 0x01) && (p[3] == 0xbd) && (p[8] == 0x24) && (p[45] >= 0x10) && (p[45] < 0x20))
++                             cVDRTtxtsubsHookListener::Hook()->PlayerTeletextData((uchar *)p, l, false);
++                          tsToPesTeletext.Reset();
++                          }
++                       }
++                    }
++#endif /* TTXTSUBS */
+                  }
+               }
+            Played += TS_SIZE;
+diff -NaurwB vdr-1.7.10/device.h vdr-1.7.10-patched/device.h
+--- vdr-1.7.10/device.h        2009-11-22 14:21:00.000000000 +0100
++++ vdr-1.7.10-patched/device.h        2009-12-18 06:25:25.000000000 +0100
+@@ -24,13 +24,22 @@
+ #include "spu.h"
+ #include "thread.h"
+ #include "tools.h"
++#ifdef USE_ROTOR
++#include <linux/dvb/frontend.h>
++#endif /* ROTOR */
++#ifndef USE_SOURCECAPS
+ #define MAXDEVICES         16 // the maximum number of devices in the system
++#endif /* SOURCECAPS */
+ #define MAXPIDHANDLES      64 // the maximum number of different PIDs per device
+ #define MAXRECEIVERS       16 // the maximum number of receivers per device
+ #define MAXVOLUME         255
+ #define VOLUMEDELTA         5 // used to increase/decrease the volume
++#ifdef USE_CHANNELSCAN
++extern bool scanning_on_receiving_device;
++#endif /* CHANNELSCAN */
++
+ enum eSetChannelResult { scrOk, scrNotAvailable, scrNoTransfer, scrFailed };
+ enum ePlayMode { pmNone,           // audio/video from decoder
+@@ -142,6 +151,35 @@
+          ///< this device/CAM combination will be skipped in the next call to
+          ///< GetDevice().
+          ///< See also ProvidesChannel().
++#ifdef USE_SOURCECAPS
++  static void SetSourceCaps(int Index = -1);
++         ///< Sets the SourceCaps of the given device according to the Setup data.
++#endif /* SOURCECAPS */
++#ifdef USE_LNBSHARE
++private:
++  char LNBstate;  // Current frequency band and polarization of the DVB-tuner
++//  cDiseqc *LNBsource;  // can not #include "diseqc.h". A workaround follows:
++  int *LNBsource;  // [DiSEqC] DiSEqC-Source
++  int LNBnr;      // Number of LNB used
++public:
++  char GetLNBconf(void) { return LNBstate; }
++  int *GetLNBsource(void) { return LNBsource; }
++  int GetLNBnr(void) { return LNBnr; }
++  static void SetLNBnr(void);
++  cDevice *GetBadDevice(const cChannel *Channel);
++         ///< Returns NULL if there is no device which uses the same LNB or if
++         ///< all of those devices are tuned to the same frequency band and 
++         ///< polarization as of the requested channel.
++         ///< Otherwise returns the first device found.
++  int GetMaxBadPriority(const cChannel *Channel);
++         ///< Returns the highest priority of all receiving devices which use
++         ///< the same LNB and are tuned to a different frequency band or 
++         ///< polarization as of the requested channel.
++         ///< Returns -1 if there are no such devices, but the primary device 
++         ///< would be affected by switching to the requested channel.
++         ///< Returns -2 if there are no such devices and the primary device 
++         ///< would not be affected by switching to the requested channel.
++#endif /* LNBSHARE */
+   static void SetAvoidDevice(cDevice *Device) { avoidDevice = Device; }
+          ///< Sets the given Device to be temporarily avoided in the next call to
+          ///< GetDevice(const cChannel, int, bool).
+@@ -152,6 +190,9 @@
+   static int nextCardIndex;
+   int cardIndex;
+ protected:
++#ifdef USE_SOURCECAPS
++  int sourceCaps[MAXSOURCECAPS];
++#endif /* SOURCECAPS */
+   cDevice(void);
+   virtual ~cDevice();
+   virtual bool Ready(void);
+@@ -239,17 +280,30 @@
+   bool SwitchChannel(const cChannel *Channel, bool LiveView);
+          ///< Switches the device to the given Channel, initiating transfer mode
+          ///< if necessary.
++
++#ifdef USE_LNBSHARE
++  bool SwitchChannelForced(const cChannel *Channel, bool LiveView);
++         ///< Switches the device to the given channel, initiating transfer mode
++         ///< if necessary. Forces the switch without taking care of the LNB configuration.
++#endif /* LNBSHARE */
++
+   static bool SwitchChannel(int Direction);
+          ///< Switches the primary device to the next available channel in the given
+          ///< Direction (only the sign of Direction is evaluated, positive values
+          ///< switch to higher channel numbers).
+ private:
++#ifndef USE_YAEPG
+   eSetChannelResult SetChannel(const cChannel *Channel, bool LiveView);
+          ///< Sets the device to the given channel (general setup).
++#endif /* YAEPG */
+ protected:
+   virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView);
+          ///< Sets the device to the given channel (actual physical setup).
+ public:
++#ifdef USE_YAEPG
++  eSetChannelResult SetChannel(const cChannel *Channel, bool LiveView);
++         ///< Sets the device to the given channel (general setup).
++#endif /* YAEPG */
+   static int CurrentChannel(void) { return primaryDevice ? currentChannel : 0; }
+          ///< Returns the number of the current channel on the primary device.
+   static void SetCurrentChannel(const cChannel *Channel) { currentChannel = Channel ? Channel->Number() : 0; }
+@@ -267,6 +321,9 @@
+   virtual bool HasProgramme(void);
+          ///< Returns true if the device is currently showing any programme to
+          ///< the user, either through replaying or live.
++#ifdef USE_ROTOR
++  virtual bool SendDiseqcCmd(dvb_diseqc_master_cmd cmd) { return false; }
++#endif /* ROTOR */
+ // PID handle facilities
+@@ -496,6 +553,9 @@
+   cTsToPes tsToPesVideo;
+   cTsToPes tsToPesAudio;
+   cTsToPes tsToPesSubtitle;
++#ifdef USE_TTXTSUBS
++  cTsToPes tsToPesTeletext;
++#endif /* TTXTSUBS */
+   bool isPlayingVideo;
+ protected:
+   virtual bool CanReplay(void) const;
+diff -NaurwB vdr-1.7.10/dvbdevice.c vdr-1.7.10-patched/dvbdevice.c
+--- vdr-1.7.10/dvbdevice.c     2009-06-06 13:17:20.000000000 +0200
++++ vdr-1.7.10-patched/dvbdevice.c     2009-12-18 06:25:25.000000000 +0100
+@@ -71,6 +71,9 @@
+ class cDvbTuner : public cThread {
+ private:
+   enum eTunerStatus { tsIdle, tsSet, tsTuned, tsLocked };
++#ifdef USE_ROTOR
++  bool SendDiseqc;
++#endif /* ROTOR */
+   int fd_frontend;
+   int cardIndex;
+   int tuneTimeout;
+@@ -83,6 +86,9 @@
+   cMutex mutex;
+   cCondVar locked;
+   cCondVar newSet;
++#ifdef USE_ROTOR
++  dvb_diseqc_master_cmd diseqc_cmd;
++#endif /* ROTOR */
+   bool GetFrontendStatus(fe_status_t &Status, int TimeoutMs = 0);
+   bool SetFrontend(void);
+   virtual void Action(void);
+@@ -91,12 +97,18 @@
+   virtual ~cDvbTuner();
+   bool IsTunedTo(const cChannel *Channel) const;
+   void Set(const cChannel *Channel, bool Tune);
++#ifdef USE_ROTOR
++  bool SendDiseqcCmd(dvb_diseqc_master_cmd cmd);
++#endif /* ROTOR */
+   bool Locked(int TimeoutMs = 0);
+   };
+ cDvbTuner::cDvbTuner(int Fd_Frontend, int CardIndex, fe_delivery_system FrontendType)
+ {
+   fd_frontend = Fd_Frontend;
++#ifdef USE_ROTOR
++  SendDiseqc = false;
++#endif /* ROTOR */
+   cardIndex = CardIndex;
+   frontendType = FrontendType;
+   tuneTimeout = 0;
+@@ -163,6 +175,19 @@
+   return tunerStatus >= tsLocked;
+ }
++#ifdef USE_ROTOR
++bool cDvbTuner::SendDiseqcCmd(dvb_diseqc_master_cmd cmd)
++{
++  cMutexLock MutexLock(&mutex);
++  if ((!SYS_DVBS & !SYS_DVBS2) || SendDiseqc)
++     return false;
++  diseqc_cmd = cmd;
++  SendDiseqc = true;
++  newSet.Broadcast();
++  return true;
++}
++#endif /* ROTOR */
++
+ bool cDvbTuner::GetFrontendStatus(fe_status_t &Status, int TimeoutMs)
+ {
+   if (TimeoutMs) {
+@@ -241,6 +266,9 @@
+                     }
+                   }
+               diseqcCommands = diseqc->Commands();
++#ifdef USE_DVBSETUP
++              isyslog("Sent DISEQC command: %s", diseqcCommands);
++#endif /* DVBSETUP */
+               }
+            frequency -= diseqc->Lof();
+            }
+@@ -303,6 +331,18 @@
+      tuneTimeout = DVBC_TUNE_TIMEOUT;
+      lockTimeout = DVBC_LOCK_TIMEOUT;
+      }
++#ifdef USE_ATSC
++  else if (frontendType == SYS_ATSC) {
++     // ATSC
++     SETCMD(DTV_DELIVERY_SYSTEM, frontendType);
++     SETCMD(DTV_FREQUENCY, FrequencyToHz(channel.Frequency()));
++     SETCMD(DTV_INVERSION, channel.Inversion());
++     SETCMD(DTV_MODULATION, channel.Modulation());
++
++     tuneTimeout = DVBT_TUNE_TIMEOUT;
++     lockTimeout = DVBT_LOCK_TIMEOUT;
++     }
++#endif /* ATSC */
+   else if (frontendType == SYS_DVBT) {
+      // DVB-T
+      SETCMD(DTV_DELIVERY_SYSTEM, frontendType);
+@@ -341,6 +381,12 @@
+         if (GetFrontendStatus(NewStatus, 10))
+            Status = NewStatus;
+         cMutexLock MutexLock(&mutex);
++#ifdef USE_ROTOR
++        if (SendDiseqc) {
++           CHECK(ioctl(fd_frontend, FE_DISEQC_SEND_MASTER_CMD, &diseqc_cmd));
++           SendDiseqc = false;
++           }
++#endif /* ROTOR */
+         switch (tunerStatus) {
+           case tsIdle:
+                break;
+@@ -483,6 +529,11 @@
+   if (fd_frontend >= 0) {
+      if (ioctl(fd_frontend, FE_GET_INFO, &frontendInfo) >= 0) {
++#ifdef USE_DVBSETUP
++        if (Setup.ChannelBlockerMode == 4)
++           frontendType = (n == Setup.PrimaryDVB - 1) ? SYS_UNDEFINED : frontendType;
++        else
++#endif /* DVBSETUP */
+         switch (frontendInfo.type) {
+           case FE_QPSK: frontendType = (frontendInfo.caps & FE_CAN_2G_MODULATION) ? SYS_DVBS2 : SYS_DVBS; break;
+           case FE_OFDM: frontendType = SYS_DVBT; break;
+@@ -796,6 +847,13 @@
+ bool cDvbDevice::SetAudioBypass(bool On)
+ {
++#ifdef USE_DVBSETUP
++ if (Setup.DolbyTransferFix && On) {
++    cChannel *c=Channels.GetByNumber(cDevice::CurrentChannel());
++    if (c->Ca(0) != 0)
++       return false;
++    }
++#endif /* DVBSETUP */
+   if (setTransferModeForDolbyDigital != 1)
+      return false;
+   return ioctl(fd_audio, AUDIO_SET_BYPASS_MODE, On) == 0;
+@@ -901,14 +959,43 @@
+ bool cDvbDevice::ProvidesSource(int Source) const
+ {
+   int type = Source & cSource::st_Mask;
++#ifdef USE_SOURCECAPS
++  if (Setup.SourceCapsSet && type == cSource::stSat && (frontendType == SYS_DVBS || frontendType == SYS_DVBS2)) {
++     for (int i = 0; i < MAXSOURCECAPS; i++)
++         if (sourceCaps[i] == Source)
++            return true;
++     return false;
++     }
++  else
++#endif /* SOURCECAPS */
+   return type == cSource::stNone
+       || type == cSource::stCable && (frontendType == SYS_DVBC_ANNEX_AC || frontendType == SYS_DVBC_ANNEX_B)
+       || type == cSource::stSat   && (frontendType == SYS_DVBS || frontendType == SYS_DVBS2)
++#ifdef USE_ATSC
++      || type == cSource::stTerr  && (frontendType == SYS_DVBT || frontendType == SYS_ATSC);
++#else
+       || type == cSource::stTerr  && (frontendType == SYS_DVBT);
++#endif /* ATSC */
+ }
+ bool cDvbDevice::ProvidesTransponder(const cChannel *Channel) const
+ {
++#ifdef USE_DVBSETUP
++  if (Setup.ChannelBlocker != 0) {
++     if ((Setup.ChannelBlockerMode == 0) ||
++         (Setup.ChannelBlockerMode == 1 && HasDecoder()) ||
++         (Setup.ChannelBlockerMode == 2 && IsPrimaryDevice()) ||
++         (Setup.ChannelBlockerMode == 3 && IsPrimaryDevice() && HasDecoder())) {
++        if ((Setup.ChannelBlocker == 1 && cSource::IsCable(Channel->Source()) && Channel->Modulation() == QAM_256) ||
++            (Setup.ChannelBlocker == 2 && cSource::IsCable(Channel->Source())) ||
++            (Setup.ChannelBlocker == 3 && cSource::IsSat(Channel->Source())) ||
++            (Setup.ChannelBlocker == 4 && strstr(::Setup.ChannelBlockerList, Channel->GetChannelID().ToString()) != NULL) || // blacklist
++            (Setup.ChannelBlocker == 5 && strstr(::Setup.ChannelBlockerList, Channel->GetChannelID().ToString()) == NULL) || // whitelist
++            (Setup.ChannelBlocker == 6))
++           return false;
++        }
++     }
++#endif /* DVBSETUP */
+   if (!ProvidesSource(Channel->Source()))
+      return false; // doesn't provide source
+   if (!cSource::IsSat(Channel->Source()))
+@@ -920,10 +1007,31 @@
+ bool cDvbDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers) const
+ {
++#ifdef USE_DVBSETUP
++  if (Setup.ChannelBlocker != 0) {
++     if ((Setup.ChannelBlockerMode == 0) ||
++         (Setup.ChannelBlockerMode == 1 && HasDecoder()) ||
++         (Setup.ChannelBlockerMode == 2 && IsPrimaryDevice()) ||
++         (Setup.ChannelBlockerMode == 3 && IsPrimaryDevice() && HasDecoder())) {
++        if ((Setup.ChannelBlocker == 1 && cSource::IsCable(Channel->Source()) && Channel->Modulation() == QAM_256) ||
++            (Setup.ChannelBlocker == 2 && cSource::IsCable(Channel->Source())) ||
++            (Setup.ChannelBlocker == 3 && cSource::IsSat(Channel->Source())) ||
++            (Setup.ChannelBlocker == 4 && strstr(::Setup.ChannelBlockerList, Channel->GetChannelID().ToString()) != NULL) || // blacklist
++            (Setup.ChannelBlocker == 5 && strstr(::Setup.ChannelBlockerList, Channel->GetChannelID().ToString()) == NULL) || // whitelist
++            (Setup.ChannelBlocker == 6))
++           return false;
++        }
++     }
++#endif /* DVBSETUP */
+   bool result = false;
+   bool hasPriority = Priority < 0 || Priority > this->Priority();
+   bool needsDetachReceivers = false;
++#ifdef USE_ANALOGTV
++  if ((Channel->Ca(0) == 0xA0) || (Channel->Ca(0) == 0xA1) || (Channel->Ca(0) == 0xA2))
++     return false;
++#endif /* ANALOGTV */
++
+   if (ProvidesTransponder(Channel)) {
+      result = hasPriority;
+      if (Priority >= 0 && Receiving(true)) {
+@@ -1040,6 +1148,13 @@
+   return dvbTuner ? dvbTuner->Locked(TimeoutMs) : false;
+ }
++#ifdef USE_ROTOR
++bool cDvbDevice::SendDiseqcCmd(dvb_diseqc_master_cmd cmd)
++{
++  return dvbTuner->SendDiseqcCmd(cmd);
++}
++#endif /* ROTOR */
++
+ int cDvbDevice::GetAudioChannelDevice(void)
+ {
+   if (HasDecoder()) {
+diff -NaurwB vdr-1.7.10/dvbdevice.h vdr-1.7.10-patched/dvbdevice.h
+--- vdr-1.7.10/dvbdevice.h     2009-10-25 14:58:20.000000000 +0100
++++ vdr-1.7.10-patched/dvbdevice.h     2009-12-18 06:25:25.000000000 +0100
+@@ -75,6 +75,9 @@
+   virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView);
+ public:
+   virtual bool HasLock(int TimeoutMs = 0);
++#ifdef USE_ROTOR
++  virtual bool SendDiseqcCmd(dvb_diseqc_master_cmd cmd);
++#endif /* ROTOR */
+ // PID handle facilities
+diff -NaurwB vdr-1.7.10/dvbosd.c vdr-1.7.10-patched/dvbosd.c
+--- vdr-1.7.10/dvbosd.c        2007-09-16 10:55:54.000000000 +0200
++++ vdr-1.7.10-patched/dvbosd.c        2009-12-18 06:25:25.000000000 +0100
+@@ -20,12 +20,26 @@
+ #define MAXNUMWINDOWS 7 // OSD windows are counted 1...7
+ #define MAXOSDMEMORY  92000 // number of bytes available to the OSD (for unmodified DVB cards)
++#ifdef USE_SOFTOSD
++  #define SOFTOSD_MAXSIZE  (720*576)
++  #define SOFTOSD_MINSIZE  (720*64)
++#endif
++
+ class cDvbOsd : public cOsd {
+ private:
+   int osdDev;
+   int osdMem;
+   bool shown;
+   void Cmd(OSD_Command cmd, int color = 0, int x0 = 0, int y0 = 0, int x1 = 0, int y1 = 0, const void *data = NULL);
++#ifdef USE_SOFTOSD
++  int GetOsdSize() {
++      int OsdSize = 0;
++      cBitmap *Bitmap;
++      for (int i = 0; (Bitmap = GetBitmap(i)) != NULL; i++)
++          OsdSize += Bitmap->Width() * Bitmap->Height();
++      return OsdSize;
++      }
++#endif
+ protected:
+   virtual void SetActive(bool On);
+ public:
+@@ -76,6 +90,44 @@
+         }
+      else if (shown) {
+         cBitmap *Bitmap;
++#ifdef USE_SOFTOSD
++        if (Setup.UseSoftOsd) {
++           if (SOFTOSD_MAXSIZE > GetOsdSize() && SOFTOSD_MINSIZE < GetOsdSize()) {
++              for (int fade = Setup.SoftOsdFadeoutSteps - 1; fade > 0; fade--) {
++                  int64_t flush_start = cTimeMs::Now();
++                  for (int i = 0; (Bitmap = GetBitmap(i)) != NULL; i++) {
++                      Cmd(OSD_SetWindow, 0, i + 1);
++                      int NumColors;
++                      const tColor *Colors = Bitmap->Colors(NumColors);
++                      if (Colors) {
++                          tColor colors[NumColors];
++                          for (int i = 0; i < NumColors; i++) {
++                              // convert AARRGGBB to AABBGGRR (the driver expects the colors the wrong way):
++                              int alpha = (Colors[i] >> 24) & 0x000000FF;
++                              alpha = (alpha * fade) / Setup.SoftOsdFadeoutSteps;
++                              colors[i] = (alpha << 24) | ((Colors[i] & 0x0000FF) << 16) | (Colors[i] & 0x00FF00) | ((Colors[i] & 0xFF0000) >> 16);
++                              }
++                          Colors = colors;
++                          Cmd(OSD_SetPalette, 0, NumColors - 1, 0, 0, 0, Colors);
++                          if (!Setup.SoftOsdPaletteOnly) {
++                             //Cmd(OSD_SetBlock, Bitmap->Width(), 0, 0, Bitmap->Width() - 1, Bitmap->Height() - 1, Bitmap->Data(0, 0));
++                             Cmd(OSD_SetBlock, Bitmap->Width(), 0, 0, 0, 0, Bitmap->Data(0, 0));
++                             }
++                          }
++                      }
++                  int flush_time = cTimeMs::Now() - flush_start;
++                  dsyslog("SOFTOSD: FadeOut Step %d from %d FlushTime = %d ms",
++                          Setup.SoftOsdFadeoutSteps - fade, Setup.SoftOsdFadeoutSteps - 1, flush_time);
++                  int wait_time = 1000 / Setup.SoftOsdRate;
++                  while (flush_time > wait_time && fade > 2) {
++                        fade--;
++                        flush_time -= wait_time;
++                        }
++                  cCondWait::SleepMs(wait_time-flush_time);
++                  }
++              }
++           }
++#endif /* SOFTOSD */
+         for (int i = 0; (Bitmap = GetBitmap(i)) != NULL; i++) {
+             Cmd(OSD_SetWindow, 0, i + 1);
+             Cmd(OSD_Close);
+@@ -83,6 +135,12 @@
+         shown = false;
+         }
+      }
++#ifdef USE_YAEPG
++ if (vidWin.bpp != 0) {
++    Cmd(OSD_SetWindow, 0, 5);
++    Cmd(OSD_Close);
++    }
++#endif /* YAEPG */
+ }
+ eOsdError cDvbOsd::CanHandleAreas(const tArea *Areas, int NumAreas)
+@@ -182,6 +240,12 @@
+             for (int i = 0; i < NumColors; i++) {
+                 // convert AARRGGBB to AABBGGRR (the driver expects the colors the wrong way):
+                 colors[i] = (Colors[i] & 0xFF000000) | ((Colors[i] & 0x0000FF) << 16) | (Colors[i] & 0x00FF00) | ((Colors[i] & 0xFF0000) >> 16);
++#ifdef USE_SOFTOSD
++                if (Setup.UseSoftOsd && !shown) {
++                   if (SOFTOSD_MAXSIZE > GetOsdSize() && SOFTOSD_MINSIZE < GetOsdSize())
++                      colors[i] = 0;
++                   }
++#endif /* SOFTOSD */
+                 }
+             Colors = colors;
+             //TODO end of stuff that should be fixed in the driver
+@@ -198,6 +262,51 @@
+          Cmd(OSD_SetWindow, 0, i + 1);
+          Cmd(OSD_MoveWindow, 0, Left() + Bitmap->X0(), Top() + Bitmap->Y0());
+          }
++#ifdef USE_YAEPG
++     if (vidWin.bpp != 0) {
++         Cmd(OSD_SetWindow, 0, 5);
++         Cmd(OSD_OpenRaw, vidWin.bpp, vidWin.x1, vidWin.y1, vidWin.x2, vidWin.y2, (void *)0);
++         }
++#endif /* YAEPG */
++#ifdef USE_SOFTOSD
++     if (Setup.UseSoftOsd) {
++        if (SOFTOSD_MAXSIZE > GetOsdSize() && SOFTOSD_MINSIZE < GetOsdSize()) {
++           for (int fade = 1; fade <= Setup.SoftOsdFadeinSteps; fade++) {
++               int64_t flush_start = cTimeMs::Now();
++               for (int i = 0; (Bitmap = GetBitmap(i)) != NULL; i++) {
++                   Cmd(OSD_SetWindow, 0, i + 1);
++                   int NumColors;
++                   const tColor *Colors = Bitmap->Colors(NumColors);
++                   if (Colors) {
++                      tColor colors[NumColors];
++                      for (int i = 0; i < NumColors; i++) {
++                          // convert AARRGGBB to AABBGGRR:
++                          int alpha = (Colors[i]>>24) & 0x000000FF;
++                          alpha = (alpha * fade) / Setup.SoftOsdFadeinSteps;
++                          colors[i] = (alpha << 24) | ((Colors[i] & 0x0000FF) << 16) | (Colors[i] & 0x00FF00) | ((Colors[i] & 0xFF0000) >> 16);
++                          }
++                      Colors = colors;
++                      Cmd(OSD_SetPalette, 0, NumColors - 1, 0, 0, 0, Colors);
++                      if (!Setup.SoftOsdPaletteOnly) {
++                         //Cmd(OSD_SetBlock, Bitmap->Width(), 0, 0, Bitmap->Width() - 1, Bitmap->Height() - 1, Bitmap->Data(0, 0));
++                         Cmd(OSD_SetBlock, Bitmap->Width(), 0, 0, 0, 0, Bitmap->Data(0, 0));
++                         }
++                      }
++                   }
++               int flush_time = cTimeMs::Now() - flush_start;
++               dsyslog("SOFTOSD: FadeIn Step %d from %d FlushTime = %d ms", fade, Setup.SoftOsdFadeinSteps, flush_time);
++               int wait_time = 1000 / Setup.SoftOsdRate;
++               while (flush_time > wait_time && fade < Setup.SoftOsdFadeinSteps - 1) {
++                     fade++;
++                     flush_time -= wait_time;
++                     }
++               if (fade == Setup.SoftOsdFadeinSteps) /* last step, don't wait */
++                  break;
++               cCondWait::SleepMs(wait_time - flush_time);
++               }
++           }
++        }
++#endif /* SOFTOSD */
+      shown = true;
+      }
+ }
+diff -NaurwB vdr-1.7.10/dvbplayer.c vdr-1.7.10-patched/dvbplayer.c
+--- vdr-1.7.10/dvbplayer.c     2009-05-31 16:12:42.000000000 +0200
++++ vdr-1.7.10-patched/dvbplayer.c     2009-12-18 06:25:25.000000000 +0100
+@@ -204,6 +204,9 @@
+   cNonBlockingFileReader *nonBlockingFileReader;
+   cRingBufferFrame *ringBuffer;
+   cPtsIndex ptsIndex;
++#ifdef USE_JUMPPLAY
++  cMarksReload marks;
++#endif /* JUMPPLAY */
+   cFileName *fileName;
+   cIndexFile *index;
+   cUnbufferedFile *replayFile;
+@@ -249,7 +252,11 @@
+ int cDvbPlayer::Speeds[] = { 0, -2, -4, -8, 1, 2, 4, 12, 0 };
+ cDvbPlayer::cDvbPlayer(const char *FileName)
++#ifdef USE_JUMPPLAY
++:cThread("dvbplayer"), marks(FileName)
++#else
+ :cThread("dvbplayer")
++#endif /* JUMPPLAY */
+ {
+   nonBlockingFileReader = NULL;
+   ringBuffer = NULL;
+@@ -357,6 +364,11 @@
+   if (index) {
+      int Index = ptsIndex.FindIndex(DeviceGetSTC());
+      if (Index >= 0) {
++#ifdef USE_JUMPPLAY
++        // set resume position to 0 if replay stops at the first mark
++        if (Setup.PlayJump && marks.First() && abs(Index - marks.First()->position) <= int(round(RESUMEBACKUP * framesPerSecond)))
++           Index = 0;
++#endif /* JUMPPLAY */
+         Index -= int(round(RESUMEBACKUP * framesPerSecond));
+         if (Index > 0)
+            Index = index->GetNextIFrame(Index, false);
+@@ -383,11 +395,29 @@
+ {
+   uchar *p = NULL;
+   int pc = 0;
++#ifdef USE_JUMPPLAY
++  bool cutIn = false;
++  int total = -1;
++#endif /* JUMPPLAY */
+   readIndex = Resume();
+   if (readIndex >= 0)
+      isyslog("resuming replay at index %d (%s)", readIndex, *IndexToHMSF(readIndex, true, framesPerSecond));
++#ifdef USE_JUMPPLAY
++  if (Setup.PlayJump && readIndex <= 0 && marks.First() && index) {
++     int Index = marks.First()->position;
++     uint16_t FileNumber;
++     off_t FileOffset;
++     if (index->Get(Index, &FileNumber, &FileOffset) && NextFile(FileNumber, FileOffset)) {
++        isyslog("PlayJump: start replay at first mark %d (%s)", Index, *IndexToHMSF(Index, true, framesPerSecond));
++        readIndex = Index;
++        }
++     }
++
++  bool LastMarkPause = false;
++#endif /* JUMPPLAY */
++
+   nonBlockingFileReader = new cNonBlockingFileReader;
+   int Length = 0;
+   bool Sleep = false;
+@@ -410,7 +440,11 @@
+           // Read the next frame from the file:
++#ifdef USE_JUMPPLAY
++          if (playMode != pmStill && playMode != pmPause && !LastMarkPause) {
++#else
+           if (playMode != pmStill && playMode != pmPause) {
++#endif /* JUMPPLAY */
+              if (!readFrame && (replayFile || readIndex >= 0)) {
+                 if (!nonBlockingFileReader->Reading()) {
+                    if (!SwitchToPlayFrame && (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward))) {
+@@ -451,6 +485,42 @@
+                          readIndex++;
+                       else
+                          eof = true;
++#ifdef USE_JUMPPLAY
++                      if (Setup.PlayJump || Setup.PauseLastMark) {
++                         // check for end mark - jump to next mark or pause
++                         readIndex++;
++                         marks.Reload();
++                         cMark *m = marks.Get(readIndex);
++                         if (m && (m->Index() & 0x01) != 0) {
++                            m = marks.Next(m);
++                            int Index;
++                            if (m)
++                               Index = m->position;
++                            else if (Setup.PauseLastMark) {
++                               // pause at last mark
++                               isyslog("PauseLastMark: pause at position %d (%s)", readIndex, *IndexToHMSF(readIndex, true, framesPerSecond));
++                               LastMarkPause = true;
++                               Index = -1;
++                               }
++                            else if (total == index->Last())
++                               // at last mark jump to end of recording
++                               Index = index->Last() - 1;
++                            else
++                               // jump but stay off end of live-recordings
++                               Index = index->GetNextIFrame(index->Last() - int(round(MAXSTUCKATEOF * framesPerSecond)), true);
++                            // don't jump in edited recordings
++                            if (Setup.PlayJump && Index > readIndex && Index > index->GetNextIFrame(readIndex, true)) {
++                               isyslog("PlayJump: %d frames to %d (%s)", Index - readIndex, Index, *IndexToHMSF(Index, true, framesPerSecond));
++                               readIndex = Index;
++                               cutIn = true;
++                               }
++                            }
++                         readIndex--;
++                         }
++                      // for detecting growing length of live-recordings
++                      if (index->Get(readIndex + 1, &FileNumber, &FileOffset, &readIndependent) && readIndependent)
++                         total = index->Last();
++#endif /* JUMPPLAY */
+                       }
+                    else // allows replay even if the index file is missing
+                       Length = MAXFRAMESIZE;
+@@ -491,6 +561,15 @@
+              // Store the frame in the buffer:
+              if (readFrame) {
++#ifdef USE_JUMPPLAY
++                if (cutIn) {
++                   if (isPesRecording)
++                      cRemux::SetBrokenLink(readFrame->Data(), readFrame->Count());
++                   else
++                      TsSetTeiOnBrokenPackets(readFrame->Data(), readFrame->Count());
++                   cutIn = false;
++                   }
++#endif /* JUMPPLAY */
+                 if (ringBuffer->Put(readFrame))
+                    readFrame = NULL;
+                 else
+@@ -550,8 +629,18 @@
+                 p = NULL;
+                 }
+              }
++#ifdef USE_JUMPPLAY
++          else {
++             if (LastMarkPause) {
++                LastMarkPause = false;
++                playMode = pmPause;
++                }
++             Sleep = true;
++             }
++#else
+           else
+              Sleep = true;
++#endif /* JUMPPLAY */
+           // Handle hitting begin/end of recording:
+diff -NaurwB vdr-1.7.10/eit.c vdr-1.7.10-patched/eit.c
+--- vdr-1.7.10/eit.c   2009-06-21 15:46:20.000000000 +0200
++++ vdr-1.7.10-patched/eit.c   2009-12-18 06:25:25.000000000 +0100
+@@ -19,13 +19,40 @@
+ #define VALID_TIME (31536000 * 2) // two years
++#ifdef USE_SETTIME
++extern char *SetTime;
++#endif /* SETTIME */
++
+ // --- cEIT ------------------------------------------------------------------
+ class cEIT : public SI::EIT {
+ public:
+   cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data, bool OnlyRunningStatus = false);
++#ifdef USE_NOEPG
++private:
++  bool allowedEPG(tChannelID kanalID);
++#endif /* NOEPG */
+   };
++#ifdef USE_NOEPG
++bool cEIT::allowedEPG(tChannelID kanalID) {
++  bool rc;
++
++  if (Setup.noEPGMode == 1) {
++     rc = false;
++     if (strstr(::Setup.noEPGList, kanalID.ToString()) != NULL)
++        rc = true;
++     }
++  else {
++     rc = true;
++     if (strstr(::Setup.noEPGList, kanalID.ToString()) != NULL)
++        rc = false;
++     }
++
++  return rc;
++}
++#endif /* NOEPG */
++
+ cEIT::cEIT(cSchedules *Schedules, int Source, u_char Tid, const u_char *Data, bool OnlyRunningStatus)
+ :SI::EIT(Data, false)
+ {
+@@ -37,6 +64,14 @@
+   if (!channel)
+      return; // only collect data for known channels
++#ifdef USE_NOEPG
++  // only use epg from channels not blocked by noEPG-patch
++  tChannelID kanalID;
++  kanalID = channel->GetChannelID();
++  if (!allowedEPG(kanalID))
++     return;
++#endif /* NOEPG */
++
+   cSchedule *pSchedule = (cSchedule *)Schedules->GetSchedule(channel, true);
+   bool Empty = true;
+@@ -82,8 +117,74 @@
+          // not be overwritten.
+          uchar TableID = pEvent->TableID();
+          if (TableID == 0x00) {
++#ifdef USE_DDEPGENTRY
++            if (pEvent->Version() == getVersionNumber()) {
++               if (Setup.MixEpgAction == 0)
++                  continue;
++               //printf("in");
++               //printf("%s", pEvent->GetTimeString());
++               // to use the info of the original epg, update the extern one,
++               // if it has less info
++               SI::Descriptor *d;
++               SI::ExtendedEventDescriptors *ExtendedEventDescriptors = NULL;
++               //SI::ExtendedEventDescriptor *eed = NULL;
++               SI::ShortEventDescriptor *ShortEventDescriptor = NULL;
++               //SI::ShortEventDescriptor *sed = NULL;
++               //SI::TimeShiftedEventDescriptor *tsed = NULL;
++               //cLinkChannels *LinkChannels = NULL;
++               for (SI::Loop::Iterator it2; (d = SiEitEvent.eventDescriptors.getNext(it2));) {
++                  if (d->getDescriptorTag() == SI::ShortEventDescriptorTag) {
++                     int LanguagePreferenceShort = -1;
++                     SI::ShortEventDescriptor *sed = (SI::ShortEventDescriptor *)d;
++                     if (I18nIsPreferredLanguage(Setup.EPGLanguages, sed->languageCode, LanguagePreferenceShort) || !ShortEventDescriptor) {
++                        delete ShortEventDescriptor;
++                        ShortEventDescriptor = sed;
++                        d = NULL; // so that it is not deleted
++                        }
++                     }
++                  else if (d->getDescriptorTag() == SI::ExtendedEventDescriptorTag) {
++                     int LanguagePreferenceExt = -1;
++                     bool UseExtendedEventDescriptor = false;
++                     SI::ExtendedEventDescriptor *eed = (SI::ExtendedEventDescriptor *)d;
++                     if (I18nIsPreferredLanguage(Setup.EPGLanguages, eed->languageCode, LanguagePreferenceExt) || !ExtendedEventDescriptors) {
++                        delete ExtendedEventDescriptors;
++                        ExtendedEventDescriptors = new SI::ExtendedEventDescriptors;
++                        UseExtendedEventDescriptor = true;
++                        }
++                     if (UseExtendedEventDescriptor) {
++                        ExtendedEventDescriptors->Add(eed);
++                        d = NULL; // so that it is not deleted
++                        }
++                     if (eed->getDescriptorNumber() == eed->getLastDescriptorNumber())
++                        UseExtendedEventDescriptor = false;
++                     }
++                     delete d;
++                  }
++                  if (pEvent) {
++                     if (ShortEventDescriptor) {
++                        char buffer[256];
++                        if (ShortEventDescriptor->text.getText(buffer, sizeof(buffer)) && pEvent->ShortText() && (strlen(ShortEventDescriptor->text.getText(buffer, sizeof(buffer))) > strlen(pEvent->ShortText()))) {
++                           pEvent->SetShortText(ShortEventDescriptor->text.getText(buffer, sizeof(buffer)));
++                           pEvent->FixEpgBugs();
++                           }
++                        }
++                     if (ExtendedEventDescriptors) {
++                        char buffer[ExtendedEventDescriptors->getMaximumTextLength(": ") + 1];
++                        //pEvent->SetDescription(ExtendedEventDescriptors->getText(buffer, sizeof(buffer), ": "));
++                        if (ExtendedEventDescriptors->getText(buffer, sizeof(buffer), ": ") && pEvent->Description() && (strlen(ExtendedEventDescriptors->getText(buffer, sizeof(buffer), ": ")) > strlen(pEvent->Description()))) {
++                           pEvent->SetDescription(ExtendedEventDescriptors->getText(buffer, sizeof(buffer), ": "));
++                           pEvent->FixEpgBugs();
++                           }
++                        }
++                     }
++                  delete ExtendedEventDescriptors;
++                  delete ShortEventDescriptor;
++                  continue;
++               }
++#else
+             if (pEvent->Version() == getVersionNumber())
+                continue;
++#endif /* DDEPGENTRY */
+             HasExternalData = ExternalData = true;
+             }
+          // If the new event has a higher table ID, let's skip it.
+@@ -108,7 +209,11 @@
+       if (newEvent)
+          pSchedule->AddEvent(newEvent);
+       if (Tid == 0x4E) { // we trust only the present/following info on the actual TS
++#ifdef USE_DDEPGENTRY
++         if (Setup.DisableVPS == 0 && SiEitEvent.getRunningStatus() >= SI::RunningStatusNotRunning)
++#else
+          if (SiEitEvent.getRunningStatus() >= SI::RunningStatusNotRunning)
++#endif /* DDEPGENTRY */
+             pSchedule->SetRunningStatus(pEvent, SiEitEvent.getRunningStatus(), channel);
+          }
+       if (OnlyRunningStatus)
+@@ -154,8 +259,46 @@
+                  }
+                  break;
+             case SI::ContentDescriptorTag:
++#ifdef USE_PARENTALRATING
++                 {
++                 int NumContents = 0;
++                 uchar Contents[MAXEVCONTENTS + 1] = { 0 };
++                 SI::ContentDescriptor *cd = (SI::ContentDescriptor *)d;
++                 SI::ContentDescriptor::Nibble Nibble;
++                 for (SI::Loop::Iterator it3; cd->nibbleLoop.getNext(Nibble, it3); ) {
++                     if (NumContents < MAXEVCONTENTS) {
++                        Contents[NumContents] = ((Nibble.getContentNibbleLevel1() & 0xF) << 4) | (Nibble.getContentNibbleLevel2() & 0xF);
++                        NumContents++;
++                        }
++                     }
++                 pEvent->SetContents(Contents);
++                 }
++#endif /* PARENTALRATING */
+                  break;
+             case SI::ParentalRatingDescriptorTag:
++#ifdef USE_PARENTALRATING
++                 {
++                 int LanguagePreferenceRating = -1;
++                 SI::ParentalRatingDescriptor *prd = (SI::ParentalRatingDescriptor *)d;
++                 SI::ParentalRatingDescriptor::Rating Rating;
++                 for (SI::Loop::Iterator it3; prd->ratingLoop.getNext(Rating, it3); ) {
++                     if (I18nIsPreferredLanguage(Setup.EPGLanguages, Rating.languageCode, LanguagePreferenceRating)) {
++                        int rate = (Rating.getRating() & 0xFF);
++                        switch (rate) {
++                          case 0x01 ... 0x0F: // minimum age = rating + 3 years
++                               rate += 3;
++                               break;
++                          case 0:             // undefined
++                          case 0x10 ... 0xFF: // defined by the broadcaster
++                          default:
++                               rate = 0; 
++                               break;
++                          }
++                        pEvent->SetParentalRating(rate);
++                        }
++                     }
++                 }
++#endif /* PARENTALRATING */
+                  break;
+             case SI::PDCDescriptorTag: {
+                  SI::PDCDescriptor *pd = (SI::PDCDescriptor *)d;
+@@ -266,6 +409,62 @@
+       if (LinkChannels)
+          channel->SetLinkChannels(LinkChannels);
+       Modified = true;
++#ifdef USE_DDEPGENTRY
++      //to avoid double epg-entrys from ext and int epg sources :EW
++      if (pEvent && pEvent->TableID() != 0x00) {
++         cEvent *pPreviousEvent = (cEvent *)pSchedule->GetPreviousEvent(pEvent);
++         if (pPreviousEvent) {
++            if (Setup.DoubleEpgAction == 0) {
++               pPreviousEvent->SetStartTime(pEvent->StartTime());
++               pPreviousEvent->SetDuration(pEvent->Duration());
++               if (Setup.DisableVPS == 0) {
++                  if (channel)
++                     pPreviousEvent->SetRunningStatus(pEvent->RunningStatus(), channel);
++                  else
++                     pPreviousEvent->SetRunningStatus(pEvent->RunningStatus());
++                  }
++               // to use the info of the original epg, update the extern one,
++               // if it has less info
++               char buffer_short_intern[256];
++               char buffer_short_extern[256];
++               int len_short_intern = 0;
++               int len_short_extern = 0;
++               if (pEvent->ShortText())
++                  len_short_intern = snprintf (buffer_short_intern, sizeof(buffer_short_intern)-1, "%s", pEvent->ShortText());
++               if (pPreviousEvent->ShortText())
++                  len_short_extern = snprintf (buffer_short_extern, sizeof(buffer_short_extern)-1, "%s", pPreviousEvent->ShortText());
++               if (len_short_intern > 0) {
++                  if (len_short_extern < 1)
++                     pPreviousEvent->SetShortText(buffer_short_intern);
++                  else if (len_short_intern > len_short_extern)
++                     pPreviousEvent->SetShortText(buffer_short_intern);
++                  }
++               if (pEvent->Description()) {
++                  char buffer_title_intern[4096];
++                  char buffer_title_extern[4096];
++                  int len_title_intern = 0;
++                  int len_title_extern = 0;
++                  if (pEvent->Description())
++                     len_title_intern = snprintf (buffer_title_intern, sizeof(buffer_title_intern)-1, "%s", pEvent->Description());
++                  if (pPreviousEvent->Description())
++                     len_title_extern = snprintf (buffer_title_extern, sizeof(buffer_title_extern)-1, "%s", pPreviousEvent->Description());
++                  if (len_title_intern > 0) {
++                     if (len_title_extern < 1)
++                        pPreviousEvent->SetDescription(buffer_title_intern);
++                     else if (len_title_intern > len_title_extern)
++                        pPreviousEvent->SetDescription(buffer_title_intern);
++                     }
++                  }
++               if (pPreviousEvent->Vps() == 0 && pEvent->Vps() != 0)
++                  pPreviousEvent->SetVps(pEvent->Vps());
++               pSchedule->DelEvent(pEvent);
++               pPreviousEvent->FixEpgBugs();
++               }
++            else
++               pSchedule->DelEvent(pPreviousEvent);
++            }
++         }
++#endif /* DDEPGENTRY */
+       }
+   if (Tid == 0x4E) {
+      if (Empty && getSectionNumber() == 0)
+@@ -304,10 +503,26 @@
+   time_t sattim = getTime();
+   time_t loctim = time(NULL);
++#ifdef USE_SETTIME
++  char timestr[20];
++  struct tm *ptm;
++  struct tm tm_r;
++  ptm = localtime_r(&sattim, &tm_r);
++#endif /* SETTIME */
++
+   int diff = abs(sattim - loctim);
+   if (diff > 2) {
+      mutex.Lock();
+      if (abs(diff - lastDiff) < 3) {
++#ifdef USE_SETTIME
++        if (SetTime) {
++           strftime(timestr, 20, "%m%d%H%M%Y.%S", ptm);
++           cString cmd = cString::sprintf("%s %s %ld", SetTime, timestr, sattim);
++           dsyslog("Executing: %s", *cmd);
++           SystemExec(cmd);
++           }
++        else
++#endif /* SETTIME */
+         if (stime(&sattim) == 0)
+            isyslog("system time changed from %s (%ld) to %s (%ld)", *TimeToString(loctim), loctim, *TimeToString(sattim), sattim);
+         else
+diff -NaurwB vdr-1.7.10/eitscan.c vdr-1.7.10-patched/eitscan.c
+--- vdr-1.7.10/eitscan.c       2006-01-07 15:10:17.000000000 +0100
++++ vdr-1.7.10-patched/eitscan.c       2009-12-18 06:25:25.000000000 +0100
+@@ -151,9 +151,17 @@
+                             if (Device->ProvidesTransponder(Channel)) {
+                                if (!Device->Receiving()) {
+                                   bool MaySwitchTransponder = Device->MaySwitchTransponder();
++#ifdef USE_LNBSHARE
++                                  if (MaySwitchTransponder && Device->GetMaxBadPriority(Channel) == -2 || Device->ProvidesTransponderExclusively(Channel) && Device->GetMaxBadPriority(Channel) <= -1 && now - lastActivity > Setup.EPGScanTimeout * 3600) {
++#else
+                                   if (MaySwitchTransponder || Device->ProvidesTransponderExclusively(Channel) && now - lastActivity > Setup.EPGScanTimeout * 3600) {
++#endif /* LNBSHARE */
+                                      if (!MaySwitchTransponder) {
++#ifdef USE_LNBSHARE
++                                        if ((Device == cDevice::ActualDevice() || Device->GetMaxBadPriority(Channel) == -1) && !currentChannel) {
++#else
+                                         if (Device == cDevice::ActualDevice() && !currentChannel) {
++#endif /* LNBSHARE */
+                                            cDevice::PrimaryDevice()->StopReplay(); // stop transfer mode
+                                            currentChannel = Device->CurrentChannel();
+                                            Skins.Message(mtInfo, tr("Starting EPG scan"));
+diff -NaurwB vdr-1.7.10/epg.c vdr-1.7.10-patched/epg.c
+--- vdr-1.7.10/epg.c   2008-05-01 16:53:55.000000000 +0200
++++ vdr-1.7.10-patched/epg.c   2009-12-18 06:38:20.000000000 +0100
+@@ -114,6 +114,11 @@
+   components = NULL;
+   startTime = 0;
+   duration = 0;
++#ifdef USE_PARENTALRATING
++  for (int i = 0; i < MAXEVCONTENTS; i++)
++      contents[i] = 0;
++  parentalRating = 0;
++#endif /* PARENTALRATING */
+   vps = 0;
+   SetSeen();
+ }
+@@ -202,6 +207,19 @@
+   duration = Duration;
+ }
++#ifdef USE_PARENTALRATING
++void cEvent::SetContents(uchar *Contents)
++{
++  for (int i = 0; i < MAXEVCONTENTS; i++)
++      contents[i] = Contents[i];
++}
++
++void cEvent::SetParentalRating(uchar ParentalRating)
++{
++  parentalRating = ParentalRating;
++}
++#endif /* PARENTALRATING */
++
+ void cEvent::SetVps(time_t Vps)
+ {
+   vps = Vps;
+@@ -257,6 +275,340 @@
+   return buf;
+ }
++#ifdef USE_PARENTALRATING
++cString cEvent::GetContentsString(int i) const
++{
++  cString str("");
++  switch (contents[i] & 0xF0) {
++    case EVCONTENTMASK_MOVIEDRAMA:
++         switch (contents[i] & 0x0F) {
++           default:
++           case 0x00:
++                str = cString(tr("Content$Movie/Drama"));
++                break;
++           case 0x01:
++                str = cString(tr("Content$Detective/Thriller"));
++                break;
++           case 0x02:
++                str = cString(tr("Content$Adventure/Western/War"));
++                break;
++           case 0x03:
++                str = cString(tr("Content$Science Fiction/Fantasy/Horror"));
++                break;
++           case 0x04:
++                str = cString(tr("Content$Comedy"));
++                break;
++           case 0x05:
++                str = cString(tr("Content$Soap/Melodrama/Folkloric"));
++                break;
++           case 0x06:
++                str = cString(tr("Content$Romance"));
++                break;
++           case 0x07:
++                str = cString(tr("Content$Serious/Classical/Religious/Historical Movie/Drama"));
++                break;
++           case 0x08:
++                str = cString(tr("Content$Adult Movie/Drama"));
++                break;
++            }
++         break;
++
++    case EVCONTENTMASK_NEWSCURRENTAFFAIRS:
++         switch (contents[i] & 0x0F) {
++           default:
++           case 0x00:
++                str = cString(tr("Content$News/Current Affairs"));
++                break;
++           case 0x01:
++                str = cString(tr("Content$News/Weather Report"));
++                break;
++           case 0x02:
++                str = cString(tr("Content$News Magazine"));
++                break;
++           case 0x03:
++                str = cString(tr("Content$Documentary"));
++                break;
++           case 0x04:
++                str = cString(tr("Content$Discussion/Inverview/Debate"));
++                break;
++           }
++         break;
++
++    case EVCONTENTMASK_SHOW:
++         switch (contents[i] & 0x0F) {
++           default:
++           case 0x00:
++                str = cString(tr("Content$Show/Game Show"));
++                break;
++           case 0x01:
++                str = cString(tr("Content$Game Show/Quiz/Contest"));
++                break;
++           case 0x02:
++                str = cString(tr("Content$Variety Show"));
++                break;
++           case 0x03:
++                str = cString(tr("Content$Talk Show"));
++                break;
++           }
++         break;
++
++    case EVCONTENTMASK_SPORTS:
++         switch (contents[i] & 0x0F) {
++           default:
++           case 0x00:
++                str = cString(tr("Content$Sports"));
++                break;
++           case 0x01:
++                str = cString(tr("Content$Special Event"));
++                break;
++           case 0x02:
++                str = cString(tr("Content$Sport Magazine"));
++                break;
++           case 0x03:
++                str = cString(tr("Content$Football"));
++                break;
++           case 0x04:
++                str = cString(tr("Content$Tennis/Squash"));
++                break;
++           case 0x05:
++                str = cString(tr("Content$Team Sports"));
++                break;
++           case 0x06:
++                str = cString(tr("Content$Athletics"));
++                break;
++           case 0x07:
++                str = cString(tr("Content$Motor Sport"));
++                break;
++           case 0x08:
++                str = cString(tr("Content$Water Sport"));
++                break;
++           case 0x09:
++                str = cString(tr("Content$Winter Sports"));
++                break;
++           case 0x0A:
++                str = cString(tr("Content$Equestrian"));
++                break;
++           case 0x0B:
++                str = cString(tr("Content$Martial Sports"));
++                break;
++           }
++         break;
++
++    case EVCONTENTMASK_CHILDRENYOUTH:
++         switch (contents[i] & 0x0F) {
++           default:
++           case 0x00:
++                str = cString(tr("Content$Children's/Youth Programmes"));
++                break;
++           case 0x01:
++                str = cString(tr("Content$Pre-school Children's Programmes"));
++                break;
++           case 0x02:
++                str = cString(tr("Content$Entertainment Programmes for 6 to 14"));
++                break;
++           case 0x03:
++                str = cString(tr("Content$Entertainment Programmes for 10 to 16"));
++                break;
++           case 0x04:
++                str = cString(tr("Content$Informational/Educational/School Programme"));
++                break;
++           case 0x05:
++                str = cString(tr("Content$Cartoons/Puppets"));
++                break;
++           }
++         break;
++
++    case EVCONTENTMASK_MUSICBALLETDANCE:
++         switch (contents[i] & 0x0F) {
++           default:
++           case 0x00:
++                str = cString(tr("Content$Music/Ballet/Dance"));
++                break;
++           case 0x01:
++                str = cString(tr("Content$Rock/Pop"));
++                break;
++           case 0x02:
++                str = cString(tr("Content$Serious/Classical Music"));
++                break;
++           case 0x03:
++                str = cString(tr("Content$Folk/Tradional Music"));
++                break;
++           case 0x04:
++                str = cString(tr("Content$Jazz"));
++                break;
++           case 0x05:
++                str = cString(tr("Content$Musical/Opera"));
++                break;
++           case 0x06:
++                str = cString(tr("Content$Ballet"));
++                break;
++           }
++         break;
++
++    case EVCONTENTMASK_ARTSCULTURE:
++         switch (contents[i] & 0x0F) {
++           default:
++           case 0x00:
++                str = cString(tr("Content$Arts/Culture"));
++                break;
++           case 0x01:
++                str = cString(tr("Content$Performing Arts"));
++                break;
++           case 0x02:
++                str = cString(tr("Content$Fine Arts"));
++                break;
++           case 0x03:
++                str = cString(tr("Content$Religion"));
++                break;
++           case 0x04:
++                str = cString(tr("Content$Popular Culture/Traditional Arts"));
++                break;
++           case 0x05:
++                str = cString(tr("Content$Literature"));
++                break;
++           case 0x06:
++                str = cString(tr("Content$Film/Cinema"));
++                break;
++           case 0x07:
++                str = cString(tr("Content$Experimental Film/Video"));
++                break;
++           case 0x08:
++                str = cString(tr("Content$Broadcasting/Press"));
++                break;
++           case 0x09:
++                str = cString(tr("Content$New Media"));
++                break;
++           case 0x0A:
++                str = cString(tr("Content$Arts/Culture Magazines"));
++                break;
++           case 0x0B:
++                str = cString(tr("Content$Fashion"));
++                break;
++           }
++         break;
++
++    case EVCONTENTMASK_SOCIALPOLITICALECONOMICS:
++         switch (contents[i] & 0x0F) {
++           default:
++           case 0x00:
++                str = cString(tr("Content$Social/Political/Economics"));
++                break;
++           case 0x01:
++                str = cString(tr("Content$Magazines/Reports/Documentary"));
++                break;
++           case 0x02:
++                str = cString(tr("Content$Economics/Social Advisory"));
++                break;
++           case 0x03:
++                str = cString(tr("Content$Remarkable People"));
++                break;
++           }
++         break;
++
++    case EVCONTENTMASK_EDUCATIONALSCIENCE:
++         switch (contents[i] & 0x0F) {
++           default:
++           case 0x00:
++                str = cString(tr("Content$Education/Science/Factual"));
++                break;
++           case 0x01:
++                str = cString(tr("Content$Nature/Animals/Environment"));
++                break;
++           case 0x02:
++                str = cString(tr("Content$Technology/Natural Sciences"));
++                break;
++           case 0x03:
++                str = cString(tr("Content$Medicine/Physiology/Psychology"));
++                break;
++           case 0x04:
++                str = cString(tr("Content$Foreign Countries/Expeditions"));
++                break;
++           case 0x05:
++                str = cString(tr("Content$Social/Spiritual Sciences"));
++                break;
++           case 0x06:
++                str = cString(tr("Content$Further Education"));
++                break;
++           case 0x07:
++                str = cString(tr("Content$Languages"));
++                break;
++           }
++         break;
++
++    case EVCONTENTMASK_LEISUREHOBBIES:
++         switch (contents[i] & 0x0F) {
++           default:
++           case 0x00:
++                str = cString(tr("Content$Leisure/Hobbies"));
++                break;
++           case 0x01:
++                str = cString(tr("Content$Tourism/Travel"));
++                break;
++           case 0x02:
++                str = cString(tr("Content$Handicraft"));
++                break;
++           case 0x03:
++                str = cString(tr("Content$Motoring"));
++                break;
++           case 0x04:
++                str = cString(tr("Content$Fitness & Health"));
++                break;
++           case 0x05:
++                str = cString(tr("Content$Cooking"));
++                break;
++           case 0x06:
++                str = cString(tr("Content$Advertisement/Shopping"));
++                break;
++           case 0x07:
++                str = cString(tr("Content$Gardening"));
++                break;
++           }
++         break;
++
++    case EVCONTENTMASK_SPECIAL:
++         switch (contents[i] & 0x0F) {
++           case 0x00:
++                str = cString(tr("Content$Original Language"));
++                break;
++           case 0x01:
++                str = cString(tr("Content$Black & White"));
++                break;
++           case 0x02:
++                str = cString(tr("Content$Unpublished"));
++                break;
++           case 0x03:
++                str = cString(tr("Content$Live Broadcast"));
++                break;
++           default:
++                str = cString(tr("Content$Special Characteristics"));
++                break;
++           }
++         break;
++
++    case EVCONTENTMASK_USERDEFINED:
++         switch (contents[i] & 0x0F) {
++           case 0x00:
++                str = cString(tr("Content$Drama")); // UK Freeview
++                break;
++            default:
++                break;
++           }
++         break;
++
++    default:
++         break;
++    }
++  return str;
++}
++
++cString cEvent::GetParentalRatingString(void) const
++{
++  if (parentalRating > 0)
++     return cString::sprintf(tr("Suitable for those aged %d and over"), parentalRating);
++  return NULL;
++}
++#endif /* PARENTALRATING */
++
+ void cEvent::Dump(FILE *f, const char *Prefix, bool InfoOnly) const
+ {
+   if (InfoOnly || startTime + duration + Setup.EPGLinger * 60 >= time(NULL)) {
+@@ -278,6 +630,17 @@
+             fprintf(f, "%sX %s\n", Prefix, *p->ToString());
+             }
+         }
++#ifdef USE_PARENTALRATING
++     if (!isempty(GetContentsString())) {
++       for (int i = 0; i < MAXEVCONTENTS; i++) {
++         if (!isempty(GetContentsString(i))) {
++            strreplace(description, '\n', '|');
++            fprintf(f, "%sG %i %i %s\n",Prefix, Contents(i) & 0xF0, Contents(i) & 0x0F, (const char *)GetContentsString(i));
++            strreplace(description, '|', '\n');
++           }
++         }
++       }
++#endif /* PARENTALRATING */
+      if (vps)
+         fprintf(f, "%sV %ld\n", Prefix, vps);
+      if (!InfoOnly)
+@@ -345,6 +708,24 @@
+                              }
+                           }
+                        break;
++#ifdef USE_PARENTALRATING
++             case 'G': if (Event) {
++                          unsigned int ContentID = 0;
++                          unsigned int ContentSubID = 0;
++                          int n = sscanf(t, "%u %u", &ContentID, &ContentSubID);
++                          if (n == 2) {
++                            if (ContentID != 0) {
++                              for (int i = 0; i < MAXEVCONTENTS; i++) {
++                                if (Event->Contents(i) == 0) {
++                                  Event->contents[i] = ContentID | ContentSubID;
++                                  break;
++                                  }
++                                }
++                              }
++                            }
++                          }
++                       break;
++#endif /* PARENTALRATING */
+              case 'e': if (Event && !Event->Title())
+                           Event->SetTitle(tr("No title"));
+                        Event = NULL;
+@@ -744,6 +1125,28 @@
+   return pe;
+ }
++#ifdef USE_DDEPGENTRY
++const cEvent *cSchedule::GetPreviousEvent(cEvent *Event) const
++{
++  if (!Event || Event->Duration() == 0 || Event->StartTime() == 0)
++     return NULL;
++  // Returns either the event info to the previous/following event to the given EventID or, if that one can't be found NULL :EW
++  cEvent *pt = NULL;
++  int epgTimeDelta = Setup.DoubleEpgTimeDelta * 60 + 1;
++  for (pt = events.First(); pt; pt = events.Next(pt))
++     if (pt && pt->TableID() == 0x00)
++        if ((Event->StartTime() - pt->StartTime()) > -  epgTimeDelta && (Event->StartTime() - pt->StartTime()) < epgTimeDelta) {
++           if ((pt->Duration() + (pt->Duration()/ 5) + 1) > Event->Duration() && (pt->Duration() - (pt->Duration()/ 5) - 1) < Event->Duration())
++              return pt;
++           else if (pt->Title() && Event->Title() && (strcmp(pt->Title(), ".") != 0 && strcmp(Event->Title(), ".") != 0)) {
++              if (strstr(pt->Title(), Event->Title()) != NULL || strstr(Event->Title(), pt->Title()) != NULL)
++                 return pt;
++              }
++           }
++  return NULL;
++}
++#endif /* DDEPGENTRY */
++
+ void cSchedule::SetRunningStatus(cEvent *Event, int RunningStatus, cChannel *Channel)
+ {
+   hasRunning = false;
+diff -NaurwB vdr-1.7.10/epg.h vdr-1.7.10-patched/epg.h
+--- vdr-1.7.10/epg.h   2006-10-07 15:47:19.000000000 +0200
++++ vdr-1.7.10-patched/epg.h   2009-12-18 06:25:25.000000000 +0100
+@@ -50,6 +50,22 @@
+ typedef u_int32_t tEventID;
++#ifdef USE_PARENTALRATING
++#define MAXEVCONTENTS                          4
++#define EVCONTENTMASK_MOVIEDRAMA               0x10
++#define EVCONTENTMASK_NEWSCURRENTAFFAIRS       0x20
++#define EVCONTENTMASK_SHOW                     0x30
++#define EVCONTENTMASK_SPORTS                   0x40
++#define EVCONTENTMASK_CHILDRENYOUTH            0x50
++#define EVCONTENTMASK_MUSICBALLETDANCE         0x60
++#define EVCONTENTMASK_ARTSCULTURE              0x70
++#define EVCONTENTMASK_SOCIALPOLITICALECONOMICS 0x80
++#define EVCONTENTMASK_EDUCATIONALSCIENCE       0x90
++#define EVCONTENTMASK_LEISUREHOBBIES           0xA0
++#define EVCONTENTMASK_SPECIAL                  0xB0
++#define EVCONTENTMASK_USERDEFINED              0xF0
++#endif /* PARENTALRATING */
++
+ class cEvent : public cListObject {
+   friend class cSchedule;
+ private:
+@@ -64,6 +80,10 @@
+   cComponents *components; // The stream components of this event
+   time_t startTime;        // Start time of this event
+   int duration;            // Duration of this event in seconds
++#ifdef USE_PARENTALRATING
++  uchar contents[MAXEVCONTENTS + 1]; // Contents of this event; list is zero-terminated
++  uchar parentalRating;    // Parental rating of this event
++#endif /* PARENTALRATING */
+   time_t vps;              // Video Programming Service timestamp (VPS, aka "Programme Identification Label", PIL)
+   time_t seen;             // When this event was last seen in the data stream
+ public:
+@@ -83,6 +103,10 @@
+   time_t StartTime(void) const { return startTime; }
+   time_t EndTime(void) const { return startTime + duration; }
+   int Duration(void) const { return duration; }
++#ifdef USE_PARENTALRATING
++  uchar Contents(int i = 0) const { return (0 <= i && i < MAXEVCONTENTS) ? contents[i] : 0; }
++  uchar ParentalRating(void) const { return parentalRating; }
++#endif /* PARENTALRATING */
+   time_t Vps(void) const { return vps; }
+   time_t Seen(void) const { return seen; }
+   bool SeenWithin(int Seconds) const { return time(NULL) - seen < Seconds; }
+@@ -92,6 +116,10 @@
+   cString GetTimeString(void) const;
+   cString GetEndTimeString(void) const;
+   cString GetVpsString(void) const;
++#ifdef USE_PARENTALRATING
++  cString GetContentsString(int i = 0) const;
++  cString GetParentalRatingString(void) const;
++#endif /* PARENTALRATING */
+   void SetEventID(tEventID EventID);
+   void SetTableID(uchar TableID);
+   void SetVersion(uchar Version);
+@@ -102,6 +130,10 @@
+   void SetComponents(cComponents *Components); // Will take ownership of Components!
+   void SetStartTime(time_t StartTime);
+   void SetDuration(int Duration);
++#ifdef USE_PARENTALRATING
++  void SetContents(uchar *Contents);
++  void SetParentalRating(uchar ParentalRating);
++#endif /* PARENTALRATING */
+   void SetVps(time_t Vps);
+   void SetSeen(void);
+   cString ToDescr(void) const;
+@@ -137,6 +169,9 @@
+   void DropOutdated(time_t SegmentStart, time_t SegmentEnd, uchar TableID, uchar Version);
+   void Cleanup(time_t Time);
+   void Cleanup(void);
++#ifdef USE_DDEPGENTRY
++  const cEvent *GetPreviousEvent(cEvent *Event) const; //:EW
++#endif /* DDEPGENTRY */
+   cEvent *AddEvent(cEvent *Event);
+   void DelEvent(cEvent *Event);
+   void HashEvent(cEvent *Event);
+diff -NaurwB vdr-1.7.10/HISTORY-liemikuutio vdr-1.7.10-patched/HISTORY-liemikuutio
+--- vdr-1.7.10/HISTORY-liemikuutio     1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.10-patched/HISTORY-liemikuutio     2009-12-18 06:25:25.000000000 +0100
+@@ -0,0 +1,132 @@
++-----------------------------------
++Liemikuutio for Video Disc Recorder
++
++Maintainer: Rolf Ahrenberg
++-----------------------------------
++
++2006-01-08: Version 1.0
++
++- Based on enAIO with these original patches:
++  Simple recordings sorting by Walter@VDRPortal
++  Alternate rename recordings by Ralf Müller
++  Menu selection by Peter Dittmann
++  Recording length by Tobias Faust
++
++2006-01-15: Version 1.1
++
++- Removed patches already found in vdr-1.3.39.
++
++2006-01-25: Version 1.2
++
++- Added "Main menu command position" feature.
++
++2006-02-05: Version 1.3
++
++- Improved menu selection response.
++
++2006-04-18: Version 1.4
++
++- Added Estonian translation (Thanks to Arthur Konovalov).
++
++2006-04-30: Version 1.5
++
++- Added progress bar view into "What's on now?" menu.
++
++2006-06-06: Version 1.6
++
++- Added French translation (Thanks to ECLiPSE).
++
++2006-06-14: Version 1.7
++
++- Fixed RENR crash.
++
++2006-07-14: Version 1.8
++
++- Fixed RENR/OSD bug.
++
++2006-08-27: Version 1.9
++
++- Some modifications to the recording length and rename recordings
++  patches (Thanks to Firefly).
++- Added k1_k3_jumps_20s patch by Petri Hintukainen.
++
++2006-08-29: Version 1.10
++
++- The cRecording:Title() method now defaults to original formatting.
++
++2006-09-04: Version 1.11
++
++- Removed unused variable from cRecording::Title() method (Thanks to
++  C.Y.M.).
++- Some modifications to the rename recordings patch (Thanks to Firefly).
++
++2006-09-13: Version 1.12
++
++- More modifications to the rename recordings patch (Thanks to Firefly).
++
++2006-10-01: Version 1.13
++
++- Removed unnecessary syslog printing (Thanks to Firefly).
++
++2007-08-14: Version 1.14
++
++- Updated for vdr-1.5.7.
++
++2007-10-16: Version 1.15
++
++- Added recmenu play patch (Thanks to Ville Skyttä).
++- Updated French translation (Thanks to ECLiPSE).
++
++2007-11-04: Version 1.16
++
++- Updated for vdr-1.5.11.
++
++2007-12-08: Version 1.17
++
++- Added binary skip patch.
++- Removed k1_k3_jumps_20s patch.
++
++2008-02-17: Version 1.18
++
++- Updated for vdr-1.5.15.
++
++2008-03-02: Version 1.19
++
++- Modified binary skip to use kPrev and kNext keys and the skip is now
++  always shortened after a direction change (Thanks to Timo Eskola).
++- Readded k1_k3_jumps_20s patch.
++
++2008-04-04: Version 1.20
++
++- Added bitrate information into rename menu.
++- Readded the path editing support of rename recordings patch (Thanks
++  to Firefly).
++
++2008-05-08: Version 1.21
++
++- Fixed rename recordings (Thanks to Firefly).
++- Added a DVB subtitles hack for old recordings (Thanks to Anssi Hannula).
++
++2009-01-08: Version 1.22
++
++- Updated for vdr-1.7.3.
++
++2009-01-25: Version 1.23
++
++- Updated for vdr-1.7.4.
++
++2009-02-27: Version 1.24
++
++- Fixed compilation under gcc-4.4.
++
++2009-04-05: Version 1.25
++
++- Fixed the length detection of recordings (Thanks to Thomas Günther).
++
++2009-04-17: Version 1.26
++
++- Fixed the length detection of audio recordings (Thanks to Thomas Günther).
++
++2009-04-26: Version 1.27
++
++- Fixed the length detection of empty recordings (Thanks to Thomas Günther).
+diff -NaurwB vdr-1.7.10/iconpatch.c vdr-1.7.10-patched/iconpatch.c
+--- vdr-1.7.10/iconpatch.c     1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.10-patched/iconpatch.c     2009-12-18 06:25:25.000000000 +0100
+@@ -0,0 +1,31 @@
++#ifdef USE_WAREAGLEICON
++
++#include "iconpatch.h"
++
++#include <langinfo.h>
++#include <locale.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++
++bool IsLangUtf8(void)
++{
++  char *CodeSet = NULL;
++  if (setlocale(LC_CTYPE, ""))
++     CodeSet = nl_langinfo(CODESET);
++  else {
++     char *LangEnv = getenv("LANG"); // last resort in case locale stuff isn't installed
++     if (LangEnv) {
++        CodeSet = strchr(LangEnv, '.');
++        if (CodeSet)
++           CodeSet++; // skip the dot
++        }
++     }
++
++  if (CodeSet && strcasestr(CodeSet, "UTF-8") != 0)
++     return true;
++
++  return false;
++}
++
++#endif /* WAREAGLEICON */
+diff -NaurwB vdr-1.7.10/iconpatch.h vdr-1.7.10-patched/iconpatch.h
+--- vdr-1.7.10/iconpatch.h     1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.10-patched/iconpatch.h     2009-12-18 06:25:25.000000000 +0100
+@@ -0,0 +1,73 @@
++#ifdef USE_WAREAGLEICON
++/*
++ * iconpatch.h: Information of iconpatch
++ *
++ * Diese Datei ist die Übersichtsdatei für den Iconpatch.
++ * Hier werden kleine Infos abgelegt.
++ * Der Iconpatch ändert die Dateien:
++ *   iconpatch.h
++ *   menu.c
++ *   recording.c
++ *   fontosd.c
++ *
++ */
++
++// Iconpatch-Variablen - Anfang
++#define ICON_NUMBERSIGN        "\x23"
++#define ICON_ASTERISK          "\x2A"
++#define ICON_GREATER           "\x3E"
++#define ICON_EXCLAM            "\x21"
++#define ICON_PLUSMINUS         "\xB1"
++
++#define ICON_RESUME            "\x80"
++#define ICON_DVD               "\x81"
++#define ICON_FOLDER            "\x82"
++#define ICON_BLANK             "\x83"
++#define ICON_CUTTING           "\x84"
++#define ICON_MOVE_FILE         "\x85"
++#define ICON_MOVE_FOLDER       "\x86"
++#define ICON_BAR_START         "\x87"
++#define ICON_BAR_FILLED        "\x88"
++#define ICON_BAR_CLEAR         "\x89"
++#define ICON_BAR_END           "\x8A"
++#define ICON_REC               "\x8B"
++#define ICON_CLOCK             "\x8C"
++#define ICON_TV_CRYPTED        "\x8D"
++#define ICON_RADIO             "\x8E"
++#define ICON_TV                "\x8F"
++#define ICON_NEW               "\x90"
++#define ICON_ARROW             "\x91"
++#define ICON_RUNNING           "\x92"
++#define ICON_VPS               "\x93"
++#define ICON_CLOCK_UH          "\x94"
++#define ICON_CLOCK_LH          "\x95"
++
++// UTF-8 Icons
++#define ICON_RESUME_UTF8       "\uE000"
++#define ICON_DVD_UTF8          "\uE001"
++#define ICON_FOLDER_UTF8       "\uE002"
++#define ICON_BLANK_UTF8        "\uE003"
++#define ICON_CUTTING_UTF8      "\uE004"
++#define ICON_MOVE_FILE_UTF8    "\uE005"
++#define ICON_MOVE_FOLDER_UTF8  "\uE006"
++#define ICON_BAR_START_UTF8    "\uE007"
++#define ICON_BAR_FILLED_UTF8   "\uE008"
++#define ICON_BAR_EMPTY_UTF8    "\uE009"
++#define ICON_BAR_CLOSE_UTF8    "\uE00A"
++#define ICON_REC_UTF8          "\uE00B"
++#define ICON_CLOCK_UTF8        "\uE00C"
++#define ICON_TV_CRYPTED_UTF8   "\uE00D"
++#define ICON_RADIO_UTF8        "\uE00E"
++#define ICON_TV_UTF8           "\uE00F"
++#define ICON_NEW_UTF8          "\uE010"
++#define ICON_ARROW_UTF8        "\uE011"
++#define ICON_RUNNING_UTF8      "\uE012"
++#define ICON_VPS_UTF8          "\uE013"
++#define ICON_CLOCK_UH_UTF8     "\uE014"
++#define ICON_CLOCK_LH_UTF8     "\uE015"
++
++// Iconpatch-Variablen - Ende
++
++bool IsLangUtf8(void);
++
++#endif /* WAREAGLEICON */
+diff -NaurwB vdr-1.7.10/keys.h vdr-1.7.10-patched/keys.h
+--- vdr-1.7.10/keys.h  2007-08-26 14:34:50.000000000 +0200
++++ vdr-1.7.10-patched/keys.h  2009-12-18 06:25:25.000000000 +0100
+@@ -71,6 +71,10 @@
+ #define kEditCut         k2
+ #define kEditTest        k8
++#ifdef USE_DVDARCHIVE
++#define kDvdChapterJumpForward k6
++#define kDvdChapterJumpBack    k4
++#endif /* DVDARCHIVE */
+ #define RAWKEY(k)        (eKeys((k) & ~k_Flags))
+ #define ISRAWKEY(k)      ((k) != kNone && ((k) & k_Flags) == 0)
+ #define NORMALKEY(k)     (eKeys((k) & ~k_Repeat))
+diff -NaurwB vdr-1.7.10/lirc.c vdr-1.7.10-patched/lirc.c
+--- vdr-1.7.10/lirc.c  2006-05-28 10:48:13.000000000 +0200
++++ vdr-1.7.10-patched/lirc.c  2009-12-18 06:25:25.000000000 +0100
+@@ -12,6 +12,9 @@
+ #include "lirc.h"
+ #include <netinet/in.h>
+ #include <sys/socket.h>
++#ifdef USE_LIRCSETTINGS
++#include "config.h"
++#endif /* LIRCSETTINGS */
+ #define REPEATDELAY 350 // ms
+ #define REPEATFREQ 100 // ms
+@@ -94,7 +97,11 @@
+               continue;
+               }
+            if (count == 0) {
++#ifdef USE_LIRCSETTINGS
++              if (strcmp(KeyName, LastKeyName) == 0 && FirstTime.Elapsed() < (unsigned int)Setup.LircRepeatDelay)
++#else
+               if (strcmp(KeyName, LastKeyName) == 0 && FirstTime.Elapsed() < REPEATDELAY)
++#endif /* LIRCSETTINGS */
+                  continue; // skip keys coming in too fast
+               if (repeat)
+                  Put(LastKeyName, false, true);
+@@ -104,18 +111,34 @@
+               timeout = -1;
+               }
+            else {
++#ifdef USE_LIRCSETTINGS
++              if (LastTime.Elapsed() < (unsigned int)Setup.LircRepeatFreq)
++#else
+               if (LastTime.Elapsed() < REPEATFREQ)
++#endif /* LIRCSETTINGS */
+                  continue; // repeat function kicks in after a short delay (after last key instead of first key)
++#ifdef USE_LIRCSETTINGS
++              if (FirstTime.Elapsed() < (unsigned int)Setup.LircRepeatDelay)
++#else
+               if (FirstTime.Elapsed() < REPEATDELAY)
++#endif /* LIRCSETTINGS */
+                  continue; // skip keys coming in too fast (for count != 0 as well)
+               repeat = true;
++#ifdef USE_LIRCSETTINGS
++              timeout = Setup.LircRepeatDelay;
++#else
+               timeout = REPEATDELAY;
++#endif /* LIRCSETTINGS */
+               }
+            LastTime.Set();
+            Put(KeyName, repeat);
+            }
+         else if (repeat) { // the last one was a repeat, so let's generate a release
++#ifdef USE_LIRCSETTINGS
++           if (LastTime.Elapsed() >= (unsigned int)Setup.LircRepeatTimeout) {
++#else
+            if (LastTime.Elapsed() >= REPEATTIMEOUT) {
++#endif /* LIRCSETTINGS */
+               Put(LastKeyName, false, true);
+               repeat = false;
+               *LastKeyName = 0;
+diff -NaurwB vdr-1.7.10/mainmenuitemsprovider.h vdr-1.7.10-patched/mainmenuitemsprovider.h
+--- vdr-1.7.10/mainmenuitemsprovider.h 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.10-patched/mainmenuitemsprovider.h 2009-12-18 06:25:25.000000000 +0100
+@@ -0,0 +1,62 @@
++#ifdef USE_MENUORG
++/*
++ * vdr-menuorg - A plugin for the Linux Video Disk Recorder
++ * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net>
++ *
++ * 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 of the License, 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
++ * this program; if not, write to the Free Software Foundation, Inc.,
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
++ *
++ * $Id$
++ *
++ */
++
++#ifndef __MAINMENUITEMSPROVIDER_H
++#define __MAINMENUITEMSPROVIDER_H
++
++#include <vector>
++
++class cOsdItem;
++class cOsdMenu;
++
++class IMenuItemDefinition
++{
++    public:
++        virtual ~IMenuItemDefinition() {};
++        virtual bool IsCustomOsdItem() = 0;
++        virtual bool IsPluginItem() = 0;
++        virtual bool IsSeparatorItem() = 0;
++        virtual cOsdItem* CustomOsdItem() = 0;
++        virtual const char* PluginMenuEntry() = 0;
++        virtual bool IsSelected() = 0;
++        virtual int PluginIndex() = 0;
++};
++
++typedef std::vector<IMenuItemDefinition*> MenuItemDefinitions;
++
++#define MENU_ITEMS_PROVIDER_SERVICE_ID "MenuOrgPatch-v0.4.2::MainMenuItemsProvider"
++
++class IMainMenuItemsProvider
++{
++    public:
++        virtual ~IMainMenuItemsProvider() {};
++        virtual bool IsCustomMenuAvailable() = 0;
++        virtual MenuItemDefinitions* MainMenuItems() = 0;
++        virtual void EnterRootMenu() = 0;
++        virtual void EnterSubMenu(cOsdItem* item) = 0;
++        virtual bool LeaveSubMenu() = 0;
++        virtual cOsdMenu* Execute(cOsdItem* item) = 0;
++};
++
++#endif //__MAINMENUITEMSPROVIDER_H
++#endif /* MENUORG */
+diff -NaurwB vdr-1.7.10/Makefile vdr-1.7.10-patched/Makefile
+--- vdr-1.7.10/Makefile        2009-10-18 15:59:25.000000000 +0200
++++ vdr-1.7.10-patched/Makefile        2009-12-18 06:25:25.000000000 +0100
+@@ -43,6 +43,18 @@
+        skinclassic.o skins.o skinsttng.o sources.o spu.o status.o svdrp.o themes.o thread.o\
+        timers.o tools.o transfer.o vdr.o videodir.o
++ifdef WAREAGLEICON
++OBJS += iconpatch.o
++endif
++
++ifdef SETUP
++OBJS += tinystr.o tinyxml.o tinyxmlerror.o tinyxmlparser.o submenu.o
++endif
++
++ifdef TTXTSUBS
++OBJS += vdrttxtsubshooks.o
++endif
++
+ ifndef NO_KBD
+ DEFINES += -DREMOTE_KBD
+ endif
+@@ -72,6 +84,14 @@
+ VDRVERSION = $(shell sed -ne '/define VDRVERSION/s/^.*"\(.*\)".*$$/\1/p' config.h)
+ APIVERSION = $(shell sed -ne '/define APIVERSION/s/^.*"\(.*\)".*$$/\1/p' config.h)
++ifdef DVDARCHIVE
++ifdef DVDCHAPJUMP
++LIBS += -ldvdread
++INCLUDES += -I/usr/include/dvdread
++DEFINES += -DUSE_DVDCHAPJUMP
++endif
++endif
++
+ all: vdr i18n
+ # Implicit rules:
+@@ -137,6 +157,26 @@
+ # Plugins:
++ifdef PLUGINAPI
++DEFINES += -DUSE_PLUGINAPI
++plugins: include-dir
++      @failed="";\
++      noapiv="";\
++      for i in `ls $(PLUGINDIR)/src | grep -v '[^a-z0-9]'`; do\
++          echo "Plugin $$i:";\
++          if ! grep -q "\$$(LIBDIR)/.*\$$(APIVERSION)" "$(PLUGINDIR)/src/$$i/Makefile" ; then\
++             sed -i -e s/VDRVERSION/APIVERSION/g $(PLUGINDIR)/src/$$i/Makefile;\
++             if ! grep -q "\$$(LIBDIR)/.*\$$(APIVERSION)" "$(PLUGINDIR)/src/$$i/Makefile" ; then\
++                echo "ERROR: plugin $$i doesn't honor APIVERSION - not compiled!";\
++                noapiv="$$noapiv $$i";\
++                continue;\
++                fi;\
++             fi;\
++          $(MAKE) -C "$(PLUGINDIR)/src/$$i" all || failed="$$failed $$i";\
++          done;\
++      if [ -n "$$noapiv" ] ; then echo; echo "*** plugins without APIVERSION:$$noapiv"; echo; fi;\
++      if [ -n "$$failed" ] ; then echo; echo "*** failed plugins:$$failed"; echo; fi
++else
+ plugins: include-dir
+       @failed="";\
+       noapiv="";\
+@@ -151,6 +191,7 @@
+           done;\
+       if [ -n "$$noapiv" ] ; then echo; echo "*** plugins without APIVERSION:$$noapiv"; echo; fi;\
+       if [ -n "$$failed" ] ; then echo; echo "*** failed plugins:$$failed"; echo; exit 1; fi
++endif
+ clean-plugins:
+       @for i in `ls $(PLUGINDIR)/src | grep -v '[^a-z0-9]'`; do $(MAKE) -C "$(PLUGINDIR)/src/$$i" clean; done
+diff -NaurwB vdr-1.7.10/MANUAL vdr-1.7.10-patched/MANUAL
+--- vdr-1.7.10/MANUAL  2009-11-22 15:28:15.000000000 +0100
++++ vdr-1.7.10-patched/MANUAL  2009-12-18 06:25:25.000000000 +0100
+@@ -822,6 +822,30 @@
+                          0 resulting in a file named 'resume', and any other
+                          value resulting in 'resume.n'.
++  Jump&Play = no         Turns playing on or off after jumping forward to the
++                         next editing mark with the '9' key.
++
++  Play&Jump = no         Turns automatic jumping over commercial breaks on or
++                         off. This includes jumping to the first mark, if the
++                         replay starts at the beginning of a recording - and
++                         stopping the replay at the last mark.
++                         With this setting enabled, the behaviour of the '8'
++                         key during replay is changed too. It moves the actual
++                         replay position not only three seconds before the
++                         next "start" mark, but also before the next "end"
++                         mark. This can be used to test, if the editing marks
++                         are correctly positioned for a "smooth" jump over a
++                         commercial break.
++
++  Pause at last mark = no
++                         Turns pausing of replay at the last editing mark on or
++                         off.
++
++  Reload marks = no      Turns reloading of editing marks on or off. This can
++                         be used if an external programme adjusts the editing
++                         marks, e.g. noad in online mode. The marks are reloaded
++                         in 10 seconds intervals.
++
+   Miscellaneous:
+   Min. event timeout = 30
+diff -NaurwB vdr-1.7.10/menu.c vdr-1.7.10-patched/menu.c
+--- vdr-1.7.10/menu.c  2009-06-21 11:56:06.000000000 +0200
++++ vdr-1.7.10-patched/menu.c  2009-12-18 06:30:06.000000000 +0100
+@@ -8,12 +8,18 @@
+  */
+ #include "menu.h"
++#ifdef USE_WAREAGLEICON
++#include "iconpatch.h"
++#endif /* WAREAGLEICON */
+ #include <ctype.h>
+ #include <limits.h>
+ #include <math.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
++#ifdef USE_LIEMIEXT
++#include <math.h>
++#endif /* LIEMIEXT */
+ #include "channels.h"
+ #include "config.h"
+ #include "cutter.h"
+@@ -30,6 +36,13 @@
+ #include "timers.h"
+ #include "transfer.h"
+ #include "videodir.h"
++#ifdef USE_MENUORG
++#include "menuorgpatch.h"
++#endif /* MENUORG */
++
++#ifdef USE_CMDRECCMDI18N
++extern const char *ConfigDirectory;
++#endif /* CMDRECCMDI18N */
+ #define MAXWAIT4EPGINFO   3 // seconds
+ #define MODETIMEOUT       3 // seconds
+@@ -190,10 +203,16 @@
+   cChannel *channel;
+   cChannel data;
+   char name[256];
++#ifdef USE_PLUGINPARAM
++  char pluginParam[256];
++#endif /* PLUGINPARAM */
+   void Setup(void);
+ public:
+   cMenuEditChannel(cChannel *Channel, bool New = false);
+   virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++  virtual const char* MenuKind() { return "MenuEditChannel"; }
++#endif /* GRAPHTFT */
+   };
+ cMenuEditChannel::cMenuEditChannel(cChannel *Channel, bool New)
+@@ -222,6 +241,9 @@
+   // Parameters for all types of sources:
+   strn0cpy(name, data.name, sizeof(name));
++#ifdef USE_PLUGINPARAM
++  strn0cpy(pluginParam, data.pluginParam, sizeof(pluginParam));
++#endif /* PLUGINPARAM */
+   Add(new cMenuEditStrItem( tr("Name"),          name, sizeof(name)));
+   Add(new cMenuEditSrcItem( tr("Source"),       &data.source));
+   Add(new cMenuEditIntItem( tr("Frequency"),    &data.frequency));
+@@ -254,6 +276,9 @@
+   ST("  T")  Add(new cMenuEditMapItem( tr("Guard"),        &data.guard,        GuardValues));
+   ST("  T")  Add(new cMenuEditMapItem( tr("Hierarchy"),    &data.hierarchy,    HierarchyValues));
+   ST(" S ")  Add(new cMenuEditMapItem( tr("Rolloff"),      &data.rollOff,      RollOffValues));
++#ifdef USE_PLUGINPARAM
++  ST("P  ")  Add(new cMenuEditStrItem( tr("Parameters"),    pluginParam, sizeof(pluginParam), tr(FileNameChars)));
++#endif /* PLUGINPARAM */
+   SetCurrent(Get(current));
+   Display();
+@@ -268,6 +293,9 @@
+      if (Key == kOk) {
+         if (Channels.HasUniqueChannelID(&data, channel)) {
+            data.name = strcpyrealloc(data.name, name);
++#ifdef USE_PLUGINPARAM
++           data.pluginParam = strcpyrealloc(data.pluginParam, pluginParam);
++#endif /* PLUGINPARAM */
+            if (channel) {
+               *channel = data;
+               isyslog("edited channel %d %s", channel->Number(), *data.ToText());
+@@ -341,6 +369,16 @@
+   if (!channel->GroupSep()) {
+      if (sortMode == csmProvider)
+         buffer = cString::sprintf("%d\t%s - %s", channel->Number(), channel->Provider(), channel->Name());
++#ifdef USE_WAREAGLEICON
++     else if (Setup.WarEagleIcons) {
++        if (channel->Vpid() == 1 || channel->Vpid() == 0)
++           buffer = cString::sprintf("%d\t%s %-30s", channel->Number(), IsLangUtf8() ? ICON_RADIO_UTF8 : ICON_RADIO, channel->Name());
++        else if (channel->Ca() == 0)
++           buffer = cString::sprintf("%d\t%s %-30s", channel->Number(), IsLangUtf8() ? ICON_TV_UTF8 : ICON_TV, channel->Name());
++        else
++           buffer = cString::sprintf("%d\t%s %-30s", channel->Number(), IsLangUtf8() ? ICON_TV_CRYPTED_UTF8 : ICON_TV_CRYPTED, channel->Name());
++        }
++#endif /* WAREAGLEICON */
+      else
+         buffer = cString::sprintf("%d\t%s", channel->Number(), channel->Name());
+      }
+@@ -371,6 +409,9 @@
+   cMenuChannels(void);
+   ~cMenuChannels();
+   virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++  virtual const char* MenuKind() { return "MenuChannels"; }
++#endif /* GRAPHTFT */
+   };
+ cMenuChannels::cMenuChannels(void)
+@@ -637,14 +678,47 @@
+         data.SetFlags(tfActive);
+      channel = data.Channel()->Number();
+      Add(new cMenuEditBitItem( tr("Active"),       &data.flags, tfActive));
++#ifdef USE_PINPLUGIN
++     if (cOsd::pinValid) Add(new cMenuEditChanItem(tr("Channel"), &channel));
++     else {
++        cString buf = cString::sprintf("%s\t%s", tr("Channel"), Channels.GetByNumber(channel)->Name());
++        Add(new cOsdItem(buf));
++        }
++#else
+      Add(new cMenuEditChanItem(tr("Channel"),      &channel));
++#endif /* PINPLUGIN */
+      Add(new cMenuEditDateItem(tr("Day"),          &data.day, &data.weekdays));
+      Add(new cMenuEditTimeItem(tr("Start"),        &data.start));
+      Add(new cMenuEditTimeItem(tr("Stop"),         &data.stop));
+      Add(new cMenuEditBitItem( tr("VPS"),          &data.flags, tfVps));
+      Add(new cMenuEditIntItem( tr("Priority"),     &data.priority, 0, MAXPRIORITY));
+      Add(new cMenuEditIntItem( tr("Lifetime"),     &data.lifetime, 0, MAXLIFETIME));
++#ifdef USE_PINPLUGIN
++     if (cOsd::pinValid || !data.fskProtection) Add(new cMenuEditBoolItem(tr("Childlock"),&data.fskProtection));
++     else {
++        cString buf = cString::sprintf("%s\t%s", tr("Childlock"), data.fskProtection ? tr("yes") : tr("no"));
++        Add(new cOsdItem(buf));
++        }
++#endif /* PINPLUGIN */
++#ifdef USE_LIEMIEXT
++     char* p = strrchr(data.file, '~');
++     if (p) {
++        p++;
++        Utf8Strn0Cpy(name, p, sizeof(name));
++        Utf8Strn0Cpy(path, data.file, sizeof(path));
++        p = strrchr(path, '~');
++        if (p)
++           p[0] = 0;
++        }
++     else {
++        Utf8Strn0Cpy(name, data.file, sizeof(name));
++        Utf8Strn0Cpy(path, "", sizeof(path));
++        }
++     Add(new cMenuEditStrItem( tr("File"),          name, sizeof(name), tr(FileNameChars)));
++     Add(new cMenuEditRecPathItem(tr("Path"),       path, sizeof(path)));
++#else
+      Add(new cMenuEditStrItem( tr("File"),          data.file, sizeof(data.file)));
++#endif /* LIEMIEXT */
+      SetFirstDayItem();
+      }
+   Timers.IncBeingEdited();
+@@ -684,6 +758,12 @@
+                           Skins.Message(mtError, tr("*** Invalid Channel ***"));
+                           break;
+                           }
++#ifdef USE_LIEMIEXT
++                       if (strlen(path))
++                          snprintf(data.file, sizeof(data.file), "%s~%s", path, name);
++                       else
++                          snprintf(data.file, sizeof(data.file), "%s", name);
++#endif /* LIEMIEXT */
+                        if (!*data.file)
+                           strcpy(data.file, data.Channel()->ShortName(true));
+                        if (timer) {
+@@ -711,13 +791,37 @@
+   return state;
+ }
++#ifdef USE_TIMERCMD
++// --- cMenuCommands ---------------------------------------------------------
++// declaration shifted so it can be used in cMenuTimers
++class cMenuCommands : public cOsdMenu {
++private:
++  cCommands *commands;
++  char *parameters;
++  eOSState Execute(void);
++public:
++  cMenuCommands(const char *Title, cCommands *Commands, const char *Parameters = NULL);
++  virtual ~cMenuCommands();
++  virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++  virtual const char* MenuKind() { return "MenuCommands"; }
++#endif /* GRAPHTFT - passt das so? */
++  };
++#endif /* TIMERCMD */
++
+ // --- cMenuTimerItem --------------------------------------------------------
+ class cMenuTimerItem : public cOsdItem {
+ private:
+   cTimer *timer;
++#ifdef USE_TIMERINFO
++  char diskStatus;
++#endif /* TIMERINFO */
+ public:
+   cMenuTimerItem(cTimer *Timer);
++#ifdef USE_TIMERINFO
++  void SetDiskStatus(char DiskStatus);
++#endif /* TIMERINFO */
+   virtual int Compare(const cListObject &ListObject) const;
+   virtual void Set(void);
+   cTimer *Timer(void) { return timer; }
+@@ -726,6 +830,9 @@
+ cMenuTimerItem::cMenuTimerItem(cTimer *Timer)
+ {
+   timer = Timer;
++#ifdef USE_TIMERINFO
++  diskStatus = ' ';
++#endif /* TIMERINFO */
+   Set();
+ }
+@@ -751,8 +858,56 @@
+      strftime(buffer, sizeof(buffer), "%Y%m%d", &tm_r);
+      day = buffer;
+      }
++#ifdef USE_LIEMIEXT
++  if (!Setup.ShowTimerStop) {
++#ifdef USE_TIMERINFO
++#ifdef USE_WAREAGLEICON
++     SetText(cString::sprintf("%c%s\t%d\t%s%s%s\t%02d:%02d\t%s",
++#else
++     SetText(cString::sprintf("%c%c\t%d\t%s%s%s\t%02d:%02d\t%s",
++#endif /* WAREAGLEICON */
++                       diskStatus,
++#else
++#ifdef USE_WAREAGLEICON
++     SetText(cString::sprintf("%s\t%d\t%s%s%s\t%02d:%02d\t%s",
++#else
++     SetText(cString::sprintf("%c\t%d\t%s%s%s\t%02d:%02d\t%s",
++#endif /* WAREAGLEICON */
++#endif /* TIMERINFO */
++#ifdef USE_WAREAGLEICON
++                       !(timer->HasFlags(tfActive)) ? " " : timer->FirstDay() ? Setup.WarEagleIcons ? IsLangUtf8() ? ICON_ARROW_UTF8 : ICON_ARROW : "!" : timer->Recording() ? Setup.WarEagleIcons ? IsLangUtf8() ? ICON_REC_UTF8 : ICON_REC : "#" : Setup.WarEagleIcons ? IsLangUtf8() ? ICON_CLOCK_UTF8 : ICON_CLOCK : ">",
++#else
++                       !(timer->HasFlags(tfActive)) ? ' ' : timer->FirstDay() ? '!' : timer->Recording() ? '#' : '>',
++#endif /* WAREAGLEICON */
++                       timer->Channel()->Number(),
++                       *name,
++                       *name && **name ? " " : "",
++                       *day,
++                       timer->Start() / 100,
++                       timer->Start() % 100,
++                       timer->File()));
++     }
++  else {
++#endif /* LIEMIEXT */
++#ifdef USE_TIMERINFO
++#ifdef USE_WAREAGLEICON
++  SetText(cString::sprintf("%c%s\t%d\t%s%s%s\t%02d:%02d\t%02d:%02d\t%s",
++#else
++  SetText(cString::sprintf("%c%c\t%d\t%s%s%s\t%02d:%02d\t%02d:%02d\t%s",
++#endif /* WAREAGLEICON */
++                    diskStatus,
++#else
++#ifdef USE_WAREAGLEICON
++  SetText(cString::sprintf("%s\t%d\t%s%s%s\t%02d:%02d\t%02d:%02d\t%s",
++#else
+   SetText(cString::sprintf("%c\t%d\t%s%s%s\t%02d:%02d\t%02d:%02d\t%s",
++#endif /* WAREAGLEICON */
++#endif /* TIMERINFO */
++#ifdef USE_WAREAGLEICON
++                    !(timer->HasFlags(tfActive)) ? " " : timer->FirstDay() ? Setup.WarEagleIcons ? IsLangUtf8() ? ICON_ARROW_UTF8 : ICON_ARROW : "!" : timer->Recording() ? Setup.WarEagleIcons ? IsLangUtf8() ? ICON_REC_UTF8 : ICON_REC : "#" : Setup.WarEagleIcons ? IsLangUtf8() ? ICON_CLOCK_UTF8 : ICON_CLOCK : ">",
++#else
+                     !(timer->HasFlags(tfActive)) ? ' ' : timer->FirstDay() ? '!' : timer->Recording() ? '#' : '>',
++#endif /* WAREAGLEICON */
+                     timer->Channel()->Number(),
+                     *name,
+                     *name && **name ? " " : "",
+@@ -762,8 +917,64 @@
+                     timer->Stop() / 100,
+                     timer->Stop() % 100,
+                     timer->File()));
++#ifdef USE_LIEMIEXT
++     }
++#endif /* LIEMIEXT */
++}
++
++#ifdef USE_TIMERINFO
++void cMenuTimerItem::SetDiskStatus(char DiskStatus)
++{
++  diskStatus = DiskStatus;
++  Set();
+ }
++// --- cTimerEntry -----------------------------------------------------------
++
++class cTimerEntry : public cListObject {
++private:
++  cMenuTimerItem *item;
++  const cTimer *timer;
++  time_t start;
++public:
++  cTimerEntry(cMenuTimerItem *item) : item(item), timer(item->Timer()), start(timer->StartTime()) {}
++  cTimerEntry(const cTimer *timer, time_t start) : item(NULL), timer(timer), start(start) {}
++  virtual int Compare(const cListObject &ListObject) const;
++  bool active(void) const { return timer->HasFlags(tfActive); }
++  time_t startTime(void) const { return start; }
++  int priority(void) const { return timer->Priority(); }
++  int duration(void) const;
++  bool repTimer(void) const { return !timer->IsSingleEvent(); }
++  bool isDummy(void) const { return item == NULL; }
++  const cTimer *Timer(void) const { return timer; }
++  void SetDiskStatus(char DiskStatus);
++  };
++
++int cTimerEntry::Compare(const cListObject &ListObject) const
++{
++  cTimerEntry *entry = (cTimerEntry *)&ListObject;
++  int r = startTime() - entry->startTime();
++  if (r == 0)
++     r = entry->priority() - priority();
++  return r;
++}
++
++int cTimerEntry::duration(void) const
++{
++  int dur = (timer->Stop()  / 100 * 60 + timer->Stop()  % 100) -
++            (timer->Start() / 100 * 60 + timer->Start() % 100);
++  if (dur < 0)
++     dur += 24 * 60;
++  return dur;
++}
++
++void cTimerEntry::SetDiskStatus(char DiskStatus)
++{
++  if (item)
++     item->SetDiskStatus(DiskStatus);
++}
++#endif /* TIMERINFO */
++
+ // --- cMenuTimers -----------------------------------------------------------
+ class cMenuTimers : public cOsdMenu {
+@@ -776,14 +987,31 @@
+   eOSState Info(void);
+   cTimer *CurrentTimer(void);
+   void SetHelpKeys(void);
++#ifdef USE_TIMERINFO
++  void ActualiseDiskStatus(void);
++  bool actualiseDiskStatus;
++#endif /* TIMERINFO */
++#ifdef USE_TIMERCMD
++  eOSState Commands(eKeys Key = kNone);
++#endif /* TIMERCMD */
+ public:
+   cMenuTimers(void);
+   virtual ~cMenuTimers();
++#ifdef USE_TIMERINFO
++  virtual void Display(void);
++#endif /* TIMERINFO */
+   virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++  virtual const char* MenuKind() { return "MenuTimers"; }
++#endif /* GRAPHTFT */
+   };
+ cMenuTimers::cMenuTimers(void)
++#ifdef USE_TIMERINFO
++:cOsdMenu(tr("Timers"), 3, CHNUMWIDTH, 10, 6, 6)
++#else
+ :cOsdMenu(tr("Timers"), 2, CHNUMWIDTH, 10, 6, 6)
++#endif /* TIMERINFO */
+ {
+   helpKeys = -1;
+   for (cTimer *timer = Timers.First(); timer; timer = Timers.Next(timer)) {
+@@ -794,6 +1022,9 @@
+   SetCurrent(First());
+   SetHelpKeys();
+   Timers.IncBeingEdited();
++#ifdef USE_TIMERINFO
++  actualiseDiskStatus = true;
++#endif /* TIMERINFO */
+ }
+ cMenuTimers::~cMenuTimers()
+@@ -832,7 +1063,11 @@
+      timer->OnOff();
+      timer->SetEventFromSchedule();
+      RefreshCurrent();
++#ifdef USE_TIMERINFO
++     Display();
++#else
+      DisplayCurrent(true);
++#endif /* TIMERINFO */
+      if (timer->FirstDay())
+         isyslog("timer %s first day set to %s", *timer->ToDescr(), *timer->PrintFirstDay());
+      else
+@@ -891,6 +1126,117 @@
+   return osContinue;
+ }
++#ifdef USE_TIMERCMD
++#define CHECK_2PTR_NULL(x_,y_) ((x_)? ((y_)? y_:""):"")
++
++eOSState cMenuTimers::Commands(eKeys Key)
++{
++  if (HasSubMenu() || Count() == 0)
++     return osContinue;
++  cTimer *ti = CurrentTimer();
++  if (ti) {
++     const cEvent *pEvent = ti->Event();
++     int iRecNumber=0;
++
++     if (!pEvent) {
++        Timers.SetEvents();
++        pEvent = ti->Event();
++        }
++     if (pEvent) {
++        // create a dummy recording to get the real filename
++        cRecording *rc_dummy = new cRecording(ti, pEvent);
++        Recordings.Load();
++        cRecording *rc = Recordings.GetByName(rc_dummy->FileName());
++
++        delete rc_dummy;
++        if (rc)
++           iRecNumber=rc->Index() + 1;
++        }
++     // TODO: Geht das so...?
++     // Parameter format TimerNumber 'ChannelId' Start Stop 'Titel' 'Subtitel' 'file' RecNumer
++     //                  1            2          3     4     5       6          7     8
++     cString parameter = cString::sprintf("%d '%s' %d %d '%s' '%s' '%s' %d", ti->Index(),
++                                         *ti->Channel()->GetChannelID().ToString(),
++                                         (int)ti->StartTime(),
++                                         (int)ti->StopTime(),
++                                         CHECK_2PTR_NULL(pEvent, pEvent->Title()),
++                                         CHECK_2PTR_NULL(pEvent, pEvent->ShortText()),
++                                         ti->File(),
++                                         iRecNumber);
++     isyslog("timercmd: %s", *parameter);
++     cMenuCommands *menu;
++     eOSState state = AddSubMenu(menu = new cMenuCommands(tr("Timer commands"), &TimerCommands, parameter));
++     if (Key != kNone)
++        state = menu->ProcessKey(Key);
++     return state;
++     }
++  return osContinue;
++}
++#endif /* TIMERCMD */
++
++#ifdef USE_TIMERINFO
++void cMenuTimers::ActualiseDiskStatus(void)
++{
++  if (!actualiseDiskStatus || !Count())
++     return;
++
++  // compute free disk space
++  int freeMB, freeMinutes, runshortMinutes;
++  VideoDiskSpace(&freeMB);
++  freeMinutes = int(double(freeMB) * 1.1 / MB_PER_MINUTE); // overestimate by 10 percent
++  runshortMinutes = freeMinutes / 5; // 20 Percent
++
++  // fill entries list
++  cTimerEntry *entry;
++  cList<cTimerEntry> entries;
++  for (cOsdItem *item = First(); item; item = Next(item))
++      entries.Add(new cTimerEntry((cMenuTimerItem *)item));
++
++  // search last start time
++  time_t last = 0;
++  for (entry = entries.First(); entry; entry = entries.Next(entry))
++      last = max(entry->startTime(), last);
++
++  // add entries for repeating timers
++  for (entry = entries.First(); entry; entry = entries.Next(entry))
++      if (entry->repTimer() && !entry->isDummy())
++  for (time_t start = cTimer::IncDay(entry->startTime(), 1);
++      start <= last;
++  start = cTimer::IncDay(start, 1))
++  if (entry->Timer()->DayMatches(start))
++     entries.Add(new cTimerEntry(entry->Timer(), start));
++
++  // set the disk-status
++  entries.Sort();
++  for (entry = entries.First(); entry; entry = entries.Next(entry)) {
++      char status = ' ';
++      if (entry->active()) {
++         freeMinutes -= entry->duration();
++         status = freeMinutes > runshortMinutes ? '+' : freeMinutes > 0 ? '~' /* ± 177 +/- */ : '-';
++         }
++      entry->SetDiskStatus(status);
++#ifdef DEBUG_TIMER_INFO
++      dsyslog("timer-info: %c | %d | %s | %s | %3d | %+5d -> %+5d",
++              status,
++              entry->startTime(),
++              entry->active() ? "aktiv " : "n.akt.",
++              entry->repTimer() ? entry->isDummy() ? "  dummy  " : "mehrmalig" : "einmalig ",
++              entry->duration(),
++              entry->active() ? freeMinutes + entry->duration() : freeMinutes,
++              freeMinutes);
++#endif
++      }
++
++  actualiseDiskStatus = false;
++}
++
++void cMenuTimers::Display(void)
++{
++  ActualiseDiskStatus();
++  cOsdMenu::Display();
++}
++#endif /* TIMERINFO */
++
+ eOSState cMenuTimers::ProcessKey(eKeys Key)
+ {
+   int TimerNumber = HasSubMenu() ? Count() : -1;
+@@ -899,18 +1245,40 @@
+   if (state == osUnknown) {
+      switch (Key) {
+        case kOk:     return Edit();
++#ifdef USE_TIMERINFO
++       case kRed:    actualiseDiskStatus = true;
++                     state = OnOff(); break; // must go through SetHelpKeys()!
++#else
+        case kRed:    state = OnOff(); break; // must go through SetHelpKeys()!
++#endif /* TIMERINFO */
+        case kGreen:  return New();
++#ifdef USE_TIMERINFO
++       case kYellow: actualiseDiskStatus = true;
++                     state = Delete(); break;
++#else
+        case kYellow: state = Delete(); break;
++#endif /* TIMERINFO */
+        case kInfo:
+        case kBlue:   return Info();
+                      break;
++#ifdef USE_TIMERCMD
++       case k1...k9: return Commands(Key);
++       case k0:      return (TimerCommands.Count()? Commands():osContinue);
++#endif /* TIMERCMD */
+        default: break;
+        }
+      }
++#ifdef USE_TIMERINFO
++  if (TimerNumber >= 0 && !HasSubMenu()) {
++     if (Timers.Get(TimerNumber)) // a newly created timer was confirmed with Ok
++        Add(new cMenuTimerItem(Timers.Get(TimerNumber)), true);
++     Sort();
++     actualiseDiskStatus = true;
++#else
+   if (TimerNumber >= 0 && !HasSubMenu() && Timers.Get(TimerNumber)) {
+      // a newly created timer was confirmed with Ok
+      Add(new cMenuTimerItem(Timers.Get(TimerNumber)), true);
++#endif /* TIMERINFO */
+      Display();
+      }
+   if (Key != kNone)
+@@ -940,6 +1308,9 @@
+ {
+   cOsdMenu::Display();
+   DisplayMenu()->SetEvent(event);
++#ifdef USE_GRAPHTFT
++  cStatus::MsgOsdSetEvent(event);
++#endif /* GRAPHTFT */
+   if (event->Description())
+      cStatus::MsgOsdTextItem(event->Description());
+ }
+@@ -987,7 +1358,12 @@
+   const cChannel *channel;
+   bool withDate;
+   int timerMatch;
++#ifdef USE_LIEMIEXT
++  bool withBar;
++  cMenuScheduleItem(const cEvent *Event, cChannel *Channel = NULL, bool WithDate = false, bool WithBar = false);
++#else
+   cMenuScheduleItem(const cEvent *Event, cChannel *Channel = NULL, bool WithDate = false);
++#endif /* LIEMIEXT */
+   static void SetSortMode(eScheduleSortMode SortMode) { sortMode = SortMode; }
+   static void IncSortMode(void) { sortMode = eScheduleSortMode((sortMode == ssmAllAll) ? ssmAllThis : sortMode + 1); }
+   static eScheduleSortMode SortMode(void) { return sortMode; }
+@@ -997,12 +1373,19 @@
+ cMenuScheduleItem::eScheduleSortMode cMenuScheduleItem::sortMode = ssmAllThis;
++#ifdef USE_LIEMIEXT
++cMenuScheduleItem::cMenuScheduleItem(const cEvent *Event, cChannel *Channel, bool WithDate, bool WithBar)
++#else
+ cMenuScheduleItem::cMenuScheduleItem(const cEvent *Event, cChannel *Channel, bool WithDate)
++#endif /* LIEMIEXT */
+ {
+   event = Event;
+   channel = Channel;
+   withDate = WithDate;
+   timerMatch = tmNone;
++#ifdef USE_LIEMIEXT
++  withBar = WithBar;
++#endif /* LIEMIEXT */
+   Update(true);
+ }
+@@ -1017,7 +1400,29 @@
+   return r;
+ }
++#ifdef USE_LIEMIEXT
++static const char * const ProgressBar[7] =
++{
++  "[      ]",
++  "[|     ]",
++  "[||    ]",
++  "[|||   ]",
++  "[||||  ]",
++  "[||||| ]",
++  "[||||||]"
++};
++#endif /* LIEMIEXT */
++
++#ifdef USE_WAREAGLEICON
++static const char *TimerMatchChars[9] =
++{
++  " ", "t", "T",
++  ICON_BLANK, ICON_CLOCK_UH, ICON_CLOCK,
++  ICON_BLANK_UTF8, ICON_CLOCK_UH_UTF8, ICON_CLOCK_UTF8
++};
++#else
+ static const char *TimerMatchChars = " tT";
++#endif /* WAREAGLEICON */
+ bool cMenuScheduleItem::Update(bool Force)
+ {
+@@ -1026,17 +1431,54 @@
+   Timers.GetMatch(event, &timerMatch);
+   if (Force || timerMatch != OldTimerMatch) {
+      cString buffer;
++#ifdef USE_WAREAGLEICON
++     const char *t = Setup.WarEagleIcons ? IsLangUtf8() ? TimerMatchChars[timerMatch+6] : TimerMatchChars[timerMatch+3] : TimerMatchChars[timerMatch];
++     const char *v = event->Vps() && (event->Vps() - event->StartTime()) ? Setup.WarEagleIcons ? IsLangUtf8() ? ICON_VPS_UTF8 : ICON_VPS : "V" : " ";
++     const char *r = event->SeenWithin(30) && event->IsRunning() ? Setup.WarEagleIcons ? IsLangUtf8() ? ICON_RUNNING_UTF8 : ICON_RUNNING : "*" : " ";
++#else
+      char t = TimerMatchChars[timerMatch];
+      char v = event->Vps() && (event->Vps() - event->StartTime()) ? 'V' : ' ';
+      char r = event->SeenWithin(30) && event->IsRunning() ? '*' : ' ';
++#endif /* WAREAGLEICON */
+      const char *csn = channel ? channel->ShortName(true) : NULL;
+      cString eds = event->GetDateString();
+      if (channel && withDate)
++#ifdef USE_WAREAGLEICON
++        buffer = cString::sprintf("%d\t%.*s\t%.*s\t%s\t%s%s%s\t%s", channel->Number(), Utf8SymChars(csn, 6), csn, Utf8SymChars(eds, 6), *eds, *event->GetTimeString(), t, v, r, event->Title());
++#else
+         buffer = cString::sprintf("%d\t%.*s\t%.*s\t%s\t%c%c%c\t%s", channel->Number(), Utf8SymChars(csn, 6), csn, Utf8SymChars(eds, 6), *eds, *event->GetTimeString(), t, v, r, event->Title());
++#endif /* WAREAGLEICON */
+      else if (channel)
++#ifdef USE_LIEMIEXT
++        if (Setup.ShowProgressBar && withBar) {
++           int progress = (int)roundf( (float)(time(NULL) - event->StartTime()) / (float)(event->Duration()) * 6.0 );
++           if (progress < 0) progress = 0;
++           else if (progress > 6) progress = 6;
++#ifdef USE_WAREAGLEICON
++           buffer = cString::sprintf("%d\t%.*s\t%s\t%s\t%s%s%s\t%s", channel->Number(), Utf8SymChars(csn, 6), csn, *event->GetTimeString(), ProgressBar[progress], t, v, r, event->Title());
++#else
++           buffer = cString::sprintf("%d\t%.*s\t%s\t%s\t%c%c%c\t%s", channel->Number(), Utf8SymChars(csn, 6), csn, *event->GetTimeString(), ProgressBar[progress], t, v, r, event->Title());
++#endif /* WAREAGLEICON */
++           }
++        else
++#ifdef USE_WAREAGLEICON
++           buffer = cString::sprintf("%d\t%.*s\t%s\t%s%s%s\t%s", channel->Number(), Utf8SymChars(csn, 6), csn, *event->GetTimeString(), t, v, r, event->Title());
++#else
+         buffer = cString::sprintf("%d\t%.*s\t%s\t%c%c%c\t%s", channel->Number(), Utf8SymChars(csn, 6), csn, *event->GetTimeString(), t, v, r, event->Title());
++#endif /* WAREAGLEICON */
++#else
++#ifdef USE_WAREAGLEICON
++        buffer = cString::sprintf("%d\t%.*s\t%s\t%s%s%s\t%s", channel->Number(), Utf8SymChars(csn, 6), csn, *event->GetTimeString(), t, v, r, event->Title());
++#else
++        buffer = cString::sprintf("%d\t%.*s\t%s\t%c%c%c\t%s", channel->Number(), Utf8SymChars(csn, 6), csn, *event->GetTimeString(), t, v, r, event->Title());
++#endif /* WAREAGLEICON */
++#endif /* LIEMIEXT */
+      else
++#ifdef USE_WAREAGLEICON
++        buffer = cString::sprintf("%.*s\t%s\t%s%s%s\t%s", Utf8SymChars(eds, 6), *eds, *event->GetTimeString(), t, v, r, event->Title());
++#else
+         buffer = cString::sprintf("%.*s\t%s\t%c%c%c\t%s", Utf8SymChars(eds, 6), *eds, *event->GetTimeString(), t, v, r, event->Title());
++#endif /* WAREAGLEICON */
+      SetText(buffer);
+      result = true;
+      }
+@@ -1062,13 +1504,21 @@
+   static void SetCurrentChannel(int ChannelNr) { currentChannel = ChannelNr; }
+   static const cEvent *ScheduleEvent(void);
+   virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++  virtual const char* MenuKind() { return now ? "MenuWhatsOnNow" : "MenuWhatsOnNext"; }
++  virtual void Display(void);
++#endif /* GRAPHTFT */
+   };
+ int cMenuWhatsOn::currentChannel = 0;
+ const cEvent *cMenuWhatsOn::scheduleEvent = NULL;
+ cMenuWhatsOn::cMenuWhatsOn(const cSchedules *Schedules, bool Now, int CurrentChannelNr)
++#ifdef USE_LIEMIEXT
++:cOsdMenu(Now ? tr("What's on now?") : tr("What's on next?"), CHNUMWIDTH, 7, 6, 4, 4)
++#else
+ :cOsdMenu(Now ? tr("What's on now?") : tr("What's on next?"), CHNUMWIDTH, 7, 6, 4)
++#endif /* LIEMIEXT */
+ {
+   now = Now;
+   helpKeys = -1;
+@@ -1080,7 +1530,11 @@
+          if (Schedule) {
+             const cEvent *Event = Now ? Schedule->GetPresentEvent() : Schedule->GetFollowingEvent();
+             if (Event)
++#ifdef USE_LIEMIEXT
++               Add(new cMenuScheduleItem(Event, Channel, false, Now), Channel->Number() == CurrentChannelNr);
++#else
+                Add(new cMenuScheduleItem(Event, Channel), Channel->Number() == CurrentChannelNr);
++#endif /* LIEMIEXT */
+             }
+          }
+       }
+@@ -1089,6 +1543,19 @@
+   SetHelpKeys();
+ }
++#ifdef USE_GRAPHTFT
++void cMenuWhatsOn::Display(void)
++{
++   cOsdMenu::Display();
++
++   if (Count() > 0) {
++      int ni = 0;
++      for (cOsdItem *item = First(); item; item = Next(item))
++          cStatus::MsgOsdEventItem(((cMenuScheduleItem*)item)->event, item->Text(), ni++, Count());
++      }
++}
++#endif /* GRAPHTFT */
++
+ bool cMenuWhatsOn::Update(void)
+ {
+   bool result = false;
+@@ -1229,6 +1696,10 @@
+   cMenuSchedule(void);
+   virtual ~cMenuSchedule();
+   virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++  virtual const char* MenuKind() { return "MenuSchedule"; }
++  virtual void Display(void);
++#endif /* GRAPHTFT */
+   };
+ cMenuSchedule::cMenuSchedule(void)
+@@ -1254,6 +1725,19 @@
+   cMenuWhatsOn::ScheduleEvent(); // makes sure any posted data is cleared
+ }
++#ifdef USE_GRAPHTFT
++void cMenuSchedule::Display(void)
++{
++   cOsdMenu::Display();
++
++   if (Count() > 0) {
++      int ni = 0;
++      for (cOsdItem *item = First(); item; item = Next(item))
++          cStatus::MsgOsdEventItem(((cMenuScheduleItem*)item)->event, item->Text(), ni++, Count());
++      }
++}
++#endif /* GRAPHTFT */
++
+ void cMenuSchedule::PrepareScheduleAllThis(const cEvent *Event, const cChannel *Channel)
+ {
+   Clear();
+@@ -1487,6 +1971,7 @@
+ // --- cMenuCommands ---------------------------------------------------------
++#ifndef USE_TIMERCMD
+ class cMenuCommands : public cOsdMenu {
+ private:
+   cCommands *commands;
+@@ -1496,7 +1981,11 @@
+   cMenuCommands(const char *Title, cCommands *Commands, const char *Parameters = NULL);
+   virtual ~cMenuCommands();
+   virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++  virtual const char* MenuKind() { return "MenuCommands"; }
++#endif /* GRAPHTFT */
+   };
++#endif /* TIMERCMD */
+ cMenuCommands::cMenuCommands(const char *Title, cCommands *Commands, const char *Parameters)
+ :cOsdMenu(Title)
+@@ -1518,6 +2007,12 @@
+   cCommand *command = commands->Get(Current());
+   if (command) {
+      bool confirmed = true;
++#ifdef USE_CMDSUBMENU
++     if (command->hasChilds()) {
++        AddSubMenu(new cMenuCommands(command->Title(), command->getChilds(), parameters));
++        return osContinue;
++        }
++#endif /* CMDSUBMENU */
+      if (command->Confirm())
+         confirmed = Interface->Confirm(cString::sprintf("%s?", command->Title()));
+      if (confirmed) {
+@@ -1568,6 +2063,9 @@
+   cMenuCam(cCamSlot *CamSlot);
+   virtual ~cMenuCam();
+   virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++  virtual const char* MenuKind() { return "MenuCam"; }
++#endif /* GRAPHTFT */
+   };
+ cMenuCam::cMenuCam(cCamSlot *CamSlot)
+@@ -1747,6 +2245,9 @@
+   cMenuRecording(const cRecording *Recording, bool WithButtons = false);
+   virtual void Display(void);
+   virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++  virtual const char* MenuKind() { return "MenuRecording"; }
++#endif /* GRAPHTFT */
+ };
+ cMenuRecording::cMenuRecording(const cRecording *Recording, bool WithButtons)
+@@ -1762,6 +2263,9 @@
+ {
+   cOsdMenu::Display();
+   DisplayMenu()->SetRecording(recording);
++#ifdef USE_GRAPHTFT
++  cStatus::MsgOsdSetRecording(recording);
++#endif /* GRAPHTFT */
+   if (recording->Info()->Description())
+      cStatus::MsgOsdTextItem(recording->Info()->Description());
+ }
+@@ -1822,7 +2326,11 @@
+   fileName = strdup(Recording->FileName());
+   name = NULL;
+   totalEntries = newEntries = 0;
++#ifdef USE_LIEMIEXT
++  SetText(Recording->Title('\t', true, Level, false));
++#else
+   SetText(Recording->Title('\t', true, Level));
++#endif /* LIEMIEXT */
+   if (*Text() == '\t')
+      name = strdup(Text() + 2); // 'Text() + 2' to skip the two '\t'
+ }
+@@ -1838,13 +2346,196 @@
+   totalEntries++;
+   if (New)
+      newEntries++;
++#ifdef USE_LIEMIEXT
++#ifdef USE_WAREAGLEICON
++  switch (Setup.ShowRecTime + Setup.ShowRecDate + Setup.ShowRecLength) {
++     case 0:
++          if (Setup.WarEagleIcons)
++             SetText(cString::sprintf("%s %s", IsLangUtf8() ? ICON_FOLDER_UTF8 : ICON_FOLDER, name));
++          else
++             SetText(cString::sprintf("%s", name));
++          break;
++     case 1:
++          if (Setup.WarEagleIcons)
++             SetText(cString::sprintf("%s %d\t%s", IsLangUtf8() ? ICON_FOLDER_UTF8 : ICON_FOLDER, totalEntries, name));
++          else
++             SetText(cString::sprintf("%d\t%s", totalEntries, name));
++          break;
++     case 2:
++     default:
++          if (Setup.WarEagleIcons)
++             SetText(cString::sprintf("%s (%d/%d)\t%s", IsLangUtf8() ? ICON_FOLDER_UTF8 : ICON_FOLDER, totalEntries, newEntries, name));
++          else
++             SetText(cString::sprintf("%d\t%d\t%s", totalEntries, newEntries, name));
++          break;
++     case 3:
++          if (Setup.WarEagleIcons)
++             SetText(cString::sprintf("%s (%d/%d)\t\t%s", IsLangUtf8() ? ICON_FOLDER_UTF8 : ICON_FOLDER, totalEntries, newEntries, name));
++          else
++             SetText(cString::sprintf("%d\t%d\t\t%s", totalEntries, newEntries, name));
++          break;
++     }
++#else
++  switch (Setup.ShowRecTime + Setup.ShowRecDate + Setup.ShowRecLength) {
++     case 0:
++          SetText(cString::sprintf("%s", name));
++          break;
++     case 1:
++          SetText(cString::sprintf("%d\t%s", totalEntries, name));
++          break;
++     case 2:
++     default:
++          SetText(cString::sprintf("%d\t%d\t%s", totalEntries, newEntries, name));
++          break;
++     case 3:
++          SetText(cString::sprintf("%d\t%d\t\t%s", totalEntries, newEntries, name));
++          break;
++     }
++#endif /* WAREAGLEICON */
++#else
++#ifdef USE_WAREAGLEICON
++  if (Setup.WarEagleIcons)
++     SetText(cString::sprintf("%s (%d/%d)\t%s", IsLangUtf8() ? ICON_FOLDER_UTF8 : ICON_FOLDER, totalEntries, newEntries, name));
++  else
++#endif /* WAREAGLEICON */
+   SetText(cString::sprintf("%d\t%d\t%s", totalEntries, newEntries, name));
++#endif /* LIEMIEXT */
++}
++
++#ifdef USE_LIEMIEXT
++// --- cMenuRenameRecording --------------------------------------------------
++
++class cMenuRenameRecording : public cOsdMenu {
++private:
++  char name[MaxFileName];
++  char path[MaxFileName];
++  cOsdItem *marksItem, *resumeItem;
++  bool isResume, isMarks;
++  cRecording *recording;
++public:
++  cMenuRenameRecording(cRecording *Recording);
++  virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++  virtual const char* MenuKind() { return "MenuRenameRecording"; }
++#endif /* GRAPHTFT */
++};
++
++cMenuRenameRecording::cMenuRenameRecording(cRecording *Recording)
++:cOsdMenu(tr("Rename recording"), 12)
++{
++  cMarks marks;
++
++  recording = Recording;
++
++  const char* pname = strrchr(recording->Name(), '~');
++  if (pname) {
++     Utf8Strn0Cpy(name, pname + 1, sizeof(name));
++     Utf8Strn0Cpy(path, recording->Name(), sizeof(path));
++     char *ppath = strrchr(path, '~');
++     if (ppath)
++        ppath[0] = 0;
++     }
++  else {
++     Utf8Strn0Cpy(name, recording->Name(), sizeof(name));
++     Utf8Strn0Cpy(path, "", sizeof(path));
++     }
++  Add(new cMenuEditStrItem(tr("Name"),      name,     sizeof(name), tr(FileNameChars)));
++  Add(new cMenuEditRecPathItem(tr("Path"),  path,     sizeof(path)                   ));
++
++  Add(new cOsdItem("", osUnknown, false));
++
++  Add(new cOsdItem(cString::sprintf("%s:\t%s", tr("Date"), *DayDateTime(recording->start)), osUnknown, false));
++
++  cChannel *channel = Channels.GetByChannelID(((cRecordingInfo *)recording->Info())->ChannelID());
++  if (channel)
++     Add(new cOsdItem(cString::sprintf("%s:\t%s", tr("Channel"), *ChannelString(channel, 0)), osUnknown, false));
++
++  int recLen = cIndexFile::Length(recording->FileName(), recording->IsPesRecording());
++  if (recLen >= 0)
++     Add(new cOsdItem(cString::sprintf("%s:\t%s", tr("Length"), *IndexToHMSF(recLen, false, recording->FramesPerSecond())), osUnknown, false));
++  else
++     recLen = 0;
++
++  int dirSize = DirSizeMB(recording->FileName());
++  double seconds = recLen / recording->FramesPerSecond();
++  cString bitRate = seconds ? cString::sprintf(" (%.2f MBit/s)", 8.0 * dirSize / seconds) : cString("");
++  Add(new cOsdItem(cString::sprintf("%s:\t%s", tr("Format"), recording->IsPesRecording() ? tr("PES") : tr("TS")), osUnknown, false));
++  Add(new cOsdItem((dirSize > 9999) ? cString::sprintf("%s:\t%.2f GB%s", tr("Size"), dirSize / 1024.0, *bitRate) : cString::sprintf("%s:\t%d MB%s", tr("Size"), dirSize, *bitRate), osUnknown, false));
++
++  Add(new cOsdItem("", osUnknown, false));
++
++  isMarks = marks.Load(recording->FileName()) && marks.Count();
++  marksItem = new cOsdItem(tr("Delete marks information?"), osUser1, isMarks);
++  Add(marksItem);
++
++  cResumeFile ResumeFile(recording->FileName(), recording->IsPesRecording());
++  isResume = (ResumeFile.Read() != -1);
++  resumeItem = new cOsdItem(tr("Delete resume information?"), osUser2, isResume);
++  Add(resumeItem);
++}
++
++eOSState cMenuRenameRecording::ProcessKey(eKeys Key)
++{
++  eOSState state = cOsdMenu::ProcessKey(Key);
++
++  if (state == osUnknown) {
++     if (Key == kOk) {
++        char buffer[MaxFileName];
++        if (Utf8StrLen(path))
++           snprintf(buffer, sizeof(buffer), "%s~%s", path, name);
++        else
++           snprintf(buffer, sizeof(buffer), "%s", name);
++        if (recording->Rename(buffer)) {
++           Recordings.ChangeState();
++           Recordings.TouchUpdate();
++           return osRecordings;
++           }
++        else
++           Skins.Message(mtError, tr("Error while accessing recording!"));
++        }
++     return osContinue;
++     }
++  else if (state == osUser1) {
++     if (isMarks && Interface->Confirm(tr("Delete marks information?"))) {
++        cMarks marks;
++        marks.Load(recording->FileName());
++        cMark *mark = marks.First();
++        while (mark) {
++          cMark *nextmark = marks.Next(mark);
++          marks.Del(mark);
++          mark = nextmark;
++          }
++        marks.Save();
++        isMarks = false;
++        marksItem->SetSelectable(isMarks);
++        SetCurrent(First());
++        Display();
++        }
++     return osContinue;
++     }
++  else if (state == osUser2) {
++     if (isResume && Interface->Confirm(tr("Delete resume information?"))) {
++        cResumeFile ResumeFile(recording->FileName(), recording->IsPesRecording());
++        ResumeFile.Delete();
++        isResume = false;
++        resumeItem->SetSelectable(isResume);
++        SetCurrent(First());
++        Display();
++        }
++     return osContinue;
+ }
++  return state;
++}
++#endif /* LIEMIEXT */
+ // --- cMenuRecordings -------------------------------------------------------
+ cMenuRecordings::cMenuRecordings(const char *Base, int Level, bool OpenSubMenus)
++#ifdef USE_LIEMIEXT
++:cOsdMenu(Base ? Base : tr("Recordings"), 9, 7, 7)
++#else
+ :cOsdMenu(Base ? Base : tr("Recordings"), 9, 7)
++#endif /* LIEMIEXT */
+ {
+   base = Base ? strdup(Base) : NULL;
+   level = Setup.RecordingDirs ? Level : -1;
+@@ -1921,7 +2612,12 @@
+   for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording)) {
+       if (!base || (strstr(recording->Name(), base) == recording->Name() && recording->Name()[strlen(base)] == '~')) {
+          cMenuRecordingItem *Item = new cMenuRecordingItem(recording, level);
++#ifdef USE_PINPLUGIN
++         if ((*Item->Text() && (!LastItem || strcmp(Item->Text(), LastItemText) != 0))
++            && (!cStatus::MsgReplayProtected(GetRecording(Item), Item->Name(), base, Item->IsDirectory(), true))) {
++#else
+          if (*Item->Text() && (!LastItem || strcmp(Item->Text(), LastItemText) != 0)) {
++#endif /* PINPLUGIN */
+             Add(Item);
+             LastItem = Item;
+             free(LastItemText);
+@@ -1971,13 +2667,43 @@
+ {
+   cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current());
+   if (ri) {
++#ifdef USE_PINPLUGIN
++     if (cStatus::MsgReplayProtected(GetRecording(ri), ri->Name(), base, ri->IsDirectory()) == true)
++        return osContinue;
++#endif /* PINPLUGIN */
+      if (ri->IsDirectory())
+         Open();
+      else {
+         cRecording *recording = GetRecording(ri);
+         if (recording) {
++#ifdef USE_DVDARCHIVE
++           int mountRet = MOUNT_DVD_REPLAY;
++           if (recording->IsOnlyOnDvd()) {
++              mountRet = recording->MountDvd();
++              }
++           if (mountRet == MOUNT_DVD_REPLAY) {
++              cReplayControl::SetRecording(recording->FileName(), recording->Title());
++              return osReplay;
++              }
++           else if (mountRet == MOUNT_DVD_LAUNCH_DVD_PLUGIN) {
++              //launch DVD plugin here
++              cPlugin *p = cPluginManager::GetPlugin("dvd");
++              cOsdObject *osd = NULL;
++              if (p) {
++                 osd = p->MainMenuAction();
++                 delete osd;
++                 osd = NULL;
++                 return osEnd;
++                 }
++              else {
++                 Skins.Message(mtError, tr("DVD plugin is not installed!"));
++                 Skins.Flush();
++                 }
++              }
++#else
+            cReplayControl::SetRecording(recording->FileName(), recording->Title());
+            return osReplay;
++#endif /* DVDARCHIVE */
+            }
+         }
+      }
+@@ -2078,12 +2804,34 @@
+   return osContinue;
+ }
++#ifdef USE_LIEMIEXT
++eOSState cMenuRecordings::Rename(void)
++{
++  if (HasSubMenu() || Count() == 0)
++     return osContinue;
++  cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current());
++  if (ri && !ri->IsDirectory()) {
++     cRecording *recording = GetRecording(ri);
++     if (recording)
++        return AddSubMenu(new cMenuRenameRecording(recording));
++     }
++  return osContinue;
++}
++#endif /* LIEMIEXT */
++
+ eOSState cMenuRecordings::ProcessKey(eKeys Key)
+ {
+   bool HadSubMenu = HasSubMenu();
+   eOSState state = cOsdMenu::ProcessKey(Key);
+   if (state == osUnknown) {
++#ifdef USE_SORTRECORDS
++     const char *RecordingsSortModeTexts[MAXSORTMODES];
++     RecordingsSortModeTexts[0] = tr("main dir alphabetically, subdirs flexible");
++     RecordingsSortModeTexts[1] = tr("main dir by date, subdirs flexible");
++     RecordingsSortModeTexts[2] = tr("all alphabetically");
++     RecordingsSortModeTexts[3] = tr("all by date");
++#endif /* SORTRECORDS */
+      switch (Key) {
+        case kPlay:
+        case kOk:     return Play();
+@@ -2092,7 +2840,26 @@
+        case kYellow: return Delete();
+        case kInfo:
+        case kBlue:   return Info();
++#ifdef USE_SORTRECORDS
++       case k0:      Setup.RecordingsSortMode = ++Setup.RecordingsSortMode % MAXSORTMODES;
++                     Set(true);
++                     Skins.Message(mtStatus, cString::sprintf("%s %d: %s", tr("Sorting"), Setup.RecordingsSortMode, RecordingsSortModeTexts[Setup.RecordingsSortMode]));
++                     return osContinue;
++       case k1...k7: return Commands(Key);
++       case k8:      return Rename();
++       case k9:      Recordings.ToggleSortOrder();
++                     Set(true);
++                     return osContinue;
++#elif defined (USE_LIEMIEXT)
++       case k0:      DirOrderState = !DirOrderState;
++                     Set(true);
++                     return osContinue;
++       case k8:      return Rename();
++       case k9:
++       case k1...k7: return Commands(Key);
++#else
+        case k1...k9: return Commands(Key);
++#endif /* LIEMIEXT & SORTRECORDS */
+        case kNone:   if (Recordings.StateChanged(recordingsState))
+                         Set(true);
+                      break;
+@@ -2142,6 +2909,9 @@
+ class cMenuSetupOSD : public cMenuSetupBase {
+ private:
+   const char *useSmallFontTexts[3];
++#ifdef USE_LIEMIEXT
++  const char *mainMenuTitle[MAXMAINMENUTITLE];
++#endif /* LIEMIEXT */
+   int osdLanguageIndex;
+   int numSkins;
+   int originalSkinIndex;
+@@ -2157,6 +2927,9 @@
+   cMenuSetupOSD(void);
+   virtual ~cMenuSetupOSD();
+   virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++  virtual const char* MenuKind() { return "MenuSetupOsd"; }
++#endif /* GRAPHTFT */
+   };
+ cMenuSetupOSD::cMenuSetupOSD(void)
+@@ -2192,12 +2965,21 @@
+   useSmallFontTexts[0] = tr("never");
+   useSmallFontTexts[1] = tr("skin dependent");
+   useSmallFontTexts[2] = tr("always");
++#ifdef USE_LIEMIEXT
++  mainMenuTitle[0]=tr("default");
++  mainMenuTitle[1]=tr("VDR - text");
++  mainMenuTitle[2]=tr("text");
++  mainMenuTitle[3]=tr("VDR - version");
++#endif /* LIEMIEXT */
+   Clear();
+   SetSection(tr("OSD"));
+   Add(new cMenuEditStraItem(tr("Setup.OSD$Language"),               &osdLanguageIndex, I18nNumLanguagesWithLocale(), &I18nLanguages()->At(0)));
+   Add(new cMenuEditStraItem(tr("Setup.OSD$Skin"),                   &skinIndex, numSkins, skinDescriptions));
+   if (themes.NumThemes())
+   Add(new cMenuEditStraItem(tr("Setup.OSD$Theme"),                  &themeIndex, themes.NumThemes(), themes.Descriptions()));
++#ifdef USE_WAREAGLEICON
++  Add(new cMenuEditBoolItem(tr("Setup.OSD$WarEagle icons"),         &data.WarEagleIcons));
++#endif /* WAREAGLEICON */
+   Add(new cMenuEditPrcItem( tr("Setup.OSD$Left (%)"),               &data.OSDLeftP, 0.0, 0.5));
+   Add(new cMenuEditPrcItem( tr("Setup.OSD$Top (%)"),                &data.OSDTopP, 0.0, 0.5));
+   Add(new cMenuEditPrcItem( tr("Setup.OSD$Width (%)"),              &data.OSDWidthP, 0.5, 1.0));
+@@ -2219,6 +3001,24 @@
+   Add(new cMenuEditBoolItem(tr("Setup.OSD$Scroll wraps"),           &data.MenuScrollWrap));
+   Add(new cMenuEditBoolItem(tr("Setup.OSD$Menu key closes"),        &data.MenuKeyCloses));
+   Add(new cMenuEditBoolItem(tr("Setup.OSD$Recording directories"),  &data.RecordingDirs));
++#ifdef USE_LIEMIEXT
++  Add(new cMenuEditStraItem(tr("Setup.OSD$Main menu title"),        &data.MainMenuTitle, MAXMAINMENUTITLE, mainMenuTitle));
++  if (data.MainMenuTitle == 1 || data.MainMenuTitle == 2)
++     Add(new cMenuEditStrItem(tr("Setup.OSD$- Text"),                data.CustomMainMenuTitle, sizeof(data.CustomMainMenuTitle)));
++  Add(new cMenuEditBoolItem(tr("Setup.OSD$Main menu command position"), &data.MenuCmdPosition, tr("bottom"), tr("top")));
++#endif /* LIEMIEXT */
++#ifdef USE_VALIDINPUT
++  Add(new cMenuEditBoolItem(tr("Setup.OSD$Show valid input"),       &data.ShowValidInput));
++#endif /* VALIDINPUT */
++#ifdef USE_SOFTOSD
++  Add(new cMenuEditBoolItem(tr("Setup.OSD$Use SoftOSD"),              &data.UseSoftOsd));
++  if (data.UseSoftOsd) {
++     Add(new cMenuEditIntItem( tr("Setup.OSD$SoftOSD Rate"),          &data.SoftOsdRate,        10, 100));
++     Add(new cMenuEditIntItem( tr("Setup.OSD$SoftOSD FadeIn Steps"),  &data.SoftOsdFadeinSteps,  1, 100));
++     Add(new cMenuEditIntItem( tr("Setup.OSD$SoftOSD FadeOut Steps"), &data.SoftOsdFadeoutSteps, 1, 100));
++     Add(new cMenuEditBoolItem(tr("Setup.OSD$SoftOSD Palette Only"),  &data.SoftOsdPaletteOnly));
++     }
++#endif /* SOFTOSD */
+   SetCurrent(Get(current));
+   Display();
+ }
+@@ -2259,6 +3059,12 @@
+   int oldSkinIndex = skinIndex;
+   int oldOsdLanguageIndex = osdLanguageIndex;
++#ifdef USE_LIEMIEXT
++  int oldMainMenuTitle = data.MainMenuTitle;
++#endif /* LIEMIEXT */
++#ifdef USE_SOFTOSD
++  bool newUseSoftOsd = data.UseSoftOsd;
++#endif /* SOFTOSD */
+   eOSState state = cMenuSetupBase::ProcessKey(Key);
+   if (ModifiedAppearance) {
+@@ -2283,6 +3089,25 @@
+      Set();
+      I18nSetLanguage(OriginalOSDLanguage);
+      }
++
++#ifdef USE_LIEMIEXT
++  if (data.MainMenuTitle != oldMainMenuTitle)
++     Set();
++#endif /* LIEMIEXT */
++#ifdef USE_SOFTOSD
++  if (data.UseSoftOsd != newUseSoftOsd)
++     Set();
++#endif /* SOFTOSD */
++#ifdef USE_CMDRECCMDI18N
++  if (Key == kOk) {
++     // try to load translated command files if available, otherwise fallback to defaults
++     LoadCommandsI18n(Commands, AddDirectory(ConfigDirectory, "commands.conf"), true);
++     LoadCommandsI18n(RecordingCommands, AddDirectory(ConfigDirectory, "reccmds.conf"), true);
++#ifdef USE_TIMERCMD
++     LoadCommandsI18n(TimerCommands, AddDirectory(ConfigDirectory, "timercmds.conf"), true);
++#endif /* TIMERCMD */
++     }
++#endif /* CMDRECCMDI18N */
+   return state;
+ }
+@@ -2290,12 +3115,18 @@
+ class cMenuSetupEPG : public cMenuSetupBase {
+ private:
++#ifdef USE_NOEPG
++  const char *noEPGModes[2];
++#endif /* NOEPG */
+   int originalNumLanguages;
+   int numLanguages;
+   void Setup(void);
+ public:
+   cMenuSetupEPG(void);
+   virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++  virtual const char* MenuKind() { return "MenuSetupEpg"; }
++#endif /* GRAPHTFT */
+   };
+ cMenuSetupEPG::cMenuSetupEPG(void)
+@@ -2312,11 +3143,19 @@
+ {
+   int current = Current();
++#ifdef USE_NOEPG
++  noEPGModes[0] = tr("blacklist");
++  noEPGModes[1] = tr("whitelist");
++#endif /* NOEPG */
++
+   Clear();
+   Add(new cMenuEditIntItem( tr("Setup.EPG$EPG scan timeout (h)"),      &data.EPGScanTimeout));
+   Add(new cMenuEditIntItem( tr("Setup.EPG$EPG bugfix level"),          &data.EPGBugfixLevel, 0, MAXEPGBUGFIXLEVEL));
+   Add(new cMenuEditIntItem( tr("Setup.EPG$EPG linger time (min)"),     &data.EPGLinger, 0));
++#ifdef USE_LIEMIEXT
++  Add(new cMenuEditBoolItem(tr("Setup.EPG$Show progress bar"),         &data.ShowProgressBar));
++#endif /* LIEMIEXT */
+   Add(new cMenuEditBoolItem(tr("Setup.EPG$Set system time"),           &data.SetSystemTime));
+   if (data.SetSystemTime)
+      Add(new cMenuEditTranItem(tr("Setup.EPG$Use time from transponder"), &data.TimeTransponder, &data.TimeSource));
+@@ -2325,6 +3164,15 @@
+   for (int i = 0; i < numLanguages; i++)
+       // TRANSLATORS: note the singular!
+       Add(new cMenuEditStraItem(tr("Setup.EPG$Preferred language"),    &data.EPGLanguages[i], I18nLanguages()->Size(), &I18nLanguages()->At(0)));
++#ifdef USE_DDEPGENTRY
++  Add(new cMenuEditIntItem( tr("Setup.EPG$Period for double EPG search(min)"),  &data.DoubleEpgTimeDelta));
++  Add(new cMenuEditBoolItem(tr("Setup.EPG$Extern double Epg entry"),   &data.DoubleEpgAction, tr("adjust"), tr("delete")));
++  Add(new cMenuEditBoolItem(tr("Setup.EPG$Mix intern and extern EPG"), &data.MixEpgAction));
++  Add(new cMenuEditBoolItem(tr("Setup.EPG$Disable running VPS event"), &data.DisableVPS));
++#endif /* DDEPGENTRY */
++#ifdef USE_NOEPG
++  Add(new cMenuEditStraItem(tr("Setup.EPG$Mode of noEPG-Patch"),       &data.noEPGMode, 2, noEPGModes));
++#endif /* NOEPG */
+   SetCurrent(Get(current));
+   Display();
+@@ -2381,6 +3229,10 @@
+ class cMenuSetupDVB : public cMenuSetupBase {
+ private:
++#ifdef USE_DVBSETUP
++  const char *ChannelBlockers[7];
++  const char *ChannelBlockerModes[4];
++#endif /* DVBSETUP */
+   int originalNumAudioLanguages;
+   int numAudioLanguages;
+   int originalNumSubtitleLanguages;
+@@ -2391,6 +3243,9 @@
+ public:
+   cMenuSetupDVB(void);
+   virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++  virtual const char* MenuKind() { return "MenuSetupDvb"; }
++#endif /* GRAPHTFT */
+   };
+ cMenuSetupDVB::cMenuSetupDVB(void)
+@@ -2419,6 +3274,21 @@
+ {
+   int current = Current();
++#ifdef USE_DVBSETUP
++  ChannelBlockers[0] = tr("none");
++  ChannelBlockers[1] = tr("qam256");
++  ChannelBlockers[2] = tr("dvb-c");
++  ChannelBlockers[3] = tr("dvb-s");
++  ChannelBlockers[4] = tr("blacklist");
++  ChannelBlockers[5] = tr("whitelist");
++  ChannelBlockers[6] = tr("all");
++
++  ChannelBlockerModes[0] = tr("none");
++  ChannelBlockerModes[1] = tr("has decoder");
++  ChannelBlockerModes[2] = tr("is primary");
++  ChannelBlockerModes[3] = tr("has decoder + is primary");
++#endif /* DVBSETUP */
++
+   Clear();
+   Add(new cMenuEditIntItem( tr("Setup.DVB$Primary DVB interface"), &data.PrimaryDVB, 1, cDevice::NumDevices()));
+@@ -2439,6 +3309,11 @@
+      Add(new cMenuEditIntItem( tr("Setup.DVB$Subtitle foreground transparency"), &data.SubtitleFgTransparency, 0, 9));
+      Add(new cMenuEditIntItem( tr("Setup.DVB$Subtitle background transparency"), &data.SubtitleBgTransparency, 0, 10));
+      }
++#ifdef USE_DVBSETUP
++  Add(new cMenuEditBoolItem(tr("Setup.DVB$Use AC3-Transfer Fix"),        &data.DolbyTransferFix));
++  Add(new cMenuEditStraItem(tr("Setup.DVB$Channel Blocker"),             &data.ChannelBlocker, 7, ChannelBlockers));
++  Add(new cMenuEditStraItem(tr("Setup.DVB$Channel Blocker Filter Mode"), &data.ChannelBlockerMode, 4, ChannelBlockerModes));
++#endif /* DVBSETUP */
+   SetCurrent(Get(current));
+   Display();
+@@ -2520,6 +3395,9 @@
+ public:
+   cMenuSetupLNB(void);
+   virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++  virtual const char* MenuKind() { return "MenuSetupLnb"; }
++#endif /* GRAPHTFT */
+   };
+ cMenuSetupLNB::cMenuSetupLNB(void)
+@@ -2534,6 +3412,23 @@
+   Clear();
++#ifdef USE_LNBSHARE
++  int numSatDevices = 0;
++  for (int i = 0; i < cDevice::NumDevices(); i++) {
++      if (cDevice::GetDevice(i)->ProvidesSource(cSource::stSat)) numSatDevices++;
++      }
++  if (numSatDevices > 1) {
++     char tmp[30];
++     for (int i = 1; i <= cDevice::NumDevices(); i++) {
++         if (cDevice::GetDevice(i - 1)->ProvidesSource(cSource::stSat)) {
++            sprintf( tmp, tr("Setup.LNB$DVB device %d uses LNB No."), i);
++            Add(new cMenuEditIntItem( tmp, &data.CardUsesLNBnr[i - 1], 1, numSatDevices ));
++            }
++         }
++     }
++  Add(new cMenuEditBoolItem(tr("Setup.LNB$Log LNB usage"), &data.VerboseLNBlog));
++#endif /* LNBSHARE */
++
+   Add(new cMenuEditBoolItem(tr("Setup.LNB$Use DiSEqC"),               &data.DiSEqC));
+   if (!data.DiSEqC) {
+      Add(new cMenuEditIntItem( tr("Setup.LNB$SLOF (MHz)"),               &data.LnbSLOF));
+@@ -2550,6 +3445,10 @@
+   int oldDiSEqC = data.DiSEqC;
+   eOSState state = cMenuSetupBase::ProcessKey(Key);
++#ifdef USE_LNBSHARE
++  if (Key == kOk) cDevice::SetLNBnr();
++#endif /* LNBSHARE */
++
+   if (Key != kNone && data.DiSEqC != oldDiSEqC)
+      Setup();
+   return state;
+@@ -2600,6 +3499,9 @@
+ public:
+   cMenuSetupCAM(void);
+   virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++  virtual const char* MenuKind() { return "MenuSetupCam"; }
++#endif /* GRAPHTFT */
+   };
+ cMenuSetupCAM::cMenuSetupCAM(void)
+@@ -2674,13 +3576,72 @@
+ class cMenuSetupRecord : public cMenuSetupBase {
+ private:
++#ifdef USE_SORTRECORDS
++  const char *RecordingsSortModeTexts[MAXSORTMODES];
++#endif /* SORTRECORDS */
++#ifdef USE_DELTIMESHIFTREC
++  const char *DelTimeshiftRecValues[3];
++#endif /* DELTIMESHIFTREC */
+   const char *pauseKeyHandlingTexts[3];
+ public:
+   cMenuSetupRecord(void);
++#ifdef USE_DVLVIDPREFER
++  eOSState ProcessKey(eKeys key);
++
++private:
++  void Set(void);
++
++  int tmpNVidPrefer,
++      tmpUseVidPrefer;
++#endif /* DVLVIDPREFER */
+   };
++#ifdef USE_DVLVIDPREFER
+ cMenuSetupRecord::cMenuSetupRecord(void)
+ {
++  Set();
++}
++
++eOSState cMenuSetupRecord::ProcessKey(eKeys key)
++{
++  eOSState s = cMenuSetupBase::ProcessKey(key);;
++
++  if (key != kNone) {
++     if (tmpNVidPrefer != data.nVidPrefer || tmpUseVidPrefer != data.UseVidPrefer) {
++        int cur = Current();
++
++        tmpNVidPrefer = data.nVidPrefer;
++        tmpUseVidPrefer = data.UseVidPrefer;
++
++        Clear();
++        Set();
++        SetCurrent(Get(cur));
++        Display();
++        cMenuSetupBase::ProcessKey(kNone);
++        return osContinue;
++        }
++     }
++  return s;
++}
++
++#else
++cMenuSetupRecord::cMenuSetupRecord(void)
++#endif /* DVLVIDPREFER */
++#ifdef USE_DVLVIDPREFER
++void cMenuSetupRecord::Set(void)
++#endif /* DVLVIDPREFER */
++{
++#ifdef USE_SORTRECORDS
++  RecordingsSortModeTexts[0] = tr("main dir alphabetically, subdirs flexible");
++  RecordingsSortModeTexts[1] = tr("main dir by date, subdirs flexible");
++  RecordingsSortModeTexts[2] = tr("all alphabetically");
++  RecordingsSortModeTexts[3] = tr("all by date");
++#endif /* SORTRECORDS */
++#ifdef USE_DELTIMESHIFTREC
++  DelTimeshiftRecValues[0] = tr("request");
++  DelTimeshiftRecValues[1] = tr("no");
++  DelTimeshiftRecValues[2] = tr("yes");
++#endif /* DELTIMESHIFTREC */  
+   pauseKeyHandlingTexts[0] = tr("do not pause live video");
+   pauseKeyHandlingTexts[1] = tr("confirm pause live video");
+   pauseKeyHandlingTexts[2] = tr("pause live video");
+@@ -2693,14 +3654,61 @@
+   Add(new cMenuEditStraItem(tr("Setup.Recording$Pause key handling"),        &data.PauseKeyHandling, 3, pauseKeyHandlingTexts));
+   Add(new cMenuEditIntItem( tr("Setup.Recording$Pause priority"),            &data.PausePriority, 0, MAXPRIORITY));
+   Add(new cMenuEditIntItem( tr("Setup.Recording$Pause lifetime (d)"),        &data.PauseLifetime, 0, MAXLIFETIME));
++#ifdef USE_DOLBYINREC
++  Add(new cMenuEditBoolItem(tr("Setup.Recording$Record Dolby Digital"),      &data.UseDolbyInRecordings));
++#endif /* DOLBYINREC */
++#ifdef USE_DVLVIDPREFER
++  tmpNVidPrefer = data.nVidPrefer;
++  tmpUseVidPrefer = data.UseVidPrefer;
++
++  Add(new cMenuEditBoolItem(tr("Setup.Recording$Video directory policy"),    &data.UseVidPrefer));
++  if (data.UseVidPrefer != 0) {
++     char tmp[ 64 ];
++     Add(new cMenuEditIntItem(tr("Setup.Recording$Number of video directories"), &data.nVidPrefer, 1, DVLVIDPREFER_MAX));
++     for (int zz = 0; zz < data.nVidPrefer; zz++) {
++         sprintf(tmp, tr("Setup.Recording$Video %d priority"), zz);
++         Add(new cMenuEditIntItem(tmp, &data.VidPreferPrio[ zz ], 0, 99));
++         sprintf(tmp, tr("Setup.Recording$Video %d min. free MB"), zz);
++         Add(new cMenuEditIntItem(tmp, &data.VidPreferSize[ zz ], -1, 99999));
++         }
++     }
++#endif /* DVLVIDPREFER */
+   Add(new cMenuEditBoolItem(tr("Setup.Recording$Use episode name"),          &data.UseSubtitle));
++#ifdef USE_DVLFRIENDLYFNAMES
++  Add(new cMenuEditBoolItem(tr("Setup.Recording$Friendly filenames"),        &data.UseFriendlyFNames));
++#endif /* DVLFRIENDLYFNAMES */
+   Add(new cMenuEditBoolItem(tr("Setup.Recording$Use VPS"),                   &data.UseVps));
+   Add(new cMenuEditIntItem( tr("Setup.Recording$VPS margin (s)"),            &data.VpsMargin, 0));
+   Add(new cMenuEditBoolItem(tr("Setup.Recording$Mark instant recording"),    &data.MarkInstantRecord));
+   Add(new cMenuEditStrItem( tr("Setup.Recording$Name instant recording"),     data.NameInstantRecord, sizeof(data.NameInstantRecord)));
+   Add(new cMenuEditIntItem( tr("Setup.Recording$Instant rec. time (min)"),   &data.InstantRecordTime, 1, MAXINSTANTRECTIME));
+   Add(new cMenuEditIntItem( tr("Setup.Recording$Max. video file size (MB)"), &data.MaxVideoFileSize, MINVIDEOFILESIZE, MAXVIDEOFILESIZETS));
++#ifdef USE_HARDLINKCUTTER
++  Add(new cMenuEditIntItem( tr("Setup.Recording$Max. recording size (GB)"),  &data.MaxRecordingSize, MINRECORDINGSIZE, MAXRECORDINGSIZE));
++#endif /* HARDLINKCUTTER */
+   Add(new cMenuEditBoolItem(tr("Setup.Recording$Split edited files"),        &data.SplitEditedFiles));
++#ifdef USE_HARDLINKCUTTER
++  Add(new cMenuEditBoolItem(tr("Setup.Recording$Hard Link Cutter"),          &data.HardLinkCutter));
++#endif /* HARDLINKCUTTER */
++#ifdef USE_DELTIMESHIFTREC
++  Add(new cMenuEditStraItem(tr("Setup.Recording$Delete timeshift recording"), &data.DelTimeshiftRec, 3, DelTimeshiftRecValues));
++#endif /* DELTIMESHIFTREC */
++#ifdef USE_LIEMIEXT
++  Add(new cMenuEditBoolItem(tr("Setup.Recording$Show date"),                 &data.ShowRecDate));
++  Add(new cMenuEditBoolItem(tr("Setup.Recording$Show time"),                 &data.ShowRecTime));
++  Add(new cMenuEditBoolItem(tr("Setup.Recording$Show length"),               &data.ShowRecLength));
++  Add(new cMenuEditBoolItem(tr("Setup.Recording$Show end of timer"),         &data.ShowTimerStop));
++#endif /* LIEMIEXT */
++#ifdef USE_SORTRECORDS
++  Add(new cMenuEditStraItem(tr("Setup.Recording$Sort recordings by"),        &data.RecordingsSortMode, MAXSORTMODES, RecordingsSortModeTexts));
++  Add(new cMenuEditBoolItem(tr("Setup.Recording$Sort directories before recordings"), &data.RecordingsSortDirsFirst));
++#endif /* SORTRECORDS */
++#ifdef USE_CUTTERQUEUE
++  Add(new cMenuEditBoolItem(tr("Setup.Recording$Cutter auto delete"),        &data.CutterAutoDelete));
++#endif /* CUTTERQUEUE */
++#ifdef USE_CUTTIME
++  Add(new cMenuEditBoolItem(tr("Setup.Recording$Cutter adjust starttime"),   &data.CutTime));
++#endif /* CUTTIME */
+ }
+ // --- cMenuSetupReplay ------------------------------------------------------
+@@ -2718,6 +3726,31 @@
+   Add(new cMenuEditBoolItem(tr("Setup.Replay$Multi speed mode"), &data.MultiSpeedMode));
+   Add(new cMenuEditBoolItem(tr("Setup.Replay$Show replay mode"), &data.ShowReplayMode));
+   Add(new cMenuEditIntItem(tr("Setup.Replay$Resume ID"), &data.ResumeID, 0, 99));
++#ifdef USE_JUMPPLAY
++  Add(new cMenuEditBoolItem(tr("Setup.Replay$Jump&Play"),        &data.JumpPlay));
++  Add(new cMenuEditBoolItem(tr("Setup.Replay$Play&Jump"),        &data.PlayJump));
++  Add(new cMenuEditBoolItem(tr("Setup.Replay$Pause at last mark"), &data.PauseLastMark));
++  Add(new cMenuEditBoolItem(tr("Setup.Replay$Reload marks"),     &data.ReloadMarks));
++#endif /* JUMPPLAY */
++#ifdef USE_LIEMIEXT
++  Add(new cMenuEditIntItem(tr("Setup.Replay$Skip Seconds"),      &data.JumpSeconds));
++  Add(new cMenuEditIntItem(tr("Setup.Replay$Skip Seconds Slow"), &data.JumpSecondsSlow));
++#endif /* LIEMIEXT */
++#ifdef USE_DVDARCHIVE
++  static const char *dvddisplaymode[3];
++  dvddisplaymode[0]=tr("Setup.Replay$Length");
++  dvddisplaymode[1]=tr("Setup.Replay$Length / Number");
++  dvddisplaymode[2]=tr("Setup.Replay$Number");
++  Add(new cMenuEditStraItem(tr("Setup.Replay$DVD display mode"),                &data.DvdDisplayMode,3,dvddisplaymode));
++  Add(new cMenuEditBoolItem(tr("Setup.Replay$DVD display leading zeros"),       &data.DvdDisplayZeros));
++  static const char *dvdtraymode[4];
++  dvdtraymode[0]=tr("Setup.Replay$never");
++  dvdtraymode[1]=tr("Setup.Replay$on begin");
++  dvdtraymode[2]=tr("Setup.Replay$on end");
++  dvdtraymode[3]=tr("Setup.Replay$on begin and end");
++  Add(new cMenuEditStraItem(tr("Setup.Replay$Tray open"),                       &data.DvdTrayMode,4,dvdtraymode));
++  Add(new cMenuEditIntItem( tr("Setup.Replay$Limit DVD to speed"),              &data.DvdSpeedLimit, 0, 50));
++#endif /* DVDARCHIVE */
+ }
+ void cMenuSetupReplay::Store(void)
+@@ -2730,13 +3763,48 @@
+ // --- cMenuSetupMisc --------------------------------------------------------
+ class cMenuSetupMisc : public cMenuSetupBase {
++#ifdef USE_VOLCTRL
++private:
++  const char *lrChannelGroupsTexts[3];
++  const char *lrForwardRewindTexts[3];
++  void Setup(void);
++#endif /* VOLCTRL */
+ public:
+   cMenuSetupMisc(void);
++#ifdef USE_VOLCTRL
++  virtual eOSState ProcessKey(eKeys Key);
++#endif /* VOLCTRL */
+   };
+ cMenuSetupMisc::cMenuSetupMisc(void)
+ {
++#ifdef USE_VOLCTRL
++  lrChannelGroupsTexts[0] = tr("no");
++  lrChannelGroupsTexts[1] = tr("Setup.Miscellaneous$only in channelinfo");
++  lrChannelGroupsTexts[2] = tr("yes");
++  lrForwardRewindTexts[0] = tr("no");
++  lrForwardRewindTexts[1] = tr("Setup.Miscellaneous$only in progress display");
++  lrForwardRewindTexts[2] = tr("yes");
++#endif /* VOLCTRL */
+   SetSection(tr("Miscellaneous"));
++#ifdef USE_VOLCTRL
++  Setup();
++}
++
++eOSState cMenuSetupMisc::ProcessKey(eKeys Key)
++{
++  int newLRVolumeControl = data.LRVolumeControl;
++  eOSState state = cMenuSetupBase::ProcessKey(Key);
++  if (Key != kNone && data.LRVolumeControl != newLRVolumeControl)
++     Setup();
++  return state;
++}
++
++void cMenuSetupMisc::Setup(void)
++{
++  int current = Current();
++  Clear();
++#endif /* VOLCTRL */
+   Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Min. event timeout (min)"),   &data.MinEventTimeout));
+   Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Min. user inactivity (min)"), &data.MinUserInactivity));
+   Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$SVDRP timeout (s)"),          &data.SVDRPTimeout));
+@@ -2744,7 +3812,21 @@
+   Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Channel entry timeout (ms)"), &data.ChannelEntryTimeout, 0));
+   Add(new cMenuEditChanItem(tr("Setup.Miscellaneous$Initial channel"),            &data.InitialChannel, tr("Setup.Miscellaneous$as before")));
+   Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Initial volume"),             &data.InitialVolume, -1, 255, tr("Setup.Miscellaneous$as before")));
++#ifdef USE_VOLCTRL
++  Add(new cMenuEditBoolItem(tr("Setup.Miscellaneous$Volume ctrl with left/right"),     &data.LRVolumeControl));
++  if (data.LRVolumeControl) {
++     Add(new cMenuEditStraItem(tr("Setup.Miscellaneous$Channelgroups with left/right"),   &data.LRChannelGroups, 3, lrChannelGroupsTexts));
++     Add(new cMenuEditStraItem(tr("Setup.Miscellaneous$Search fwd/back with left/right"), &data.LRForwardRewind, 3, lrForwardRewindTexts));
++     }
++  SetCurrent(Get(current));
++  Display();
++#endif /* VOLCTRL */
+   Add(new cMenuEditBoolItem(tr("Setup.Miscellaneous$Emergency exit"),             &data.EmergencyExit));
++#ifdef USE_LIRCSETTINGS
++  Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Lirc repeat delay"),          &data.LircRepeatDelay, 0, 1000));
++  Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Lirc repeat freq"),           &data.LircRepeatFreq, 0, 1000));
++  Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Lirc repeat timeout"),        &data.LircRepeatTimeout, 0, 5000));
++#endif /* LIRCSETTINGS */
+ }
+ // --- cMenuSetupPluginItem --------------------------------------------------
+@@ -2769,6 +3851,9 @@
+ public:
+   cMenuSetupPlugins(void);
+   virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++  virtual const char* MenuKind() { return "MenuSetupPlugins"; }
++#endif /* GRAPHTFT */
+   };
+ cMenuSetupPlugins::cMenuSetupPlugins(void)
+@@ -2818,6 +3903,9 @@
+ public:
+   cMenuSetup(void);
+   virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++  virtual const char* MenuKind() { return "MenuSetup"; }
++#endif /* GRAPHTFT */
+   };
+ cMenuSetup::cMenuSetup(void)
+@@ -2907,24 +3995,90 @@
+ cMenuMain::cMenuMain(eOSState State)
+ :cOsdMenu("")
+ {
++#ifdef USE_SETUP
++  // Load Menu Configuration
++  cString menuXML = cString::sprintf("%s/setup/vdr-menu.%s.xml", cPlugin::ConfigDirectory(), Setup.OSDLanguage);
++  if (access(menuXML, 04) == -1)
++     menuXML = cString::sprintf("%s/setup/vdr-menu.xml", cPlugin::ConfigDirectory());
++  subMenu.LoadXml(menuXML);
++  nrDynamicMenuEntries = 0;
++#endif /* SETUP */
++
+   replaying = false;
+   stopReplayItem = NULL;
+   cancelEditingItem = NULL;
+   stopRecordingItem = NULL;
+   recordControlsState = 0;
++
++#ifdef USE_MENUORG
++  MenuOrgPatch::EnterRootMenu();
++#endif /* MENUORG */
++
+   Set();
+   // Initial submenus:
++#ifdef USE_MAINMENUHOOKS
++  cOsdMenu *menu = NULL;
++#endif /* MAINMENUHOOKS */
+   switch (State) {
++#ifdef USE_MAINMENUHOOKS
++    case osSchedule:
++        {
++          cPlugin *p = cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osSchedule", &menu);
++          if (p && !menu)
++             isyslog("MainMenuHook::osSchedule: plugin %s claims to support service but didn't return menu", p->Name());
++
++          if (!menu)
++             menu = new cMenuSchedule;
++        }
++        break;
++    case osChannels:
++        {
++          cPlugin *p = cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osChannels", &menu);
++          if (p && !menu)
++             isyslog("MainMenuHook::osChannels: plugin %s claims to support service but didn't return menu", p->Name());
++
++          if (!menu)
++             menu = new cMenuChannels;
++        }
++        break;
++    case osTimers:
++        {
++          cPlugin *p = cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osTimers", &menu);
++          if (p && !menu)
++             isyslog("MainMenuHook::osTimers: plugin %s claims to support service but didn't return menu", p->Name());
++
++          if (!menu)
++             menu = new cMenuTimers;
++        }
++        break;
++    case osRecordings:
++        {
++          cPlugin *p = cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osRecordings", &menu);
++          if (p && !menu)
++             isyslog("MainMenuHook::osRecordings: plugin %s claims to support service but didn't return menu", p->Name());
++
++          if (!menu)
++             menu = new cMenuRecordings(NULL, 0, true);
++        }
++        break;
++    case osSetup:      menu = new cMenuSetup; break;
++    case osCommands:   menu = new cMenuCommands(tr("Commands"), &Commands); break;
++#else
+     case osSchedule:   AddSubMenu(new cMenuSchedule); break;
+     case osChannels:   AddSubMenu(new cMenuChannels); break;
+     case osTimers:     AddSubMenu(new cMenuTimers); break;
+     case osRecordings: AddSubMenu(new cMenuRecordings(NULL, 0, true)); break;
+     case osSetup:      AddSubMenu(new cMenuSetup); break;
+     case osCommands:   AddSubMenu(new cMenuCommands(tr("Commands"), &Commands)); break;
++#endif /* MAINMENUHOOKS */
+     default: break;
+     }
++#ifdef USE_MAINMENUHOOKS
++  if (menu)
++     AddSubMenu(menu);
++#endif /* MAINMENUHOOKS */
+ }
+ cOsdObject *cMenuMain::PluginOsdObject(void)
+@@ -2934,38 +4088,159 @@
+   return o;
+ }
++#ifdef USE_SETUP
++void cMenuMain::Set(int current)
++#else
+ void cMenuMain::Set(void)
++#endif /* SETUP */
+ {
+   Clear();
+   SetTitle("VDR");
+   SetHasHotkeys();
++#ifdef USE_MENUORG
++  if (MenuOrgPatch::IsCustomMenuAvailable()) {
++     MenuItemDefinitions* menuItems = MenuOrgPatch::MainMenuItems();
++     for (MenuItemDefinitions::iterator i = menuItems->begin(); i != menuItems->end(); i++) {
++         cOsdItem* osdItem = NULL;
++         if ((*i)->IsCustomOsdItem()) {
++            osdItem = (*i)->CustomOsdItem();
++            if (osdItem &&  !(*i)->IsSeparatorItem())
++               osdItem->SetText(hk(osdItem->Text()));
++            }
++         else if ((*i)->IsPluginItem()) {
++            const char *item = (*i)->PluginMenuEntry();
++            if (item)
++               osdItem = new cMenuPluginItem(hk(item), (*i)->PluginIndex());
++            }
++         if (osdItem) {
++            Add(osdItem);
++            if ((*i)->IsSelected())
++               SetCurrent(osdItem);
++            }
++         }
++     }
++  else {
++#endif /* MENUORG */
++
++#ifdef USE_SETUP
++  stopReplayItem = NULL;
++  cancelEditingItem = NULL;
++  stopRecordingItem = NULL;
++
++  // remember initial dynamic MenuEntries added
++  nrDynamicMenuEntries = Count();
++  for (cSubMenuNode *node = subMenu.GetMenuTree()->First(); node; node = subMenu.GetMenuTree()->Next(node)) {
++      cSubMenuNode::Type type = node->GetType();
++      if (type==cSubMenuNode::PLUGIN) {
++         const char *item = node->GetPluginMainMenuEntry();
++#ifdef USE_PINPLUGIN
++         if (item && !cStatus::MsgPluginProtected(cPluginManager::GetPlugin(node->GetPluginIndex()), true))
++#else
++         if (item)
++#endif /* PINPLUGIN */
++            Add(new cMenuPluginItem(hk(item), node->GetPluginIndex()));
++         }
++      else if (type==cSubMenuNode::MENU) {
++         cString item = cString::sprintf("%s%s", node->GetName(), *subMenu.GetMenuSuffix());
++#ifdef USE_PINPLUGIN
++         if (!cStatus::MsgMenuItemProtected(item, true))
++            Add(new cOsdItem(hk(item), osUnknown, node));
++#else
++            Add(new cOsdItem(hk(item)));
++#endif /* PINPLUGIN */
++         }
++      else if ((type==cSubMenuNode::COMMAND) || (type==cSubMenuNode::THREAD)) {
++#ifdef USE_PINPLUGIN
++         if (!cStatus::MsgMenuItemProtected(node->GetName(), true))
++            Add(new cOsdItem(hk(node->GetName()), osUnknown, node));
++#else
++            Add(new cOsdItem(hk(node->GetName())));
++#endif /* PINPLUGIN */
++         }
++      else if (type==cSubMenuNode::SYSTEM) {
++         const char *item = node->GetName();
++#ifdef USE_PINPLUGIN
++         if (cStatus::MsgMenuItemProtected(item, true))
++            ; // nothing to do ;)
++         else
++#endif /* PINPLUGIN */
++         if (strcmp(item, "Schedule") == 0)
++            Add(new cOsdItem(hk(tr("Schedule")), osSchedule));
++         else if (strcmp(item, "Channels") == 0)
++            Add(new cOsdItem(hk(tr("Channels")), osChannels));
++         else if (strcmp(item, "Timers") == 0)
++            Add(new cOsdItem(hk(tr("Timers")), osTimers));
++         else if (strcmp(item, "Recordings") == 0)
++            Add(new cOsdItem(hk(tr("Recordings")), osRecordings));
++         else if (strcmp(item, "Setup") == 0) {
++            cString itemSetup = cString::sprintf("%s%s", tr("Setup"), *subMenu.GetMenuSuffix());
++            Add(new cOsdItem(hk(itemSetup), osSetup));
++            }
++         else if (strcmp(item, "Commands") == 0 && Commands.Count() > 0) {
++            cString itemCommands = cString::sprintf("%s%s", tr("Commands"), *subMenu.GetMenuSuffix());
++            Add(new cOsdItem(hk(itemCommands), osCommands));
++            }
++         }
++     }
++  if (current >=0 && current<Count()) {
++     SetCurrent(Get(current));
++     }
++
++#else /* NO SETUP */
++
+   // Basic menu items:
++#ifdef USE_PINPLUGIN
++  if (!cStatus::MsgMenuItemProtected("Schedule", true))   Add(new cOsdItem(hk(tr("Schedule")),   osSchedule));
++  if (!cStatus::MsgMenuItemProtected("Channels", true))   Add(new cOsdItem(hk(tr("Channels")),   osChannels));
++  if (!cStatus::MsgMenuItemProtected("Timers", true))     Add(new cOsdItem(hk(tr("Timers")),     osTimers));
++  if (!cStatus::MsgMenuItemProtected("Recordings", true)) Add(new cOsdItem(hk(tr("Recordings")), osRecordings));
++#else
+   Add(new cOsdItem(hk(tr("Schedule")),   osSchedule));
+   Add(new cOsdItem(hk(tr("Channels")),   osChannels));
+   Add(new cOsdItem(hk(tr("Timers")),     osTimers));
+   Add(new cOsdItem(hk(tr("Recordings")), osRecordings));
++#endif /* PINPLUGIN */
+   // Plugins:
+   for (int i = 0; ; i++) {
+       cPlugin *p = cPluginManager::GetPlugin(i);
+       if (p) {
++#ifdef USE_PINPLUGIN
++         if (!cStatus::MsgPluginProtected(p, true)) {
++#endif /* PINPLUGIN */
+          const char *item = p->MainMenuEntry();
+          if (item)
+             Add(new cMenuPluginItem(hk(item), i));
+          }
++#ifdef USE_PINPLUGIN
++         }
++#endif /* PINPLUGIN */
+       else
+          break;
+       }
+   // More basic menu items:
++#ifdef USE_PINPLUGIN
++  if (!cStatus::MsgMenuItemProtected("Setup", true)) Add(new cOsdItem(hk(tr("Setup")), osSetup));
++#else
+   Add(new cOsdItem(hk(tr("Setup")),      osSetup));
++#endif /* PINPLUGIN */
+   if (Commands.Count())
++#ifdef USE_PINPLUGIN
++     if (!cStatus::MsgMenuItemProtected("Commands", true))
++#endif /* PINPLUGIN */
+      Add(new cOsdItem(hk(tr("Commands")),  osCommands));
++#endif /* SETUP */
++
++#ifdef USE_MENUORG
++  }
++#endif /* MENUORG */
++
+   Update(true);
+   Display();
+@@ -2974,13 +4249,40 @@
+ bool cMenuMain::Update(bool Force)
+ {
+   bool result = false;
+-
++#ifdef USE_SETUP
++  cOsdItem *fMenu = NULL;
++  if (Force && subMenu.isTopMenu()) {
++     fMenu = First();
++     nrDynamicMenuEntries = 0;
++     }
++
++  if (subMenu.isTopMenu()) {
++#endif /* SETUP */
++#ifdef USE_LIEMIEXT
++// this extension is not included in the original Liemikuutio
++  if (Setup.MainMenuTitle) {
++     if (Setup.MainMenuTitle == 1)
++        SetTitle(cString::sprintf("%s  -  %s", tr("VDR"), Setup.CustomMainMenuTitle));
++     else if (Setup.MainMenuTitle == 2)
++        SetTitle(cString::sprintf("%s", Setup.CustomMainMenuTitle));
++     else if (Setup.MainMenuTitle == 3)
++        SetTitle(cString::sprintf("%s %s", tr("VDR"), VDRVERSION));
++     }
++  else
++#endif /* LIEMIEXT */
+   // Title with disk usage:
+   if (FreeDiskSpace.HasChanged(Force)) {
+      //XXX -> skin function!!!
+      SetTitle(cString::sprintf("%s  -  %s", tr("VDR"), FreeDiskSpace.FreeDiskSpaceString()));
+      result = true;
+      }
++#ifdef USE_SETUP
++     }
++  else {
++     SetTitle(cString::sprintf("%s  -  %s", tr("VDR"), subMenu.GetParentMenuTitel()));
++     result = true;
++     }
++#endif /* SETUP */
+   bool NewReplaying = cControl::Control() != NULL;
+   if (Force || NewReplaying != replaying) {
+@@ -2988,6 +4290,9 @@
+      // Replay control:
+      if (replaying && !stopReplayItem)
+         // TRANSLATORS: note the leading blank!
++#ifdef USE_LIEMIEXT
++        if (Setup.MenuCmdPosition) Ins(stopReplayItem = new cOsdItem(tr(" Stop replaying"), osStopReplay)); else
++#endif /* LIEMIEXT */
+         Add(stopReplayItem = new cOsdItem(tr(" Stop replaying"), osStopReplay));
+      else if (stopReplayItem && !replaying) {
+         Del(stopReplayItem->Index());
+@@ -3002,6 +4307,9 @@
+   bool CutterActive = cCutter::Active();
+   if (CutterActive && !cancelEditingItem) {
+      // TRANSLATORS: note the leading blank!
++#ifdef USE_LIEMIEXT
++     if (Setup.MenuCmdPosition) Ins(cancelEditingItem = new cOsdItem(tr(" Cancel editing"), osCancelEdit)); else
++#endif /* LIEMIEXT */
+      Add(cancelEditingItem = new cOsdItem(tr(" Cancel editing"), osCancelEdit));
+      result = true;
+      }
+@@ -3022,6 +4330,9 @@
+      while ((s = cRecordControls::GetInstantId(s)) != NULL) {
+            cOsdItem *item = new cOsdItem(osStopRecord);
+            item->SetText(cString::sprintf("%s%s", tr(STOP_RECORDING), s));
++#ifdef USE_LIEMIEXT
++           if (Setup.MenuCmdPosition) Ins(item); else
++#endif /* LIEMIEXT */
+            Add(item);
+            if (!stopRecordingItem)
+               stopRecordingItem = item;
+@@ -3029,6 +4340,12 @@
+      result = true;
+      }
++#ifdef USE_SETUP
++  // adjust nrDynamicMenuEntries
++  if (fMenu != NULL)
++     nrDynamicMenuEntries = fMenu->Index();
++#endif /* SETUP */
++
+   return result;
+ }
+@@ -3039,13 +4356,69 @@
+   eOSState state = cOsdMenu::ProcessKey(Key);
+   HadSubMenu |= HasSubMenu();
++#ifdef USE_PINPLUGIN
++  cOsdItem* item = Get(Current());
++
++  if (item && item->Text() && state != osBack && state != osContinue && Key != kNone)
++     if (cStatus::MsgMenuItemProtected(item->Text()))
++        return osContinue;
++#endif /* PINPLUGIN */
++
++#ifdef USE_MAINMENUHOOKS
++  cOsdMenu *menu = NULL;
++#endif /* MAINMENUHOOKS */
+   switch (state) {
++#ifdef USE_MAINMENUHOOKS
++    case osSchedule:
++        {
++          cPlugin *p = cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osSchedule", &menu);
++          if (p && !menu)
++             isyslog("MainMenuHook::osSchedule: plugin %s claims to support service but didn't return menu", p->Name());
++
++          if (!menu)
++             menu = new cMenuSchedule;
++        }
++        break;
++    case osChannels:
++        {
++          cPlugin *p = cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osChannels", &menu);
++          if (p && !menu)
++             isyslog("MainMenuHook::osChannels: plugin %s claims to support service but didn't return menu", p->Name());
++
++          if (!menu)
++             menu = new cMenuChannels;
++        }
++        break;
++    case osTimers:
++        {
++          cPlugin *p = cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osTimers", &menu);
++          if (p && !menu)
++             isyslog("MainMenuHook::osTimers: plugin %s claims to support service but didn't return menu", p->Name());
++
++          if (!menu)
++             menu = new cMenuTimers;
++        }
++        break;
++    case osRecordings:
++        {
++          cPlugin *p = cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osRecordings", &menu);
++          if (p && !menu)
++             isyslog("MainMenuHook::osRecordings: plugin %s claims to support service but didn't return menu", p->Name());
++
++          if (!menu)
++             menu = new cMenuRecordings;
++        }
++        break;
++    case osSetup:      menu = new cMenuSetup; break;
++    case osCommands:   menu = new cMenuCommands(tr("Commands"), &Commands); break;
++#else
+     case osSchedule:   return AddSubMenu(new cMenuSchedule);
+     case osChannels:   return AddSubMenu(new cMenuChannels);
+     case osTimers:     return AddSubMenu(new cMenuTimers);
+     case osRecordings: return AddSubMenu(new cMenuRecordings);
+     case osSetup:      return AddSubMenu(new cMenuSetup);
+     case osCommands:   return AddSubMenu(new cMenuCommands(tr("Commands"), &Commands));
++#endif /* MAINMENUHOOKS */
+     case osStopRecord: if (Interface->Confirm(tr("Stop recording?"))) {
+                           cOsdItem *item = Get(Current());
+                           if (item) {
+@@ -3064,6 +4437,9 @@
+                          if (item) {
+                             cPlugin *p = cPluginManager::GetPlugin(item->PluginIndex());
+                             if (p) {
++#ifdef USE_PINPLUGIN
++                               if (!cStatus::MsgPluginProtected(p)) {
++#endif /* PINPLUGIN */
+                                cOsdObject *menu = p->MainMenuAction();
+                                if (menu) {
+                                   if (menu->IsMenu())
+@@ -3075,9 +4451,60 @@
+                                   }
+                                }
+                             }
++#ifdef USE_PINPLUGIN
++                         }
++#endif /* PINPLUGIN */
+                          state = osEnd;
+                        }
+                        break;
++#ifdef USE_SETUP
++    case osBack:       {
++                         int newCurrent = 0;
++                         if (subMenu.Up(&newCurrent)) {
++                            Set(newCurrent);
++                            return osContinue;
++                            }
++                         else
++                            return osEnd;
++                       }
++                       break;
++#endif /* SETUP */
++#ifdef USE_MENUORG
++    case osBack:       {
++                          if (MenuOrgPatch::IsCustomMenuAvailable()) {
++                             bool leavingMenuSucceeded = MenuOrgPatch::LeaveSubMenu();
++                             Set();
++                             stopReplayItem = NULL;
++                             cancelEditingItem = NULL;
++                             stopRecordingItem = NULL;
++                             recordControlsState = 0;
++                             Update(true);
++                             Display();
++                             if (leavingMenuSucceeded)
++                                return osContinue;
++                             else
++                                return osEnd;
++                             }
++                       }
++                       break;
++    case osUser1:      {
++                          if (MenuOrgPatch::IsCustomMenuAvailable()) {
++                             MenuOrgPatch::EnterSubMenu(Get(Current()));
++                             Set();
++                             return osContinue;
++                             }
++                       }
++                       break;
++    case osUser2:      {
++                          if (MenuOrgPatch::IsCustomMenuAvailable()) {
++                             cOsdMenu* osdMenu = MenuOrgPatch::Execute(Get(Current()));
++                             if (osdMenu)
++                                return AddSubMenu(osdMenu);
++                             return osEnd;
++                            }
++                       }
++                       break;
++#endif /* MENUORG */
+     default: switch (Key) {
+                case kRecord:
+                case kRed:    if (!HadSubMenu)
+@@ -3094,9 +4521,67 @@
+                case kBlue:   if (!HadSubMenu)
+                                 state = replaying ? osStopReplay : cReplayControl::LastReplayed() ? osReplay : osContinue;
+                              break;
++#ifdef USE_SETUP
++               case kOk:     if (state == osUnknown) {
++                                cString buffer;
++#ifdef USE_PINPLUGIN
++                                cSubMenuNode *node = Get(Current())->SubMenu();
++#else
++                                int index = Current()-nrDynamicMenuEntries;
++                                cSubMenuNode *node = subMenu.GetNode(index);
++#endif /* PINPLUGIN */
++
++                                if (node != NULL) {
++                                   if (node->GetType() == cSubMenuNode::MENU) {
++#ifdef USE_PINPLUGIN
++                                      subMenu.Down(node, Current());
++#else
++                                      subMenu.Down(index);
++#endif /* PINPLUGIN */
++                                      }
++                                   else if (node->GetType() == cSubMenuNode::COMMAND) {
++                                      bool confirmed = true;
++                                      if (node->CommandConfirm()) {
++                                         buffer = cString::sprintf("%s?", node->GetName());
++                                         confirmed = Interface->Confirm(buffer);
++                                         }
++                                      if (confirmed) {
++                                         const char *Result = subMenu.ExecuteCommand(node->GetCommand());
++                                         if (Result)
++                                            return AddSubMenu(new cMenuText(node->GetName(), Result, fontFix));
++                                         return osEnd;
++                                         }
++                                      }
++                                   else if (node->GetType() == cSubMenuNode::THREAD) {
++                                      bool confirmed = true;
++                                      if (node->CommandConfirm()) {
++                                         buffer = cString::sprintf("%s?", node->GetName());
++                                         confirmed = Interface->Confirm(buffer);
++                                         }
++                                      if (confirmed) {
++                                         buffer = cString::sprintf("%s", node->GetCommand());
++                                         cExecCmdThread *execcmd = new cExecCmdThread(node->GetCommand());
++                                         if (execcmd->Start())
++                                            dsyslog("executing command '%s'", *buffer);
++                                         else
++                                            esyslog("ERROR: can't execute command '%s'", *buffer);
++                                         return osEnd;
++                                         }
++                                      }
++                                   }
++
++                                Set();
++                                return osContinue;
++                                }
++                             break;
++#endif /* SETUP */
+                default:      break;
+                }
+     }
++#ifdef USE_MAINMENUHOOKS
++  if (menu)
++     return AddSubMenu(menu);
++#endif /* MAINMENUHOOKS */
+   if (!HasSubMenu() && Update(HadSubMenu))
+      Display();
+   if (Key != kNone) {
+@@ -3242,7 +4727,14 @@
+   if (Direction) {
+      while (Channel) {
+            Channel = Direction > 0 ? Channels.Next(Channel) : Channels.Prev(Channel);
++#ifdef USE_PINPLUGIN
++           if (cStatus::MsgChannelProtected(0, Channel) == false)
++#endif /* PINPLUGIN */
++#ifdef USE_LNBSHARE
++           if (Channel && !Channel->GroupSep() && cDevice::GetDevice(Channel, 0, true) && cDevice::PrimaryDevice()->GetMaxBadPriority(Channel) < 0)
++#else
+            if (Channel && !Channel->GroupSep() && cDevice::GetDevice(Channel, 0, true))
++#endif /* LNBSHARE */
+               return Channel;
+            }
+      }
+@@ -3300,6 +4792,13 @@
+     case kLeft:
+     case kRight|k_Repeat:
+     case kRight:
++#ifdef USE_VOLCTRL
++         if (Setup.LRVolumeControl && !Setup.LRChannelGroups) {
++            cRemote::Put(NORMALKEY(Key) == kLeft ? kVolDn : kVolUp, true);
++            break;
++            }
++         // else fall through
++#endif /* VOLCTRL */
+     case kNext|k_Repeat:
+     case kNext:
+     case kPrev|k_Repeat:
+@@ -3459,6 +4958,17 @@
+ eOSState cDisplayVolume::ProcessKey(eKeys Key)
+ {
+   switch (Key) {
++#ifdef USE_VOLCTRL
++    case kLeft|k_Repeat:
++    case kLeft:
++    case kRight|k_Repeat:
++    case kRight:
++         if (Setup.LRVolumeControl) {
++            cRemote::Put(NORMALKEY(Key) == kLeft ? kVolDn : kVolUp, true);
++            break;
++            }
++         // else fall through
++#endif /* VOLCTRL */
+     case kVolUp|k_Repeat:
+     case kVolUp:
+     case kVolDn|k_Repeat:
+@@ -3708,6 +5218,10 @@
+ cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer, bool Pause)
+ {
++#ifdef USE_DVLRECSCRIPTADDON
++  const cChannel *recChan = NULL;
++  char *chanName = NULL;
++#endif /* DVLRECSCRIPTADDON */
+   // We're going to manipulate an event here, so we need to prevent
+   // others from modifying any EPG data:
+   cSchedulesLock SchedulesLock;
+@@ -3752,11 +5266,26 @@
+      return;
+      }
++#ifdef USE_DVLRECSCRIPTADDON
++  if (timer)
++     if ((recChan = timer->Channel()) != NULL)
++        chanName = strdup(recChan->Name());
++  if (chanName != NULL) {
++     cRecordingUserCommand::InvokeCommand(RUC_BEFORERECORDING, fileName, chanName);
++     free(chanName);
++     }
++  else
++#endif /* DVLRECSCRIPTADDON */
+   cRecordingUserCommand::InvokeCommand(RUC_BEFORERECORDING, fileName);
+   isyslog("record %s", fileName);
+   if (MakeDirs(fileName, true)) {
+      const cChannel *ch = timer->Channel();
++#ifdef USE_TTXTSUBS
++     int TPid[2] = { ch->Tpid(), 0 };
++     recorder = new cRecorder(fileName, ch->GetChannelID(), timer->Priority(), ch->Vpid(), ch->Apids(), ch->Dpids(), ch->Spids(), TPid);
++#else
+      recorder = new cRecorder(fileName, ch->GetChannelID(), timer->Priority(), ch->Vpid(), ch->Apids(), ch->Dpids(), ch->Spids());
++#endif /* TTXTSUBS */
+      if (device->AttachReceiver(recorder)) {
+         Recording.WriteInfo();
+         cStatus::MsgRecording(device, Recording.Name(), Recording.FileName(), true);
+@@ -3814,11 +5343,25 @@
+ void cRecordControl::Stop(void)
+ {
+   if (timer) {
++#ifdef USE_DVLRECSCRIPTADDON
++     char *chanName = NULL;
++     const cChannel *recChan = NULL;
++
++     recChan = timer -> Channel();
++     if (recChan != NULL)
++        chanName = strdup(recChan -> Name());
++#endif /* DVLRECSCRIPTADDON */
+      DELETENULL(recorder);
+      timer->SetRecording(false);
+      timer = NULL;
+      cStatus::MsgRecording(device, NULL, fileName, false);
++#ifdef USE_DVLRECSCRIPTADDON
++     cRecordingUserCommand::InvokeCommand(RUC_AFTERRECORDING, fileName, chanName);
++     if (chanName != NULL)
++        free(chanName);
++#else
+      cRecordingUserCommand::InvokeCommand(RUC_AFTERRECORDING, fileName);
++#endif /* DVLRECSCRIPTADDON */
+      }
+ }
+@@ -3865,6 +5408,19 @@
+      int Priority = Timer ? Timer->Priority() : Pause ? Setup.PausePriority : Setup.DefaultPriority;
+      cDevice *device = cDevice::GetDevice(channel, Priority, false);
+      if (device) {
++#ifdef USE_LNBSHARE
++        cDevice *tmpDevice;
++        while ((tmpDevice = device->GetBadDevice(channel))) {
++              if (tmpDevice->Replaying() == false) {
++//               Stop(tmpDevice);
++                 if (tmpDevice->IsPrimaryDevice() )
++                    tmpDevice->SwitchChannelForced(channel, true);
++                 else
++                    tmpDevice->SwitchChannelForced(channel, false);
++              } else
++                 tmpDevice->SwitchChannelForced(channel, false);
++              }
++#endif /* LNBSHARE */
+         dsyslog("switching device %d to channel %d", device->DeviceNumber() + 1, channel->Number());
+         if (!device->SwitchChannel(channel, false)) {
+            ShutdownHandler.RequestEmergencyExit();
+@@ -3874,6 +5430,9 @@
+            for (int i = 0; i < MAXRECORDCONTROLS; i++) {
+                if (!RecordControls[i]) {
+                   RecordControls[i] = new cRecordControl(device, Timer, Pause);
++#ifdef USE_PINPLUGIN
++                  cStatus::MsgRecordingFile(RecordControls[i]->FileName());
++#endif /* PINPLUGIN */
+                   return RecordControls[i]->Process(time(NULL));
+                   }
+                }
+@@ -4004,12 +5563,22 @@
+ // --- cReplayControl --------------------------------------------------------
++#ifdef USE_LIEMIEXT
++#define REPLAYCONTROLSKIPLIMIT   9    // s
++#define REPLAYCONTROLSKIPSECONDS 90   // s
++#define REPLAYCONTROLSKIPTIMEOUT 5000 // ms
++#endif /* LIEMIEXT */
++
+ cReplayControl *cReplayControl::currentReplayControl = NULL;
+ char *cReplayControl::fileName = NULL;
+ char *cReplayControl::title = NULL;
+ cReplayControl::cReplayControl(void)
++#ifdef USE_JUMPPLAY
++:cDvbPlayerControl(fileName), marks(fileName)
++#else
+ :cDvbPlayerControl(fileName)
++#endif /* JUMPPLAY */
+ {
+   currentReplayControl = this;
+   displayReplay = NULL;
+@@ -4017,23 +5586,96 @@
+   lastCurrent = lastTotal = -1;
+   lastPlay = lastForward = false;
+   lastSpeed = -2; // an invalid value
++#ifdef USE_LIEMIEXT
++  lastSkipKey = kNone;
++  lastSkipSeconds = REPLAYCONTROLSKIPSECONDS;
++  lastSkipTimeout.Set(0);
++#endif /* LIEMIEXT */
+   timeoutShow = 0;
+   timeSearchActive = false;
+   cRecording Recording(fileName);
++#ifdef USE_DVDARCHIVE
++  canJumpChapters = (Recording.GetDvdType() == DVD_VIDEO_ARCHIVE_TYPE);
++  dvdchapters = NULL;
++  if (canJumpChapters) {
++     const char *ret = Recording.GetDvdChapters();
++     if (ret)
++        dvdchapters = strdup(ret);
++     else
++        canJumpChapters=false;
++     }
++#endif /* DVDARCHIVE */
+   cStatus::MsgReplaying(this, Recording.Name(), Recording.FileName(), true);
++#ifndef USE_JUMPPLAY
+   marks.Load(fileName, Recording.FramesPerSecond(), Recording.IsPesRecording());
++#endif /* JUMPPLAY */
+   SetTrackDescriptions(false);
+ }
+ cReplayControl::~cReplayControl()
+ {
+   Hide();
++#ifdef USE_DVDARCHIVE
++  free(dvdchapters);
++#endif /* DVDARCHIVE */
+   cStatus::MsgReplaying(this, NULL, fileName, false);
+   Stop();
+   if (currentReplayControl == this)
+      currentReplayControl = NULL;
+ }
++#ifdef USE_DELTIMESHIFTREC
++void cReplayControl::Stop(void)
++{
++  int dummy;
++  bool playing = GetIndex(dummy, dummy, false);
++  cRecordControl* rc = cRecordControls::GetRecordControl(fileName);
++
++  if (playing && rc && rc->InstantId()) {
++     isyslog("found Timeshiftrecording");
++
++     if ((Setup.DelTimeshiftRec != 0 ) || (Interface->Confirm(tr("Delete recording?")))) {
++        cRecordControl *rc = cRecordControls::GetRecordControl(fileName);
++        if (rc) {
++           cTimer *timer = rc->Timer();
++           if (timer) {
++              const char* reccmd_backup = cRecordingUserCommand::GetCommand();
++              cRecordingUserCommand::SetCommand(NULL);
++ 
++              timer->Skip();
++              cRecordControls::Process(time(NULL));
++              if (timer->IsSingleEvent()) {
++                 isyslog("deleting timer %s", *timer->ToDescr());
++                 Timers.Del(timer);
++                 }
++              Timers.SetModified();
++
++              // restore reccmd
++              cRecordingUserCommand::SetCommand(reccmd_backup);
++              }
++           }
++        isyslog("stop replaying %s", fileName);
++        cDvbPlayerControl::Stop();
++
++        if (Setup.DelTimeshiftRec != 1) {
++           cRecording *recording = Recordings.GetByName(fileName);;
++           if (recording) {
++              if (recording->Delete()) {
++                 Recordings.DelByName(fileName);
++                 ClearLastReplayed(fileName);
++                 return;
++                 }
++              else
++                 Skins.Message(mtError, tr("Error while deleting recording!"));
++              }
++           }
++        }
++     else
++        cDvbPlayerControl::Stop();
++     }
++}
++#endif /* DELTIMESHIFTREC */
++
+ void cReplayControl::SetRecording(const char *FileName, const char *Title)
+ {
+   free(fileName);
+@@ -4128,6 +5770,9 @@
+      if (Initial) {
+         if (title)
+            displayReplay->SetTitle(title);
++#ifdef USE_OSDMAXITEMS
++        displayReplay->SetButtons(tr("Jump"), tr("Skip +60s"), tr("Skip -60s"), tr("Button$Stop"));
++#endif /* OSDMAXITEMS */
+         lastCurrent = lastTotal = -1;
+         }
+      if (Total != lastTotal) {
+@@ -4249,8 +5894,15 @@
+         ShowTimed(2);
+         bool Play, Forward;
+         int Speed;
++#ifdef USE_JUMPPLAY
++        if (GetReplayMode(Play, Forward, Speed) && !Play) {
++           Goto(Current, true);
++           displayFrames = true;
++           }
++#else
+         if (GetReplayMode(Play, Forward, Speed) && !Play)
+            Goto(Current, true);
++#endif /* JUMPPLAY */
+         }
+      marks.Save();
+      }
+@@ -4263,8 +5915,22 @@
+      if (GetIndex(Current, Total)) {
+         cMark *m = Forward ? marks.GetNext(Current) : marks.GetPrev(Current);
+         if (m) {
++#ifdef USE_JUMPPLAY
++           bool Play2, Forward2;
++           int Speed;
++           if (Setup.JumpPlay && GetReplayMode(Play2, Forward2, Speed) &&
++               Play2 && Forward && m->position < Total - SecondsToFrames(3, FramesPerSecond())) {
++              Goto(m->position);
++              Play();
++              }
++           else {
++              Goto(m->position, true);
++              displayFrames = true;
++              }
++#else
+            Goto(m->position, true);
+            displayFrames = true;
++#endif /* JUMPPLAY */
+            }
+         }
+      }
+@@ -4293,11 +5959,43 @@
+      }
+ }
++#ifdef USE_DVDARCHIVE
++void cReplayControl::ChaptersJump(bool Forward)
++{
++  int Current, Total;
++  if (GetIndex(Current, Total)) {
++     int position = -1;
++     char *buf, *pos;
++     cString old1("-1");
++     cString old2("-1");
++     buf = strdup(dvdchapters);
++     pos = strtok(buf, ",");
++     while (pos != NULL && position == -1) {
++           if (pos && atoi(pos) > Current)
++           position = Forward ? atoi(pos) : ((Current - atoi(old1)) <= (3 * FramesPerSecond())) ? atoi(old2) : atoi(old1);
++           old2 = old1;
++           old1 = strdup(pos);
++           if(position == -1) pos = strtok(NULL, ",");
++           }
++     if (!pos && !Forward)
++        position = ((Current - atoi(old1)) <= (3 * FramesPerSecond())) ? atoi(old2) : atoi(old1);
++        if (position >= 0) {
++           Goto(position);
++           Play();
++           }
++        }
++}
++
++#endif /* DVDARCHIVE */
+ void cReplayControl::EditCut(void)
+ {
+   if (fileName) {
+      Hide();
++#ifdef USE_CUTTERQUEUE
++     if (!cCutter::Active() || Interface->Confirm(tr("Cutter already running - Add to cutting queue?"))) {
++#else
+      if (!cCutter::Active()) {
++#endif /* CUTTERQUEUE */
+         if (!marks.Count())
+            Skins.Message(mtError, tr("No editing marks defined!"));
+         else if (!cCutter::Start(fileName))
+@@ -4319,7 +6017,11 @@
+      if (!m)
+         m = marks.GetNext(Current);
+      if (m) {
++#ifdef USE_JUMPPLAY
++        if ((m->Index() & 0x01) != 0 && !Setup.PlayJump)
++#else
+         if ((m->Index() & 0x01) != 0)
++#endif /* JUMPPLAY */
+            m = marks.Next(m);
+         if (m) {
+            Goto(m->position - SecondsToFrames(3, FramesPerSecond()));
+@@ -4341,6 +6043,9 @@
+ {
+   if (!Active())
+      return osEnd;
++#ifdef USE_JUMPPLAY
++  marks.Reload();
++#endif /* JUMPPLAY */
+   if (visible) {
+      if (timeoutShow && time(NULL) > timeoutShow) {
+         Hide();
+@@ -4358,7 +6063,32 @@
+      TimeSearchProcess(Key);
+      return osContinue;
+      }
++#ifdef USE_DVDARCHIVE
++  bool isOnMark = false;
++  if (canJumpChapters) {
++     int Current, Total;
++     GetIndex(Current, Total);
++     cMark *m = marks.Get(Current);
++     if (m && (m->position == Current)) isOnMark = true;
++     }
++#endif /* DVDARCHIVE */
+   bool DoShowMode = true;
++#ifdef USE_VOLCTRL
++  if (Setup.LRVolumeControl && (!Setup.LRForwardRewind || (Setup.LRForwardRewind == 1 && !visible))) {
++     switch (Key) {
++       // Left/Right volume control
++       case kLeft|k_Repeat:
++       case kLeft:
++       case kRight|k_Repeat:
++       case kRight:
++            cRemote::Put(NORMALKEY(Key) == kLeft ? kVolDn : kVolUp, true);
++            return osContinue;
++            break;
++       default:
++            break;
++       }
++     }
++#endif /* VOLCTRL */
+   switch (Key) {
+     // Positioning:
+     case kPlay:
+@@ -4376,25 +6106,82 @@
+     case kFastFwd:
+     case kRight:   Forward(); break;
+     case kRed:     TimeSearch(); break;
++#ifdef USE_LIEMIEXT
++    case kGreen|k_Repeat:
++    case kGreen:   SkipSeconds(-Setup.JumpSeconds); break;
++    case kYellow|k_Repeat:
++    case kYellow:  SkipSeconds( Setup.JumpSeconds); break;
++    case k1|k_Repeat:
++    case k1:       SkipSeconds(-Setup.JumpSecondsSlow); break;
++    case k3|k_Repeat:
++    case k3:       SkipSeconds( Setup.JumpSecondsSlow); break;
++    case kPrev|k_Repeat:
++    case kPrev:    if (lastSkipTimeout.TimedOut()) {
++                      lastSkipSeconds = REPLAYCONTROLSKIPSECONDS;
++                      lastSkipKey = kPrev;
++                      }
++                   else if (RAWKEY(lastSkipKey) != kPrev && lastSkipSeconds > (2 * REPLAYCONTROLSKIPLIMIT)) {
++                      lastSkipSeconds /= 2;
++                      lastSkipKey = kNone;
++                      }
++                   lastSkipTimeout.Set(REPLAYCONTROLSKIPTIMEOUT);
++                   SkipSeconds(-lastSkipSeconds); break;
++    case kNext|k_Repeat:
++    case kNext:    if (lastSkipTimeout.TimedOut()) {
++                      lastSkipSeconds = REPLAYCONTROLSKIPSECONDS;
++                      lastSkipKey = kNext;    
++                      }
++                   else if (RAWKEY(lastSkipKey) != kNext && lastSkipSeconds > (2 * REPLAYCONTROLSKIPLIMIT)) {
++                      lastSkipSeconds /= 2;
++                      lastSkipKey = kNone;
++                      }
++                   lastSkipTimeout.Set(REPLAYCONTROLSKIPTIMEOUT);
++                   SkipSeconds(lastSkipSeconds); break;
++#else
+     case kGreen|k_Repeat:
+     case kGreen:   SkipSeconds(-60); break;
+     case kYellow|k_Repeat:
+     case kYellow:  SkipSeconds( 60); break;
++#endif /* LIEMIEXT */
+     case kStop:
+     case kBlue:    Hide();
+                    Stop();
+                    return osEnd;
++#ifdef USE_DVDARCHIVE
++    case kDvdChapterJumpForward|k_Repeat:
++    case kDvdChapterJumpForward: if (canJumpChapters && !isOnMark) {
++                                    ChaptersJump(true);
++                                    }
++                                 else {
++                                    DoShowMode = false;
++                                    MarkMove(true);
++                                    }
++                                 break;
++    case kDvdChapterJumpBack|k_Repeat:
++    case kDvdChapterJumpBack: if (canJumpChapters && !isOnMark) {
++                                 ChaptersJump(false);
++                                 }
++                              else {
++                                 DoShowMode = false;
++                                 MarkMove(false);
++                                 }
++                              break;
++#endif /* DVDARCHIVE */
+     default: {
+       DoShowMode = false;
+       switch (Key) {
+         // Editing:
+         case kMarkToggle:      MarkToggle(); break;
++#ifndef USE_LIEMIEXT
+         case kPrev|k_Repeat:
+         case kPrev:
++#endif /* LIEMIEXT */
+         case kMarkJumpBack|k_Repeat:
+         case kMarkJumpBack:    MarkJump(false); break;
++#ifndef USE_LIEMIEXT
+         case kNext|k_Repeat:
+         case kNext:
++#endif /* LIEMIEXT */
+         case kMarkJumpForward|k_Repeat:
+         case kMarkJumpForward: MarkJump(true); break;
+         case kMarkMoveBack|k_Repeat:
+@@ -4414,7 +6201,16 @@
+                            else
+                               Show();
+                            break;
++#ifdef USE_DELTIMESHIFTREC
++            case kBack: {  cRecordControl* rc = cRecordControls::GetRecordControl(fileName);
++                           if (rc && rc->InstantId())
++                              return osEnd;
++                           else
++                              return osRecordings;
++                        }
++#else
+             case kBack:    return osRecordings;
++#endif /* DELTIMESHIFTREC */
+             default:       return osUnknown;
+             }
+           }
+diff -NaurwB vdr-1.7.10/menu.h vdr-1.7.10-patched/menu.h
+--- vdr-1.7.10/menu.h  2008-02-10 17:01:53.000000000 +0100
++++ vdr-1.7.10-patched/menu.h  2009-12-18 06:25:25.000000000 +0100
+@@ -18,6 +18,9 @@
+ #include "menuitems.h"
+ #include "recorder.h"
+ #include "skins.h"
++#ifdef USE_SETUP
++#include "submenu.h"
++#endif /* SETUP */
+ class cMenuText : public cOsdMenu {
+ private:
+@@ -29,12 +32,19 @@
+   void SetText(const char *Text);
+   virtual void Display(void);
+   virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++  virtual const char* MenuKind() { return "MenuText"; }
++#endif /* GRAPHTFT */
+   };
+ class cMenuEditTimer : public cOsdMenu {
+ private:
+   cTimer *timer;
+   cTimer data;
++#ifdef USE_LIEMIEXT
++  char name[MaxFileName];
++  char path[MaxFileName];
++#endif /* LIEMIEXT */
+   int channel;
+   bool addIfConfirmed;
+   cMenuEditDateItem *firstday;
+@@ -43,6 +53,9 @@
+   cMenuEditTimer(cTimer *Timer, bool New = false);
+   virtual ~cMenuEditTimer();
+   virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++  virtual const char* MenuKind() { return "MenuTimerEdit"; }
++#endif /* GRAPHTFT */
+   };
+ class cMenuEvent : public cOsdMenu {
+@@ -52,22 +65,37 @@
+   cMenuEvent(const cEvent *Event, bool CanSwitch = false, bool Buttons = false);
+   virtual void Display(void);
+   virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++  virtual const char* MenuKind() { return "MenuEvent"; }
++#endif /* GRAPHTFT */
+   };
+ class cMenuMain : public cOsdMenu {
+ private:
++#ifdef USE_SETUP
++  int    nrDynamicMenuEntries;
++#endif /* SETUP */
+   bool replaying;
+   cOsdItem *stopReplayItem;
+   cOsdItem *cancelEditingItem;
+   cOsdItem *stopRecordingItem;
+   int recordControlsState;
+   static cOsdObject *pluginOsdObject;
++#ifdef USE_SETUP
++  void Set(int current=0);
++  bool Update(bool Force = false);
++  cSubMenu subMenu;
++#else
+   void Set(void);
+   bool Update(bool Force = false);
++#endif /* SETUP */
+ public:
+   cMenuMain(eOSState State = osUnknown);
+   virtual eOSState ProcessKey(eKeys Key);
+   static cOsdObject *PluginOsdObject(void);
++#ifdef USE_GRAPHTFT
++  virtual const char* MenuKind() { return "MenuMain"; }
++#endif /* GRAPHTFT */
+   };
+ class cDisplayChannel : public cOsdObject {
+@@ -163,12 +191,18 @@
+   eOSState Delete(void);
+   eOSState Info(void);
+   eOSState Commands(eKeys Key = kNone);
++#ifdef USE_LIEMIEXT
++  eOSState Rename(void);
++#endif /* LIEMIEXT */
+ protected:
+   cRecording *GetRecording(cMenuRecordingItem *Item);
+ public:
+   cMenuRecordings(const char *Base = NULL, int Level = 0, bool OpenSubMenus = false);
+   ~cMenuRecordings();
+   virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++  virtual const char* MenuKind() { return "MenuRecordings"; }
++#endif /* GRAPHTFT */
+   };
+ class cRecordControl {
+@@ -212,11 +246,21 @@
+ class cReplayControl : public cDvbPlayerControl {
+ private:
+   cSkinDisplayReplay *displayReplay;
++#ifdef USE_JUMPPLAY
++  cMarksReload marks;
++#else
+   cMarks marks;
++#endif /* JUMPPLAY */
+   bool visible, modeOnly, shown, displayFrames;
+   int lastCurrent, lastTotal;
+   bool lastPlay, lastForward;
+   int lastSpeed;
++#ifdef USE_LIEMIEXT
++  int lastSkipSeconds;
++  int lastSkipSecondsSlow;
++  eKeys lastSkipKey;
++  cTimeMs lastSkipTimeout;
++#endif /* LIEMIEXT */
+   time_t timeoutShow;
+   bool timeSearchActive, timeSearchHide;
+   int timeSearchTime, timeSearchPos;
+@@ -234,9 +278,17 @@
+   void MarkMove(bool Forward);
+   void EditCut(void);
+   void EditTest(void);
++#ifdef USE_DVDARCHIVE
++  void ChaptersJump(bool Forward);
++  bool canJumpChapters;
++  char *dvdchapters;
++#endif /* DVDARCHIVE */
+ public:
+   cReplayControl(void);
+   virtual ~cReplayControl();
++#ifdef USE_DELTIMESHIFTREC
++  void Stop(void);
++#endif /* DELTIMESHIFTREC */
+   virtual cOsdObject *GetInfo(void);
+   virtual eOSState ProcessKey(eKeys Key);
+   virtual void Show(void);
+diff -NaurwB vdr-1.7.10/menuitems.c vdr-1.7.10-patched/menuitems.c
+--- vdr-1.7.10/menuitems.c     2009-05-03 15:37:55.000000000 +0200
++++ vdr-1.7.10-patched/menuitems.c     2009-12-18 06:25:25.000000000 +0100
+@@ -33,9 +33,20 @@
+   free(name);
+ }
++#ifdef USE_VALIDINPUT
++void cMenuEditItem::SetValue(const char *Value, bool HasPre, bool HasSucc)
++#else
+ void cMenuEditItem::SetValue(const char *Value)
++#endif /* VALIDINPUT */
+ {
+   cString buffer = cString::sprintf("%s:\t%s", name, Value);
++#ifdef USE_VALIDINPUT
++  if (Setup.ShowValidInput) {
++     if (HasPre && HasSucc) buffer = cString::sprintf("%s:\t<%s>", name, Value);
++     else if (HasPre)       buffer = cString::sprintf("%s:\t<%s",  name, Value);
++     else if (HasSucc)      buffer = cString::sprintf("%s:\t%s>",  name, Value);
++     }
++#endif /* VALIDINPUT */
+   SetText(buffer);
+   cStatus::MsgOsdCurrentItem(buffer);
+ }
+@@ -127,7 +138,11 @@
+ {
+   char buf[16];
+   snprintf(buf, sizeof(buf), "%s", *value ? trueString : falseString);
++#ifdef USE_VALIDINPUT
++  SetValue(buf, *value, !*value);
++#else
+   SetValue(buf);
++#endif /* VALIDINPUT */
+ }
+ // --- cMenuEditBitItem ------------------------------------------------------
+@@ -685,6 +700,170 @@
+   return osContinue;
+ }
++#ifdef USE_LIEMIEXT
++// --- cMenuEditRecPathItem --------------------------------------------------
++
++cMenuEditRecPathItem::cMenuEditRecPathItem(const char* Name, char* Path,
++   int Length): cMenuEditStrItem(Name, Path, Length, tr(FileNameChars))
++{
++  SetBase(Path);
++}
++
++cMenuEditRecPathItem::~cMenuEditRecPathItem()
++{
++}
++
++void cMenuEditRecPathItem::SetBase(const char* Path)
++{
++  if (!Path)
++      base[0] = 0;
++  Utf8Strn0Cpy(base, Path, sizeof(base));
++  char* p = strrchr(base, '~');
++  if (p)
++     p[0] = 0;
++  else
++     base[0] = 0;
++}
++
++void cMenuEditRecPathItem::FindNextLevel()
++{
++  char item[MaxFileName];
++
++  for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording))
++  {
++     char* p;
++     Utf8Strn0Cpy(item, recording->Name(), sizeof(item));
++     stripspace(value);
++     lengthUtf8 = Utf8ToArray(value, valueUtf8, length);
++     if (!lengthUtf8)
++        p = strchr(item, '~');
++     else {
++        if (strstr(item, value) != item)
++           continue;
++        if (item[strlen(value)] != '~')
++           continue;
++        p = strchr(item + strlen(value) + 1, '~');
++        }
++     if (!p)
++        continue;
++     p[0] = 0;
++     Utf8Strn0Cpy(base, value, length);
++     Utf8Strn0Cpy(value, item, length);
++     lengthUtf8 = Utf8ToArray(value, valueUtf8, length);
++     return;
++     }
++}
++
++void cMenuEditRecPathItem::Find(bool Next)
++{
++  char item[MaxFileName];
++  char lastItem[MaxFileName] = "";
++
++  for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording))
++  {
++     const char* recName = recording->Name();
++     if (Utf8StrLen(base) && strstr(recName, base) != recName)
++        continue;
++     if (strlen(base) && recName[strlen(base)] != '~')
++        continue;
++     Utf8Strn0Cpy(item, recName, sizeof(item));
++     char* p = strchr(item + strlen(base) + 1, '~');
++     if (!p)
++        continue;
++     p[0] = 0;
++     if (!Next && (strcmp(item, value) == 0)) {
++        if (strlen(lastItem))
++           Utf8Strn0Cpy(value, lastItem, length);
++        lengthUtf8 = Utf8ToArray(value, valueUtf8, length);
++        return;
++        }
++     if (strcmp(lastItem, item) != 0) {
++        if(Next && Utf8StrLen(lastItem) && strcmp(lastItem, value) == 0) {
++           Utf8Strn0Cpy(value, item, length);
++           lengthUtf8 = Utf8ToArray(value, valueUtf8, length);
++           return;
++           }
++        Utf8Strn0Cpy(lastItem, item, sizeof(lastItem));
++        }
++     }
++}
++
++void cMenuEditRecPathItem::SetHelpKeys(void)
++{
++  cSkinDisplay::Current()->SetButtons(tr("Rename$Up"), tr("Rename$Down"), tr("Rename$Previous"), tr("Rename$Next"));
++}
++
++eOSState cMenuEditRecPathItem::ProcessKey(eKeys Key)
++{
++  switch (Key) {
++    case kLeft:
++    case kRed:    // one level up
++                  if (!InEditMode())
++                     return cMenuEditItem::ProcessKey(Key);
++                  Utf8Strn0Cpy(value, base, lengthUtf8);
++                  lengthUtf8 = Utf8ToArray(value, valueUtf8, length);
++                  SetBase(base);
++                  pos = Utf8StrLen(base);
++                  if (pos)
++                     pos++;
++                  if (!lengthUtf8) {
++                     Utf8Strn0Cpy(value, " ", length);
++                     lengthUtf8 = Utf8ToArray(value, valueUtf8, length);
++                     }
++                  break;
++    case kRight:
++    case kGreen:  // one level down
++                  if (InEditMode())
++                     FindNextLevel();
++                  else
++                     EnterEditMode();
++                  if (!lengthUtf8) {
++                     Utf8Strn0Cpy(value, " ", length);
++                     lengthUtf8 = Utf8ToArray(value, valueUtf8, length);
++                     }
++                  pos = Utf8StrLen(base);
++                  if (pos)
++                     pos++;
++                  SetHelpKeys();
++                  break;
++    case kUp|k_Repeat:
++    case kUp:
++    case kYellow|k_Repeat:
++    case kYellow: // previous directory in list
++                  if (!InEditMode())
++                     return cMenuEditItem::ProcessKey(Key);
++                  Find(false);
++                  pos = Utf8StrLen(base);
++                  if (pos)
++                     pos++;
++                  break;
++    case kDown|k_Repeat:
++    case kDown:
++    case kBlue|k_Repeat:
++    case kBlue:   // next directory in list
++                  if (!InEditMode())
++                     return cMenuEditItem::ProcessKey(Key);
++                  Find(true);
++                  pos = Utf8StrLen(base);
++                  if (pos)
++                     pos++;
++                  break;
++    case kOk:     // done
++                  if (!InEditMode())
++                     return cMenuEditItem::ProcessKey(Key);
++                  stripspace(value);
++                  lengthUtf8 = Utf8ToArray(value, valueUtf8, length);
++                  cSkinDisplay::Current()->SetButtons(NULL);
++                  LeaveEditMode(Key == kOk);
++                  break;
++    default:
++                  return cMenuEditItem::ProcessKey(Key);
++    }
++  Set();
++  return osContinue;
++}
++#endif /* LIEMIEXT */
++
+ // --- cMenuEditStraItem -----------------------------------------------------
+ cMenuEditStraItem::cMenuEditStraItem(const char *Name, int *Value, int NumStrings, const char * const *Strings)
+@@ -696,7 +875,11 @@
+ void cMenuEditStraItem::Set(void)
+ {
++#ifdef USE_VALIDINPUT
++  SetValue(strings[*value], (*value > min), (*value < max));
++#else
+   SetValue(strings[*value]);
++#endif /* VALIDINPUT */
+ }
+ // --- cMenuEditChanItem -----------------------------------------------------
+diff -NaurwB vdr-1.7.10/menuitems.h vdr-1.7.10-patched/menuitems.h
+--- vdr-1.7.10/menuitems.h     2009-05-03 14:50:34.000000000 +0200
++++ vdr-1.7.10-patched/menuitems.h     2009-12-18 06:25:25.000000000 +0100
+@@ -21,7 +21,11 @@
+ public:
+   cMenuEditItem(const char *Name);
+   ~cMenuEditItem();
++#ifdef USE_VALIDINPUT
++  void SetValue(const char *Value, bool HasPre=false, bool HasSucc=false);
++#else
+   void SetValue(const char *Value);
++#endif /* VALIDINPUT */
+   };
+ class cMenuEditIntItem : public cMenuEditItem {
+@@ -90,26 +94,46 @@
+ class cMenuEditStrItem : public cMenuEditItem {
+ private:
++#ifdef USE_LIEMIEXT
++  int offset;
++#else
+   char *value;
+   int length;
+   const char *allowed;
+   int pos, offset;
++#endif /* LIEMIEXT */
+   bool insert, newchar, uppercase;
++#ifndef USE_LIEMIEXT
+   int lengthUtf8;
+   uint *valueUtf8;
++#endif /* LIEMIEXT */
+   uint *allowedUtf8;
+   uint *charMapUtf8;
+   uint *currentCharUtf8;
+   eKeys lastKey;
+   cTimeMs autoAdvanceTimeout;
++#ifndef USE_LIEMIEXT
+   void SetHelpKeys(void);
++#endif /* LIEMIEXT */
+   uint *IsAllowed(uint c);
+   void AdvancePos(void);
++#ifndef USE_LIEMIEXT
+   virtual void Set(void);
++#endif /* LIEMIEXT */
+   uint Inc(uint c, bool Up);
+   void Insert(void);
+   void Delete(void);
+ protected:
++#ifdef USE_LIEMIEXT
++  char *value;
++  int length;
++  uint *valueUtf8;
++  int lengthUtf8;
++  const char *allowed;
++  int pos;
++  void SetHelpKeys(void);
++  virtual void Set(void);
++#endif /* LIEMIEXT */
+   void EnterEditMode(void);
+   void LeaveEditMode(bool SaveValue = false);
+   bool InEditMode(void) { return valueUtf8 != NULL; }
+@@ -119,6 +143,21 @@
+   virtual eOSState ProcessKey(eKeys Key);
+   };
++#ifdef USE_LIEMIEXT
++class cMenuEditRecPathItem : public cMenuEditStrItem {
++protected:
++  char base[MaxFileName];
++  virtual void SetHelpKeys(void);
++  void SetBase(const char* Path);
++  void FindNextLevel();
++  void Find(bool Next);
++public:
++  cMenuEditRecPathItem(const char* Name, char* Path, int Length);
++  ~cMenuEditRecPathItem();
++  virtual eOSState ProcessKey(eKeys Key);
++  };
++#endif /* LIEMIEXT */
++
+ class cMenuEditStraItem : public cMenuEditIntItem {
+ private:
+   const char * const *strings;
+@@ -197,6 +236,9 @@
+   cMenuSetupPage(void);
+   virtual eOSState ProcessKey(eKeys Key);
+   void SetPlugin(cPlugin *Plugin);
++#ifdef USE_GRAPHTFT
++  const char* MenuKind() { return "MenuSetupPage"; }
++#endif /* GRAPHTFT */
+   };
+ #endif //__MENUITEMS_H
+diff -NaurwB vdr-1.7.10/menuorgpatch.h vdr-1.7.10-patched/menuorgpatch.h
+--- vdr-1.7.10/menuorgpatch.h  1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.10-patched/menuorgpatch.h  2009-12-18 06:25:25.000000000 +0100
+@@ -0,0 +1,102 @@
++#ifdef USE_MENUORG
++/*
++ * vdr-menuorg - A plugin for the Linux Video Disk Recorder
++ * Copyright (c) 2007 - 2008 Tobias Grimm <vdr@e-tobi.net>
++ *
++ * 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 of the License, 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
++ * this program; if not, write to the Free Software Foundation, Inc.,
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
++ *
++ * $Id$
++ *
++ */
++
++#ifndef __MENUORGPATCH_H
++#define __MENUORGPATCH_H
++
++#include "mainmenuitemsprovider.h"
++
++class MenuOrgPatch
++{
++    private:
++        static IMainMenuItemsProvider* _mainMenuItemsProvider;
++
++    private:
++        static IMainMenuItemsProvider* MainMenuItemsProvider()
++        {
++            if (!_mainMenuItemsProvider)
++            {
++                IMainMenuItemsProvider* mainMenuItemsProvider;
++
++                if (cPluginManager::CallFirstService(MENU_ITEMS_PROVIDER_SERVICE_ID, &mainMenuItemsProvider))
++                {
++                    _mainMenuItemsProvider = mainMenuItemsProvider;
++                }
++            }
++            return _mainMenuItemsProvider;
++        }
++
++    public:
++        static bool IsCustomMenuAvailable()
++        {
++            return (MainMenuItemsProvider() != NULL) && (MainMenuItemsProvider()->IsCustomMenuAvailable());
++        }
++
++        static void EnterRootMenu()
++        {
++            if (MainMenuItemsProvider())
++            {
++                MainMenuItemsProvider()->EnterRootMenu();
++            }
++        }
++
++        static bool LeaveSubMenu()
++        {
++            if (MainMenuItemsProvider())
++            {
++                return MainMenuItemsProvider()->LeaveSubMenu();
++            }
++            return false;
++        }
++
++        static void EnterSubMenu(cOsdItem* item)
++        {
++            if (MainMenuItemsProvider())
++            {
++                MainMenuItemsProvider()->EnterSubMenu(item);
++            }
++        }
++
++        static MenuItemDefinitions* MainMenuItems()
++        {
++            if (MainMenuItemsProvider())
++            {
++                return MainMenuItemsProvider()->MainMenuItems();
++            }
++            return NULL;
++        }
++
++        static cOsdMenu* Execute(cOsdItem* item)
++        {
++            if (MainMenuItemsProvider())
++            {
++                return MainMenuItemsProvider()->Execute(item);
++            }
++            return NULL;
++        }
++};
++
++IMainMenuItemsProvider* MenuOrgPatch::_mainMenuItemsProvider = NULL;
++
++#endif //__MENUORGPATCH_H
++#endif /* MENUORG */
+diff -NaurwB vdr-1.7.10/osdbase.c vdr-1.7.10-patched/osdbase.c
+--- vdr-1.7.10/osdbase.c       2009-06-01 13:54:50.000000000 +0200
++++ vdr-1.7.10-patched/osdbase.c       2009-12-18 06:25:25.000000000 +0100
+@@ -22,6 +22,9 @@
+   state = State;
+   selectable = true;
+   fresh = true;
++#if defined (USE_SETUP) && defined (USE_PINPLUGIN)
++  subMenu = 0;
++#endif /* SETUP & PINPLUGIN */
+ }
+ cOsdItem::cOsdItem(const char *Text, eOSState State, bool Selectable)
+@@ -31,8 +34,23 @@
+   selectable = Selectable;
+   fresh = true;
+   SetText(Text);
++#if defined (USE_SETUP) && defined (USE_PINPLUGIN)
++  subMenu = 0;
++#endif /* SETUP & PINPLUGIN */
+ }
++#if defined (USE_SETUP) && defined (USE_PINPLUGIN)
++cOsdItem::cOsdItem(const char *Text, eOSState State, cSubMenuNode* SubMenu)
++{
++  text = NULL;
++  state = State;
++  selectable = true;
++  fresh = true;
++  SetText(Text);
++  subMenu = SubMenu;
++}
++#endif /* SETUP & PINPLUGIN */
++
+ cOsdItem::~cOsdItem()
+ {
+   free(text);
+@@ -77,6 +95,9 @@
+ {
+   isMenu = true;
+   digit = 0;
++#ifdef USE_LIEMIEXT
++  key_nr = -1;
++#endif /* LIEMIEXT */
+   hasHotkeys = false;
+   title = NULL;
+   SetTitle(Title);
+@@ -97,6 +118,9 @@
+   free(status);
+   displayMenu->Clear();
+   cStatus::MsgOsdClear();
++#ifdef USE_GRAPHTFT
++  cStatus::MsgOsdMenuDestroy();
++#endif /* GRAPHTFT */
+   if (!--displayMenuCount)
+      DELETENULL(displayMenu);
+ }
+@@ -119,7 +143,11 @@
+         digit = -1; // prevents automatic hotkeys - input already has them
+      if (digit >= 0) {
+         digit++;
++#ifdef USE_LIEMIEXT
++        buffer = cString::sprintf(" %2d%s %s", digit, (digit > 9) ? "" : " ", s);
++#else
+         buffer = cString::sprintf(" %c %s", (digit < 10) ? '0' + digit : ' ' , s);
++#endif /* LIEMIEXT */
+         s = buffer;
+         }
+      }
+@@ -199,9 +227,15 @@
+      subMenu->Display();
+      return;
+      }
++#ifdef USE_OSDMAXITEMS
++  displayMenuItems = displayMenu->MaxItems();
++#endif /* OSDMAXITEMS */
+   displayMenu->SetMessage(mtStatus, NULL);
+   displayMenu->Clear();
+   cStatus::MsgOsdClear();
++#ifdef USE_GRAPHTFT
++  cStatus::MsgOsdMenuDisplay(MenuKind());
++#endif /* GRAPHTFT */
+   displayMenu->SetTabs(cols[0], cols[1], cols[2], cols[3], cols[4]);//XXX
+   displayMenu->SetTitle(title);
+   cStatus::MsgOsdTitle(title);
+@@ -297,6 +331,9 @@
+ void cOsdMenu::CursorUp(void)
+ {
++#ifdef USE_OSDMAXITEMS
++  displayMenuItems = displayMenu->MaxItems();
++#endif /* OSDMAXITEMS */
+   int tmpCurrent = current;
+   int lastOnScreen = first + displayMenuItems - 1;
+   int last = Count() - 1;
+@@ -335,6 +372,9 @@
+ void cOsdMenu::CursorDown(void)
+ {
++#ifdef USE_OSDMAXITEMS
++  displayMenuItems = displayMenu->MaxItems();
++#endif /* OSDMAXITEMS */
+   int tmpCurrent = current;
+   int lastOnScreen = first + displayMenuItems - 1;
+   int last = Count() - 1;
+@@ -375,6 +415,9 @@
+ void cOsdMenu::PageUp(void)
+ {
++#ifdef USE_OSDMAXITEMS
++  displayMenuItems = displayMenu->MaxItems();
++#endif /* OSDMAXITEMS */
+   int oldCurrent = current;
+   int oldFirst = first;
+   current -= displayMenuItems;
+@@ -409,6 +452,9 @@
+ void cOsdMenu::PageDown(void)
+ {
++#ifdef USE_OSDMAXITEMS
++  displayMenuItems = displayMenu->MaxItems();
++#endif /* OSDMAXITEMS */
+   int oldCurrent = current;
+   int oldFirst = first;
+   current += displayMenuItems;
+@@ -449,6 +495,64 @@
+      }
+ }
++#ifdef USE_LIEMIEXT
++#define MENUKEY_TIMEOUT 1500
++
++eOSState cOsdMenu::HotKey(eKeys Key)
++{
++  bool match = false;
++  bool highlight = false;
++  int  item_nr;
++  int  i;
++
++  if (Key == kNone) {
++     if (lastActivity.TimedOut())
++        Key = kOk;
++     else
++        return osContinue;
++     }
++  else {
++     lastActivity.Set(MENUKEY_TIMEOUT);
++     }
++  for (cOsdItem *item = Last(); item; item = Prev(item)) {
++      const char *s = item->Text();
++      i = 0;
++      item_nr = 0;
++      if (s && (s = skipspace(s)) != '\0' && '0' <= s[i] && s[i] <= '9') {
++         do {
++            item_nr = item_nr * 10 + (s[i] - '0');
++            }
++         while (!((s[++i] == '\t')||(s[i] == ' ')) && (s[i] != '\0') && ('0' <= s[i]) && (s[i] <= '9'));
++         if ((Key == kOk) && (item_nr == key_nr)) {
++            current = item->Index();
++            RefreshCurrent();
++            Display();
++            cRemote::Put(kOk, true);
++            key_nr = -1;
++            break;
++            }
++         else if (Key != kOk) {
++            if (!highlight && (item_nr == (Key - k0))) {
++               highlight = true;
++               current = item->Index();
++               }
++            if (!match && (key_nr == -1) && ((item_nr / 10) == (Key - k0))) {
++               match = true;
++               key_nr = (Key - k0);
++               }
++            else if (((key_nr == -1) && (item_nr == (Key - k0))) || (!match && (key_nr >= 0) && (item_nr == (10 * key_nr + Key - k0)))) {
++               current = item->Index();
++               cRemote::Put(kOk, true);
++               key_nr = -1;
++               break;
++               }
++            }
++         }
++      }
++  if ((!match) && (Key != kNone)) {
++     key_nr = -1;
++     }
++#else
+ eOSState cOsdMenu::HotKey(eKeys Key)
+ {
+   for (cOsdItem *item = First(); item; item = Next(item)) {
+@@ -463,6 +567,7 @@
+             }
+          }
+       }
++#endif /* LIEMIEXT */
+   return osContinue;
+ }
+@@ -501,8 +606,13 @@
+         }
+      }
+   switch (Key) {
++#ifdef USE_LIEMIEXT
++    case kNone:
++    case k0...k9: return hasHotkeys ? HotKey(Key) : osUnknown;
++#else
+     case k0:      return osUnknown;
+     case k1...k9: return hasHotkeys ? HotKey(Key) : osUnknown;
++#endif /* LIEMIEXT */
+     case kUp|k_Repeat:
+     case kUp:   CursorUp();   break;
+     case kDown|k_Repeat:
+diff -NaurwB vdr-1.7.10/osdbase.h vdr-1.7.10-patched/osdbase.h
+--- vdr-1.7.10/osdbase.h       2007-11-03 15:50:52.000000000 +0100
++++ vdr-1.7.10-patched/osdbase.h       2009-12-18 06:25:25.000000000 +0100
+@@ -15,6 +15,10 @@
+ #include "skins.h"
+ #include "tools.h"
++#if defined (USE_SETUP) && defined (USE_PINPLUGIN)
++#include "submenu.h"
++#endif /* SETUP & PINPLUGIN */
++
+ enum eOSState { osUnknown,
+                 osContinue,
+                 osSchedule,
+@@ -51,16 +55,26 @@
+   char *text;
+   eOSState state;
+   bool selectable;
++#if defined (USE_SETUP) && defined (USE_PINPLUGIN)
++  cSubMenuNode* subMenu;
++#endif /* SETUP & PINPLUGIN */
+ protected:
+   bool fresh;
+ public:
+   cOsdItem(eOSState State = osUnknown);
+   cOsdItem(const char *Text, eOSState State = osUnknown, bool Selectable = true);
++#if defined (USE_SETUP) && defined (USE_PINPLUGIN)
++  cOsdItem(const char *Text, eOSState State, cSubMenuNode* SubMenu);
++#endif /* SETUP & PINPLUGIN */
+   virtual ~cOsdItem();
+   bool Selectable(void) const { return selectable; }
+   void SetText(const char *Text, bool Copy = true);
+   void SetSelectable(bool Selectable);
+   void SetFresh(bool Fresh);
++#if defined (USE_SETUP) && defined (USE_PINPLUGIN)
++  void SetSubMenu(cSubMenuNode* SubMenu) { subMenu = SubMenu; }
++  cSubMenuNode* SubMenu() { return subMenu; }
++#endif /* SETUP & PINPLUGIN */
+   const char *Text(void) const { return text; }
+   virtual void Set(void) {}
+   virtual eOSState ProcessKey(eKeys Key);
+@@ -95,6 +109,10 @@
+   char *status;
+   int digit;
+   bool hasHotkeys;
++#ifdef USE_LIEMIEXT
++  int key_nr;
++  cTimeMs lastActivity;
++#endif /* LIEMIEXT */
+ protected:
+   void SetDisplayMenu(void);
+   cSkinDisplayMenu *DisplayMenu(void) { return displayMenu; }
+@@ -129,6 +147,9 @@
+   void Ins(cOsdItem *Item, bool Current = false, cOsdItem *Before = NULL);
+   virtual void Display(void);
+   virtual eOSState ProcessKey(eKeys Key);
++#ifdef USE_GRAPHTFT
++  virtual const char* MenuKind() { return "MenuUnknown"; }
++#endif /* GRAPHTFT */
+   };
+ #endif //__OSDBASE_H
+diff -NaurwB vdr-1.7.10/osd.c vdr-1.7.10-patched/osd.c
+--- vdr-1.7.10/osd.c   2009-05-09 12:42:35.000000000 +0200
++++ vdr-1.7.10-patched/osd.c   2009-12-18 06:25:25.000000000 +0100
+@@ -725,6 +725,9 @@
+ int cOsd::osdWidth = 0;
+ int cOsd::osdHeight = 0;
+ cVector<cOsd *> cOsd::Osds;
++#ifdef USE_PINPLUGIN
++bool cOsd::pinValid = false;
++#endif /* PINPLUGIN */
+ cOsd::cOsd(int Left, int Top, uint Level)
+ {
+@@ -735,6 +738,9 @@
+   width = height = 0;
+   level = Level;
+   active = false;
++#ifdef USE_YAEPG
++  vidWin.bpp = 0;
++#endif /* YAEPG */
+   for (int i = 0; i < Osds.Size(); i++) {
+       if (Osds[i]->level > level) {
+          Osds.Insert(this, i);
+diff -NaurwB vdr-1.7.10/osd.h vdr-1.7.10-patched/osd.h
+--- vdr-1.7.10/osd.h   2009-05-08 15:41:03.000000000 +0200
++++ vdr-1.7.10-patched/osd.h   2009-12-18 06:25:25.000000000 +0100
+@@ -401,6 +401,12 @@
+        ///< 7: vertical,   falling, upper
+   virtual void Flush(void);
+        ///< Actually commits all data to the OSD hardware.
++#ifdef USE_PINPLUGIN
++  static bool pinValid;
++#endif /* PINPLUGIN */
++#ifdef USE_YAEPG
++  tArea vidWin;
++#endif /* YAEPG */
+   };
+ class cOsdProvider {
+diff -NaurwB vdr-1.7.10/pat.c vdr-1.7.10-patched/pat.c
+--- vdr-1.7.10/pat.c   2009-08-16 17:01:03.000000000 +0200
++++ vdr-1.7.10-patched/pat.c   2009-12-18 06:25:25.000000000 +0100
+@@ -13,6 +13,9 @@
+ #include "libsi/section.h"
+ #include "libsi/descriptor.h"
+ #include "thread.h"
++#ifdef USE_TTXTSUBS
++#include "vdrttxtsubshooks.h"
++#endif /* TTXTSUBS */
+ #define PMT_SCAN_TIMEOUT  10 // seconds
+@@ -341,6 +344,11 @@
+         char DLangs[MAXDPIDS][MAXLANGCODE2] = { "" };
+         char SLangs[MAXSPIDS][MAXLANGCODE2] = { "" };
+         int Tpid = 0;
++#ifdef USE_TTXTSUBS
++        char TLangs[MAXTPAGES][MAXLANGCODE2] = { "" };
++        int TPages[MAXTPAGES + 1] = { 0 };
++        int NumTPages = 0;
++#endif /* TTXTSUBS */
+         int NumApids = 0;
+         int NumDpids = 0;
+         int NumSpids = 0;
+@@ -422,8 +430,24 @@
+                                     NumSpids++;
+                                     }
+                                  break;
++#ifdef USE_TTXTSUBS
++                            case SI::TeletextDescriptorTag: {
++                                 Tpid = esPid;
++                                 SI::TeletextDescriptor *sd = (SI::TeletextDescriptor *)d;
++                                 SI::TeletextDescriptor::Teletext ttxt;
++                                 for (SI::Loop::Iterator it; sd->teletextLoop.getNext(ttxt, it); ) {
++                                     if ((NumTPages < MAXTPAGES) && ttxt.languageCode[0] && ((ttxt.getTeletextType() == 0x02) || (ttxt.getTeletextType() == 0x05))) {
++                                        char *s = TLangs[NumTPages];
++                                        strn0cpy(s, I18nNormalizeLanguageCode(ttxt.languageCode), MAXLANGCODE1);
++                                        TPages[NumTPages] = (ttxt.getTeletextPageNumber() & 0xff) | ((ttxt.getTeletextMagazineNumber() & 0xff) << 8) | ((ttxt.getTeletextType() & 0xff) << 16);
++                                        NumTPages++;
++                                        }
++                                     }
++                                 }
++#else
+                             case SI::TeletextDescriptorTag:
+                                  Tpid = esPid;
++#endif /* TTXTSUBS */                                 
+                                  break;
+                             case SI::ISO639LanguageDescriptorTag: {
+                                  SI::ISO639LanguageDescriptor *ld = (SI::ISO639LanguageDescriptor *)d;
+@@ -452,6 +476,18 @@
+             }
+         if (Setup.UpdateChannels >= 2) {
+            Channel->SetPids(Vpid, Ppid, Vtype, Apids, ALangs, Dpids, DLangs, Spids, SLangs, Tpid);
++#ifdef USE_TTXTSUBS
++           if (NumTPages < MAXTPAGES) {
++              int manualPageNumber = cVDRTtxtsubsHookListener::Hook()->ManualPageNumber(Channel);
++              if (manualPageNumber) {
++                 char *s = TLangs[NumTPages];
++                 strn0cpy(s, "man", MAXLANGCODE1);
++                 TPages[NumTPages] = manualPageNumber;
++                 NumTPages++;
++                 }
++              }
++           Channel->SetTPidData(TLangs, TPages);
++#endif /* TTXTSUBS */
+            Channel->SetCaIds(CaDescriptors->CaIds());
+            Channel->SetSubtitlingDescriptors(SubtitlingTypes, CompositionPageIds, AncillaryPageIds);
+            }
+diff -NaurwB vdr-1.7.10/plugin.c vdr-1.7.10-patched/plugin.c
+--- vdr-1.7.10/plugin.c        2009-04-05 12:16:48.000000000 +0200
++++ vdr-1.7.10-patched/plugin.c        2009-12-18 06:25:25.000000000 +0100
+@@ -317,6 +317,14 @@
+   char *p = strchr(s, ' ');
+   if (p)
+      *p = 0;
++#ifdef USE_PLUGINMISSING
++  struct stat st;
++  if (stat (cString::sprintf("%s/%s%s%s%s", directory, LIBVDR_PREFIX, s, SO_INDICATOR, APIVERSION), &st) && errno == ENOENT) {
++     esyslog("WARN: missing plugin '%s'", s);
++     fprintf(stderr, "vdr: missing plugin '%s'\n", s);
++     }
++  else
++#endif /* PLUGINMISSING */
+   dlls.Add(new cDll(cString::sprintf("%s/%s%s%s%s", directory, LIBVDR_PREFIX, s, SO_INDICATOR, APIVERSION), Args));
+   free(s);
+ }
+@@ -325,7 +333,11 @@
+ {
+   for (cDll *dll = dlls.First(); dll; dll = dlls.Next(dll)) {
+       if (!dll->Load(Log))
++#ifdef USE_PLUGINMISSING
++         ;
++#else
+          return false;
++#endif /* PLUGINMISSING */
+       }
+   return true;
+ }
+diff -NaurwB vdr-1.7.10/po/de_DE.po vdr-1.7.10-patched/po/de_DE.po
+--- vdr-1.7.10/po/de_DE.po     2009-11-22 12:30:27.000000000 +0100
++++ vdr-1.7.10-patched/po/de_DE.po     2009-12-18 06:25:25.000000000 +0100
+@@ -806,6 +806,30 @@
+ msgid "Setup.Miscellaneous$Emergency exit"
+ msgstr "Notausstieg"
++msgid "Setup.Miscellaneous$Volume ctrl with left/right"
++msgstr "Lautstärke mit Rechts/Links regeln"
++
++msgid "Setup.Miscellaneous$Channelgroups with left/right"
++msgstr "Kanalgruppen mit Rechts/Links"
++
++msgid "Setup.Miscellaneous$only in channelinfo"
++msgstr "nur in Kanalinfo"
++
++msgid "Setup.Miscellaneous$Search fwd/back with left/right"
++msgstr "Vor-/Rücklauf mit Rechts/Links"
++
++msgid "Setup.Miscellaneous$only in progress display"
++msgstr "nur in Fortschrittsanzeige"
++
++msgid "Setup.Miscellaneous$Lirc repeat delay"
++msgstr "Lirc Verzögerung"
++
++msgid "Setup.Miscellaneous$Lirc repeat freq"
++msgstr "Lirc Frequenz"
++
++msgid "Setup.Miscellaneous$Lirc repeat timeout"
++msgstr "Lirc Zeitbeschränkung"
++
+ msgid "Plugins"
+ msgstr "Plugins"
+@@ -1028,3 +1052,306 @@
+ #, c-format
+ msgid "VDR will shut down in %s minutes"
+ msgstr "VDR wird in %s Minuten ausschalten"
++
++msgid "Parameters"
++msgstr "Parameter"
++
++msgid "Channel locked by LNB!"
++msgstr "Kanal durch LNB gesperrt!"
++
++msgid "Childlock"
++msgstr "Kindersicherung"
++
++msgid "Path"
++msgstr "Pfad"
++
++msgid "Timer commands"
++msgstr "Befehle für Aufzeichnungen"
++
++msgid "Rename recording"
++msgstr "Aufzeichnung umbenennen"
++
++msgid "Date"
++msgstr "Datum"
++
++msgid "Length"
++msgstr "Länge"
++
++msgid "Size"
++msgstr "Größe"
++
++msgid "Delete marks information?"
++msgstr "Marks löschen?"
++
++msgid "Delete resume information?"
++msgstr "Resume löschen?"
++
++msgid "DVD plugin is not installed!"
++msgstr "Das DVD-Plugin ist nicht installiert!"
++
++msgid "main dir alphabetically, subdirs flexible"
++msgstr "Alphabet für Haupt-, flexibel für Unterverzeichnisse"
++
++msgid "main dir by date, subdirs flexible"
++msgstr "Datum für Haupt-, flexibel für Unterverzeichnisse"
++
++msgid "all alphabetically"
++msgstr "Alles alphabetisch"
++
++msgid "all by date"
++msgstr "Alles nach Datum"
++
++msgid "Sorting"
++msgstr "Sortierung"
++
++msgid "Setup.OSD$WarEagle icons"
++msgstr "WarEagle icons verwenden"
++
++msgid "Setup.OSD$Main menu title"
++msgstr "Hauptmenü Titel"
++
++msgid "Setup.OSD$- Text"
++msgstr "- Text"
++
++msgid "default"
++msgstr "Voreinstellung"
++
++msgid "VDR - text"
++msgstr "VDR - Text"
++
++msgid "text"
++msgstr "Text"
++
++msgid "VDR - version"
++msgstr "VDR - Version"
++
++msgid "Setup.OSD$Main menu command position"
++msgstr "Befehle Position im Hauptmenü"
++
++msgid "Setup.OSD$Show valid input"
++msgstr "Zeige gültige Eingabe"
++
++msgid "Setup.EPG$Show progress bar"
++msgstr "Zeitbalken anzeigen"
++
++msgid "Setup.EPG$Period for double EPG search(min)"
++msgstr "Zeitspanne für dop. EPG-Suche (min)"
++
++msgid "Setup.EPG$Extern double Epg entry"
++msgstr "Doppelten externen EPG-Eintrag"
++
++msgid "Setup.EPG$Mode of noEPG-Patch"
++msgstr "Art des noEPG-Patches"
++
++msgid "adjust"
++msgstr "anwenden"
++
++msgid "delete"
++msgstr "löschen"
++
++msgid "Setup.EPG$Mix intern and extern EPG"
++msgstr "Internes und externes EPG mischen"
++
++msgid "Setup.EPG$Disable running VPS event"
++msgstr "Erk. des lauf. VPS-Events abschalten"
++
++msgid "Setup.DVB$Use AC3-Transfer Fix"
++msgstr "AC3-Transfer Fix benutzen"
++
++msgid "Setup.DVB$Channel Blocker"
++msgstr "Sperre Kanäle"
++
++msgid "Setup.DVB$Channel Blocker Filter Mode"
++msgstr "Kanal Sperren Filter"
++
++msgid "qam256"
++msgstr "qam256"
++
++msgid "dvb-c"
++msgstr "dvb-c"
++
++msgid "dvb-s"
++msgstr "dvb-s"
++
++msgid "all"
++msgstr "alle"
++
++msgid "blacklist"
++msgstr "Blacklist"
++
++msgid "whitelist"
++msgstr "Whitelist"
++
++msgid "has decoder"
++msgstr "mit Decoder"
++
++msgid "is primary"
++msgstr "ist primär"
++
++msgid "has decoder + is primary"
++msgstr "mit Decoder und primär"
++
++msgid "Setup.LNB$DVB device %d uses LNB No."
++msgstr "DVB-Empfänger %d nutzt LNB Nr."
++
++msgid "Setup.LNB$Log LNB usage"
++msgstr "LNB-Nutzung protokollieren"
++
++msgid "Setup.Recording$Record Dolby Digital"
++msgstr "Dolby Digital Ton aufzeichnen"
++
++msgid "Setup.Recording$Video directory policy"
++msgstr "Videoverzeichnispolitik"
++
++msgid "Setup.Recording$Number of video directories"
++msgstr "Anzahl der Videoverzeichnisse"
++
++msgid "Setup.Recording$Video %d priority"
++msgstr "Video %d Priorität"
++
++msgid "Setup.Recording$Video %d min. free MB"
++msgstr "Video %d min. MB frei"
++
++msgid "Setup.Recording$Friendly filenames"
++msgstr "Freundliche Dateinamen"
++
++msgid "Setup.Recording$Max. recording size (GB)"
++msgstr "Max. Aufnahmengröße (GB)"
++
++msgid "Setup.Recording$Hard Link Cutter"
++msgstr "Hard Link Cutter"
++
++msgid "Setup.Recording$Delete timeshift recording"
++msgstr "Zeitversetzte Aufnahme löschen"
++
++msgid "request"
++msgstr "abfragen"
++
++msgid "Setup.Recording$Show date"
++msgstr "Aufnahmedatum anzeigen"
++
++msgid "Setup.Recording$Show time"
++msgstr "Aufnahmezeit anzeigen"
++
++msgid "Setup.Recording$Show length"
++msgstr "Länge der Aufnahme anzeigen"
++
++msgid "Setup.Recording$Show end of timer"
++msgstr "Ende für Timer anzeigen"
++
++msgid "Setup.Recording$Sort recordings by"
++msgstr "Aufnahmen sortieren nach"
++
++msgid "Setup.Recording$Sort directories before recordings"
++msgstr "Verzeichnisse vor Aufnahmen einsortieren"
++
++msgid "Setup.Recording$Cutter auto delete"
++msgstr "Aufnahmen nach dem Schneiden löschen"
++
++msgid "Setup.Recording$Cutter adjust starttime"
++msgstr "Startzeit beim Schneiden anpassen"
++
++msgid "Setup.Replay$Jump&Play"
++msgstr "Wiedergabe nach Sprung"
++
++msgid "Setup.Replay$Play&Jump"
++msgstr "Sprung bei Schnittmarke"
++
++msgid "Setup.Replay$Pause at last mark"
++msgstr "Pause bei letzter Marke"
++
++msgid "Setup.Replay$Reload marks"
++msgstr "Marken aktualisieren"
++
++msgid "Setup.Replay$Skip Seconds"
++msgstr "Sprungweite in Sekunden"
++
++msgid "Setup.Replay$Skip Seconds Slow"
++msgstr "Sprungweite in Sekunden Langsam"
++
++msgid "Setup.Replay$Length"
++msgstr "Länge"
++
++msgid "Setup.Replay$Length / Number"
++msgstr "Länge / Nummer"
++
++msgid "Setup.Replay$Number"
++msgstr "Nummer"
++
++msgid "Setup.Replay$DVD display mode"
++msgstr "DVD Anzeige"
++
++msgid "Setup.Replay$DVD display leading zeros"
++msgstr "DVD führende Nullen anzeigen"
++
++msgid "Setup.Replay$never"
++msgstr "nie"
++
++msgid "Setup.Replay$on begin"
++msgstr "am Anfang"
++
++msgid "Setup.Replay$on end"
++msgstr "am Ende"
++
++msgid "Setup.Replay$on begin and end"
++msgstr "am Anfang und Ende"
++
++msgid "Setup.Replay$Tray open"
++msgstr "DVD-Schublade öffnen"
++
++msgid "Setup.Replay$Limit DVD to speed"
++msgstr "DVD drosseln auf"
++
++msgid "Jump"
++msgstr "Springen: "
++
++msgid "Skip +60s"
++msgstr "Sprung +60s"
++
++msgid "Skip -60s"
++msgstr "Sprung -60s"
++
++msgid "Cutter already running - Add to cutting queue?"
++msgstr "Schnitt bereits aktiv - zur Schnitt-Liste hinzufügen?"
++
++msgid "Format"
++msgstr "Format"
++
++msgid "PES"
++msgstr "PES"
++
++msgid "TS"
++msgstr "TS"
++
++msgid "Rename$Up"
++msgstr "Höher"
++
++msgid "Rename$Down"
++msgstr "Tiefer"
++
++msgid "Rename$Previous"
++msgstr "Vorheriger"
++
++msgid "Rename$Next"
++msgstr "Nächster"
++
++msgid "Please mount %s"
++msgstr "Bitte %s einlegen!"
++
++msgid "Please mount DVD %04d"
++msgstr "Bitte DVD %04d einlegen!"
++
++msgid "Please mount DVD %d"
++msgstr "Bitte DVD %d einlegen!"
++
++msgid "Please wait. Checking DVD..."
++msgstr "Bitte warten. Überprüfe DVD..."
++
++msgid "No index-file found. Creating may take minutes. Create one?"
++msgstr "Keine Index-Datei gefunden. Erstellung kann Minuten dauern. Erstellen?"
++
++msgid "Please wait. Creating index-file..."
++msgstr "Bitte warten. Index-Datei wird erstellt..."
++
++msgid "Wrong DVD!"
++msgstr "Falsche DVD!"
+diff -NaurwB vdr-1.7.10/po/et_EE.po vdr-1.7.10-patched/po/et_EE.po
+--- vdr-1.7.10/po/et_EE.po     2009-11-22 12:28:38.000000000 +0100
++++ vdr-1.7.10-patched/po/et_EE.po     2009-12-18 06:25:25.000000000 +0100
+@@ -1028,3 +1028,33 @@
+ #, c-format
+ msgid "VDR will shut down in %s minutes"
+ msgstr "VDR lülitub välja %s minuti pärast"
++
++msgid "Rename recording"
++msgstr "Ümbernimetamine"
++
++msgid "Setup.OSD$Main menu command position"
++msgstr "Käsu asukoht peamenüüs"
++
++msgid "Setup.EPG$Show progress bar"
++msgstr "Edenemisriba"
++
++msgid "Setup.Recording$Show date"
++msgstr "Salvestuse kuupäev"
++
++msgid "Setup.Recording$Show time"
++msgstr "Salvestuse kellaaeg"
++
++msgid "Setup.Recording$Show length"
++msgstr "Salvestuse pikkus"
++
++msgid "Rename$Up"
++msgstr "Üles"
++
++msgid "Rename$Down"
++msgstr "Alla"
++
++msgid "Rename$Previous"
++msgstr "Eelmine"
++
++msgid "Rename$Next"
++msgstr "Järgmine"
+diff -NaurwB vdr-1.7.10/po/fi_FI.po vdr-1.7.10-patched/po/fi_FI.po
+--- vdr-1.7.10/po/fi_FI.po     2009-11-22 12:28:39.000000000 +0100
++++ vdr-1.7.10-patched/po/fi_FI.po     2009-12-18 06:25:25.000000000 +0100
+@@ -42,6 +42,253 @@
+ msgid "Starting EPG scan"
+ msgstr "Ohjelmaoppaan päivitys aloitettu"
++msgid "Content$Movie/Drama"
++msgstr "Elokuva/draama"
++
++msgid "Content$Detective/Thriller"
++msgstr "Etsivä/trilleri"
++
++msgid "Content$Adventure/Western/War"
++msgstr "Seikkailu/western/sota"
++
++msgid "Content$Science Fiction/Fantasy/Horror"
++msgstr "Scifi/fantasia/kauhu"
++
++msgid "Content$Comedy"
++msgstr "Komedia"
++
++msgid "Content$Soap/Melodrama/Folkloric"
++msgstr "Saippua/melodraama/kansanperinne"
++
++msgid "Content$Romance"
++msgstr "Romanssi"
++
++msgid "Content$Serious/Classical/Religious/Historical Movie/Drama"
++msgstr "Vakava/klassinen/uskonnollinen/historiallinen elokuva/draama"
++
++msgid "Content$Adult Movie/Drama"
++msgstr "Aikuiselokuva/draama"
++
++msgid "Content$News/Current Affairs"
++msgstr "Uutiset/ajankohtaisohjelma"
++
++msgid "Content$News/Weather Report"
++msgstr "Uutiset/säätiedot"
++
++msgid "Content$News Magazine"
++msgstr "Uutismakasiini"
++
++msgid "Content$Documentary"
++msgstr "Dokumentti"
++
++msgid "Content$Discussion/Inverview/Debate"
++msgstr "Keskustelu/haastattelu/väittely"
++
++msgid "Content$Show/Game Show"
++msgstr "Show/visailu"
++
++msgid "Content$Game Show/Quiz/Contest"
++msgstr "Visailu/kilpailu"
++
++msgid "Content$Variety Show"
++msgstr "Varietee"
++
++msgid "Content$Talk Show"
++msgstr "Keskusteluohjelma"
++
++msgid "Content$Sports"
++msgstr "Urheilua"
++
++msgid "Content$Special Event"
++msgstr "Erikoistapahtuma"
++
++msgid "Content$Sport Magazine"
++msgstr "Urheilumakasiini"
++
++msgid "Content$Football"
++msgstr "Jalkapallo"
++
++msgid "Content$Tennis/Squash"
++msgstr "Tennis/Squash"
++
++msgid "Content$Team Sports"
++msgstr "Joukkueurheilua"
++
++msgid "Content$Athletics"
++msgstr "Yleisurheilua"
++
++msgid "Content$Motor Sport"
++msgstr "Moottoriurheilua"
++
++msgid "Content$Water Sport"
++msgstr "Vesiurheilua"
++
++msgid "Content$Winter Sports"
++msgstr "Talviurheilua"
++
++msgid "Content$Equestrian"
++msgstr "Ratsastusta"
++
++msgid "Content$Martial Sports"
++msgstr "Kamppailu-urheilua"
++
++msgid "Content$Children's/Youth Programmes"
++msgstr "Lasten ja nuorten ohjelma"
++
++msgid "Content$Pre-school Children's Programmes"
++msgstr "Alle kouluikäisten ohjelma"
++
++msgid "Content$Entertainment Programmes for 6 to 14"
++msgstr "Viihdeohjelma 6-14 vuotiaille"
++
++msgid "Content$Entertainment Programmes for 10 to 16"
++msgstr "Viihdeohjelma 10-16 vuotiaille"
++
++msgid "Content$Informational/Educational/School Programme"
++msgstr "Opetus/kouluohjelma"
++
++msgid "Content$Cartoons/Puppets"
++msgstr "Piirretty/nukke-esitys"
++
++msgid "Content$Music/Ballet/Dance"
++msgstr "Musiikki/baletti/tanssi"
++
++msgid "Content$Rock/Pop"
++msgstr "Rock/pop"
++
++msgid "Content$Serious/Classical Music"
++msgstr "Vakava/klassinen musiikki"
++
++msgid "Content$Folk/Tradional Music"
++msgstr "Folk/kansanmusiikki"
++
++msgid "Content$Jazz"
++msgstr "Jazz"
++
++msgid "Content$Musical/Opera"
++msgstr "Musikaali/ooppera"
++
++msgid "Content$Ballet"
++msgstr "Baletti"
++
++msgid "Content$Arts/Culture"
++msgstr "Taide/kulttuuri"
++
++msgid "Content$Performing Arts"
++msgstr "Performanssitaide"
++
++msgid "Content$Fine Arts"
++msgstr "Kuvataide"
++
++msgid "Content$Religion"
++msgstr "Uskonto"
++
++msgid "Content$Popular Culture/Traditional Arts"
++msgstr "Populaarikulttuuri/perinnetaiteet"
++
++msgid "Content$Literature"
++msgstr "Kirjallisuus"
++
++msgid "Content$Film/Cinema"
++msgstr "Elokuvataide"
++
++msgid "Content$Experimental Film/Video"
++msgstr "Kokeellinen elokuva/video"
++
++msgid "Content$Broadcasting/Press"
++msgstr "Televisio/radio/lehdistö"
++
++msgid "Content$New Media"
++msgstr "Uusmedia"
++
++msgid "Content$Arts/Culture Magazines"
++msgstr "Taide/kulttuurimakasiini"
++
++msgid "Content$Fashion"
++msgstr "Muoti"
++
++msgid "Content$Social/Political/Economics"
++msgstr "Yhteiskunta/politiikka/talous"
++
++msgid "Content$Magazines/Reports/Documentary"
++msgstr "Makasiini/reportaasi/dokumentti"
++
++msgid "Content$Economics/Social Advisory"
++msgstr "Talous/yhteiskunnallinen neuvonta"
++
++msgid "Content$Remarkable People"
++msgstr "Merkittävät henkilöt"
++
++msgid "Content$Education/Science/Factual"
++msgstr "Koulutus/tiede"
++
++msgid "Content$Nature/Animals/Environment"
++msgstr "Luonto/eläimet/ympäristö"
++
++msgid "Content$Technology/Natural Sciences"
++msgstr "Teknologia/luonnontiede"
++
++msgid "Content$Medicine/Physiology/Psychology"
++msgstr "Lääketiede/fysiologia/psykologia"
++
++msgid "Content$Foreign Countries/Expeditions"
++msgstr "Vieraat maat/tutkimusretket"
++
++msgid "Content$Social/Spiritual Sciences"
++msgstr "Yhteiskunta/hengelliset tieteet"
++
++msgid "Content$Further Education"
++msgstr "Jatkokoulutus"
++
++msgid "Content$Languages"
++msgstr "Kielet"
++
++msgid "Content$Leisure/Hobbies"
++msgstr "Vapaa-aika ja harrastukset"
++
++msgid "Content$Tourism/Travel"
++msgstr "Turismi/matkustaminen"
++
++msgid "Content$Handicraft"
++msgstr "Käsityöt"
++
++msgid "Content$Motoring"
++msgstr "Autoilu"
++
++msgid "Content$Fitness & Health"
++msgstr "Kuntoilu & terveys"
++
++msgid "Content$Cooking"
++msgstr "Ruuanlaitto"
++
++msgid "Content$Advertisement/Shopping"
++msgstr "Mainostaminen/ostaminen"
++
++msgid "Content$Gardening"
++msgstr "Puutarhanhoito"
++
++msgid "Content$Original Language"
++msgstr "Alkuperäiskieli"
++
++msgid "Content$Black & White"
++msgstr "Mustavalkoinen"
++
++msgid "Content$Unpublished"
++msgstr "Julkaisematon"
++
++msgid "Content$Live Broadcast"
++msgstr "Suoralähetys"
++
++msgid "Content$Special Characteristics"
++msgstr "Erikoisominaisuus"
++
++msgid "Content$Drama"
++msgstr "Draama"
++
++#, c-format
++msgid "Suitable for those aged %d and over"
++msgstr "Kielletty alle %d vuotiailta"
++
+ msgid "No title"
+ msgstr "Ei esitystä"
+@@ -1031,3 +1278,252 @@
+ #, c-format
+ msgid "VDR will shut down in %s minutes"
+ msgstr "VDR sammuu %s minuutin kuluttua"
++
++msgid "Channel locked by LNB!"
++msgstr ""
++
++msgid "Childlock"
++msgstr ""
++
++msgid "Path"
++msgstr "Polku"
++
++msgid "Timer commands"
++msgstr "Ajastinkomennot"
++
++msgid "Rename recording"
++msgstr "Nimeä tallenne"
++
++msgid "Date"
++msgstr "Päiväys"
++
++msgid "Length"
++msgstr "Pituus"
++
++msgid "Size"
++msgstr "Koko"
++
++msgid "Delete marks information?"
++msgstr "Poista tallenteen merkinnät"
++
++msgid "Delete resume information?"
++msgstr "Poista tallenteen paluutiedot"
++
++msgid "DVD plugin is not installed!"
++msgstr ""
++
++msgid "main dir alphabetically, subdirs flexible"
++msgstr ""
++
++msgid "main dir by date, subdirs flexible"
++msgstr ""
++
++msgid "all alphabetically"
++msgstr ""
++
++msgid "all by date"
++msgstr ""
++
++msgid "Sorting"
++msgstr ""
++
++msgid "Setup.OSD$WarEagle icons"
++msgstr ""
++
++msgid "Setup.OSD$Main menu command position"
++msgstr "Komentojen sijainti päävalikossa"
++
++msgid "Setup.OSD$Show valid input"
++msgstr ""
++
++msgid "Setup.EPG$Show progress bar"
++msgstr "Näytä aikajana"
++
++msgid "Setup.EPG$Period for double EPG search(min)"
++msgstr ""
++
++msgid "Setup.EPG$Extern double Epg entry"
++msgstr ""
++
++msgid "adjust"
++msgstr ""
++
++msgid "delete"
++msgstr ""
++
++msgid "Setup.EPG$Mix intern and extern EPG"
++msgstr ""
++
++msgid "Setup.EPG$Disable running VPS event"
++msgstr ""
++
++msgid "Setup.DVB$Use AC3-Transfer Fix"
++msgstr ""
++
++msgid "Setup.DVB$Channel Blocker"
++msgstr ""
++
++msgid "Setup.DVB$Channel Blocker Filter Mode"
++msgstr ""
++
++msgid "Setup.LNB$DVB device %d uses LNB No."
++msgstr ""
++
++msgid "Setup.LNB$Log LNB usage"
++msgstr ""
++
++msgid "Setup.Recording$Record Dolby Digital"
++msgstr "Dolby Digital tallennus"
++
++msgid "Setup.Recording$Video directory policy"
++msgstr ""
++
++msgid "Setup.Recording$Number of video directories"
++msgstr ""
++
++msgid "Setup.Recording$Video %d priority"
++msgstr ""
++
++msgid "Setup.Recording$Video %d min. free MB"
++msgstr ""
++
++msgid "Setup.Recording$Friendly filenames"
++msgstr ""
++
++msgid "Setup.Recording$Max. recording size (GB)"
++msgstr ""
++
++msgid "Setup.Recording$Hard Link Cutter"
++msgstr ""
++
++msgid "Setup.Recording$Show date"
++msgstr "Näytä tallenteen päiväys"
++
++msgid "Setup.Recording$Show time"
++msgstr "Näytä tallenteen ajankohta"
++
++msgid "Setup.Recording$Show length"
++msgstr "Näytä tallenteen kesto"
++
++msgid "Setup.OSD$Main menu title"
++msgstr ""
++
++msgid "Setup.Recording$Show end of timer"
++msgstr ""
++
++msgid "Setup.Recording$Sort recordings by"
++msgstr ""
++
++msgid "Setup.Recording$Sort directories before recordings"
++msgstr ""
++
++msgid "Setup.Recording$Cutter auto delete"
++msgstr ""
++
++msgid "Setup.Replay$Jump&Play"
++msgstr ""
++
++msgid "Setup.Replay$Play&Jump"
++msgstr ""
++
++msgid "Setup.Replay$Pause at last mark"
++msgstr ""
++
++msgid "Setup.Replay$Reload marks"
++msgstr ""
++
++msgid "Setup.Replay$Skip Seconds"
++msgstr ""
++
++msgid "Setup.Replay$Skip Seconds Slow"
++msgstr ""
++
++msgid "Setup.Replay$Length"
++msgstr ""
++
++msgid "Setup.Replay$Length / Number"
++msgstr ""
++
++msgid "Setup.Replay$Number"
++msgstr ""
++
++msgid "Setup.Replay$DVD display mode"
++msgstr ""
++
++msgid "Setup.Replay$DVD display leading zeros"
++msgstr ""
++
++msgid "Setup.Replay$never"
++msgstr ""
++
++msgid "Setup.Replay$on begin"
++msgstr ""
++
++msgid "Setup.Replay$on end"
++msgstr ""
++
++msgid "Setup.Replay$on begin and end"
++msgstr ""
++
++msgid "Setup.Replay$Tray open"
++msgstr ""
++
++msgid "Setup.Replay$Limit DVD to speed"
++msgstr ""
++
++msgid "Jump"
++msgstr ""
++
++msgid "Skip +60s"
++msgstr ""
++
++msgid "Skip -60s"
++msgstr ""
++
++msgid "Cutter already running - Add to cutting queue?"
++msgstr ""
++
++msgid "Format"
++msgstr "Tiedostomuoto"
++
++msgid "PES"
++msgstr "PES"
++
++msgid "TS"
++msgstr "TS"
++
++msgid "Rename$Up"
++msgstr "Ylemmäs"
++
++msgid "Rename$Down"
++msgstr "Alemmas"
++
++msgid "Rename$Previous"
++msgstr "Edellinen"
++
++msgid "Rename$Next"
++msgstr "Seuraava"
++
++msgid "Please mount %s"
++msgstr ""
++
++msgid "Please mount DVD %04d"
++msgstr ""
++
++msgid "Please mount DVD %d"
++msgstr ""
++
++msgid "Please wait. Checking DVD..."
++msgstr ""
++
++msgid "No index-file found. Creating may take minutes. Create one?"
++msgstr ""
++
++msgid "Please wait. Creating index-file..."
++msgstr ""
++
++msgid "Wrong DVD!"
++msgstr ""
++
++msgid "Parameters"
++msgstr "Parametrit"
+diff -NaurwB vdr-1.7.10/po/fr_FR.po vdr-1.7.10-patched/po/fr_FR.po
+--- vdr-1.7.10/po/fr_FR.po     2009-11-22 12:28:39.000000000 +0100
++++ vdr-1.7.10-patched/po/fr_FR.po     2009-12-18 06:25:25.000000000 +0100
+@@ -8,6 +8,7 @@
+ # Pierre Briec <pbriec@free.fr>, 2006
+ # Bruno Roussel <bruno.roussel@free.fr>, 2007
+ # Michael Nival <mnival@club-internet.fr>, 2007
++# Patrice Staudt <patrice.staudt@laposte.net>, 2007, 2008
+ #
+ msgid ""
+ msgstr ""
+@@ -1034,3 +1035,249 @@
+ #, c-format
+ msgid "VDR will shut down in %s minutes"
+ msgstr "VDR s'arrêtera dans %s minutes"
++
++msgid "Channel locked by LNB!"
++msgstr "Chaîne interdite par la LNB"
++
++msgid "Childlock"
++msgstr "Adulte"
++
++msgid "Path"
++msgstr "Dossiers"
++
++msgid "Format"
++msgstr "Format"
++
++msgid "PES"
++msgstr "PES"
++
++msgid "TS"
++msgstr "TS"
++
++msgid "Timer commands"
++msgstr "Commandes de programmation"
++
++msgid "Rename recording"
++msgstr "Renommer l'enregistrement"
++
++msgid "Date"
++msgstr "Date"
++
++msgid "Length"
++msgstr "Longeur"
++
++msgid "Size"
++msgstr "Taille"
++
++msgid "Delete marks information?"
++msgstr "Effacer marque d'information?"
++
++msgid "Delete resume information?"
++msgstr "Effacer resume information?"
++
++msgid "DVD plugin is not installed!"
++msgstr "Le plugin de DVD n'est pas installé!"
++
++msgid "main dir alphabetically, subdirs flexible"
++msgstr "Dir principal alphabétiquement, subdirs flexibles"
++
++msgid "main dir by date, subdirs flexible"
++msgstr "Dir principal à la date, subdirs flexibles"
++
++msgid "all alphabetically"
++msgstr "tous alphabétiquement"
++
++msgid "all by date"
++msgstr "tous à la date"
++
++msgid "Sorting"
++msgstr "Triage"
++
++msgid "Setup.OSD$WarEagle icons"
++msgstr "Icônes WarEagle"
++
++msgid "Setup.OSD$Main menu command position"
++msgstr "Position des commandes dans le menu"
++
++msgid "Setup.OSD$Show valid input"
++msgstr "Afficher l'entrée valide"
++
++msgid "Setup.EPG$Show progress bar"
++msgstr "Montrer la barre de progression"
++
++msgid "Setup.EPG$Period for double EPG search(min)"
++msgstr "Intervalle de recherche EPG de double(min)"
++
++msgid "Setup.EPG$Extern double Epg entry"
++msgstr "Double entrée EPG externe"
++
++msgid "adjust"
++msgstr "ajuster"
++
++msgid "delete"
++msgstr "effacer"
++
++msgid "Setup.EPG$Mix intern and extern EPG"
++msgstr "Mélanger les EPG interne et externe"
++
++msgid "Setup.EPG$Disable running VPS event"
++msgstr "Arreter la reconnaissance des événements VPS"
++
++msgid "Setup.DVB$Use AC3-Transfer Fix"
++msgstr "Utiliser le correctif AC3-Transfer"
++
++msgid "Setup.DVB$Channel Blocker"
++msgstr ""
++
++msgid "Setup.DVB$Channel Blocker Filter Mode"
++msgstr ""
++
++msgid "Setup.LNB$DVB device %d uses LNB No."
++msgstr "La carte DVB %d utilise la LNB No."
++
++msgid "Setup.LNB$Log LNB usage"
++msgstr "Protocoller l'utilisation du LNB"
++
++msgid "Setup.Recording$Record Dolby Digital"
++msgstr "Enregistrer en Dolby Digital"
++
++msgid "Setup.Recording$Video directory policy"
++msgstr "Régles de répertoires vidéo"
++
++msgid "Setup.Recording$Number of video directories"
++msgstr "Nombre de répertoires vidéo"
++
++msgid "Setup.Recording$Video %d priority"
++msgstr "Video %d prioritaires"
++
++msgid "Setup.Recording$Video %d min. free MB"
++msgstr "Video %d min. librei Mo"
++
++msgid "Setup.Recording$Friendly filenames"
++msgstr "Noms de fichiers facile"
++
++msgid "Setup.Recording$Max. recording size (GB)"
++msgstr "Taille max. de l'enregistrement (GB)"
++
++msgid "Setup.Recording$Hard Link Cutter"
++msgstr "Découpe Hard links"
++
++msgid "Setup.Recording$Show date"
++msgstr "Montrer la date d'enregistrement"
++
++msgid "Setup.Recording$Show time"
++msgstr "Montrer l'heure d'enregistrement"
++
++msgid "Setup.Recording$Show length"
++msgstr "Montrer la longueur de l'enregistrement"
++
++msgid "Setup.OSD$Main menu title"
++msgstr ""
++
++msgid "Setup.Recording$Show end of timer"
++msgstr "Montrer la fin du temporisateur"
++
++msgid "Setup.Recording$Sort recordings by"
++msgstr "Ordonner les enregistrement suivant"
++
++msgid "Setup.Recording$Sort directories before recordings"
++msgstr "Les dossiers devant les enregistrements"
++
++msgid "Setup.Recording$Cutter auto delete"
++msgstr "Effacer l'enregistrement après la découpe"
++
++msgid "Setup.Replay$Jump&Play"
++msgstr "Lecture après saut"
++
++msgid "Setup.Replay$Play&Jump"
++msgstr "Saut sur les marques de découpes"
++
++msgid "Setup.Replay$Pause at last mark"
++msgstr "Pause après la dernière marque"
++
++msgid "Setup.Replay$Reload marks"
++msgstr "Actualiser les marques"
++
++msgid "Setup.Replay$Skip Seconds"
++msgstr "Longueur de saut en secondes"
++
++msgid "Setup.Replay$Skip Seconds Slow"
++msgstr "Longueur de saut lent en secondes"
++
++msgid "Setup.Replay$Length"
++msgstr "Longueur"
++
++msgid "Setup.Replay$Length / Number"
++msgstr "Longueur / Numéro"
++
++msgid "Setup.Replay$Number"
++msgstr "Numéro"
++
++msgid "Setup.Replay$DVD display mode"
++msgstr "Afficher le DVD"
++
++msgid "Setup.Replay$DVD display leading zeros"
++msgstr "Afficher les zéros devant le numéro du DVD"
++
++msgid "Setup.Replay$never"
++msgstr "jamais"
++
++msgid "Setup.Replay$on begin"
++msgstr "au début"
++
++msgid "Setup.Replay$on end"
++msgstr "à la fin"
++
++msgid "Setup.Replay$on begin and end"
++msgstr "au début et à la fin"
++
++msgid "Setup.Replay$Tray open"
++msgstr "Ouvrir le tiroir du lecteur DVD"
++
++msgid "Setup.Replay$Limit DVD to speed"
++msgstr "Ralentir la vitesse du DVD à"
++
++msgid "Jump"
++msgstr "Saut"
++
++msgid "Skip +60s"
++msgstr "Avance +60s"
++
++msgid "Skip -60s"
++msgstr "Recule -60s"
++
++msgid "Cutter already running - Add to cutting queue?"
++msgstr "Découpe déjà active - l'ajouter à la liste de découpe?"
++
++msgid "Rename$Up"
++msgstr "Haut"
++
++msgid "Rename$Down"
++msgstr "Bas"
++
++msgid "Rename$Previous"
++msgstr "Précédent"
++
++msgid "Rename$Next"
++msgstr "Suivant"
++
++msgid "Please mount %s"
++msgstr "Mettez %s dans le lecteur"
++
++msgid "Please mount DVD %04d"
++msgstr "Mettez le DVD %04d dans le lecteur"
++
++msgid "Please mount DVD %d"
++msgstr "Mettez le DVD %d dans le lecteur"
++
++msgid "Please wait. Checking DVD..."
++msgstr "Un moment SVP. Vérification du DVD..."
++
++msgid "No index-file found. Creating may take minutes. Create one?"
++msgstr "Pas trouvé de fichiers index. La création de ce ficher prends quelques minutes. Créer?"
++
++msgid "Please wait. Creating index-file..."
++msgstr "Attendez, svp. Le fichier index est en cours de création..."
++
++msgid "Wrong DVD!"
++msgstr "Mauvais DVD!"
+diff -NaurwB vdr-1.7.10/po/it_IT.po vdr-1.7.10-patched/po/it_IT.po
+--- vdr-1.7.10/po/it_IT.po     2009-11-22 12:28:39.000000000 +0100
++++ vdr-1.7.10-patched/po/it_IT.po     2009-12-18 06:25:25.000000000 +0100
+@@ -37,6 +37,9 @@
+ msgid "*** Invalid Channel ***"
+ msgstr "*** Canale NON valido ***"
++msgid "Channel locked by LNB!"
++msgstr "Canale bloccato dal LNB!"
++
+ msgid "Channel not available!"
+ msgstr "Canale non disponibile!"
+@@ -46,6 +49,253 @@
+ msgid "Starting EPG scan"
+ msgstr "Inizio scansione EPG"
++msgid "Content$Movie/Drama"
++msgstr "Film/Dramma"
++
++msgid "Content$Detective/Thriller"
++msgstr "Investigativo/Giallo"
++
++msgid "Content$Adventure/Western/War"
++msgstr "Avventura/Western/Guerra"
++
++msgid "Content$Science Fiction/Fantasy/Horror"
++msgstr "Finzione/Fantasia/Horror"
++
++msgid "Content$Comedy"
++msgstr "Commedia"
++
++msgid "Content$Soap/Melodrama/Folkloric"
++msgstr "Telenovella/Melodramma/Folcloristico"
++
++msgid "Content$Romance"
++msgstr "Romanzo"
++
++msgid "Content$Serious/Classical/Religious/Historical Movie/Drama"
++msgstr "Serio/Classico/Religioso/Film storico/Dramma"
++
++msgid "Content$Adult Movie/Drama"
++msgstr "Film per adulti/Dramma"
++
++msgid "Content$News/Current Affairs"
++msgstr "Notizie/Ultima ora"
++
++msgid "Content$News/Weather Report"
++msgstr "Notizie/Previsioni meteo"
++
++msgid "Content$News Magazine"
++msgstr "Rivista di notizie"
++
++msgid "Content$Documentary"
++msgstr "Documentario"
++
++msgid "Content$Discussion/Inverview/Debate"
++msgstr "Discussione/Intervista/Dibattito"
++
++msgid "Content$Show/Game Show"
++msgstr "Spettacolo/Gioco a premi"
++
++msgid "Content$Game Show/Quiz/Contest"
++msgstr "Gioco a premi/Quiz/Gara"
++
++msgid "Content$Variety Show"
++msgstr "Spettacolo di varietà"
++
++msgid "Content$Talk Show"
++msgstr "Talk Show"
++
++msgid "Content$Sports"
++msgstr "Sport"
++
++msgid "Content$Special Event"
++msgstr "Evento speciale"
++
++msgid "Content$Sport Magazine"
++msgstr "Rivista di sport"
++
++msgid "Content$Football"
++msgstr "Calcio"
++
++msgid "Content$Tennis/Squash"
++msgstr "Tennis/Squash"
++
++msgid "Content$Team Sports"
++msgstr "Sport di squadra"
++
++msgid "Content$Athletics"
++msgstr "Atletica"
++
++msgid "Content$Motor Sport"
++msgstr "Sport motoristici"
++
++msgid "Content$Water Sport"
++msgstr "Sport acquatici"
++
++msgid "Content$Winter Sports"
++msgstr "Sport invernali"
++
++msgid "Content$Equestrian"
++msgstr "Equitazione"
++
++msgid "Content$Martial Sports"
++msgstr "Arti marziali"
++
++msgid "Content$Children's/Youth Programmes"
++msgstr "Programmi per ragazzi/giovani"
++
++msgid "Content$Pre-school Children's Programmes"
++msgstr "Programmi per ragazzi prescolastici"
++
++msgid "Content$Entertainment Programmes for 6 to 14"
++msgstr "Programmi di intrattenimento da 6 a 14"
++
++msgid "Content$Entertainment Programmes for 10 to 16"
++msgstr "Programmi di intrattenimento da 10 a 16"
++
++msgid "Content$Informational/Educational/School Programme"
++msgstr "Informativo/Educativo/Programma scolastico"
++
++msgid "Content$Cartoons/Puppets"
++msgstr "Cartoni/Pupazzi"
++
++msgid "Content$Music/Ballet/Dance"
++msgstr "Musica/Balletto/Danza"
++
++msgid "Content$Rock/Pop"
++msgstr "Rock/Pop"
++
++msgid "Content$Serious/Classical Music"
++msgstr "Serio/Musica classica"
++
++msgid "Content$Folk/Tradional Music"
++msgstr "Folclore/Musica tradizionale"
++
++msgid "Content$Jazz"
++msgstr "Jazz"
++
++msgid "Content$Musical/Opera"
++msgstr "Musical/Opera"
++
++msgid "Content$Ballet"
++msgstr "Balletto"
++
++msgid "Content$Arts/Culture"
++msgstr "Arte/Cultura"
++
++msgid "Content$Performing Arts"
++msgstr "Arti di rendimento"
++
++msgid "Content$Fine Arts"
++msgstr "Arti fine"
++
++msgid "Content$Religion"
++msgstr "Religione"
++
++msgid "Content$Popular Culture/Traditional Arts"
++msgstr "Cultura popolare/Arti tradizionali"
++
++msgid "Content$Literature"
++msgstr "Letteratura"
++
++msgid "Content$Film/Cinema"
++msgstr "Film/Cinema"
++
++msgid "Content$Experimental Film/Video"
++msgstr "Film esperimentale/Video"
++
++msgid "Content$Broadcasting/Press"
++msgstr "Trasmissione/Stampa"
++
++msgid "Content$New Media"
++msgstr "Nuovo programma"
++
++msgid "Content$Arts/Culture Magazines"
++msgstr "Arte/Riviste di cultura"
++
++msgid "Content$Fashion"
++msgstr "Moda"
++
++msgid "Content$Social/Political/Economics"
++msgstr "Società/Politica/Economia"
++
++msgid "Content$Magazines/Reports/Documentary"
++msgstr "Riviste/Reportage/Documentari"
++
++msgid "Content$Economics/Social Advisory"
++msgstr "Economia/Consulenza sociale"
++
++msgid "Content$Remarkable People"
++msgstr "Personaggi importanti"
++
++msgid "Content$Education/Science/Factual"
++msgstr "Educazione/Scienza/Fatti"
++
++msgid "Content$Nature/Animals/Environment"
++msgstr "Natura/Animali/Ambiente"
++
++msgid "Content$Technology/Natural Sciences"
++msgstr "Tecnologia/Scienze naturali"
++
++msgid "Content$Medicine/Physiology/Psychology"
++msgstr "Medicina/Filosofia/Psicologia"
++
++msgid "Content$Foreign Countries/Expeditions"
++msgstr "Paesi esteri/Spedizioni"
++
++msgid "Content$Social/Spiritual Sciences"
++msgstr "Società/Scienze spirituali"
++
++msgid "Content$Further Education"
++msgstr "Altra educazione"
++
++msgid "Content$Languages"
++msgstr "Lingua"
++
++msgid "Content$Leisure/Hobbies"
++msgstr "Tempo libero/Hobby"
++
++msgid "Content$Tourism/Travel"
++msgstr "Turismo/Viaggi"
++
++msgid "Content$Handicraft"
++msgstr "Artigianato"
++
++msgid "Content$Motoring"
++msgstr "Motori"
++
++msgid "Content$Fitness & Health"
++msgstr "Culturismo & Salute"
++
++msgid "Content$Cooking"
++msgstr "Cucina"
++
++msgid "Content$Advertisement/Shopping"
++msgstr "Pubblicità/Acquisti"
++
++msgid "Content$Gardening"
++msgstr "Giardinaggio"
++
++msgid "Content$Original Language"
++msgstr "Lingua madre"
++
++msgid "Content$Black & White"
++msgstr "Bianco & Nero"
++
++msgid "Content$Unpublished"
++msgstr "Non pubblicato"
++
++msgid "Content$Live Broadcast"
++msgstr "Trasmissione dal vivo"
++
++msgid "Content$Special Characteristics"
++msgstr "Caratteristiche speciali"
++
++msgid "Content$Drama"
++msgstr "Dramma"
++
++#, c-format
++msgid "Suitable for those aged %d and over"
++msgstr "Adatto per coloro con %d di età e oltre"
++
+ msgid "No title"
+ msgstr "Senza titolo"
+@@ -325,6 +575,9 @@
+ msgid "Rolloff"
+ msgstr "Rolloff"
++msgid "Parameters"
++msgstr "Parametri"
++
+ msgid "Channel settings are not unique!"
+ msgstr "Parametri canale non univoci!"
+@@ -376,9 +629,15 @@
+ msgid "Lifetime"
+ msgstr "Scadenza"
++msgid "Childlock"
++msgstr "Filtro fam."
++
+ msgid "File"
+ msgstr "Nome"
++msgid "Path"
++msgstr "Percorso"
++
+ msgid "First day"
+ msgstr "1° giorno"
+@@ -397,6 +656,9 @@
+ msgid "Timer still recording - really delete?"
+ msgstr "Timer in registrazione - eliminare?"
++msgid "Timer commands"
++msgstr "Comandi timer"
++
+ msgid "Event"
+ msgstr "Evento"
+@@ -457,6 +719,24 @@
+ msgid "Button$Rewind"
+ msgstr "Riavvolgi"
++msgid "Rename recording"
++msgstr "Rinomina registrazione"
++
++msgid "Date"
++msgstr "Data"
++
++msgid "Length"
++msgstr "Durata"
++
++msgid "Size"
++msgstr "Dimensione"
++
++msgid "Delete marks information?"
++msgstr "Eliminare informazione marcatori?"
++
++msgid "Delete resume information?"
++msgstr "Eliminare informazione ripristino?"
++
+ msgid "Recordings"
+ msgstr "Registrazioni"
+@@ -469,6 +749,9 @@
+ msgid "Error while accessing recording!"
+ msgstr "Errore accesso alla registrazione!"
++msgid "DVD plugin is not installed!"
++msgstr "Il plugin DVD non è installato!"
++
+ msgid "Delete recording?"
+ msgstr "Eliminare la registrazione?"
+@@ -478,6 +761,21 @@
+ msgid "Recording commands"
+ msgstr "Comandi di registrazione"
++msgid "main dir alphabetically, subdirs flexible"
++msgstr "Dir. princ. in ordine alfabetico, sottodir. flessibili"
++
++msgid "main dir by date, subdirs flexible"
++msgstr "Dir. princ. per data, sottodir. flessibili"
++
++msgid "all alphabetically"
++msgstr "Tutte in ordine alfabetico"
++
++msgid "all by date"
++msgstr "Tutte per data"
++
++msgid "Sorting"
++msgstr "Ordinamento"
++
+ msgid "never"
+ msgstr "mai"
+@@ -487,6 +785,18 @@
+ msgid "always"
+ msgstr "sempre"
++msgid "default"
++msgstr "predefinito"
++
++msgid "VDR - text"
++msgstr "VDR - Testo"
++
++msgid "text"
++msgstr "Testo"
++
++msgid "VDR - version"
++msgstr "VDR - Versione"
++
+ msgid "OSD"
+ msgstr "OSD"
+@@ -499,6 +809,9 @@
+ msgid "Setup.OSD$Theme"
+ msgstr "Tema colori"
++msgid "Setup.OSD$WarEagle icons"
++msgstr "Icone WarEagle"
++
+ msgid "Setup.OSD$Left (%)"
+ msgstr "Sinistra (%)"
+@@ -568,12 +881,30 @@
+ msgid "Setup.OSD$Recording directories"
+ msgstr "Directory di registrazione"
++msgid "Setup.OSD$Main menu title"
++msgstr "Titolo menu principale"
++
++msgid "Setup.OSD$- Text"
++msgstr "- Testo"
++
++msgid "Setup.OSD$Main menu command position"
++msgstr "Posizione comandi menu princ."
++
++msgid "Setup.OSD$Show valid input"
++msgstr "Mostra ingresso valido"
++
+ msgid "EPG"
+ msgstr "Guida programmi EPG"
+ msgid "Button$Scan"
+ msgstr "Scansione"
++msgid "blacklist"
++msgstr "lista nera"
++
++msgid "whitelist"
++msgstr "elenco autorizzati"
++
+ msgid "Setup.EPG$EPG scan timeout (h)"
+ msgstr "Scadenza aggiorn. EPG (ore)"
+@@ -583,6 +914,9 @@
+ msgid "Setup.EPG$EPG linger time (min)"
+ msgstr "Mostra vecchi dati EPG (min)"
++msgid "Setup.EPG$Show progress bar"
++msgstr "Mostra barra avanzamento"
++
+ msgid "Setup.EPG$Set system time"
+ msgstr "Imposta orario di sistema"
+@@ -597,6 +931,27 @@
+ msgid "Setup.EPG$Preferred language"
+ msgstr "Lingua preferita"
++msgid "Setup.EPG$Period for double EPG search(min)"
++msgstr "Tempo doppia ricerca EPG (min)"
++
++msgid "Setup.EPG$Extern double Epg entry"
++msgstr "Doppio valore EPG esterno"
++
++msgid "adjust"
++msgstr "regola"
++
++msgid "delete"
++msgstr "elimina"
++
++msgid "Setup.EPG$Mix intern and extern EPG"
++msgstr "Mescola EPG interno e esterno"
++
++msgid "Setup.EPG$Disable running VPS event"
++msgstr "Disabilita esec. evento VPS"
++
++msgid "Setup.EPG$Mode of noEPG-Patch"
++msgstr "Modalità di noEPG-Patch"
++
+ msgid "pan&scan"
+ msgstr "pan&scan"
+@@ -627,6 +982,27 @@
+ msgid "DVB"
+ msgstr "Scheda DVB"
++msgid "qam256"
++msgstr "qam256"
++
++msgid "dvb-c"
++msgstr "dvb-c"
++
++msgid "dvb-s"
++msgstr "dvb-s"
++
++msgid "all"
++msgstr "tutti"
++
++msgid "has decoder"
++msgstr "con decoder"
++
++msgid "is primary"
++msgstr "primaria"
++
++msgid "has decoder + is primary"
++msgstr "con decoder e primaria"
++
+ msgid "Setup.DVB$Primary DVB interface"
+ msgstr "Scheda DVB primaria"
+@@ -666,9 +1042,25 @@
+ msgid "Setup.DVB$Subtitle background transparency"
+ msgstr "Trasparenza sfondo sottotitoli"
++msgid "Setup.DVB$Use AC3-Transfer Fix"
++msgstr "Utilizza correzione AC3-Transfer"
++
++msgid "Setup.DVB$Channel Blocker"
++msgstr "Blocco canale"
++
++msgid "Setup.DVB$Channel Blocker Filter Mode"
++msgstr "Modalità filtro blocco canale"
++
+ msgid "LNB"
+ msgstr "LNB"
++#, c-format
++msgid "Setup.LNB$DVB device %d uses LNB No."
++msgstr "La scheda DVB %d utilizza LNB No."
++
++msgid "Setup.LNB$Log LNB usage"
++msgstr "Registra utilizzo LNB"
++
+ msgid "Setup.LNB$Use DiSEqC"
+ msgstr "Utilizza DiSEqC"
+@@ -720,6 +1112,9 @@
+ msgid "pause live video"
+ msgstr "pausa video dal vivo"
++msgid "request"
++msgstr "chiedi"
++
+ msgid "Recording"
+ msgstr "Registrazione"
+@@ -747,9 +1142,29 @@
+ msgid "Setup.Recording$Pause lifetime (d)"
+ msgstr "Scadenza pausa (gg)"
++msgid "Setup.Recording$Record Dolby Digital"
++msgstr "Registra Dolby Digital"
++
++msgid "Setup.Recording$Video directory policy"
++msgstr "Regole directory video"
++
++msgid "Setup.Recording$Number of video directories"
++msgstr "Numero di directory video"
++
++#, c-format
++msgid "Setup.Recording$Video %d priority"
++msgstr "Priorità video %d "
++
++#, c-format
++msgid "Setup.Recording$Video %d min. free MB"
++msgstr "Min. MB disponibili video %d "
++
+ msgid "Setup.Recording$Use episode name"
+ msgstr "Utilizza nome episodio"
++msgid "Setup.Recording$Friendly filenames"
++msgstr "Nomi file semplici"
++
+ msgid "Setup.Recording$Use VPS"
+ msgstr "Utilizza VPS"
+@@ -768,9 +1183,39 @@
+ msgid "Setup.Recording$Max. video file size (MB)"
+ msgstr "Dim. massima file video (MB)"
++msgid "Setup.Recording$Max. recording size (GB)"
++msgstr "Dim. massima reg. (GB)"
++
+ msgid "Setup.Recording$Split edited files"
+ msgstr "Dividi i file modificati"
++msgid "Setup.Recording$Hard Link Cutter"
++msgstr "Taglia collegamenti Hard"
++
++msgid "Setup.Recording$Delete timeshift recording"
++msgstr "Elimina reg. timeshift"
++
++msgid "Setup.Recording$Show date"
++msgstr "Mostra data registrazione"
++
++msgid "Setup.Recording$Show time"
++msgstr "Mostra ora registrazione"
++
++msgid "Setup.Recording$Show length"
++msgstr "Mostra durata registrazione"
++
++msgid "Setup.Recording$Show end of timer"
++msgstr "Mostra fine del timer"
++
++msgid "Setup.Recording$Sort recordings by"
++msgstr "Ordina registrazioni per"
++
++msgid "Setup.Recording$Sort directories before recordings"
++msgstr "Ordina directory prima di reg."
++
++msgid "Setup.Recording$Cutter auto delete"
++msgstr "Elimina taglio automatico"
++
+ msgid "Replay"
+ msgstr "Riproduzione"
+@@ -783,6 +1228,63 @@
+ msgid "Setup.Replay$Resume ID"
+ msgstr "ID di ripristino"
++msgid "Setup.Replay$Jump&Play"
++msgstr "Vai a & Riproduci"
++
++msgid "Setup.Replay$Play&Jump"
++msgstr "Riproduci & Vai a"
++
++msgid "Setup.Replay$Pause at last mark"
++msgstr "Pausa all'ultimo marcatore"
++
++msgid "Setup.Replay$Reload marks"
++msgstr "Ricarica marcatori"
++
++msgid "Setup.Replay$Skip Seconds"
++msgstr "Salta secondi"
++
++msgid "Setup.Replay$Skip Seconds Slow"
++msgstr "Salta secondi lentamente"
++
++msgid "Setup.Replay$Length"
++msgstr "Durata"
++
++msgid "Setup.Replay$Length / Number"
++msgstr "Durata / Numero"
++
++msgid "Setup.Replay$Number"
++msgstr "Numero"
++
++msgid "Setup.Replay$DVD display mode"
++msgstr "Mod. visualizzazione DVD"
++
++msgid "Setup.Replay$DVD display leading zeros"
++msgstr "Mostra zeri davanti al DVD"
++
++msgid "Setup.Replay$never"
++msgstr "mai"
++
++msgid "Setup.Replay$on begin"
++msgstr "all'inizio"
++
++msgid "Setup.Replay$on end"
++msgstr "alla fine"
++
++msgid "Setup.Replay$on begin and end"
++msgstr "all'inizio e alla fine"
++
++msgid "Setup.Replay$Tray open"
++msgstr "Apri vassoio"
++
++msgid "Setup.Replay$Limit DVD to speed"
++msgstr "Limita velocità DVD a"
++
++msgid "Setup.Miscellaneous$only in channelinfo"
++msgstr "solo nelle info canale"
++
++msgid "Setup.Miscellaneous$only in progress display"
++msgstr "solo nel canale in esec."
++
+ msgid "Miscellaneous"
+ msgstr "Generici"
+@@ -810,9 +1312,27 @@
+ msgid "Setup.Miscellaneous$Initial volume"
+ msgstr "Volume iniziale"
++msgid "Setup.Miscellaneous$Volume ctrl with left/right"
++msgstr "Controllo volume con Sinistra/Destra"
++
++msgid "Setup.Miscellaneous$Channelgroups with left/right"
++msgstr "Gruppi canali con Sinistra/Destra"
++
++msgid "Setup.Miscellaneous$Search fwd/back with left/right"
++msgstr "Cerca avanti/indietro con Sinistra/Destra"
++
+ msgid "Setup.Miscellaneous$Emergency exit"
+ msgstr "Uscita di emergenza"
++msgid "Setup.Miscellaneous$Lirc repeat delay"
++msgstr "Ritardo ripetizione LIRC"
++
++msgid "Setup.Miscellaneous$Lirc repeat freq"
++msgstr "Frequenza ripetizione LIRC"
++
++msgid "Setup.Miscellaneous$Lirc repeat timeout"
++msgstr "Scadenza ripetizione LIRC"
++
+ msgid "Plugins"
+ msgstr "Plugins"
+@@ -885,10 +1405,22 @@
+ msgid "Pausing live video..."
+ msgstr "Pausa del canale in visione..."
++msgid "Jump"
++msgstr "Vai a"
++
++msgid "Skip +60s"
++msgstr "Salta +60s"
++
++msgid "Skip -60s"
++msgstr "Salta - 60s"
++
+ #. TRANSLATORS: note the trailing blank!
+ msgid "Jump: "
+ msgstr "Vai a: "
++msgid "Cutter already running - Add to cutting queue?"
++msgstr "Taglio in esecuzione - Aggiungere alla coda tagli?"
++
+ msgid "No editing marks defined!"
+ msgstr "Nessun marcatore di modifica definito!"
+@@ -919,6 +1451,18 @@
+ msgid "Button$Insert"
+ msgstr "Inserisci"
++msgid "Rename$Up"
++msgstr "Su"
++
++msgid "Rename$Down"
++msgstr "Giù"
++
++msgid "Rename$Previous"
++msgstr "Precedente"
++
++msgid "Rename$Next"
++msgstr "Successivo"
++
+ msgid "Plugin"
+ msgstr "Plugin"
+@@ -937,6 +1481,30 @@
+ msgid "Index file regeneration complete"
+ msgstr ""
++#, c-format
++msgid "Please mount %s"
++msgstr "Monta %s"
++
++#, c-format
++msgid "Please mount DVD %04d"
++msgstr "Monta il DVD %04d"
++
++#, c-format
++msgid "Please mount DVD %d"
++msgstr "Monta il DVD %d"
++
++msgid "Please wait. Checking DVD..."
++msgstr "Attendere prego. Verifica DVD..."
++
++msgid "No index-file found. Creating may take minutes. Create one?"
++msgstr "Nessun indice trovato. La creazione può impiegare alcuni minuti. Crearne uno?"
++
++msgid "Please wait. Creating index-file..."
++msgstr "Attendere prego. Creazione indice..."
++
++msgid "Wrong DVD!"
++msgstr "DVD errato!"
++
+ msgid "Can't shutdown - option '-s' not given!"
+ msgstr "Impossibile spegnere - parametro '-s' non assegnato!"
+diff -NaurwB vdr-1.7.10/po/nl_NL.po vdr-1.7.10-patched/po/nl_NL.po
+--- vdr-1.7.10/po/nl_NL.po     2009-11-22 12:28:39.000000000 +0100
++++ vdr-1.7.10-patched/po/nl_NL.po     2009-12-18 06:25:25.000000000 +0100
+@@ -1032,3 +1032,240 @@
+ #, c-format
+ msgid "VDR will shut down in %s minutes"
+ msgstr "VDR zal na %s minuten uitschakelen"
++
++msgid "Channel locked by LNB!"
++msgstr "Kanaal geblokkeerd door LNB"
++
++msgid "Childlock"
++msgstr "Kinderslot"
++
++msgid "Path"
++msgstr "Pad"
++
++msgid "Timer commands"
++msgstr "Timer commando's"
++
++msgid "Rename recording"
++msgstr "Hernoem opnamen"
++
++msgid "Date"
++msgstr "Datum"
++
++msgid "Length"
++msgstr "Lengte"
++
++msgid "Size"
++msgstr "Grootte"
++
++msgid "Delete marks information?"
++msgstr "Verwijder informatiemarkeringen?"
++
++msgid "Delete resume information?"
++msgstr "Verwijder hervattingsmarkeringen?"
++
++msgid "DVD plugin is not installed!"
++msgstr "DVd plugin is niet geÃnstalleerd!"
++
++msgid "main dir alphabetically, subdirs flexible"
++msgstr "hoofdmap alfabetisch, submappen flexibel"
++
++msgid "main dir by date, subdirs flexible"
++msgstr "hoofdmap op datum, submappen flexibel"
++
++msgid "all alphabetically"
++msgstr "alles alfabetisch"
++
++msgid "all by date"
++msgstr "alles op datum"
++
++msgid "Sorting"
++msgstr "Sortering"
++
++msgid "Setup.OSD$WarEagle icons"
++msgstr "WarEagle symbolen"
++
++msgid "Setup.OSD$Main menu command position"
++msgstr "Positie commandobalk in hoofdmenu"
++
++msgid "Setup.OSD$Show valid input"
++msgstr "Toon geldige invoer"
++
++msgid "Setup.EPG$Show progress bar"
++msgstr "Toon voortschreidingsbalk"
++
++msgid "Setup.EPG$Period for double EPG search(min)"
++msgstr "Tijdsduur starten dubbele EPG zoekactie (min)"
++
++msgid "Setup.EPG$Extern double Epg entry"
++msgstr "externe dubbele EPG invoer"
++
++msgid "adjust"
++msgstr "afstellen"
++
++msgid "delete"
++msgstr "verwijderen"
++
++msgid "Setup.EPG$Mix intern and extern EPG"
++msgstr "Mix in- en externe EPG"
++
++msgid "Setup.EPG$Disable running VPS event"
++msgstr "Schakel aktieve VPS uitzending uit"
++
++msgid "Setup.DVB$Use AC3-Transfer Fix"
++msgstr "Gebruik AC3-Transfer Fix"
++
++msgid "Setup.DVB$Channel Blocker"
++msgstr ""
++
++msgid "Setup.DVB$Channel Blocker Filter Mode"
++msgstr ""
++
++msgid "Setup.LNB$DVB device %d uses LNB No."
++msgstr "LNB kaart %d gebruikt LNB Nr."
++
++msgid "Setup.LNB$Log LNB usage"
++msgstr "Houd LNB gebruik bij"
++
++msgid "Setup.Recording$Record Dolby Digital"
++msgstr "Neem Dolby Digital spoor op"
++
++msgid "Setup.Recording$Video directory policy"
++msgstr "Omgang m.b.t. Videomappen"
++
++msgid "Setup.Recording$Number of video directories"
++msgstr "Aantal videomappen"
++
++msgid "Setup.Recording$Video %d priority"
++msgstr "Video %d prioriteit"
++
++msgid "Setup.Recording$Video %d min. free MB"
++msgstr "Video %d min. vrij MB"
++
++msgid "Setup.Recording$Friendly filenames"
++msgstr "Handzame bestandsnamen"
++
++msgid "Setup.Recording$Max. recording size (GB)"
++msgstr "Max. opnamegrootte (GB)"
++
++msgid "Setup.Recording$Hard Link Cutter"
++msgstr "'Hard link' bestandsbewerker"
++
++msgid "Setup.Recording$Show date"
++msgstr "Toon datum"
++
++msgid "Setup.Recording$Show time"
++msgstr "Toon tijd"
++
++msgid "Setup.Recording$Show length"
++msgstr "Toon lengte"
++
++msgid "Setup.OSD$Main menu title"
++msgstr ""
++
++msgid "Setup.Recording$Show end of timer"
++msgstr "Toon einde van timers"
++
++msgid "Setup.Recording$Sort recordings by"
++msgstr "Sorteer opnames op"
++
++msgid "Setup.Recording$Sort directories before recordings"
++msgstr "Sorteer mappen vÃÃr opnames"
++
++msgid "Setup.Recording$Cutter auto delete"
++msgstr "Bestandsbewerker automatisch wissen"
++
++msgid "Setup.Replay$Jump&Play"
++msgstr "Spring&Speel"
++
++msgid "Setup.Replay$Play&Jump"
++msgstr "Speel&Spring"
++
++msgid "Setup.Replay$Pause at last mark"
++msgstr "Pauzeer bij laatste markering"
++
++msgid "Setup.Replay$Reload marks"
++msgstr "Herlaadt markeringen"
++
++msgid "Setup.Replay$Skip Seconds"
++msgstr "Spring seconden"
++
++msgid "Setup.Replay$Skip Seconds Slow"
++msgstr "Spring seconden langzaam"
++
++msgid "Setup.Replay$Length"
++msgstr "Lengte"
++
++msgid "Setup.Replay$Length / Number"
++msgstr "Lengte / Nummer"
++
++msgid "Setup.Replay$Number"
++msgstr "Nummer"
++
++msgid "Setup.Replay$DVD display mode"
++msgstr "DVD displayinstellingen"
++
++msgid "Setup.Replay$DVD display leading zeros"
++msgstr "DVD toon voorloopnullen"
++
++msgid "Setup.Replay$never"
++msgstr "nooit"
++
++msgid "Setup.Replay$on begin"
++msgstr "aan het begin"
++
++msgid "Setup.Replay$on end"
++msgstr "bij het einde"
++
++msgid "Setup.Replay$on begin and end"
++msgstr "bij het begin en aan het einde"
++
++msgid "Setup.Replay$Tray open"
++msgstr "Open DVD lade"
++
++msgid "Setup.Replay$Limit DVD to speed"
++msgstr "Beperk DVD omw. snelheid"
++
++msgid "Jump"
++msgstr "Spring"
++
++msgid "Skip +60s"
++msgstr "Spring +60s verder"
++
++msgid "Skip -60s"
++msgstr "Spring -60s terug"
++
++msgid "Cutter already running - Add to cutting queue?"
++msgstr "Bestandsbewerker aktief - aan wachtrij toeveogen?"
++
++msgid "Rename$Up"
++msgstr "Boven"
++
++msgid "Rename$Down"
++msgstr "Onder"
++
++msgid "Rename$Previous"
++msgstr "Vorige"
++
++msgid "Rename$Next"
++msgstr "Volgende"
++
++msgid "Please mount %s"
++msgstr "Koppel %s aan a.u.b."
++
++msgid "Please mount DVD %04d"
++msgstr "Koppel DVD %04d aan a.u.b."
++
++msgid "Please mount DVD %d"
++msgstr "Koppel DVD %d aan a.u.b."
++
++msgid "Please wait. Checking DVD..."
++msgstr "Even wachten, DVD wordt gecontroleerd..."
++
++msgid "No index-file found. Creating may take minutes. Create one?"
++msgstr "Geen indexbestand gevonden. Aanmaken duurt even, doorgaan?"
++
++msgid "Please wait. Creating index-file..."
++msgstr "Even wachten indexbestand wordt aangemaakt..."
++
++msgid "Wrong DVD!"
++msgstr "Verkeerde DVD!"
+diff -NaurwB vdr-1.7.10/po/ru_RU.po vdr-1.7.10-patched/po/ru_RU.po
+--- vdr-1.7.10/po/ru_RU.po     2009-11-22 12:28:39.000000000 +0100
++++ vdr-1.7.10-patched/po/ru_RU.po     2009-12-18 06:25:25.000000000 +0100
+@@ -1029,3 +1029,249 @@
+ #, c-format
+ msgid "VDR will shut down in %s minutes"
+ msgstr "VDR ÒëÚÛîçØâáï çÕàÕ× %s ÜØÝãâ"
++
++msgid "Channel locked by LNB!"
++msgstr "ºÞÝÒÕàâÕà ÑÛÞÚØàãÕâ ÚÐÝÐÛ"
++
++msgid "Childlock"
++msgstr "´ÕâáÚÐï ÑÛÞÚØàÞÒÚÐ"
++
++msgid "Path"
++msgstr "¿ãâì"
++
++msgid "Timer commands"
++msgstr "ºÞÜÜÐÝÔë âÐÙÜÕàÐ"
++
++msgid "Rename recording"
++msgstr "¿ÕàÕØÜÕÝÞÒÐâì ×ÐßØáì"
++
++msgid "Date"
++msgstr "´ÐâÐ"
++
++msgid "Length"
++msgstr "´ÛØÝÐ"
++
++msgid "Size"
++msgstr "ÀÐ×ÜÕà"
++
++msgid "Format"
++msgstr ""
++
++msgid "PES"
++msgstr ""
++
++msgid "TS"
++msgstr ""
++
++msgid "Delete marks information?"
++msgstr "ÃÔÐÛØâì ÜÕâÚØ?"
++
++msgid "Delete resume information?"
++msgstr "²ÞááâÐÝÞÒØâì ÜÕâÚØ?"
++
++msgid "DVD plugin is not installed!"
++msgstr "¼ÞÔãÛì DVD ÝÕ ãáâÐÝÞÒÛÕÝ!"
++
++msgid "main dir alphabetically, subdirs flexible"
++msgstr "ÓÛÐÒÝÐï ÔØàÕÚâÞàØï ßÞ ÐÛäÐÒØâã, ßÞÔÔØàÕÚâÞàØØ ÓØÑÚÞ"
++
++msgid "main dir by date, subdirs flexible"
++msgstr "ÓÛÐÒÝÐï ÔØàÕÚâÞàØï ßÞ ÒàÕÜÕÝØ, ßÞÔÔØàÕÚâÞàØØ ÓØÑÚÞ"
++
++msgid "all alphabetically"
++msgstr "ÒáÕ ßÞ ÐÛäÐÒØâã"
++
++msgid "all by date"
++msgstr "ÒáÕ ßÞ ÒàÕÜÕÝØ"
++
++msgid "Sorting"
++msgstr "ÁÞàâØàÞÒÚÐ"
++
++msgid "Setup.OSD$WarEagle icons"
++msgstr "WarEagle ØÚÞÝÚØ"
++
++msgid "Setup.OSD$Main menu command position"
++msgstr "ÀÐ×ÜÕéÕÝØÕ ÚÞÜÐÝÔ Ò ÓÛÐÒÝÞÜ ÜÕÝî"
++
++msgid "Setup.OSD$Show valid input"
++msgstr "¿ÞÚÐ× ßàÐÒØÛìÝÞÓÞ ÒÒÞÔÐ"
++
++msgid "Setup.EPG$Show progress bar"
++msgstr "¿ÞÚÐ× ÑÐÛÚØ ßàÞÓàÕááÐ"
++
++msgid "Setup.EPG$Period for double EPG search(min)"
++msgstr "¿ÕàØÞÔ ÔÛï ßÞØáÚРÔÒÞÙÝëå EPG(min)"
++
++msgid "Setup.EPG$Extern double Epg entry"
++msgstr "²ÝÕèÝØÙ ØáâÞçÝØÚ ÔÒÞÙÝÞÓÞ EPG"
++
++msgid "adjust"
++msgstr "ÝÐáâàÞØâì"
++
++msgid "delete"
++msgstr "ãÔÐÛØâì"
++
++msgid "Setup.EPG$Mix intern and extern EPG"
++msgstr "ÁÜÕèØÒÐÝØÕ ÒÝãâàÕÝÝÕÓÞ Ø ÒÝÕèÝÕÓÞ EPG"
++
++msgid "Setup.EPG$Disable running VPS event"
++msgstr "·ÐßàÕâ VPS áÞÑëâØï"
++
++msgid "Setup.DVB$Use AC3-Transfer Fix"
++msgstr "¸áßÞÛì×ÞÒÐâì ÚÞààÕÚâØàÞÒÚã AC3-Transfer"
++
++msgid "Setup.DVB$Channel Blocker"
++msgstr ""
++
++msgid "Setup.DVB$Channel Blocker Filter Mode"
++msgstr ""
++
++msgid "Setup.LNB$DVB device %d uses LNB No."
++msgstr "DVB ãáâàÞÙáâÒÞ %d ØáßÞÛì×ãÕâ  LNB No."
++
++msgid "Setup.LNB$Log LNB usage"
++msgstr "»ÞÓØàÞÒÐÝØÕ ØáßÞÛì×ÞÒÐÝØï LNB"
++
++msgid "Setup.Recording$Record Dolby Digital"
++msgstr "·ÐßØáì Dolby Digital"
++
++msgid "Setup.Recording$Video directory policy"
++msgstr "¿ÞàïÔÞÚ ÒØÔÕÞ ÔØàÕÚâÞàØØ"
++
++msgid "Setup.Recording$Number of video directories"
++msgstr "ºÞÛØçÕáâÒÞ ÒØÔÕÞ ÔØàÕÚâÞàØÙ"
++
++msgid "Setup.Recording$Video %d priority"
++msgstr "Video %d ßàØÞàØâÕâ"
++
++msgid "Setup.Recording$Video %d min. free MB"
++msgstr "Video %d ÜØÝ. áÒÞÑÞÔÝÞ MB"
++
++msgid "Setup.Recording$Friendly filenames"
++msgstr "ÇØâÐÑÕÛìÝëÕ ØÜÕÝРäÐÙÛÞÒ"
++
++msgid "Setup.Recording$Max. recording size (GB)"
++msgstr "¼ÐÚá. àÐ×ÜÕà ×ÐßØáØ (GB)"
++
++msgid "Setup.Recording$Hard Link Cutter"
++msgstr "¼ÞÝâÐÖ á hard link"
++
++msgid "Setup.Recording$Show date"
++msgstr "¿ÞÚÐ×ëÒÐâì ÔÐâã"
++
++msgid "Setup.Recording$Show time"
++msgstr "¿ÞÚÐ×ëÒÐâì ÒàÕÜï ×ÐßØáØ"
++
++msgid "Setup.Recording$Show length"
++msgstr "¿ÞÚÐ×ëÒÐâì ßàÞÔÞÛÖØâÕÛìÝÞáâì ×ÐßØáØ"
++
++msgid "Setup.OSD$Main menu title"
++msgstr "½Ð×ÒÐÝØÕ ÓÛÐÒÝÞÓÞ ÜÕÝî"
++
++msgid "Setup.Recording$Show end of timer"
++msgstr "¿ÞÚÐ× ÞÚÞÝçÐÝØï âÐÙÜÕàÐ"
++
++msgid "Setup.Recording$Sort recordings by"
++msgstr "ÁÞàâØàÞÒÚР×ÐßØáÕÙ ßÞ"
++
++msgid "Setup.Recording$Sort directories before recordings"
++msgstr "ÁÞàâØàÞÒÚРÔØàÕÚâÞàØÙ ÔÞ ×ÐßØáØ"
++
++msgid "Setup.Recording$Cutter auto delete"
++msgstr "°ÒâÞÜÐâØçÕáÚÞÕ ãÔÐÛÕÝØÕ ÜÞÝâÐÖÐ"
++
++msgid "Setup.Replay$Jump&Play"
++msgstr "Jump&Play"
++
++msgid "Setup.Replay$Play&Jump"
++msgstr "Play&Jump"
++
++msgid "Setup.Replay$Pause at last mark"
++msgstr "¿Ðã×РÝРßÞáÛÕÔÝÕÙ ÜÕâÚÕ"
++
++msgid "Setup.Replay$Reload marks"
++msgstr "¿ÕàÕÓàã×Øâì ÜÕâÚØ"
++
++msgid "Setup.Replay$Skip Seconds"
++msgstr "¿àÞßãáÚ áÕÚãÝÔ"
++
++msgid "Setup.Replay$Skip Seconds Slow"
++msgstr "¿àÞßãáÚ áÕÚãÝÔ ÜÕÔÛÕÝÝÞ"
++
++msgid "Setup.Replay$Length"
++msgstr "´ÛØÝÐ"
++
++msgid "Setup.Replay$Length / Number"
++msgstr "´ÛØÝР/ ½ÞÜÕà"
++
++msgid "Setup.Replay$Number"
++msgstr "½ÞÜÕà"
++
++msgid "Setup.Replay$DVD display mode"
++msgstr "ÀÕÖØÜ ßÞÚÐ×РDVD"
++
++msgid "Setup.Replay$DVD display leading zeros"
++msgstr "ßÞÚÐ× ÒÕÔãéØå ÝãÛÕÙ"
++
++msgid "Setup.Replay$never"
++msgstr "ÝØÚÞÓÔÐ"
++
++msgid "Setup.Replay$on begin"
++msgstr "Ò ÝÐçÐÛÕ"
++
++msgid "Setup.Replay$on end"
++msgstr "² ÚÞÝæÕ"
++
++msgid "Setup.Replay$on begin and end"
++msgstr "Ò ÝÐçÐÛÕ Ø Ò ÚÞÝæÕ"
++
++msgid "Setup.Replay$Tray open"
++msgstr "¾âÚàëâì"
++
++msgid "Setup.Replay$Limit DVD to speed"
++msgstr "¿àÕÔÕÛ áÚÞàÞáâØ DVD"
++
++msgid "Jump"
++msgstr "¿àëÖÞÚ"
++
++msgid "Skip +60s"
++msgstr "¿àÞßãáÚ +60s"
++
++msgid "Skip -60s"
++msgstr "¿àÞßãáÚ -60s"
++
++msgid "Cutter already running - Add to cutting queue?"
++msgstr "¼ÞÝâÐÖÕà àÐÑÞâÐÕâ - ´ÞÑÐÒØâì Ò ÞçÕàÕÔì?"
++
++msgid "Rename$Up"
++msgstr "²ÒÕàå"
++
++msgid "Rename$Down"
++msgstr "²ÝØ×"
++
++msgid "Rename$Previous"
++msgstr "¿àÕÔëÔãéØÙ"
++
++msgid "Rename$Next"
++msgstr "ÁÛÕÔãîéØÙ"
++
++msgid "Please mount %s"
++msgstr "¿ÞÖÐÛãÙáâРßÞÔÜÞÝâØàãÙâÕ %s"
++
++msgid "Please mount DVD %04d"
++msgstr "¿ÞÖÐÛãÙáâРßÞÔÜÞÝâØàãÙâÕ DVD %04d"
++
++msgid "Please mount DVD %d"
++msgstr "¿ÞÖÐÛãÙáâРßÞÔÜÞÝâØàãÙâÕ DVD %d"
++
++msgid "Please wait. Checking DVD..."
++msgstr "¿ÞÖÐÛãÙáâРßÞÔÞÖÔØâÕ. ¿àÞÒÕàÚРDVD..."
++
++msgid "No index-file found. Creating may take minutes. Create one?"
++msgstr "¸ÝÔÕÚá äÐÙÛ ÝÕ ÝÐÙÔÕÝ. ÁÞ×ÔÐÝØÕ âàÕÑãÕâ ÒàÕÜÕÝØ. ÁÞ×ÔÐâì?"
++
++msgid "Please wait. Creating index-file..."
++msgstr "¿ÞÖÐÛãÙáâРßÞÔÞÖÔØâÕ. ÁÞ×ÔÐÝØÕ ØÝÔÕÚá äÐÙÛÐ..."
++
++msgid "Wrong DVD!"
++msgstr "¾èØÑÚРDVD!"
+diff -NaurwB vdr-1.7.10/README.cmdsubmenu vdr-1.7.10-patched/README.cmdsubmenu
+--- vdr-1.7.10/README.cmdsubmenu       1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.10-patched/README.cmdsubmenu       2009-12-18 06:25:25.000000000 +0100
+@@ -0,0 +1,54 @@
++CmdSubmenu patch for VDR
++------------------------
++
++With this patch the commands and recording commands menus can be organised
++hierarchically. To create a submenu entry, prefix the name by one ore more "-".
++
++
++Standard:
++
++description_1 : cmd_1
++description_2 : cmd_2
++
++
++A submenu with two entries:
++
++Submenu title ... : echo "submenu"
++-description_1 : cmd_1
++-description_2 : cmd_2
++
++The dummy command in the title row is necessary.
++
++
++* History
++
++  2003-10-08: Version 0.1 - Albu at vdrportal.de
++    http://vdrportal.de/board/thread.php?threadid=6319
++
++  2003-10-09: Version 0.2 - Tobias Grimm <tg@e-tobi.net>
++    - Added Define CMD_SUBMENUS in Makefile
++
++  2004-05-28: Version 0.3 - Thomas Günther <tom@toms-cafe.de>
++    - Fixed compilation with gcc-3.3.3
++    - Added new virtual method AddConfig in cConfig
++    - Redefining of method Add in cListBase to virtual no longer necessary
++    - Improved code in menu.c
++    http://toms-cafe.de/vdr/download/vdr-cmdsubmenu-0.3.diff
++
++  2004-12-20: Version 0.4 - Thomas Günther <tom@toms-cafe.de>
++    - Solved conflict with jumpplay patch 0.6
++    http://toms-cafe.de/vdr/download/vdr-cmdsubmenu-0.4.diff
++
++  2006-04-22: Version 0.5 - Thomas Günther <tom@toms-cafe.de>
++    - Added version define CMDSUBMENUVERSNUM
++    - Reformated to VDR style indentions
++    - Added description in README.cmdsubmenu
++    http://toms-cafe.de/vdr/download/vdr-cmdsubmenu-0.5-1.3.47.diff
++
++  2006-04-23: Version 0.6 - Thomas Günther <tom@toms-cafe.de>
++    - Fixed menus with more than one level
++    http://toms-cafe.de/vdr/download/vdr-cmdsubmenu-0.6-1.3.47.diff
++
++  2006-05-15: Version 0.7 - Thomas Günther <tom@toms-cafe.de>
++    - Fixed build with G++ 4.1 (extra qualification)
++    http://toms-cafe.de/vdr/download/vdr-cmdsubmenu-0.7-1.4.0.diff
+diff -NaurwB vdr-1.7.10/README-HLCUTTER vdr-1.7.10-patched/README-HLCUTTER
+--- vdr-1.7.10/README-HLCUTTER 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.10-patched/README-HLCUTTER 2009-12-18 06:25:25.000000000 +0100
+@@ -0,0 +1,117 @@
++
++                    VDR-HLCUTTER README
++
++
++Written by:           Udo Richter
++Available at:         http://www.udo-richter.de/vdr/patches.html#hlcutter
++                      http://www.udo-richter.de/vdr/patches.en.html#hlcutter
++Contact:              udo_richter@gmx.de
++
++
++
++About
++-----
++
++The hard link cutter patch changes the recording editing algorithms of VDR to
++use filesystem hard links to 'copy' recording files whenever possible to speed
++up editing recordings noticeably.
++
++The patch has matured to be quite stable, at least I'm using it without issues.
++Nevertheless the patch is still in development and should be used with caution. 
++The patch is EXPERIMENTAL for multiple /videoxx folders. The safety checks 
++should prevent data loss, but you should always carefully check the results.
++
++While editing a recording, the patch searches for any 00x.vdr files that dont
++contain editing marks and would normally be copied 1:1 unmodified to the edited
++recording. In this case the current target 00x.vdr file will be aborted, and 
++the cutter process attempts to duplicate the source file as a hard link, so 
++that both files share the same disk space. If this succeeds, the editing 
++process fast-forwards through the duplicated file and continues normally 
++beginning with the next source file. If hard linking fails, the cutter process
++continues with plain old copying. (but does not take up the aborted last file.)
++
++After editing, the un-edited recording can be deleted as usual, the hard linked
++copies will continue to exist as the only remaining copy.
++
++To be effective, the default 'Max. video file size (MB)' should be lowered. 
++The patch lowers the smallest possible file size to 1mb. Since VDR only 
++supports up to 255 files, this would limit the recording size to 255Mb or
++10 minutes, in other words: This setting is insane!
++
++To make sure that the 255 file limit will not be reached, the patch also 
++introduces "Max. recording size (GB)" with a default of 100Gb (66 hours), and 
++increases the file size to 2000Mb early enough, so that 100Gb-recordings will
++fit into the 255 files.
++
++Picking the right parameters can be tricky. The smaller the file size, the 
++faster the editing process works. However, with a small file size, long 
++recordings will fall back to 2000Mb files soon, that are slow on editing again.
++
++Here are some examples:
++
++Max file size:      100Gb   100Gb   100Gb   100Gb   100Gb   100Gb   100Gb
++Max recording size: 1Mb     10Mb    20Mb    30Mb    40Mb    50Mb    100Mb
++
++Small files:        1-203   1-204   1-205   1-206   1-207   1-209   1-214
++  GBytes:           0.2     2.0     4.0     6.0     8.1     10.2    20.9
++  Hours:            0.13    1.3     2.65    4       5.4     6.8     13.9
++
++Big (2000mb) files: 204-255 204-255 206-255 207-255 208-255 210-255 215-255
++  GBytes:           101.5   99.6    97.7    95.7    93.8    89.8    80.1
++  Hours:            67      66      65      63      62      60      53
++
++A recording limit of 100Gb keeps plenty of reserve without blocking too much
++file numbers. And with a file size of 30-40Mb, recordings of 4-5 hours fit into
++small files completely. (depends on bit rate of course)
++
++
++
++The patch must be enabled in Setup-> Recordings-> Hard Link Cutter. When 
++disabled, the cutter process behaves identical to VDR's default cutter.
++
++There's a //#define HARDLINK_TEST_ONLY in the videodir.c file that enables a
++test-mode that hard-links 00x.vdr_ files only, and continues the classic 
++editing. The resulting 00x.vdr and 00x.vdr_ files should be identical. If you 
++delete the un-edited recording, dont forget to delete the *.vdr_ files too, 
++they will now eat real disk space.
++
++Note: 'du' displays the disk space of hard links only on first appearance, and
++usually you will see a noticeably smaller size on the edited recording.
++
++
++History
++-------
++Version 0.2.0
++
++  New: Support for multiple /videoXX recording folders, using advanced searching
++       for matching file systems where a hard link can be created.
++       Also supports deep mounted file systems.
++  Fix: Do not fail if last mark is a cut-in. (Again.)
++
++Version 0.1.4
++  New: Dynamic increase of file size before running out of xxx.vdr files
++  Fix: Last edit mark is not a cut-out
++  Fix: Write error if link-copied file is smaller than allowed file size
++  Fix: Broken index/marks if cut-in is at the start of a new file
++  Fix: Clear dangeling pointer to free'd cUnbufferedFile, 
++       thx to Matthias Schwarzott
++
++Version 0.1.0
++  Initial release
++
++
++
++
++Future plans
++------------
++
++Since original and edited copy share disk space, free space is wrong if one of
++them is moved to *.del. Free space should only count files with hard link 
++count = 1. This still goes wrong if all copies get deleted.
++
++
++For more safety, the hard-linked files may be made read-only, as modifications
++to one copy will affect the other copy too. (except deleting, of course)
++
++
++SetBrokenLink may get lost on rare cases, this needs some more thoughts.Index: vdr-1.5.9/README.jumpplay
+diff -NaurwB vdr-1.7.10/README.jumpplay vdr-1.7.10-patched/README.jumpplay
+--- vdr-1.7.10/README.jumpplay 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.10-patched/README.jumpplay 2009-12-18 06:25:25.000000000 +0100
+@@ -0,0 +1,92 @@
++JumpPlay patch for VDR
++----------------------
++
++This patch changes the replay behaviour for recordings that contain editing
++marks. It allows to immediately continue the replay after jumping forward to
++the next mark, and to automatically jump over the commercial break to the next
++"start" mark, if an "end" mark is reached.
++
++The features of this patch can be turned on or off with parameters in the replay
++setup. See MANUAL for description of this parameters: "Jump&Play", "Play&Jump",
++"Pause at last mark" and "Reload marks".
++
++
++* History
++
++  2003-07-04: jumpandrun.diff - the Noad <theNoad@SoftHome.net>
++    Jump&Play
++
++  2003-12-06: Version 0.0 - Torsten Kunkel <vdr@tkunkel.de>
++    Play&Jump (only if progressbar is visible)
++    Setup parameters Jump&Play and Play&Jump in the replay setup
++
++  2004-01-20: Version 0.1 - Thomas Günther <tom@toms-cafe.de>
++    Jump&Play:
++      - fixed speed after jump
++      - fixed removing of marks
++    Play&Jump:
++      - jump only on "end" marks
++
++  2004-01-27: Version 0.2 - Thomas Günther <tom@toms-cafe.de>
++    Jump&Play:
++      - fixed double jump
++    Play&Jump:
++      - fixed mark detection: fuzzy detection (until 3 seconds after mark)
++      - jump without progressbar
++      - mode "progressbar only" for old behaviour
++
++  2004-01-31: Version 0.3 - Thomas Günther <tom@toms-cafe.de>
++    Jump&Play:
++      - fixed display frames
++    Play&Jump:
++      - fixed end of playing at last mark
++
++  2004-07-11: Version 0.4 - Thomas Günther <tom@toms-cafe.de>
++    Jump&Play:
++      - don't play after jump to end
++    Play&Jump:
++      - don't prevent jumping after hide or show
++    Less conflicts with other patches (Elchi/AutoPID)
++
++  2004-08-21: Version 0.5 - Thomas Günther <tom@toms-cafe.de>
++    Play&Jump:
++      - exact jumps, replay like edited recording (no fuzzy mark detection)
++      - jump to first mark if replay starts at the beginning
++      - check jump marks with '8' key
++      - mode "progressbar only" removed
++    Description in README.jumpplay
++
++  2004-12-28: Version 0.6 - Thomas Günther <tom@toms-cafe.de>
++    Adapted noad extensions (from the Noad <theNoad@SoftHome.net>) to
++    jumpplay-0.5:
++      - cyclic reloading of marks found by noad online-scan
++      - don't stop after the last mark in case of live-recordings
++    New setup parameter "Load marks interval (s)"
++    Updated description in README.jumpplay
++
++  2006-04-14: Version 0.7 - Thomas Günther <tom@toms-cafe.de>
++    Fixed jump to first mark (crashed with plugin extrecmenu-0.9)
++    Added version define JUMPPLAYVERSNUM
++    Added placeholders for Czech language texts
++    Cleaned up i18n entries (support only VDR >= 1.3.29)
++    Improved description of i18n placeholders - hoping for real language texts
++
++  2006-05-12: Version 0.8 - Thomas Günther <tom@toms-cafe.de>
++    Fixed segfault in dvbplayer thread while the replaycontrol thread is
++    reloading the marks (thanks to horchi at vdrportal.de for reporting this -
++    see http://vdrportal.de/board/thread.php?postid=450463#post450463):
++    New class cMarksReload checks the timestamp of marks.vdr in 10 seconds
++    intervals, so the marks in the threads dvbplayer and replaycontrol can be
++    reloaded independently
++    Changed setup parameter "Load marks interval (s)" to "Reload marks"
++    Updated description in README.jumpplay
++
++  2006-05-28: Version 0.9 - Thomas Günther <tom@toms-cafe.de>
++    New setup parameter "Pause at last mark"
++    Updated description in README.jumpplay
++    Moved parameters description to MANUAL
++
++  2009-03-31: Version 1.0 - Thomas Günther <tom@toms-cafe.de>
++    Play&Jump:
++      - set resume position to 0 if replay stops at the first mark
++    Added French language texts (thanks to Michaël Nival)
+diff -NaurwB vdr-1.7.10/README.MainMenuHooks vdr-1.7.10-patched/README.MainMenuHooks
+--- vdr-1.7.10/README.MainMenuHooks    1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.10-patched/README.MainMenuHooks    2009-12-18 06:25:25.000000000 +0100
+@@ -0,0 +1,55 @@
++This is a "patch" for the Video Disk Recorder (VDR).
++
++* Authors:
++Tobias Grimm  <vdr at e-tobi dot net>
++Martin Prochnow  <nordlicht at martins-kabuff dot de>
++Frank Schmirler  <vdrdev at schmirler dot de>
++Christian Wieninger  <cwieninger at gmx dot de>
++
++* Description:
++This patch allows plugins to replace the VDR mainmenus "Schedule",
++"Channels", "Timers" and "Recordings" by a different implementation.
++
++The patch is based on a suggestion of Christian Wieninger back in 2006
++(http://www.linuxtv.org/pipermail/vdr/2006-March/008234.html). It is
++meant to be an interim solution for VDR 1.4 until (maybe) VDR 1.5
++introduces an official API for this purpose.
++
++* Installation
++Change into the VDR source directory, then issue
++  patch -p1 < path/to/MainMenuHooks-v1_0.patch
++and recompile.
++
++* Notes for plugin authors
++The following code sample shows the required plugin code for replacing
++the original Schedule menu:
++
++bool cMyPlugin::Service(const char *Id, void *Data)
++{
++  cOsdMenu **menu = (cOsdMenu**) Data;
++  if (MySetup.replaceSchedule &&
++            strcmp(Id, "MainMenuHooksPatch-v1.0::osSchedule") == 0) {
++    if (menu)
++      *menu = (cOsdMenu*) MainMenuAction();
++    return true;
++  }
++  return false;
++}
++
++A plugin can replace more than one menu at a time. Simply replace the
++call to MainMenuAction() in the sample above by appropriate code.
++
++Note that a plugin *should* offer a setup option which allows the user
++to enable or disable the replacement. "Disabled" would be a reasonable
++default setting. By testing for define MAINMENUHOOKSVERSNUM, a plugin
++can leave the setup option out at compiletime.
++
++In case there is an internal problem when trying to open the replacement
++menu, it is safe to return true even though Data is NULL. However an
++OSD message should indicate the problem to the user.
++
++Feel free to ship this patch along with your plugin. However if you
++think you need to modify the patch, we'd encourage you to contact the
++authors first or at least use a service id which differs in more than
++just the version number.
++
+diff -NaurwB vdr-1.7.10/README.sortrec vdr-1.7.10-patched/README.sortrec
+--- vdr-1.7.10/README.sortrec  1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.10-patched/README.sortrec  2009-12-18 06:25:25.000000000 +0100
+@@ -0,0 +1,48 @@
++Sort Recordings patch for VDR
++-----------------------------
++Copyright (C) 2005      Frank99 @vdr-portal.de
++Copyright (C) 2006-2008 Christoph Haubrich
++
++Released under the same license as VDR itself, for details see vdr.c or
++http://firefly.vdr-developer.org/patches
++
++This patch changes the sort behaviour of the recordings menu. It is based
++on the patch available here: http://www.vdr-portal.de/board/thread.php?threadid=36031
++Required for this patch is the liemikuutio-patch for VDR which can be found here:
++http://www.saunalahti.fi/%7Erahrenbe/vdr/patches/
++
++There are four sorting modes available after this patch is applied:
++
++mode   behaviour for      behaviour for
++       main directory     sub directories
++--------------------------------------------------------------------------
++  0    alphabetically     if special character(*) is found alphabetically,
++                          else by date
++  1    by date            if special character(*) is found alphabetically,
++                          else by date
++  2    alphabetically     alphabetically
++  3    by date            by date
++
++(*) if the name of a  subdirectory ends with one of ".-$" (dot, hyphen, dollar sign) 
++    it is sorted alphabetically in sort mode 0 and 1
++
++Sort mode 0 with none of the special characters at the end of any subdir 
++corresponds to the default sorting mode of the original VDR.
++
++The sorting mode can be switched through in the recording menu with the '0' key 
++(0->1->2->3->0->...), a default for startup can be set in the setup->recordings menu.
++
++Additionally the sort order (ascending/descending) can be toggled by the '9' key
++(which is always set to ascending after a restart)
++
++If you like the to see subdirectories before recordings you can select to put 
++directories first in the setup->recordings menu. 
++
++If you would like the sorting to ignore a leading '%' (as normally displayed before
++cutted recordings) you can achive this by setting the environment variable LC_COLLATE
++properly (eg. LC_COLLATE=de_DE@euo in runvdr for germany).
++
++History:
++2006-08-13 v3, sortrec release for VDR 1.4.1 and liemikuutio 1.8
++2007-01-28 v3a, moved #ifdef from optimized-rename-patch to sortrec
++2008-03-29 v3b, removed ASCII-170 and ASCII-183 to make sortrec Utf8-ready
+diff -NaurwB vdr-1.7.10/README.timer-info vdr-1.7.10-patched/README.timer-info
+--- vdr-1.7.10/README.timer-info       1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.10-patched/README.timer-info       2009-12-18 06:25:25.000000000 +0100
+@@ -0,0 +1,53 @@
+++------------------------------------------------------------------------------+
++|               Info about the timer-info-patch by Brougs78                    |
++|                brougs78@gmx.net / home.pages.at/brougs78                     |
+++------------------------------------------------------------------------------+
++
++
++README timer-info:
++------------------
++
++Features:
++ - Shows info, if it is possible to record an event in the timer menu of vdr.
++   For calculations the free space incl. the deleted recordings is used,
++   considering an average consumtion of 25.75 MB/min (also used by vdr itself).
++   The first column in the timer-list shows:
++      ( + ) recording will be most probably possible (enough space)
++      (+/-) recording may be possible
++      ( - ) recording will most probably fail (to less space)
++   The calculations also consider repeating timers.
++ - It is possible to deactivate the patch in the OSD-menu of VDR.
++
++
++HISTORY timer-info:
++-------------------
++
++25.11.2004: v0.1
++ - Initial release
++
++11.01.2005: v0.1b
++ - Bugfixes for vdr-1.3.18
++ - In the menu the free recording-time no longer includes the space of the
++   deleted recordings, because this slowed the vdr down to much.
++
++08.07.2005: v0.1c
++ - Made the patch configurable
++
++29.01.2006: v0.2 - Thomas Günther <tom@toms-cafe.de>
++ - Rewritten great parts for vdr-1.3.38+
++   http://toms-cafe.de/vdr/download/vdr-timer-info-0.2-1.3.38+.diff
++
++05.02.2006: v0.3 - Thomas Günther <tom@toms-cafe.de>
++ - Fixed refresh of timer menu in cMenuTimers::OnOff
++ - Fixed check of repeating timers
++ - Syslog debug messages can be enabled with Define DEBUG_TIMER_INFO
++   http://toms-cafe.de/vdr/download/vdr-timer-info-0.3-1.3.38+.diff
++
++03.03.2006: v0.4 - Thomas Günther <tom@toms-cafe.de>
++ - Adapted to vdr-1.3.44
++ - Removed setup parameter "Show timer-info"
++   http://toms-cafe.de/vdr/download/vdr-timer-info-0.4-1.3.44.diff
++
++03.03.2006: v0.5 - Tobias Grimm <tg@e-tobi.net>
++ - Adapted to vdr-1.3.45
++   http://toms-cafe.de/vdr/download/vdr-timer-info-0.4-1.3.45.diff
+diff -NaurwB vdr-1.7.10/receiver.c vdr-1.7.10-patched/receiver.c
+--- vdr-1.7.10/receiver.c      2007-08-12 13:52:59.000000000 +0200
++++ vdr-1.7.10-patched/receiver.c      2009-12-18 06:25:25.000000000 +0100
+@@ -12,7 +12,11 @@
+ #include <stdio.h>
+ #include "tools.h"
++#ifdef USE_TTXTSUBS
++cReceiver::cReceiver(tChannelID ChannelID, int Priority, int Pid, const int *Pids1, const int *Pids2, const int *Pids3, const int *Pids4)
++#else
+ cReceiver::cReceiver(tChannelID ChannelID, int Priority, int Pid, const int *Pids1, const int *Pids2, const int *Pids3)
++#endif /* TTXTSUBS */
+ {
+   device = NULL;
+   channelID = ChannelID;
+@@ -32,6 +36,12 @@
+      while (*Pids3 && numPids < MAXRECEIVEPIDS)
+            pids[numPids++] = *Pids3++;
+      }
++#ifdef USE_TTXTSUBS
++  if (Pids4) {
++     while (*Pids4 && numPids < MAXRECEIVEPIDS)
++           pids[numPids++] = *Pids4++;
++     }
++#endif /* TTXTSUBS */
+   if (numPids >= MAXRECEIVEPIDS)
+      dsyslog("too many PIDs in cReceiver");
+ }
+diff -NaurwB vdr-1.7.10/receiver.h vdr-1.7.10-patched/receiver.h
+--- vdr-1.7.10/receiver.h      2007-01-05 12:00:36.000000000 +0100
++++ vdr-1.7.10-patched/receiver.h      2009-12-18 06:25:25.000000000 +0100
+@@ -38,10 +38,15 @@
+                ///< will be delivered only ONCE, so the cReceiver must make sure that
+                ///< it will be able to buffer the data if necessary.
+ public:
++#ifdef USE_TTXTSUBS
++  cReceiver(tChannelID ChannelID, int Priority, int Pid, const int *Pids1 = NULL, const int *Pids2 = NULL, const int *Pids3 = NULL, const int *Pids4 = NULL);
++#else
+   cReceiver(tChannelID ChannelID, int Priority, int Pid, const int *Pids1 = NULL, const int *Pids2 = NULL, const int *Pids3 = NULL);
++#endif /* TTXTSUBS */
+                ///< Creates a new receiver for the channel with the given ChannelID with
+                ///< the given Priority. Pid is a single PID (typically the video PID), while
+                ///< Pids1...Pids3 are pointers to zero terminated lists of PIDs.
++               ///< ifdef USE_TTXTSUBS: Pids1...Pids4 are pointers to zero terminated lists of PIDs.
+                ///< If any of these PIDs are 0, they will be silently ignored.
+                ///< The total number of non-zero PIDs must not exceed MAXRECEIVEPIDS.
+                ///< Priority may be any value in the range -99..99. Negative values indicate
+diff -NaurwB vdr-1.7.10/recorder.c vdr-1.7.10-patched/recorder.c
+--- vdr-1.7.10/recorder.c      2009-11-21 16:58:12.000000000 +0100
++++ vdr-1.7.10-patched/recorder.c      2009-12-18 06:25:25.000000000 +0100
+@@ -21,8 +21,21 @@
+ // --- cRecorder -------------------------------------------------------------
++#ifdef USE_TTXTSUBS
++cRecorder::cRecorder(const char *FileName, tChannelID ChannelID, int Priority, int VPid, const int *APids, const int *DPids, const int *SPids, const int *EPids)
++#ifdef USE_DOLBYINREC
++:cReceiver(ChannelID, Priority, VPid, APids, Setup.UseDolbyInRecordings ? DPids : NULL, SPids, EPids)
++#else
++:cReceiver(ChannelID, Priority, VPid, APids, Setup.UseDolbyDigital ? DPids : NULL, SPids, EPids)
++#endif /* DOLBYINREC */
++#else
+ cRecorder::cRecorder(const char *FileName, tChannelID ChannelID, int Priority, int VPid, const int *APids, const int *DPids, const int *SPids)
++#ifdef USE_DOLBYINREC
++:cReceiver(ChannelID, Priority, VPid, APids, Setup.UseDolbyInRecordings ? DPids : NULL, SPids)
++#else
+ :cReceiver(ChannelID, Priority, VPid, APids, Setup.UseDolbyDigital ? DPids : NULL, SPids)
++#endif /* DOLBYINREC */
++#endif /* TTXTSUBS */
+ ,cThread("recording")
+ ,recordingInfo(FileName)
+ {
+@@ -87,7 +100,11 @@
+ bool cRecorder::NextFile(void)
+ {
+   if (recordFile && frameDetector->IndependentFrame()) { // every file shall start with an independent frame
++#ifndef USE_HARDLINKCUTTER
+      if (fileSize > MEGABYTE(off_t(Setup.MaxVideoFileSize)) || RunningLowOnDiskSpace()) {
++#else
++     if (fileSize > fileName->MaxFileSize() || RunningLowOnDiskSpace()) {
++#endif /* HARDLINKCUTTER */
+         recordFile = fileName->NextFile();
+         fileSize = 0;
+         }
+diff -NaurwB vdr-1.7.10/recorder.h vdr-1.7.10-patched/recorder.h
+--- vdr-1.7.10/recorder.h      2009-01-06 11:44:58.000000000 +0100
++++ vdr-1.7.10-patched/recorder.h      2009-12-18 06:25:25.000000000 +0100
+@@ -34,7 +34,11 @@
+   virtual void Receive(uchar *Data, int Length);
+   virtual void Action(void);
+ public:
++#ifdef USE_TTXTSUBS
++  cRecorder(const char *FileName, tChannelID ChannelID, int Priority, int VPid, const int *APids, const int *DPids, const int *SPids, const int *EPids = NULL);
++#else
+   cRecorder(const char *FileName, tChannelID ChannelID, int Priority, int VPid, const int *APids, const int *DPids, const int *SPids);
++#endif /* TTXTSUBS */
+                // Creates a new recorder for the channel with the given ChannelID and
+                // the given Priority that will record the given PIDs into the file FileName.
+   virtual ~cRecorder();
+diff -NaurwB vdr-1.7.10/recording.c vdr-1.7.10-patched/recording.c
+--- vdr-1.7.10/recording.c     2009-11-22 12:20:53.000000000 +0100
++++ vdr-1.7.10-patched/recording.c     2009-12-18 06:29:45.000000000 +0100
+@@ -8,6 +8,9 @@
+  */
+ #include "recording.h"
++#ifdef USE_WAREAGLEICON
++#include "iconpatch.h"
++#endif /* WAREAGLEICON */
+ #include <ctype.h>
+ #include <dirent.h>
+ #include <errno.h>
+@@ -26,6 +29,14 @@
+ #include "tools.h"
+ #include "videodir.h"
++#if defined (USE_DVDCHAPJUMP) && defined (USE_DVDARCHIVE)
++#include <assert.h>
++/* libdvdread stuff */
++#include <dvdread/dvd_reader.h>
++#include <dvdread/ifo_types.h>
++#include <dvdread/ifo_read.h>
++#endif /* DVDCHAPJUMP & DVDARCHIVE */
++
+ #define SUMMARYFALLBACK
+ #define RECEXT       ".rec"
+@@ -50,6 +61,9 @@
+ #endif
+ #define INFOFILESUFFIX    "/info"
+ #define MARKSFILESUFFIX   "/marks"
++#ifdef USE_DVDARCHIVE /* ??? */
++#define DVDARCHIVEFILENAME "/dvd.vdr"
++#endif /* DVDARCHIVE */
+ #define MINDISKSPACE 1024 // MB
+@@ -67,6 +81,13 @@
+ bool VfatFileSystem = false;
+ int InstanceId = 0;
++#ifdef USE_LIEMIEXT
++bool DirOrderState = false;
++#endif /* LIEMIEXT */
++
++#ifdef USE_DVLFRIENDLYFNAMES
++char *MakeFriendlyFilename(char **buf);
++#endif /* DVLFRIENDLYFNAMES */
+ cRecordings DeletedRecordings(true);
+@@ -602,9 +623,24 @@
+ {
+   resume = RESUME_NOT_INITIALIZED;
+   titleBuffer = NULL;
++#ifdef USE_SORTRECORDS
++  for (int i = 0; i < MAXSORTMODES; i++) {
++      sortBuffer[i] = NULL;
++      lastDirsFirst[i] = -1;
++      }
++#else
+   sortBuffer = NULL;
++#endif /* SORTRECORDS */
+   fileName = NULL;
+   name = NULL;
++#ifdef USE_DVDARCHIVE
++  dvdname = NULL;
++  dvdtrack = NULL;
++  dvdchapters = NULL;
++  isArchived = false;
++  isOnlyOnDvd = false;
++  dvdtype = DVD_TYPE_UNKNOWN;
++#endif /* DVDARCHIVE */
+   fileSizeMB = -1; // unknown
+   channel = Timer->Channel()->Number();
+   instanceId = InstanceId;
+@@ -639,6 +675,11 @@
+               break;
+            }
+      if (Timer->IsSingleEvent()) {
++#ifdef USE_DVLFRIENDLYFNAMES
++    if (Setup.UseFriendlyFNames == 1)
++       Timer -> SetFile(MakeFriendlyFilename(&name));
++    else
++#endif /* DVLFRIENDLYFNAMES */
+         Timer->SetFile(name); // this was an instant recording, so let's set the actual data
+         Timers.SetModified();
+         }
+@@ -649,6 +690,10 @@
+      name = strdup(cString::sprintf("%s~%s", Timer->File(), Subtitle));
+   // substitute characters that would cause problems in file names:
+   strreplace(name, '\n', ' ');
++#ifdef USE_DVLFRIENDLYFNAMES
++  if (Setup.UseFriendlyFNames == 1)
++     MakeFriendlyFilename(&name);
++#endif /* DVLFRIENDLYFNAMES */
+   start = Timer->StartTime();
+   priority = Timer->Priority();
+   lifetime = Timer->Lifetime();
+@@ -671,12 +716,27 @@
+   framesPerSecond = DEFAULTFRAMESPERSECOND;
+   deleted = 0;
+   titleBuffer = NULL;
++#ifdef USE_SORTRECORDS
++  for (int i = 0; i < MAXSORTMODES; i++) {
++      sortBuffer[i] = NULL;
++      lastDirsFirst[i] = -1;
++      }
++#else
+   sortBuffer = NULL;
++#endif /* SORTRECORDS */
+   fileName = strdup(FileName);
+   FileName += strlen(VideoDirectory) + 1;
+   const char *p = strrchr(FileName, '/');
+   name = NULL;
++#ifdef USE_DVDARCHIVE
++  dvdname = NULL;
++  dvdtrack = NULL;
++  dvdchapters = NULL;
++  isArchived = false;
++  isOnlyOnDvd = false;
++  dvdtype = DVD_TYPE_NOT_READ;
++#endif /* DVDARCHIVE */
+   info = new cRecordingInfo;
+   if (p) {
+      time_t now = time(NULL);
+@@ -765,15 +825,34 @@
+            LOG_ERROR_STR(*SummaryFileName);
+         }
+ #endif
++#ifdef USE_DVDARCHIVE
++     if (CheckFileExistence("dvd.vdr")) {
++        GetDvdName(fileName);
++        isArchived = true;
++        if (!CheckFileExistence("001.vdr"))
++           isOnlyOnDvd = true;
++        }
++#endif /* DVDARCHIVE */
+      }
+ }
+ cRecording::~cRecording()
+ {
+   free(titleBuffer);
++#ifdef USE_SORTRECORDS
++  for (int i = 0; i < MAXSORTMODES; i++) {
++      free(sortBuffer[i]);
++      }
++#else
+   free(sortBuffer);
++#endif /* SORTRECORDS */
+   free(fileName);
+   free(name);
++#ifdef USE_DVDARCHIVE
++  free(dvdname);
++  free(dvdtrack);
++  free(dvdchapters);
++#endif /* DVDARCHIVE */
+   delete info;
+ }
+@@ -793,21 +872,46 @@
+         t++;
+         }
+   if (s1 && s2)
++#ifdef USE_SORTRECORDS
++    if (Setup.RecordingsSortDirsFirst)
++       *s1 = 'b';
++
++    if ((Setup.RecordingsSortMode <= 1 && s1 != s && !strchr(".-$ª·", *(s1 - 1))) ||
++        (Setup.RecordingsSortMode == 1 && s1 == s) ||
++        (Setup.RecordingsSortMode == 3))
++#endif /* SORTRECORDS */
+      memmove(s1 + 1, s2, t - s2 + 1);
+   return s;
+ }
+ char *cRecording::SortName(void) const
+ {
++#ifdef USE_SORTRECORDS
++  if (!sortBuffer[Setup.RecordingsSortMode] ||
++        lastDirsFirst[Setup.RecordingsSortMode] != Setup.RecordingsSortDirsFirst) {
++     free(sortBuffer[Setup.RecordingsSortMode]);
++     lastDirsFirst[Setup.RecordingsSortMode] = Setup.RecordingsSortDirsFirst;
++     char *s = StripEpisodeName(strdup(FileName() + strlen(VideoDirectory)));
++#else
+   if (!sortBuffer) {
+      char *s = StripEpisodeName(strdup(FileName() + strlen(VideoDirectory) + 1));
++#endif /* SORTRECORDS */
+      strreplace(s, '/', 'a'); // some locales ignore '/' when sorting
+      int l = strxfrm(NULL, s, 0) + 1;
++#ifdef USE_SORTRECORDS
++     sortBuffer[Setup.RecordingsSortMode] = MALLOC(char, l);
++     strxfrm(sortBuffer[Setup.RecordingsSortMode], s, l);
++#else
+      sortBuffer = MALLOC(char, l);
+      strxfrm(sortBuffer, s, l);
++#endif /* SORTRECORDS */
+      free(s);
+      }
++#ifdef USE_SORTRECORDS
++  return sortBuffer[Setup.RecordingsSortMode];
++#else
+   return sortBuffer;
++#endif /* SORTRECORDS */
+ }
+ int cRecording::GetResume(void) const
+@@ -822,7 +926,15 @@
+ int cRecording::Compare(const cListObject &ListObject) const
+ {
+   cRecording *r = (cRecording *)&ListObject;
++#ifdef USE_SORTRECORDS
++  return Recordings.GetSortOrder() * strcasecmp(SortName(), r->SortName());
++#else
++#ifdef USE_LIEMIEXT
++  if (DirOrderState)
++     return strcasecmp(FileName(), r->FileName());
++#endif /* LIEMIEXT */
+   return strcasecmp(SortName(), r->SortName());
++#endif  /* USE_SORTRECORDS */
+ }
+ const char *cRecording::FileName(void) const
+@@ -840,9 +952,359 @@
+   return fileName;
+ }
++#ifdef USE_DVDARCHIVE
++bool cRecording::CheckFileExistence(const char* FileNameToTest, const bool useVideoDir) const
++{
++  if (!useVideoDir || (useVideoDir && FileName())) {
++     cString filename = cString::sprintf("%s%s%s", useVideoDir ? FileName() : "",
++                                                   useVideoDir ? "/" : "",
++                                                   FileNameToTest);
++     struct stat statBuf;
++     if (lstat(filename, &statBuf) == -1) return false;
++     return S_ISREG(statBuf.st_mode) || S_ISLNK(statBuf.st_mode);
++     }
++  return false;
++}
++
++bool cRecording::GetDvdName(const char* Directory) const
++{
++  char* filename = (char*)alloca(strlen(Directory) + strlen(DVDARCHIVEFILENAME) + 1);
++  if (filename) {
++     strcpy(filename, Directory);
++     char *end = filename + strlen(filename);
++     strcpy(end, DVDARCHIVEFILENAME);
++     FILE* file;
++     if ((file = fopen(filename, "r"))) {
++        cReadLine ReadLine;
++        char* buffer = (char*)alloca(BUFSIZ);
++        if (buffer) {
++           buffer = ReadLine.Read(file);
++           if (buffer)
++              ((cRecording*)this)->dvdname = strdup(buffer);
++           else
++              ((cRecording*)this)->dvdname = NULL;
++
++           buffer = ReadLine.Read(file);
++           if (buffer) {
++              ((cRecording*)this)->dvdtrack = strdup(buffer);
++              if (atoi(buffer) == 0)
++                 ((cRecording*)this)->dvdtype = DVD_VIDEO_TYPE;
++              else
++                 ((cRecording*)this)->dvdtype = DVD_VIDEO_ARCHIVE_TYPE;
++              }
++           else {
++              ((cRecording*)this)->dvdtrack = NULL;
++              ((cRecording*)this)->dvdtype = DVD_ARCHIVE_TYPE;
++              }
++
++           fclose(file);
++           return true;
++           }
++        }
++     }
++     return false;
++}
++
++bool cRecording::GetDvdChaptersFromDvd(int title) const
++{
++#ifdef USE_DVDCHAPJUMP
++  cString buf;
++
++  dvd_reader_t *dvd;
++  ifo_handle_t *ifo_file;
++  tt_srpt_t *tt_srpt;
++  ifo_handle_t *vts_file;
++  pgc_t *cur_pgc;
++
++  dvd = DVDOpen(DVD_DEVICE);
++  if (!dvd) {
++     esyslog("DVD-ARCHIVE: Couldn't open DVD device %s!", DVD_DEVICE);
++     return false;
++     }
++
++  /* open title manager */
++  ifo_file = ifoOpen(dvd,0);
++  if (!ifo_file) {
++     esyslog("DVD-ARCHIVE: Can't open VMG info.");
++     DVDClose(dvd);
++     return false;
++     }
++
++  /* read total_title */
++  tt_srpt = ifo_file->tt_srpt;
++
++  /* get total chapters */
++  int title_set_nr   = tt_srpt->title[title-1].title_set_nr;
++  int total_chap     = tt_srpt->title[title-1].nr_of_ptts;
++  int local_title_id = tt_srpt->title[title-1].vts_ttn - 1;
++
++  /* access title set file */
++  vts_file = ifoOpen(dvd, title_set_nr);
++  if (!vts_file) {
++     esyslog("DVD-ARCHIVE: Can't open info file for title set %d!",title_set_nr);
++     DVDClose(dvd);
++     return false;
++     }
++
++  /* find program chain and check programs 
++     all chapters should be in the same prog chain and
++     should be numbered from 1 to <total_chap>
++  */
++  {
++    vts_ptt_srpt_t *vts_ptt_srpt = vts_file->vts_ptt_srpt;
++    int pgc_nr = vts_ptt_srpt->title[local_title_id].ptt[0].pgcn;
++    int pg     = vts_ptt_srpt->title[local_title_id].ptt[0].pgn;
++    int p;
++
++    assert(pg==1);
++    for (p=1; p<total_chap; p++) {
++        int next_pg;
++        int this_pgc;
++        this_pgc = vts_ptt_srpt->title[local_title_id].ptt[p].pgcn;
++        assert(pgc_nr == this_pgc);
++        next_pg = vts_ptt_srpt->title[local_title_id].ptt[p].pgn;
++        assert(pg+1 == next_pg);
++        pg = next_pg;
++        }
++
++    /* fetch program chain */
++    cur_pgc = vts_file->vts_pgcit->pgci_srp[pgc_nr-1].pgc;
++    assert(cur_pgc->nr_of_programs == total_chap);
++  }
++
++  /* --- main cell loop --- */
++  {
++    pgc_program_map_t *chap_cell;
++    cell_playback_t *cell_pb;
++    int c;
++    int chap;
++
++    /* total cells in chain */
++    int total_cell = cur_pgc->nr_of_cells;
++
++    /* get info */
++    chap_cell = cur_pgc->program_map;
++    cell_pb   = cur_pgc->cell_playback;
++
++    /* loop through all cells */
++    chap = -1;
++    int position = 0;
++    for (c=0; c<total_cell; c++) {
++        int cell_mode;
++        const char *mode;
++
++        dvd_time_t *time = (dvd_time_t *)&cell_pb->playback_time;
++
++        int framerate = time->frame_u>>6;
++        assert(framerate == 1 || framerate == 3);
++        int frames_per_sec = (framerate == 1) ? 25 : 30;
++
++        /* upper 4 bits are first digit, down 4 bits are second digit */
++        int hour = (time->hour>>4) * 10 + (time->hour&15);
++        int minute = (time->minute>>4) * 10 + (time->minute&15);
++        int second = (time->second>>4) * 10 + (time->second&15);
++        /* upper 4 bits are first digit, down 4 bits are second digit */
++        int frame = (time->frame_u>>4&3) * 10 + (time->frame_u&15);
++
++        int frames = ((hour * 3600) + (minute * 60) + second) * frames_per_sec + frame;
++
++        /* this cell is the begin of a new chapter! */
++        if (chap_cell[chap+1] == c+1) {
++           cString oldbuf = cString::sprintf("%s", *buf);
++           buf = cString::sprintf("%s%d%s", *oldbuf, position, ((chap+2) < total_chap) ? "," : "");
++           chap++;
++           }
++
++      /* cell_mode: 0=normal, 1=first of angle, 2=in angle, 3=last of angle */
++      cell_mode = cell_pb->block_mode;
++      if ((cell_mode==0) || (cell_mode==1)) {
++         /* only account for normal or begin of angle cells */
++         position += frames;
++         mode = "counted";
++         }
++      else
++         mode = "skipped";
++
++      cell_pb++;
++    }
++  }
++
++  ifoClose(ifo_file);
++  ifoClose(vts_file);
++  DVDClose(dvd);
++
++  ((cRecording*)this)->dvdchapters = strdup(buf);
++
++  return true;
++#else
++  return false;
++#endif /* DVDCHAPJUMP */
++}
++
++const char *cRecording::GetDvdChapters(void) const
++{
++  // Read chapters from dvd
++  if (dvdtype == DVD_VIDEO_ARCHIVE_TYPE) {
++     if (dvdtrack) {
++        if (!GetDvdChaptersFromDvd(atoi(dvdtrack)))
++           ((cRecording*)this)->dvdchapters = NULL;
++        else
++           isyslog("DVD-ARCHIVE: Using following positions for chapter jumping: %s", dvdchapters);
++        return dvdchapters;
++        }
++     }
++  return NULL;
++}
++
++int cRecording::MountDvd(void) const
++{
++  cString cmd;
++  if (Setup.DvdSpeedLimit > 0) {
++     cmd = cString::sprintf("speedcontrol -x %d %s", Setup.DvdSpeedLimit, DVD_DEVICE);
++     SystemExec(cmd);
++     }
++
++  cString msg;
++  if (atoi(dvdname) == 0)
++     msg = cString::sprintf(tr("Please mount %s"), dvdname);
++  else {
++     if (Setup.DvdDisplayZeros)
++        msg = cString::sprintf(tr("Please mount DVD %04d"), atoi(dvdname));
++     else
++        msg = cString::sprintf(tr("Please mount DVD %d"), atoi(dvdname));
++     }
++
++  bool rep = true;
++  while (rep) {
++        if (Setup.DvdTrayMode==1 || Setup.DvdTrayMode==3)
++           cmd = cString::sprintf("umount %s; eject %s", DVD_DEVICE, DVD_DEVICE);
++        else
++           cmd = cString::sprintf("umount %s", DVD_DEVICE);
++        SystemExec(cmd);
++
++        if (Interface->Confirm(msg, 300)) {
++           Skins.Message(mtStatus, tr("Please wait. Checking DVD..."));
++           Skins.Flush();
++           cmd = cString::sprintf("eject -t %s; mkdir -p %s; mount -o ro -t %s %s %s",
++                                                             DVD_DEVICE, DVD_MOUNT_PATH,
++                                                             (dvdtrack ? "udf" : "iso9660"),
++                                                             DVD_DEVICE, DVD_MOUNT_PATH);
++           SystemExec(cmd);
++
++           bool correctDvd = true;
++
++           char *olddvdname, *olddvdtrack;
++           int olddvdtype;
++           olddvdname = dvdname;
++           olddvdtrack = dvdtrack;
++           olddvdtype = dvdtype;
++           if (GetDvdName(DVD_MOUNT_PATH)) {
++              if (atoi(dvdname) != atoi(olddvdname)) correctDvd = false;
++              }
++           ((cRecording*)this)->dvdname = olddvdname;
++           ((cRecording*)this)->dvdtrack = olddvdtrack;
++           ((cRecording*)this)->dvdtype = olddvdtype;
++
++           if (correctDvd) {
++              if (dvdtrack == NULL) {
++                 // Archived DVD in VDR format
++                 char fn[BUFSIZ];
++                 strcpy(fn, FileName());
++                 char *p = strrchr(fn, '/');
++                 cmd = cString::sprintf("find '%s' -name '%s'", DVD_MOUNT_PATH, p+1);
++                 }
++              else {
++                 // Either archived DVD in DVD-Video format or DVD-Video which
++                 // should be played with the DVD plugin
++                 cmd = cString::sprintf("find '%s' -iname 'VIDEO_TS'", DVD_MOUNT_PATH);
++                 }
++
++              cReadLine pipe;
++              FILE* file;
++              char *dirname = NULL;
++              if ((file = popen(cmd, "r")) != (FILE *)NULL) {
++                 if ((dirname = pipe.Read(file)) != NULL) {
++                    pclose(file);
++                    if (dvdtrack != NULL && atoi(dvdtrack) == 0) {
++                       // It is a valid Video-DVD and DVD plugin can be started
++                       return MOUNT_DVD_LAUNCH_DVD_PLUGIN;
++                       }
++                    else {
++                       // It is a valid Archive-DVD or an archived Video-DVD
++                       // and the links can now be established
++                       cString srcFn;
++                       int n = 1;
++
++                       do {
++                            if (dvdtrack == NULL)
++                               srcFn = cString::sprintf("%s/%03d.vdr", dirname, n);
++                            else
++                               srcFn = cString::sprintf("%s/VTS_%02d_%d.VOB", dirname, atoi(dvdtrack), n);
++
++                            if (!access(srcFn, R_OK)) {
++                               cmd = cString::sprintf("ln -sf '%s' '%s/%03d.vdr'", *srcFn, FileName(), n);
++                               SystemExec(cmd);
++                               isyslog("DVD-ARCHIVE: Linking %s/%03d.vdr -> %s", FileName(), n, *srcFn);
++                               }
++                            else
++                               break;
++                          } while ( ++n < 999);
++
++                       if (!CheckFileExistence("index.vdr")) {
++                          if (dvdtrack == NULL)
++                             srcFn = cString::sprintf("%s/index.vdr", dirname);
++                          else
++                             srcFn = cString::sprintf("%s/index_%02d.vdr", dirname, atoi(dvdtrack));
++
++                          if (!CheckFileExistence(srcFn, false)) {
++                             msg = cString::sprintf(tr("No index-file found. Creating may take minutes. Create one?"));
++                             if (Interface->Confirm(msg, 300)) {
++                                Skins.Message(mtStatus, tr("Please wait. Creating index-file..."));
++                                cmd = cString::sprintf("speedcontrol -x 999 %s; cd %s && genindex &", DVD_DEVICE, FileName());
++                                SystemExec(cmd);
++                                return MOUNT_DVD_ABORT;
++                                }
++                             }
++                          else {
++                             cmd = cString::sprintf("ln -sf '%s' '%s/index.vdr'", *srcFn, FileName());
++                             SystemExec(cmd);
++                             isyslog("DVD-ARCHIVE: Linking %s/index.vdr -> %s", FileName(), *srcFn);
++                             }
++                          }
++                       return MOUNT_DVD_REPLAY;
++                       }
++                    }
++                 else {
++                    Skins.Message(mtError, tr("Wrong DVD!"), 3);
++                    Skins.Flush();
++                    }
++                 }
++              pclose(file);
++              }
++           else {
++              Skins.Message(mtError, tr("Wrong DVD!"), 3);
++              Skins.Flush();
++              }
++           }
++        else {
++           rep = false;
++           }
++        }
++  return MOUNT_DVD_ABORT;
++}
++
++#endif /* DVDARCHIVE */
++#ifdef USE_LIEMIEXT
++const char *cRecording::Title(char Delimiter, bool NewIndicator, int Level, bool Original) const
++#else
+ const char *cRecording::Title(char Delimiter, bool NewIndicator, int Level) const
++#endif /* LIEMIEXT */
+ {
++#ifdef USE_WAREAGLEICON
++  const char *New = NewIndicator && IsNew() ? Setup.WarEagleIcons ? IsLangUtf8() ? ICON_NEW_UTF8 : ICON_NEW : "*" : " ";
++#else
+   char New = NewIndicator && IsNew() ? '*' : ' ';
++#endif /* WAREAGLEICON */
+   free(titleBuffer);
+   titleBuffer = NULL;
+   if (Level < 0 || Level == HierarchyLevels()) {
+@@ -853,7 +1315,14 @@
+         s++;
+      else
+         s = name;
++#ifdef USE_LIEMIEXT
++     if (Original) {
++#endif /* LIEMIEXT */
++#ifdef USE_WAREAGLEICON
++     titleBuffer = strdup(cString::sprintf("%02d.%02d.%02d%c%02d:%02d%s%c%s",
++#else
+      titleBuffer = strdup(cString::sprintf("%02d.%02d.%02d%c%02d:%02d%c%c%s",
++#endif /* WAREAGLEICON */
+                             t->tm_mday,
+                             t->tm_mon + 1,
+                             t->tm_year % 100,
+@@ -863,6 +1332,80 @@
+                             New,
+                             Delimiter,
+                             s));
++#ifdef USE_LIEMIEXT
++        }
++     else {
++        cString RecLength("---");
++        if (Setup.ShowRecLength && FileName()) {
++           int length = cIndexFile::Length(FileName(), IsPesRecording());
++           if (length >= 0)
++              RecLength = cString::sprintf("%d'", length / SecondsToFrames(60, framesPerSecond));
++           }
++#endif /* LIEMIEXT */
++#ifdef USE_DVDARCHIVE
++#ifdef USE_WAREAGLEICON
++        if (isArchived && !isOnlyOnDvd) New = Setup.WarEagleIcons ? IsLangUtf8() ? ICON_DVD_UTF8 : ICON_DVD : "~";
++#else
++        if (isArchived && !isOnlyOnDvd) New = '~';
++#endif /* WAREAGLEICON */
++
++        if (isOnlyOnDvd && Setup.DvdDisplayMode >= 1) {
++           char oldLength[21];
++
++           if (strrchr(RecLength, '\''))
++              sprintf(oldLength,"%s", *RecLength);
++           else
++              oldLength[0] = 0;
++
++           if (dvdname) {
++              if (atoi(dvdname) != 0) {
++                 cString tmp;
++                 if (Setup.DvdDisplayZeros)
++                    tmp = cString::sprintf("%04d", atoi(dvdname));
++                 else {
++                    int num = atoi(dvdname);
++                    bool displaySpace = !(Setup.DvdDisplayMode == 1 && oldLength[0] != 0);
++                    // ugly hack to have 2 spaces instead of one 0 for each place
++                    tmp = cString::sprintf("%s%s%s%d", displaySpace && (num < 1000) ? "  " : "",
++                                                       displaySpace && (num < 100)  ? "  " : "",
++                                                       displaySpace && (num < 10)   ? "  " : "",
++                                                       num);
++                    }
++                 ((cRecording*)this)->dvdname = strdup(tmp);
++                 }
++              }
++
++           RecLength = strdup(cString::sprintf("%s%s%s%s", (Setup.ShowRecLength && (Setup.DvdDisplayMode == 1) && (oldLength[0] != 0)) ? oldLength : "",
++                                                           (dvdname && isArchived && isOnlyOnDvd && Setup.ShowRecLength && (Setup.DvdDisplayMode == 1) && (oldLength[0] != 0)) ? " / " : "",
++                                                           (dvdname && isArchived && isOnlyOnDvd) ? dvdname : "",
++                                                           (dvdname && isArchived && isOnlyOnDvd) ? " " : ""
++                                                           ));
++           }
++#endif /* DVDARCHIVE */
++#ifdef USE_LIEMIEXT
++        cString RecDate = cString::sprintf("%02d.%02d.%02d", t->tm_mday, t->tm_mon + 1, t->tm_year % 100);
++        cString RecTime = cString::sprintf("%02d:%02d", t->tm_hour, t->tm_min);
++        cString RecDelimiter = cString::sprintf("%c", Delimiter);
++#ifdef USE_WAREAGLEICON
++        titleBuffer = strdup(cString::sprintf("%s%s%s%s%s%s%s%s",
++#else
++        titleBuffer = strdup(cString::sprintf("%s%s%s%c%s%s%s%s",
++#endif /* WAREAGLEICON */
++                               (Setup.ShowRecDate ? *RecDate        : ""),
++                               (Setup.ShowRecDate && Setup.ShowRecTime ? *RecDelimiter : ""),
++                               (Setup.ShowRecTime ? *RecTime        : ""),
++                               New,
++                               (Setup.ShowRecTime || Setup.ShowRecDate ? *RecDelimiter : ""),
++#ifdef USE_DVDARCHIVE
++                               (((Setup.ShowRecLength + Setup.DvdDisplayMode) > 0) ? *RecLength    : ""),
++                               (((Setup.ShowRecLength + Setup.DvdDisplayMode) > 0) ? *RecDelimiter : ""),
++#else
++                               (Setup.ShowRecLength ? *RecLength    : ""),
++                               (Setup.ShowRecLength ? *RecDelimiter : ""),
++#endif /* DVDARCHIVE */
++                               s));
++        }
++#endif /* LIEMIEXT */
+      // let's not display a trailing '~':
+      if (!NewIndicator)
+         stripspace(titleBuffer);
+@@ -891,6 +1434,17 @@
+   return titleBuffer;
+ }
++#ifdef USE_CUTTIME
++void cRecording::SetStartTime(time_t Start) 
++{
++  start = Start;
++  if (fileName) {
++     free(fileName);
++     fileName = NULL;
++     }
++}
++#endif /* CUTTIME */
++
+ const char *cRecording::PrefixFileName(char Prefix)
+ {
+   cString p = PrefixVideoFileName(FileName(), Prefix);
+@@ -999,6 +1553,51 @@
+   resume = RESUME_NOT_INITIALIZED;
+ }
++#ifdef USE_LIEMIEXT
++bool cRecording::Rename(const char *newName)
++{
++  bool result = false;
++  struct tm tm_r;
++  struct tm *t = localtime_r(&start, &tm_r);
++  char *localNewName = ExchangeChars(strdup(newName), true);
++  const char *fmt = isPesRecording ? NAMEFORMATPES : NAMEFORMATTS;
++  int ch = isPesRecording ? priority : channel;
++  int ri = isPesRecording ? lifetime : instanceId;
++  char *newFileName = strdup(cString::sprintf(fmt, VideoDirectory, localNewName, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, ch, ri));
++  free(localNewName);
++  if (strcmp(FileName(), newFileName)) {
++     if (access(newFileName, F_OK) == 0) {
++        isyslog("recording %s already exists", newFileName);
++        }
++     else {
++        isyslog("renaming recording %s to %s", FileName(), newFileName);
++        result = MakeDirs(newFileName, true);
++        if (result)
++           result = RenameVideoFile(FileName(), newFileName);
++        if (result) {
++           free(fileName);
++           fileName = strdup(newFileName);
++           free(name);
++           name = strdup(newName);
++#ifdef USE_SORTRECORDS
++           for (int i = 0; i < MAXSORTMODES; i++) {
++              free(sortBuffer[i]);
++              sortBuffer[i] = NULL;
++           }
++#else
++           free(sortBuffer);
++           sortBuffer = NULL;
++#endif /* SORTRECORDS */
++           free(titleBuffer);
++           titleBuffer = NULL;
++           }
++        }
++     }
++  free(newFileName);
++  return result;
++}
++#endif /* LIEMIEXT */
++
+ // --- cRecordings -----------------------------------------------------------
+ cRecordings Recordings;
+@@ -1011,6 +1610,9 @@
+   deleted = Deleted;
+   lastUpdate = 0;
+   state = 0;
++#ifdef USE_SORTRECORDS
++  SortOrder = 1;
++#endif /* SORTRECORDS */
+ }
+ cRecordings::~cRecordings()
+@@ -1297,14 +1899,72 @@
+   return NULL;
+ }
++#ifdef USE_JUMPPLAY
++// --- cMarksReload ----------------------------------------------------------
++
++#define MARKS_RELOAD_MS 10000
++
++time_t cMarksReload::lastsavetime = 0;
++
++cMarksReload::cMarksReload(const char *RecordingFileName)
++:recDir(RecordingFileName)
++{
++  struct stat sbuf;
++  cRecording rec(recDir);
++  if (Load(recDir, rec.FramesPerSecond(), rec.IsPesRecording()) && stat(FileName(), &sbuf) == 0)
++     lastmodtime = sbuf.st_mtime;
++  else
++     lastmodtime = 0;
++  nextreload.Set(MARKS_RELOAD_MS - cTimeMs::Now() % MARKS_RELOAD_MS);
++}
++
++bool cMarksReload::Reload(void)
++{
++  // Check the timestamp of marks.vdr in 10 seconds intervals
++  // Independent but synchronized reloading of marks in two threads
++  if ((Setup.ReloadMarks && nextreload.TimedOut()) || lastsavetime > lastmodtime) {
++     nextreload.Set(MARKS_RELOAD_MS - cTimeMs::Now() % MARKS_RELOAD_MS);
++     struct stat sbuf;
++     if (stat(FileName(), &sbuf) == 0 && sbuf.st_mtime != lastmodtime) {
++        lastmodtime = sbuf.st_mtime;
++        cRecording rec(recDir);
++        if (Load(recDir, rec.FramesPerSecond(), rec.IsPesRecording()))
++           return true;
++        }
++     }
++  return false;
++}
++
++bool cMarksReload::Save(void)
++{
++  bool ok = cMarks::Save();
++  struct stat sbuf;
++  if (ok && stat(FileName(), &sbuf) == 0)
++     lastsavetime = lastmodtime = sbuf.st_mtime;
++  return ok;
++}
++#endif /* JUMPPLAY */
++
+ // --- cRecordingUserCommand -------------------------------------------------
+ const char *cRecordingUserCommand::command = NULL;
++#ifdef USE_DVLRECSCRIPTADDON
++void cRecordingUserCommand::InvokeCommand(const char *State, const char *RecordingFileName, char *chanName)
++#else
+ void cRecordingUserCommand::InvokeCommand(const char *State, const char *RecordingFileName)
++#endif /* DVLRECSCRIPTADDON */
+ {
+   if (command) {
++#ifdef USE_DVLRECSCRIPTADDON
++     cString cmd;
++     if (chanName != NULL)
++        cmd = cString::sprintf("%s %s \"%s\" \"%s\"", command, State, *strescape(RecordingFileName, "\\\"$"), chanName);
++     else
++        cmd = cString::sprintf("%s %s \"%s\"", command, State, *strescape(RecordingFileName, "\\\"$"));
++#else
+      cString cmd = cString::sprintf("%s %s \"%s\"", command, State, *strescape(RecordingFileName, "\\\"$"));
++#endif /* DVLRECSCRIPTADDON */
+      isyslog("executing '%s'", *cmd);
+      SystemExec(cmd);
+      }
+@@ -1746,6 +2406,17 @@
+      }
+ }
++#ifdef USE_LIEMIEXT
++int cIndexFile::Length(const char *FileName, bool IsPesRecording)
++{
++  struct stat buf;
++  cString fullname = cString::sprintf("%s%s", FileName, IsPesRecording ? INDEXFILESUFFIX ".vdr" : INDEXFILESUFFIX);
++  if (FileName && *fullname && access(fullname, R_OK) == 0 && stat(fullname, &buf) == 0)
++     return buf.st_size ? (buf.st_size - 1) / sizeof(tIndexTs) + 1 : 0;
++  return -1;
++}
++#endif /* LIEMIEXT */
++
+ // --- cFileName -------------------------------------------------------------
+ #define MAXFILESPERRECORDINGPES 255
+@@ -1775,6 +2446,48 @@
+ cFileName::~cFileName()
+ {
+   Close();
++#ifdef USE_DVDARCHIVE
++
++  char fn[BUFSIZ];
++  strcpy(fn, fileName);
++
++  char *p;
++  if((p = strrchr(fn, '/'))) {
++    p[0] = 0;
++  }
++
++  cString cmd = cString::sprintf("find \"%s\" -type l -lname \"%s/*\"", fn, DVD_MOUNT_PATH);
++
++  bool isOnDvd = false;
++
++  cReadLine pipe;
++  FILE* file;
++  char* filename;
++  if ((file = popen(cmd, "r")) != (FILE *)NULL) {
++     while ((filename = pipe.Read(file)) != NULL) {
++           isOnDvd = true;
++           unlink(filename);
++           isyslog("DVD-ARCHIVE: Deleting %s", filename);
++           }
++     pclose(file);
++     }
++
++  if (isOnDvd) {
++     if (Setup.DvdTrayMode==2 || Setup.DvdTrayMode==3)
++        cmd = cString::sprintf("umount %s; eject %s", DVD_DEVICE, DVD_DEVICE);
++     else
++        cmd = cString::sprintf("umount %s", DVD_DEVICE);
++
++     SystemExec(cmd);
++
++     if (Setup.DvdSpeedLimit > 0) {
++        cmd = cString::sprintf("speedcontrol -x 999 %s", DVD_DEVICE);
++        SystemExec(cmd);
++        }
++     }
++
++#endif /* DVDARCHIVE */
++
+   free(fileName);
+ }
+@@ -1904,6 +2617,22 @@
+   return NULL;
+ }
++#ifdef USE_HARDLINKCUTTER
++off_t cFileName::MaxFileSize() {
++  const int maxVideoFileSize = isPesRecording ? MAXVIDEOFILESIZEPES : MAXVIDEOFILESIZETS;
++  const int setupMaxVideoFileSize = min(maxVideoFileSize, Setup.MaxVideoFileSize);
++  const int maxFileNumber = isPesRecording ? 255 : 65535;
++
++  const off_t smallFiles = (maxFileNumber * off_t(maxVideoFileSize) - 1024 * Setup.MaxRecordingSize)
++                           / max(maxVideoFileSize - setupMaxVideoFileSize, 1);
++
++  if (fileNumber <= smallFiles)
++     return MEGABYTE(off_t(setupMaxVideoFileSize));
++
++  return MEGABYTE(off_t(maxVideoFileSize));
++}
++#endif /* HARDLINKCUTTER */
++
+ cUnbufferedFile *cFileName::NextFile(void)
+ {
+   return SetOffset(fileNumber + 1);
+@@ -1955,3 +2684,113 @@
+      LOG_ERROR;
+   return r;
+ }
++
++#ifdef USE_DVLFRIENDLYFNAMES
++char *MakeFriendlyFilename(char **buf)
++{
++  char *b, *x, *y;
++
++  if (buf == NULL || *buf == NULL)
++     return(NULL);
++
++  b = (char *)malloc(strlen(*buf) * 2);
++  x = *buf;
++  y = b;
++
++  while (*x != 0) {
++        switch (*x) {
++          case 'Ä':
++               *y = 'A';
++               y++;
++               *y = 'e';
++               y++; x++;
++               break;
++
++          case 'ä':
++               *y = 'a';
++               y++;
++               *y = 'e';
++               y++; x++;
++               break;
++
++          case 'Ö':
++               *y = 'O';
++               y++;
++               *y = 'e';
++               y++; x++;
++               break;
++
++          case 'ö':
++               *y = 'o';
++               y++;
++               *y = 'e';
++               y++; x++;
++               break;
++
++          case 'Ü':
++               *y = 'U';
++               y++;
++               *y = 'e';
++               y++; x++;
++               break;
++
++          case 'ü':
++               *y = 'u';
++               y++;
++               *y = 'e';
++               y++; x++;
++               break;
++
++          case 'ß':
++               *y = 's';
++               y++;
++               *y = 's';
++               y++; x++;
++               break;
++
++          // chars to replace
++          case ':':
++          case ';':
++          case '?':
++          case ' ':
++          case '\t':
++               *y = '_';
++               y++; x++;
++               break;
++
++          // chars to simply strip
++          case '\"':
++          case '*':
++          case '{':
++          case '}':
++          case '[':
++          case ']':
++          case '=':
++          case '<':
++          case '>':
++          case '#':
++          case '`':
++          case '|':
++          case '\\':
++          case '\n':
++          case '\r':
++               x++;
++               break;
++
++          default:
++               *y = *x;
++               y++; x++;
++               break;
++          }
++        }
++  *y = 0;
++
++  x = strdup(b);
++  free(b);
++
++  free(*buf);
++  *buf = x;
++
++  return(*buf);
++}
++#endif /* DVLFRIENDLYFNAMES */
+diff -NaurwB vdr-1.7.10/recording.h vdr-1.7.10-patched/recording.h
+--- vdr-1.7.10/recording.h     2009-11-21 17:12:55.000000000 +0100
++++ vdr-1.7.10-patched/recording.h     2009-12-18 06:25:25.000000000 +0100
+@@ -20,6 +20,9 @@
+ extern bool VfatFileSystem;
+ extern int InstanceId;
++#ifdef USE_LIEMIEXT
++extern bool DirOrderState;
++#endif /* LIEMIEXT */
+ void RemoveDeletedRecordings(void);
+ void AssertFreeDiskSpace(int Priority = 0, bool Force = false);
+@@ -63,6 +66,9 @@
+   const cEvent *GetEvent(void) const { return event; }
+   const char *Title(void) const { return event->Title(); }
+   const char *ShortText(void) const { return event->ShortText(); }
++#ifdef  USE_GRAPHTFT
++  tEventID EventID(void) const { return event->EventID(); }
++#endif  /* GRAPHTFT */
+   const char *Description(void) const { return event->Description(); }
+   const cComponents *Components(void) const { return event->Components(); }
+   const char *Aux(void) const { return aux; }
+@@ -74,12 +80,36 @@
+   bool Write(void) const;
+   };
++#ifdef USE_SORTRECORDS
++#define SORTRECORDINGSVERSNUM 3
++#define MAXSORTMODES 4
++#endif /* SORTRECORDS */
++
++#ifdef USE_DVDARCHIVE
++#define MOUNT_DVD_ABORT              0
++#define MOUNT_DVD_REPLAY             1
++#define MOUNT_DVD_LAUNCH_DVD_PLUGIN  2
++#define DVD_DEVICE                   "/dev/cdrom"
++#define DVD_MOUNT_PATH               "/tmp/vdr.dvd"
++
++#define DVD_TYPE_UNKNOWN            -1
++#define DVD_TYPE_NOT_READ            0
++#define DVD_VIDEO_TYPE               1
++#define DVD_ARCHIVE_TYPE             2
++#define DVD_VIDEO_ARCHIVE_TYPE       3
++#endif /* DVDARCHIVE */
++
+ class cRecording : public cListObject {
+   friend class cRecordings;
+ private:
+   mutable int resume;
+   mutable char *titleBuffer;
++#ifdef USE_SORTRECORDS
++  mutable char *sortBuffer[MAXSORTMODES];
++  mutable char lastDirsFirst[MAXSORTMODES];
++#else
+   mutable char *sortBuffer;
++#endif /* SORTRECORDS */
+   mutable char *fileName;
+   mutable char *name;
+   mutable int fileSizeMB;
+@@ -88,6 +118,15 @@
+   bool isPesRecording;
+   double framesPerSecond;
+   cRecordingInfo *info;
++#ifdef USE_DVDARCHIVE
++  char *dvdname;
++  char *dvdtrack;
++  char *dvdchapters;
++  bool isArchived;
++  bool isOnlyOnDvd;
++  int dvdtype;
++  bool GetDvdChaptersFromDvd(int title) const;
++#endif /* DVDARCHIVE */
+   cRecording(const cRecording&); // can't copy cRecording
+   cRecording &operator=(const cRecording &); // can't assign cRecording
+   static char *StripEpisodeName(char *s);
+@@ -104,8 +143,23 @@
+   virtual int Compare(const cListObject &ListObject) const;
+   const char *Name(void) const { return name; }
+   const char *FileName(void) const;
++#ifdef USE_DVDARCHIVE
++  bool CheckFileExistence(const char* FileNameToTest, const bool useVideoDir = true) const;
++  bool GetDvdName(const char* Directory) const;
++  bool IsOnlyOnDvd(void) const { return isOnlyOnDvd; }
++  int MountDvd(void) const;
++  int GetDvdType(void) const { return dvdtype; }
++  const char *GetDvdChapters(void) const ;
++#endif /* DVDARCHIVE */
++#ifdef USE_LIEMIEXT
++  const char *Title(char Delimiter = ' ', bool NewIndicator = false, int Level = -1, bool Original = true) const;
++#else
+   const char *Title(char Delimiter = ' ', bool NewIndicator = false, int Level = -1) const;
++#endif /* LIEMIEXT */
+   const cRecordingInfo *Info(void) const { return info; }
++#ifdef USE_CUTTIME
++  void SetStartTime(time_t Start);
++#endif /* CUTTIME */
+   const char *PrefixFileName(char Prefix);
+   int HierarchyLevels(void) const;
+   void ResetResume(void) const;
+@@ -124,6 +178,11 @@
+        // Changes the file name so that it will be visible in the "Recordings" menu again and
+        // not processed by cRemoveDeletedRecordingsThread.
+        // Returns false in case of error
++#ifdef USE_LIEMIEXT
++  bool Rename(const char *newName);
++       // Changes the file name
++       // Returns false in case of error
++#endif /* LIEMIEXT */
+   };
+ class cRecordings : public cList<cRecording>, public cThread {
+@@ -132,6 +191,9 @@
+   bool deleted;
+   time_t lastUpdate;
+   int state;
++#ifdef USE_SORTRECORDS
++  int SortOrder;
++#endif /* SORTRECORDS */
+   const char *UpdateFileName(void);
+   void Refresh(bool Foreground = false);
+   void ScanVideoDir(const char *DirName, bool Foreground = false, int LinkLevel = 0);
+@@ -162,6 +224,10 @@
+   void AddByName(const char *FileName, bool TriggerUpdate = true);
+   void DelByName(const char *FileName);
+   int TotalFileSizeMB(void); ///< Only for deleted recordings!
++#ifdef USE_SORTRECORDS
++  void ToggleSortOrder(void) { SortOrder *= -1; }
++  const int GetSortOrder(void) { return SortOrder; }
++#endif /* SORTRECORDS */
+   };
+ extern cRecordings Recordings;
+@@ -194,6 +260,20 @@
+   cMark *GetNext(int Position);
+   };
++#ifdef USE_JUMPPLAY
++class cMarksReload : public cMarks {
++private:
++  cString recDir;
++  cTimeMs nextreload;
++  time_t lastmodtime;
++  static time_t lastsavetime;
++public:
++  cMarksReload(const char *RecordingFileName);
++  bool Reload(void);
++  bool Save(void);
++  };
++#endif /* JUMPPLAY */
++
+ #define RUC_BEFORERECORDING "before"
+ #define RUC_AFTERRECORDING  "after"
+ #define RUC_EDITEDRECORDING "edited"
+@@ -202,8 +282,15 @@
+ private:
+   static const char *command;
+ public:
++#ifdef USE_DELTIMESHIFTREC
++  static const char *GetCommand(void) { return command; }
++#endif /* DELTIMESHIFTREC */
+   static void SetCommand(const char *Command) { command = Command; }
++#ifdef USE_DVLRECSCRIPTADDON
++  static void InvokeCommand(const char *State, const char *RecordingFileName, char *chanName = NULL);
++#else
+   static void InvokeCommand(const char *State, const char *RecordingFileName);
++#endif /* DVLRECSCRIPTADDON */
+   };
+ // The maximum size of a single frame (up to HDTV 1920x1080):
+@@ -216,9 +303,23 @@
+ // before the next independent frame, to have a complete Group Of Pictures):
+ #define MAXVIDEOFILESIZETS  1048570 // MB
+ #define MAXVIDEOFILESIZEPES    2000 // MB
++#ifdef USE_HARDLINKCUTTER
++#define MINVIDEOFILESIZE          1 // MB
++#else
+ #define MINVIDEOFILESIZE        100 // MB
++#endif /* HARDLINKCUTTER */
+ #define MAXVIDEOFILESIZEDEFAULT MAXVIDEOFILESIZEPES
++#ifdef USE_HARDLINKCUTTER
++#define MINRECORDINGSIZE      25 // GB
++#define MAXRECORDINGSIZE     500 // GB
++#define DEFAULTRECORDINGSIZE 100 // GB
++// Dynamic recording size:
++// Keep recording file size at Setup.MaxVideoFileSize for as long as possible,
++// but switch to MAXVIDEOFILESIZE early enough, so that Setup.MaxRecordingSize
++// will be reached, before recording to file 255.vdr
++#endif /* HARDLINKCUTTER */
++
+ struct tIndexTs;
+ class cIndexFileGenerator;
+@@ -235,6 +336,9 @@
+   void ConvertFromPes(tIndexTs *IndexTs, int Count);
+   void ConvertToPes(tIndexTs *IndexTs, int Count);
+   bool CatchUp(int Index = -1);
++#ifdef USE_DVDARCHIVE
++  bool isOnDVD;
++#endif /* DVDARCHIVE */
+ public:
+   cIndexFile(const char *FileName, bool Record, bool IsPesRecording = false);
+   ~cIndexFile();
+@@ -248,6 +352,10 @@
+   bool StoreResume(int Index) { return resumeFile.Save(Index); }
+   bool IsStillRecording(void);
+   void Delete(void);
++ #ifdef USE_LIEMIEXT
++   static int Length(const char *FileName, bool IsPesRecording = false);
++        ///< Calculates the recording length without reading the index.
++#endif /* LIEMIEXT */
+   };
+ class cFileName {
+@@ -267,6 +375,9 @@
+   cUnbufferedFile *Open(void);
+   void Close(void);
+   cUnbufferedFile *SetOffset(int Number, off_t Offset = 0);
++#ifdef USE_HARDLINKCUTTER
++  off_t MaxFileSize();  // Dynamic file size for this file
++#endif /* HARDLINKCUTTER */
+   cUnbufferedFile *NextFile(void);
+   };
+diff -NaurwB vdr-1.7.10/remux.c vdr-1.7.10-patched/remux.c
+--- vdr-1.7.10/remux.c 2009-11-22 12:23:27.000000000 +0100
++++ vdr-1.7.10-patched/remux.c 2009-12-18 06:25:25.000000000 +0100
+@@ -215,6 +215,32 @@
+   return i;
+ }
++#ifdef USE_TTXTSUBS
++int cPatPmtGenerator::MakeTeletextDescriptor(uchar *Target, cChannel *Channel)
++{
++  int i = 0, j = 0;
++  Target[i++] = SI::TeletextDescriptorTag;
++  int l = i;
++  Target[i++] = 0x00; // length
++  for (int n = 0; Channel->TPages(n); n++) {
++      const char *Language = Channel->Tlang(n);
++      int Pages = Channel->TPages(n);
++      Target[i++] = *Language++;
++      Target[i++] = *Language++;
++      Target[i++] = *Language++;
++      Target[i++] = ((Pages >> 13) & 0xf8) | ((Pages >> 8) & 0x7); // teletext type & magazine number
++      Target[i++] = Pages & 0xff; // teletext page number
++      j++;
++      }
++  if (j > 0) {
++     Target[l] = j * 5; // update length
++     IncEsInfoLength(i);
++     return i;
++     }
++  return 0;
++}
++#endif /* TTXTSUBS */
++
+ int cPatPmtGenerator::MakeLanguageDescriptor(uchar *Target, const char *Language)
+ {
+   int i = 0;
+@@ -295,6 +321,9 @@
+   numPmtPackets = 0;
+   if (Channel) {
+      int Vpid = Channel->Vpid();
++#ifdef USE_TTXTSUBS
++     int Tpid = Channel->Tpid();
++#endif /* TTXTSUBS */
+      int Ppid = Channel->Ppid();
+      uchar *p = buf;
+      int i = 0;
+@@ -330,6 +359,12 @@
+          i += MakeStream(buf + i, 0x06, Channel->Spid(n));
+          i += MakeSubtitlingDescriptor(buf + i, Channel->Slang(n), Channel->SubtitlingType(n), Channel->CompositionPageId(n), Channel->AncillaryPageId(n));
+          }
++#ifdef USE_TTXTSUBS
++     if (Tpid) {
++        i += MakeStream(buf + i, 0x06, Tpid);
++        i += MakeTeletextDescriptor(buf + i, Channel);
++        }
++#endif /* TTXTSUBS */
+      int sl = i - SectionLength - 2 + 4; // -2 = SectionLength storage, +4 = length of CRC
+      buf[SectionLength] |= (sl >> 8) & 0x0F;
+@@ -402,6 +437,9 @@
+   patVersion = pmtVersion = -1;
+   pmtPid = -1;
+   vpid = vtype = 0;
++#ifdef USE_TTXTSUBS
++  tpid = 0;
++#endif /* TTXTSUBS */
+ }
+ void cPatPmtParser::ParsePat(const uchar *Data, int Length)
+@@ -486,6 +524,9 @@
+      int NumDpids = 0;
+      int NumSpids = 0;
+      vpid = vtype = 0;
++#ifdef USE_TTXTSUBS
++     tpid = 0;
++#endif /* TTXTSUBS */
+      SI::PMT::Stream stream;
+      for (SI::Loop::Iterator it; Pmt.streamLoop.getNext(stream, it); ) {
+          dbgpatpmt("     stream type = %02X, pid = %d", stream.getStreamType(), stream.getPid());
+@@ -566,6 +607,12 @@
+                                     NumSpids++;
+                                     }
+                                  break;
++#ifdef USE_TTXTSUBS
++                            case SI::TeletextDescriptorTag:
++                                 dbgpatpmt(" teletext");
++                                 tpid = stream.getPid();
++                                 break;
++#endif /* TTXTSUBS */
+                             case SI::ISO639LanguageDescriptorTag: {
+                                  SI::ISO639LanguageDescriptor *ld = (SI::ISO639LanguageDescriptor *)d;
+                                  dbgpatpmt(" '%s'", ld->languageCode);
+diff -NaurwB vdr-1.7.10/remux.h vdr-1.7.10-patched/remux.h
+--- vdr-1.7.10/remux.h 2009-11-21 16:55:34.000000000 +0100
++++ vdr-1.7.10-patched/remux.h 2009-12-18 06:25:25.000000000 +0100
+@@ -170,6 +170,9 @@
+   int MakeStream(uchar *Target, uchar Type, int Pid);
+   int MakeAC3Descriptor(uchar *Target);
+   int MakeSubtitlingDescriptor(uchar *Target, const char *Language, uchar SubtitlingType, uint16_t CompositionPageId, uint16_t AncillaryPageId);
++#ifdef USE_TTXTSUBS
++  int MakeTeletextDescriptor(uchar *Target, cChannel *Channel);
++#endif /* TTXTSUBS */
+   int MakeLanguageDescriptor(uchar *Target, const char *Language);
+   int MakeCRC(uchar *Target, const uchar *Data, int Length);
+   void GeneratePmtPid(cChannel *Channel);
+@@ -215,6 +218,9 @@
+   int vpid;
+   int vtype;
+   bool updatePrimaryDevice;
++#ifdef USE_TTXTSUBS
++  int tpid;
++#endif /* TTXTSUBS */
+ protected:
+   int SectionLength(const uchar *Data, int Length) { return (Length >= 3) ? ((int(Data[1]) & 0x0F) << 8)| Data[2] : 0; }
+ public:
+diff -NaurwB vdr-1.7.10/skinclassic.c vdr-1.7.10-patched/skinclassic.c
+--- vdr-1.7.10/skinclassic.c   2008-02-23 11:31:58.000000000 +0100
++++ vdr-1.7.10-patched/skinclassic.c   2009-12-18 06:25:25.000000000 +0100
+@@ -314,8 +314,52 @@
+   for (int i = 0; i < MaxTabs; i++) {
+       const char *s = GetTabbedText(Text, i);
+       if (s) {
++#ifdef USE_LIEMIEXT
++         bool isprogressbar = false;
++         int now = 0, total = 0;
++         // check if progress bar: "[|||||||   ]"
++         if ((strlen(s) > 5 && s[0] == '[' && s[strlen(s) - 1] == ']')) {
++            const char *p = s + 1;
++            // update status
++            isprogressbar = true;
++            for (; *p != ']'; ++p) {
++                // check if progressbar characters
++                if (*p == ' ' || *p == '|') {
++                   // update counters
++                   ++total;
++                   if (*p == '|')
++                      ++now;
++                   }
++                else {
++                   // wrong character detected; not a progressbar
++                   isprogressbar = false;
++                   break;
++                   }
++                }
++            }
++         int xt = x0 + Tab(i);
++         if (Setup.ShowProgressBar && isprogressbar) {
++            // define x coordinates of progressbar
++            int px0 = xt;
++            int px1 = (Tab(i + 1)?Tab(i+1):x1) - 5;
++            int px = px0 + max((int)((float) now * (float) (px1 - px0) / (float) total), 1);
++            // define y coordinates of progressbar
++            int py0 = y + 4;
++            int py1 = y + lineHeight - 4;
++            // draw background
++            osd->DrawRectangle(px0, y, (Tab(i + 1)?Tab(i+1):x1) - 1, y + lineHeight - 1, ColorBg);
++            // draw progressbar
++            osd->DrawRectangle(px0,    py0, px,  py1, ColorFg);
++            osd->DrawRectangle(px + 1, py0, px1, py0 + 1, ColorFg);
++            osd->DrawRectangle(px + 1, py1 - 1, px1, py1, ColorFg);
++            osd->DrawRectangle(px1 - 1, py0, px1, py1, ColorFg);
++            }
++         else
++            osd->DrawText(xt, y, s, ColorFg, ColorBg, font, x2 - xt);
++#else
+          int xt = x0 + Tab(i);
+          osd->DrawText(xt, y, s, ColorFg, ColorBg, font, x2 - xt);
++#endif /* LIEMIEXT */
+          }
+       if (!Tab(i + 1))
+          break;
+diff -NaurwB vdr-1.7.10/skins.c vdr-1.7.10-patched/skins.c
+--- vdr-1.7.10/skins.c 2009-06-06 17:12:31.000000000 +0200
++++ vdr-1.7.10-patched/skins.c 2009-12-18 06:37:57.000000000 +0100
+@@ -238,7 +238,11 @@
+      }
+   cSkinDisplay::Current()->SetMessage(Type, s);
+   cSkinDisplay::Current()->Flush();
++#ifdef USE_STATUS_EXTENSION
++  cStatus::MsgOsdStatusMessage(Type, s);
++#else
+   cStatus::MsgOsdStatusMessage(s);
++#endif
+   eKeys k = kNone;
+   if (Type != mtStatus) {
+      k = Interface->Wait(Seconds);
+@@ -249,7 +253,11 @@
+         }
+      else {
+         cSkinDisplay::Current()->SetMessage(Type, NULL);
++#ifdef USE_STATUS_EXTENSION
++        cStatus::MsgOsdStatusMessage(Type, NULL);
++#else
+         cStatus::MsgOsdStatusMessage(NULL);
++#endif
+         }
+      }
+   else if (!s && displayMessage) {
+diff -NaurwB vdr-1.7.10/skinsttng.c vdr-1.7.10-patched/skinsttng.c
+--- vdr-1.7.10/skinsttng.c     2008-02-23 11:23:44.000000000 +0100
++++ vdr-1.7.10-patched/skinsttng.c     2009-12-18 06:25:25.000000000 +0100
+@@ -558,8 +558,52 @@
+   for (int i = 0; i < MaxTabs; i++) {
+       const char *s = GetTabbedText(Text, i);
+       if (s) {
++#ifdef USE_LIEMIEXT
++         bool isprogressbar = false;
++         int now = 0, total = 0;
++         // check if progress bar: "[|||||||   ]"
++         if ((strlen(s) > 5 && s[0] == '[' && s[strlen(s) - 1] == ']')) {
++            const char *p = s + 1;
++            // update status
++            isprogressbar = true;
++            for (; *p != ']'; ++p) {
++                // check if progressbar characters
++                if (*p == ' ' || *p == '|') {
++                   // update counters
++                   ++total;
++                   if (*p == '|')
++                      ++now;
++                   }
++                else {
++                   // wrong character detected; not a progressbar
++                   isprogressbar = false;
++                   break;
++                   }
++                }
++            }
+          int xt = x3 + 5 + Tab(i);
++         if (Setup.ShowProgressBar && isprogressbar) {
++            // define x coordinates of progressbar
++            int px0 = xt;
++            int px1 = x3 + (Tab(i + 1)?Tab(i + 1):x4-x3-5) - 1;
++            int px = px0 + max((int)((float) now * (float) (px1 - px0) / (float) total), 1);
++            // define y coordinates of progressbar
++            int py0 = y + 4;
++            int py1 = y + lineHeight - 4;
++            // draw background
++            osd->DrawRectangle(px0, y, (Tab(i + 1)?Tab(i + 1):x4-x3-5) - 1, y + lineHeight - 1, ColorBg);
++            // draw progressbar
++            osd->DrawRectangle(px0,    py0, px,  py1, ColorFg);
++            osd->DrawRectangle(px + 1, py0, px1, py0 + 1, ColorFg);
++            osd->DrawRectangle(px + 1, py1 - 1, px1, py1, ColorFg);
++            osd->DrawRectangle(px1 - 1, py0, px1, py1, ColorFg);
++            }
++         else
+          osd->DrawText(xt, y, s, ColorFg, ColorBg, font, x4 - xt);
++#else
++         int xt = x3 + 5 + Tab(i);
++         osd->DrawText(xt, y, s, ColorFg, ColorBg, font, x4 - xt);
++#endif /* LIEMIEXT */
+          }
+       if (!Tab(i + 1))
+          break;
+@@ -602,6 +646,21 @@
+      y += ts.Height();
+      }
+   y += font->Height();
++#ifdef USE_PARENTALRATING
++  if (!isempty(Event->GetParentalRatingString())) {
++     const cFont *font = cFont::GetFont(fontSml);
++     ts.Set(osd, xl, y, x4 - xl, y4 - y, Event->GetParentalRatingString(), font, Theme.Color(clrMenuEventShortText), Theme.Color(clrBackground));
++     y += ts.Height();
++     }
++  int i = 0;
++  while (Event->Contents(i++)) {
++        if (!isempty(Event->GetContentsString())) {
++           const cFont *font = cFont::GetFont(fontSml);
++           ts.Set(osd, xl, y, x4 - xl, y4 - y, Event->GetContentsString(), font, Theme.Color(clrMenuEventShortText), Theme.Color(clrBackground));
++           y += ts.Height();
++           }
++        }
++#endif /* PARENTALRATING */
+   if (!isempty(Event->Description())) {
+      int yt = y;
+      int yb = y4 - Roundness;
+diff -NaurwB vdr-1.7.10/sources.c vdr-1.7.10-patched/sources.c
+--- vdr-1.7.10/sources.c       2008-02-10 15:07:26.000000000 +0100
++++ vdr-1.7.10-patched/sources.c       2009-12-18 06:25:25.000000000 +0100
+@@ -37,6 +37,9 @@
+   char buffer[16];
+   char *q = buffer;
+   switch (Code & st_Mask) {
++#ifdef USE_PLUGINPARAM
++    case stPlug:  *q++ = 'P'; break;
++#endif /* PLUGINPARAM */
+     case stCable: *q++ = 'C'; break;
+     case stSat:   *q++ = 'S';
+                   {
+@@ -56,6 +59,9 @@
+ {
+   int type = stNone;
+   switch (toupper(*s)) {
++#ifdef USE_PLUGINPARAM
++    case 'P': type = stPlug;  break;
++#endif /* PLUGINPARAM */
+     case 'C': type = stCable; break;
+     case 'S': type = stSat;   break;
+     case 'T': type = stTerr;  break;
+@@ -68,7 +74,11 @@
+      int pos = 0;
+      bool dot = false;
+      bool neg = false;
++#ifdef USE_SOURCECAPS
++     while (*++s && !isblank(*s)) {
++#else
+      while (*++s) {
++#endif /* SOURCECAPS */
+            switch (toupper(*s)) {
+              case '0' ... '9': pos *= 10;
+                                pos += *s - '0';
+diff -NaurwB vdr-1.7.10/sources.conf vdr-1.7.10-patched/sources.conf
+--- vdr-1.7.10/sources.conf    2009-10-18 16:08:53.000000000 +0200
++++ vdr-1.7.10-patched/sources.conf    2009-12-18 06:25:25.000000000 +0100
+@@ -194,3 +194,7 @@
+ # Terrestrial
+ T       Terrestrial
++
++# Plugin PLUGINPARAM
++
++#P       Plugin
+diff -NaurwB vdr-1.7.10/sources.h vdr-1.7.10-patched/sources.h
+--- vdr-1.7.10/sources.h       2005-05-14 11:30:41.000000000 +0200
++++ vdr-1.7.10-patched/sources.h       2009-12-18 06:25:25.000000000 +0100
+@@ -16,10 +16,17 @@
+ public:
+   enum eSourceType {
+     stNone  = 0x0000,
++#ifdef USE_PLUGINPARAM
++    stPlug  = 0x2000,
++#endif /* PLUGINPARAM */
+     stCable = 0x4000,
+     stSat   = 0x8000,
+     stTerr  = 0xC000,
++#ifdef USE_PLUGINPARAM
++    st_Mask = 0xE000,
++#else
+     st_Mask = 0xC000,
++#endif /* PLUGINPARAM */
+     st_Neg  = 0x0800,
+     st_Pos  = 0x07FF,
+     };
+@@ -35,6 +42,9 @@
+   static cString ToString(int Code);
+   static int FromString(const char *s);
+   static int FromData(eSourceType SourceType, int Position = 0, bool East = false);
++#ifdef USE_PLUGINPARAM
++  static bool IsPlug(int Code) { return (Code & st_Mask) == stPlug; }
++#endif /* PLUGINPARAM */
+   static bool IsCable(int Code) { return (Code & st_Mask) == stCable; }
+   static bool IsSat(int Code) { return (Code & st_Mask) == stSat; }
+   static bool IsTerr(int Code) { return (Code & st_Mask) == stTerr; }
+diff -NaurwB vdr-1.7.10/status.c vdr-1.7.10-patched/status.c
+--- vdr-1.7.10/status.c        2008-02-16 15:46:31.000000000 +0100
++++ vdr-1.7.10-patched/status.c        2009-12-18 06:37:57.000000000 +0100
+@@ -83,11 +83,22 @@
+       sm->OsdTitle(Title);
+ }
++#ifdef USE_STATUS_EXTENSION
++void cStatus::MsgOsdStatusMessage(eMessageType type, const char *Message)
++{
++  for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++  {
++      sm->OsdStatusMessage(type, Message);
++      sm->OsdStatusMessage(Message); // For comaptibilty
++  }
++}
++#else
+ void cStatus::MsgOsdStatusMessage(const char *Message)
+ {
+   for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
+       sm->OsdStatusMessage(Message);
+ }
++#endif
+ void cStatus::MsgOsdHelpKeys(const char *Red, const char *Green, const char *Yellow, const char *Blue)
+ {
+@@ -124,3 +135,88 @@
+   for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
+       sm->OsdProgramme(PresentTime, PresentTitle, PresentSubtitle, FollowingTime, FollowingTitle, FollowingSubtitle);
+ }
++
++#ifdef USE_PINPLUGIN
++bool cStatus::MsgChannelProtected(const cDevice* Device, const cChannel* Channel)
++{
++  for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++      if (sm->ChannelProtected(Device, Channel) == true)
++         return true;
++      return false;
++}
++
++bool cStatus::MsgReplayProtected(const cRecording* Recording, const char* Name, const char* Base, bool isDirectory, int menuView)
++{
++  for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++      if (sm->ReplayProtected(Recording, Name, Base, isDirectory, menuView) == true)
++         return true;
++      return false;
++}
++
++void cStatus::MsgRecordingFile(const char* FileName)
++{
++  for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++      sm->RecordingFile(FileName);
++}
++
++void cStatus::MsgTimerCreation(cTimer* Timer, const cEvent *Event)
++{
++  for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++      sm->TimerCreation(Timer, Event);
++}
++
++bool cStatus::MsgPluginProtected(cPlugin* Plugin, int menuView)
++{
++  for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++      if (sm->PluginProtected(Plugin, menuView) == true)
++         return true;
++      return false;
++}
++
++void cStatus::MsgUserAction(const eKeys key, const cOsdObject* Interact)
++{
++  for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++      sm->UserAction(key, Interact);
++}
++
++bool cStatus::MsgMenuItemProtected(const char* Name, int menuView)
++{
++  for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++      if (sm->MenuItemProtected(Name, menuView) == true)
++         return true;
++      return false;
++}
++#endif /* PINPLUGIN */
++
++#ifdef USE_GRAPHTFT
++void cStatus::MsgOsdSetEvent(const cEvent* event)
++{
++  for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++      sm->OsdSetEvent(event);
++}
++
++void cStatus::MsgOsdSetRecording(const cRecording* recording)
++{
++  for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++      sm->OsdSetRecording(recording);
++}
++
++void cStatus::MsgOsdMenuDisplay(const char* kind)
++{
++  for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++      sm->OsdMenuDisplay(kind);
++}
++
++void cStatus::MsgOsdMenuDestroy()
++{
++  for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++      sm->OsdMenuDestroy();
++}
++
++void cStatus::MsgOsdEventItem(const cEvent* Event, const char *Text, int Index, int Count)
++{
++  for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++      sm->OsdEventItem(Event, Text, Index, Count);
++}
++#endif /* GRAPHTFT */
++
+diff -NaurwB vdr-1.7.10/status.h vdr-1.7.10-patched/status.h
+--- vdr-1.7.10/status.h        2008-02-16 16:00:33.000000000 +0100
++++ vdr-1.7.10-patched/status.h        2009-12-18 06:37:57.000000000 +0100
+@@ -14,6 +14,13 @@
+ #include "device.h"
+ #include "player.h"
+ #include "tools.h"
++#ifdef USE_PINPLUGIN
++#include "plugin.h"
++#endif /* PINPLUGIN */
++#ifdef USE_STATUS_EXTENSION
++#include "skins.h"
++#endif /* STATUS_EXTENSION */
++
+ enum eTimerChange { tcMod, tcAdd, tcDel };
+@@ -64,6 +71,11 @@
+   virtual void OsdStatusMessage(const char *Message) {}
+                // Message has been displayed in the status line of the menu.
+                // If Message is NULL, the status line has been cleared.
++#ifdef USE_STATUS_EXTENSION
++  virtual void OsdStatusMessage(eMessageType type, const char *Message) {}
++               // Message has been displayed in the status line of the menu.
++               // If Message is NULL, the status line has been cleared.
++#endif
+   virtual void OsdHelpKeys(const char *Red, const char *Green, const char *Yellow, const char *Blue) {}
+                // The help keys have been set to the given values (may be NULL).
+   virtual void OsdItem(const char *Text, int Index) {}
+@@ -80,6 +92,37 @@
+                // The OSD displays the single line Text with the current channel information.
+   virtual void OsdProgramme(time_t PresentTime, const char *PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle) {}
+                // The OSD displays the given programme information.
++#ifdef USE_PINPLUGIN
++  virtual bool ChannelProtected(const cDevice *Device, const cChannel* Channel)  { return false; }
++               // Checks if a channel is protected.
++  virtual bool ReplayProtected(const cRecording* Recording, const char* Name, const char* Base, bool isDirectory, int menuView = false) { return false; }
++               // Checks if a recording is protected.
++  virtual void RecordingFile(const char* FileName) {}
++               // The given DVB device has started recording to FileName. FileName is the name of the
++               // recording directory
++  virtual void TimerCreation(cTimer* Timer, const cEvent *Event) {}
++               // The given timer is created
++  virtual bool PluginProtected(cPlugin* Plugin, int menuView = false)  { return false; }
++               // Checks if a plugin is protected.
++  virtual void UserAction(const eKeys key, const cOsdObject* Interact) {}
++               // report user action
++  virtual bool MenuItemProtected(const char* Name, int menuView = false)  { return false; }
++               // Checks if a menu entry is protected.
++#endif /* PINPLUGIN */
++#ifdef USE_GRAPHTFT
++  virtual void OsdSetRecording(const cRecording* recording) {}
++               // The OSD displays the recording information.
++  virtual void OsdSetEvent(const cEvent* event) {}
++               // The OSD displays the event information.
++  virtual void OsdMenuDisplay(const char* kind) {}
++               // report menu creation
++  virtual void OsdMenuDestroy() {}
++               // report menu destruvtion
++  virtual void OsdEventItem(const cEvent* Event, const char *Text, int Index, int Count) {}
++               // The OSD displays the given single line Event as menu item at Index.
++
++#endif /* GRAPHTFT */
++
+ public:
+   cStatus(void);
+   virtual ~cStatus();
+@@ -94,13 +137,33 @@
+   static void MsgSetSubtitleTrack(int Index, const char * const *Tracks);
+   static void MsgOsdClear(void);
+   static void MsgOsdTitle(const char *Title);
++#ifdef USE_STATUS_EXTENSION
++  static void MsgOsdStatusMessage(eMessageType type, const char *Message);
++#else
+   static void MsgOsdStatusMessage(const char *Message);
++#endif
+   static void MsgOsdHelpKeys(const char *Red, const char *Green, const char *Yellow, const char *Blue);
+   static void MsgOsdItem(const char *Text, int Index);
+   static void MsgOsdCurrentItem(const char *Text);
+   static void MsgOsdTextItem(const char *Text,  bool Scroll = false);
+   static void MsgOsdChannel(const char *Text);
+   static void MsgOsdProgramme(time_t PresentTime, const char *PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle);
++#ifdef USE_PINPLUGIN
++  static bool MsgChannelProtected(const cDevice* Device, const cChannel* Channel);
++  static bool MsgReplayProtected(const cRecording* Recording, const char* Name, const char* Base, bool isDirectory, int menuView = false);
++  static void MsgRecordingFile(const char* FileName);
++  static void MsgTimerCreation(cTimer* Timer, const cEvent *Event);
++  static bool MsgPluginProtected(cPlugin* Plugin, int menuView = false);
++  static void MsgUserAction(const eKeys key, const cOsdObject* Interact);
++  static bool MsgMenuItemProtected(const char* Name, int menuView = false);
++#endif /* PINPLUGIN */
++#ifdef USE_GRAPHTFT
++  static void MsgOsdSetEvent(const cEvent* event);
++  static void MsgOsdSetRecording(const cRecording* recording);
++  static void MsgOsdMenuDisplay(const char* kind);
++  static void MsgOsdMenuDestroy();
++  static void MsgOsdEventItem(const cEvent* Event, const char *Text, int Index, int Count);
++#endif /* GRAPHTFT */
+   };
+ #endif //__STATUS_H
+diff -NaurwB vdr-1.7.10/submenu.c vdr-1.7.10-patched/submenu.c
+--- vdr-1.7.10/submenu.c       1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.10-patched/submenu.c       2009-12-18 06:25:25.000000000 +0100
+@@ -0,0 +1,949 @@
++#ifdef USE_SETUP
++/****************************************************************************
++ * DESCRIPTION:
++ *             Submenu
++ *
++ * $Id: vdr-1.3.44-Setup-0.3.0.diff,v 1.1 2006/03/04 09:58:47 ralf Exp $
++ *
++ * Contact:    ranga@teddycats.de
++ *
++ * Copyright (C) 2004, 2005 by Ralf Dotzert
++ *
++ * modified for the VDR Extensions Patch by zulu @vdr-portal
++ ****************************************************************************/
++
++#ifndef SUBMENU_H
++#include "submenu.h"
++#include "plugin.h"
++#ifdef USE_WAREAGLEICON
++#include "iconpatch.h"
++#endif /* WAREAGLEICON */
++
++static const char* TAG_SYSTEM      = "system";
++static const char* TAG_PLUGIN      = "plugin";
++static const char* TAG_COMMAND     = "command";
++static const char* TAG_THREAD      = "thread";
++static const char* TAG_MENU        = "menu";
++static const char* TAG_UNDEFINED   = "undefined";
++static const char* TRUE_STR        = "yes";
++
++
++//################################################################################
++//# SubMenuNode
++//################################################################################
++
++cSubMenuNode::cSubMenuNode(TiXmlElement *xml, int level,  cSubMenuNodes *currentMenu, cSubMenuNodes *parentMenu)
++{
++  init();
++  _parentMenu  = parentMenu;
++  _currentMenu = currentMenu;
++  _level       = level;
++
++  if (xml != NULL && xml->Type() == TiXmlNode::ELEMENT) {
++     const char *tag = xml->Value();
++
++     if (cSubMenuNode::IsType(tag) != cSubMenuNode::UNDEFINED) {
++        SetType(tag);
++        SetName(xml->Attribute("name"));
++        if ((_type == COMMAND) || (_type == THREAD)) {
++           SetCommand(xml->Attribute("execute"));
++           const char *confirmStr = xml->Attribute("confirm");
++           if (confirmStr != NULL && strcmp(confirmStr, TRUE_STR) == 0)
++              _commandConfirm = true;
++           }
++        else if (_type == PLUGIN) { // Add Plugin Index
++           SetCustomTitle(xml->Attribute("title"));
++           SetPlugin();
++           }
++        else if (_type == MENU && xml->NoChildren() == false) {
++           xml = xml->FirstChildElement();
++           do {
++              cSubMenuNode *node = new cSubMenuNode(xml, level+1, &_subMenus, currentMenu);
++              _subMenus.Add(node);
++              } while ((xml=xml->NextSiblingElement()) != NULL);
++           }
++        }
++     }
++  else
++     throw "Invalid XML Node";
++}
++
++/**
++ * Construct new Node empty Node
++ *
++ *
++ */
++cSubMenuNode::cSubMenuNode(cSubMenuNodes *currentMenu, cSubMenuNodes *parentMenu)
++{
++  init();
++  _parentMenu  = parentMenu;
++  _currentMenu = currentMenu;
++
++}
++
++
++/**
++ *
++ */
++void cSubMenuNode::init()
++{
++  _name                = NULL;
++  _command             = NULL;
++  _title               = NULL;
++  _pluginMainMenuEntry = NULL;
++  _type                = UNDEFINED;
++  _level               = 0;
++  _parentMenu          = NULL;
++  _currentMenu         = NULL;
++  _pluginIndex         = 0;
++  _commandConfirm      = false;
++}
++
++
++cSubMenuNode::~ cSubMenuNode()
++{
++  if (_name != NULL)
++     free((void*)_name);
++  if (_command != NULL)
++     free((void*)_command);
++  if (_title != NULL)
++     free((void*)_title);
++  if (_pluginMainMenuEntry != NULL)
++     free((void*)_pluginMainMenuEntry);
++}
++
++/**
++ *
++ */
++void cSubMenuNode::SetPlugin()
++{
++  bool found = false;
++  for (int i = 0; ; i++) {
++      cPlugin *p = cPluginManager::GetPlugin(i);
++      if (p) {
++         if (strcmp(_name, p->Name()) == 0 && p->MainMenuEntry() != NULL) {
++            SetPluginMainMenuEntry(p->MainMenuEntry());
++            _pluginIndex = i;
++            found = true;
++            break;
++            }
++         }
++      else
++         break;
++      }
++
++      if (!found)
++         _type = UNDEFINED;
++}
++
++
++bool cSubMenuNode::SaveXml(TiXmlElement *root)
++{
++  bool ok = true;
++
++  if (root!=NULL) {
++     TiXmlElement *e = NULL;
++     switch(_type) {
++        case SYSTEM:
++           e = new TiXmlElement(TAG_SYSTEM);
++           e->SetAttribute("name", GetName());
++           break;
++        case COMMAND:
++           e = new TiXmlElement(TAG_COMMAND);
++           e->SetAttribute("name", GetName());
++           e->SetAttribute("execute", GetCommand());
++           if (_commandConfirm)
++              e->SetAttribute("confirm", TRUE_STR);
++           break;
++        case THREAD:
++           e = new TiXmlElement(TAG_THREAD);
++           e->SetAttribute("name", GetName());
++           e->SetAttribute("execute", GetCommand());
++           if (_commandConfirm)
++              e->SetAttribute("confirm", TRUE_STR);
++           break;
++        case PLUGIN:
++           e = new TiXmlElement(TAG_PLUGIN);
++           e->SetAttribute("name", GetName());
++           if (GetCustomTitle() != NULL && strcmp(GetCustomTitle(), "") != 0)
++              e->SetAttribute("title", GetCustomTitle());
++           break;
++        case MENU:
++           e = new TiXmlElement(TAG_MENU);
++           e->SetAttribute("name", GetName());
++           break;
++        case UNDEFINED:
++        default:
++           ok = false;
++           break;
++        }
++        if (ok) {
++           root->LinkEndChild(e);
++           if (HasSubMenus())
++              for (cSubMenuNode *node = _subMenus.First(); node; node = _subMenus.Next(node))
++                  node->SaveXml(e);
++           }
++     }
++
++  return(ok);
++}
++
++
++cSubMenuNode::Type cSubMenuNode::IsType(const char *name)
++{
++  Type type = UNDEFINED;
++
++  if (strcmp(name ,TAG_SYSTEM) == 0)
++     type = cSubMenuNode::SYSTEM;
++  else if (strcmp(name ,TAG_PLUGIN) == 0)
++     type = cSubMenuNode::PLUGIN;
++  else if (strcmp(name ,TAG_COMMAND) == 0)
++     type = cSubMenuNode::COMMAND;
++  else if (strcmp(name ,TAG_THREAD) == 0)
++     type = cSubMenuNode::THREAD;
++  else if (strcmp(name ,TAG_MENU) == 0)
++     type = cSubMenuNode::MENU;
++
++  return(type);
++}
++
++void cSubMenuNode::SetType(const char *name)
++{
++   _type = IsType(name);
++}
++
++void cSubMenuNode::SetType(enum Type type)
++{
++  _type = type;
++}
++
++
++cSubMenuNode::Type cSubMenuNode::GetType()
++{
++  return(_type);
++}
++
++const char *cSubMenuNode::GetTypeAsString()
++{
++  const char *str=NULL;
++  switch(_type) {
++     case SYSTEM:
++        str = TAG_SYSTEM;
++        break;
++     case COMMAND:
++        str = TAG_COMMAND;
++        break;
++     case THREAD:
++        str = TAG_THREAD;
++        break;
++     case PLUGIN:
++        str = TAG_PLUGIN;
++        break;
++     case MENU:
++        str = TAG_MENU;
++        break;
++     case UNDEFINED:
++        str = TAG_UNDEFINED;
++     default:
++        break;
++    }
++
++  return(str);
++}
++
++void cSubMenuNode::SetCommand(const char *command)
++{
++  if (_command != NULL)
++     free((void*)_command);
++
++  if (command != NULL)
++     _command = strdup(command);
++  else
++     _command = NULL;
++}
++
++const char *cSubMenuNode::GetCommand()
++{
++  return(_command);
++}
++
++bool cSubMenuNode::CommandConfirm()
++{
++  return(_commandConfirm);
++}
++
++void cSubMenuNode::SetCommandConfirm(int val)
++{
++  if (val == 1)
++     _commandConfirm = true;
++  else
++     _commandConfirm = false;
++}
++
++void cSubMenuNode::SetCustomTitle(const char *title)
++{
++  if (_title != NULL)
++     free((void*)_title);
++
++  if (title != NULL)
++     _title = strdup(title);
++  else
++     _title = NULL;
++}
++
++const char *cSubMenuNode::GetCustomTitle()
++{
++  return(_title);
++}
++
++void cSubMenuNode::SetName(const char *name)
++{
++  if (_name)
++     free ((void*)_name);
++
++  if (name != NULL)
++     _name = strdup(name);
++  else
++     _name = NULL;
++}
++
++const char *cSubMenuNode::GetName()
++{
++  return(_name);
++}
++
++int cSubMenuNode::GetLevel()
++{
++  return(_level);
++}
++
++void cSubMenuNode::SetLevel(int level)
++{
++  _level = level;
++  if (HasSubMenus()) { //Adjust Levels of Subnodes
++     for (cSubMenuNode *node = _subMenus.First(); node; node = _subMenus.Next(node))
++         node->SetLevel(level+1);
++     }
++}
++
++int cSubMenuNode::GetPluginIndex()
++{
++  return(_pluginIndex);
++}
++
++void cSubMenuNode::SetPluginIndex(int index)
++{
++  _pluginIndex = index;
++}
++
++void cSubMenuNode::SetPluginMainMenuEntry(const char *mainMenuEntry)
++{
++  if (_pluginMainMenuEntry != NULL)
++     free((void*)_pluginMainMenuEntry);
++
++  if (_title != NULL && strcmp(_title, "") != 0)
++     _pluginMainMenuEntry = strdup(_title);
++  else if (mainMenuEntry != NULL)
++     _pluginMainMenuEntry = strdup(mainMenuEntry);
++  else
++     _pluginMainMenuEntry = NULL;
++}
++
++const char *cSubMenuNode::GetPluginMainMenuEntry()
++{
++  return(_pluginMainMenuEntry);
++}
++
++
++cSubMenuNodes *cSubMenuNode::GetParentMenu()
++{
++  return(_parentMenu);
++}
++
++void cSubMenuNode::SetParentMenu(cSubMenuNodes *parent)
++{
++  _parentMenu = parent;
++}
++
++cSubMenuNodes *cSubMenuNode::GetCurrentMenu()
++{
++  return(_currentMenu);
++}
++
++void cSubMenuNode::SetCurrentMenu(cSubMenuNodes *current)
++{
++  _currentMenu = current;
++}
++
++
++cSubMenuNodes *cSubMenuNode::GetSubMenus()
++{
++  return(&_subMenus);
++}
++
++bool cSubMenuNode::HasSubMenus()
++{
++  if (_subMenus.Count() > 0)
++     return(true);
++  else
++     return(false);
++}
++
++
++void cSubMenuNode::Print(int index)
++{
++  for (int i = 0; i < index; i++)
++      printf(" ");
++
++  printf("Name=%s Type=%s Level=%d", _name, GetTypeAsString(), _level);
++  if (_type == COMMAND || _type == THREAD)
++     printf(" Command=%s", _command);
++  else if (_type == PLUGIN && _title != NULL)
++     printf(" Title=%s", _title);
++  printf("\n");
++
++  for (cSubMenuNode *node = _subMenus.First(); node; node = _subMenus.Next(node))
++      node->Print(index+4);
++}
++
++
++//################################################################################
++//#
++//################################################################################
++cSubMenu::cSubMenu()
++{
++  _commandResult         = NULL;
++  _currentMenuTree       = &_menuTree;
++  _currentParentMenuTree = NULL;
++#ifdef USE_PINPLUGIN
++  _currentParentIndex    = -1;
++#endif /* PINPLUGIN */
++  _nodeArray             = NULL;
++  _nrNodes               = 0;
++}
++
++
++cSubMenu::~cSubMenu()
++{
++  if (_commandResult)
++     free(_commandResult);
++  if (_nodeArray)
++     free(_nodeArray);
++  _nrNodes = 0;
++}
++
++
++bool cSubMenu::LoadXml(cString fname)
++{
++  TiXmlDocument  xmlDoc = TiXmlDocument(fname);
++  TiXmlElement  *root   = NULL;
++  cSubMenuNode  *node   = NULL;
++
++  bool  ok = true;
++  // Clear previously loaded Menu
++  _menuTree.Clear();
++  _fname = fname;
++
++  if ((ok = xmlDoc.LoadFile())) {
++     if ((root = xmlDoc.FirstChildElement("menus")) != NULL) {
++        cString tmp = root->Attribute("suffix");
++#ifdef USE_WAREAGLEICON
++        if      (strcmp(tmp, "ICON_FOLDER") == 0)      tmp = cString::sprintf(" %s", IsLangUtf8() ? ICON_FOLDER_UTF8 : ICON_FOLDER);
++        else if (strcmp(tmp, "ICON_MOVE_FOLDER") == 0) tmp = cString::sprintf(" %s", IsLangUtf8() ? ICON_MOVE_FOLDER_UTF8 : ICON_MOVE_FOLDER);
++#endif /* WAREAGLEICON */
++        if (*tmp)
++           _menuSuffix = tmp;
++        else
++           _menuSuffix = cString::sprintf(" ");
++
++        if ((root = root->FirstChildElement()) != NULL) {
++           do {
++              try {
++                 node = new cSubMenuNode(root, 0,  &_menuTree, NULL);
++                 _menuTree.Add(node);
++                 }
++              catch (char *message) {
++                 esyslog("ERROR: while decoding XML Node");
++                 ok = false;
++                 }
++              } while (ok == true && (root = root->NextSiblingElement()) != NULL);
++           addMissingPlugins();
++           removeUndefinedNodes();
++           }
++        }
++     else {
++        esyslog("ERROR: in %s, missing Tag <menus>\n", *fname);
++        ok = false;
++        }
++     }
++  else {
++     esyslog("ERROR: in %s : %s  Col=%d Row=%d\n",
++            *fname,
++            xmlDoc.ErrorDesc(),
++            xmlDoc.ErrorCol(),
++            xmlDoc.ErrorRow());
++     ok = false;
++     }
++
++  return(ok);
++}
++
++
++bool cSubMenu::SaveXml()
++{
++  return(SaveXml(_fname));
++}
++
++
++bool cSubMenu::SaveXml(cString fname)
++{
++  bool ok = true;
++
++  if (*_fname) {
++     TiXmlDocument xml = TiXmlDocument(fname);
++     TiXmlComment  comment;
++     comment.SetValue("\n\
++-    VDR Menu-Configuration File\n\
++-\n\
++-\n\
++-   Example:\n\
++-\n\
++ <menus>\n\
++    <system name=\"Schedule\" />\n\
++    <system name=\"Channels\" />\n\
++    <system name=\"Timers\" />\n\
++    <system name=\"Recordings\" />\n\
++    <menu name=\"System\">\n\
++        <system name=\"Setup\" />\n\
++        <system name=\"Commands\" />\n\
++        <plugin name=\"setup\" title=\"My Setup\" />\n\
++        <command name=\"myCommand1\" execute=\"/usr/bin/mycommand1\" />\n\
++        <command name=\"myCommand2\" execute=\"/usr/bin/mycommand2\" confirm=\"yes\" />\n\
++        <thread name=\"myCommand3\" execute=\"/usr/bin/mycommand3\" confirm=\"yes\" />\n\
++        <plugin name=\"epgsearch\" title=\"myProgram\" />\n\
++        <menu name=\"mySubSubMenu\">\n\
++            ...\n\
++        </menu>\n\
++    </menu>\n\
++     <menu name=\"Suche\">\n\
++        <plugin name=\"epgsearch\" />\n\
++        ...\n\
++    </menu>\n\
++ </menus>\n\
++");
++
++     TiXmlElement root("menus");
++     root.SetAttribute("suffix", _menuSuffix);
++     for (cSubMenuNode *node = _menuTree.First(); node; node = _menuTree.Next(node))
++         node->SaveXml(&root);
++
++         if (xml.InsertEndChild(comment) != NULL && xml.InsertEndChild(root) != NULL)
++            ok = xml.SaveFile(fname);
++     }
++  else
++     ok = false;
++
++  return(ok);
++}
++
++
++cSubMenuNodes *cSubMenu::GetMenuTree()
++{
++  return(_currentMenuTree);
++}
++
++
++void cSubMenu::PrintMenuTree()
++{
++  for (cSubMenuNode *node = _menuTree.First(); node; node = _menuTree.Next(node))
++      node->Print();
++}
++
++
++int cSubMenu::GetNrOfNodes()
++{
++  if (_nrNodes == 0) {
++     if ((_nrNodes = countNodes(&_menuTree)) > 0) {
++        _nodeArray = (cSubMenuNode**) malloc(sizeof(cSubMenuNode*)*_nrNodes);
++        int index = 0;
++        tree2Array(&_menuTree, index);
++        }
++     }
++
++  return(_nrNodes);
++}
++
++
++/**
++ * returns the specified node within the current menu
++ * @param index position in the current menu
++ * @return node or null if not found
++ */
++cSubMenuNode *cSubMenu::GetNode(int index)
++{
++  cSubMenuNode *node = NULL;
++  if (_currentMenuTree == NULL || (node=_currentMenuTree->Get(index)) == NULL)
++     esyslog("ERROR: illegal call of cSubMenu::GetNode(%d)", index);
++
++  return(node);
++}
++
++
++/**
++ * Get the specified Node
++ * @param index specfies the absolut indes in the list of all nodes
++ * @return node or NULL if not found
++ */
++cSubMenuNode *cSubMenu::GetAbsNode(int index)
++{
++  cSubMenuNode *node = NULL;
++  GetNrOfNodes();
++  if (_nrNodes > 0 && index >= 0 && index < _nrNodes)
++     node = _nodeArray[index];
++
++  return(node);
++}
++
++
++#ifdef USE_PINPLUGIN
++bool cSubMenu::Down(cSubMenuNode *node, int currentIndex)
++#else
++bool cSubMenu::Down(int index)
++#endif /* PINPLUGIN */
++{
++  bool ok = true;
++#ifdef USE_PINPLUGIN
++  if (_currentMenuTree != NULL && node && node->GetType() == cSubMenuNode::MENU) {
++#else
++  cSubMenuNode *node = NULL;
++
++  if (_currentMenuTree != NULL && (node=_currentMenuTree->Get(index)) != NULL && node->GetType() == cSubMenuNode::MENU) {
++#endif /* PINPLUGIN */
++     _currentParentMenuTree = _currentMenuTree;
++#ifdef USE_PINPLUGIN
++     _currentParentIndex = currentIndex;
++#endif /* PINPLUGIN */
++     _currentMenuTree = node->GetSubMenus();
++     }
++  else {
++     ok = false;
++#ifdef USE_PINPLUGIN
++     esyslog("ERROR: illegal call of cSubMenu::Down");
++#else
++     esyslog("ERROR: illegal call of cSubMenu::Down(%d)", index);
++#endif /* PINPLUGIN */
++     }
++
++  return(ok);
++}
++
++bool cSubMenu::Up(int *parentIndex)
++{
++  bool ok = true;
++
++  if (_currentMenuTree != NULL && parentIndex != NULL) {
++#ifndef USE_PINPLUGIN
++     cSubMenuNode *node = NULL;
++#endif /* PINPLUGIN */
++     *parentIndex = 0;
++#ifdef USE_PINPLUGIN
++     if (_currentParentIndex >= 0)
++        *parentIndex = _currentParentIndex;
++#else
++     if (_currentParentMenuTree != NULL)
++        for (int i = 0; (node = _currentParentMenuTree->Get(i)) != NULL; i++) {
++            if (_currentMenuTree == node->GetSubMenus()) {
++               *parentIndex = i;
++               break;
++               }
++            }
++#endif /* PINPLUGIN */
++
++     _currentMenuTree = _currentParentMenuTree;
++     if (_currentMenuTree != NULL)
++        _currentParentMenuTree = _currentMenuTree->Get(0)->GetParentMenu();
++     else
++        ok = false;
++     }
++  else {
++     ok = false;
++     esyslog("ERROR: illegal call of cSubMenu::Up()");
++     }
++
++  return(ok);
++}
++
++const char *cSubMenu::ExecuteCommand(const char *cmd)
++{
++  free(_commandResult);
++  _commandResult = NULL;
++
++  dsyslog("executing command '%s'", cmd);
++  FILE *p = popen(cmd, "r");
++  if (p) {
++     int l = 0;
++     int c;
++     while ((c = fgetc(p)) != EOF) {
++           if (l % 20 == 0)
++              _commandResult = (char *)realloc(_commandResult, l + 21);
++           _commandResult[l++] = c;
++           }
++     if (_commandResult)
++        _commandResult[l] = 0;
++     pclose(p);
++     }
++  else
++     esyslog("ERROR: can't open pipe for command '%s'", cmd);
++
++  return _commandResult;
++}
++
++/**
++ * Move Menu Entry to new Position
++ * @param index  index of menu entry to move 
++ * @param toIndex index of destination
++ * @param where After ore before the destination index
++ */
++void cSubMenu::MoveMenu(int index, int toIndex, enum Where where)
++{
++  if (index < 0 || index > _nrNodes || // invalid index is ignored
++     toIndex < 0 || toIndex > _nrNodes || index == toIndex)
++     return;
++
++  cSubMenuNode *srcNode  = GetAbsNode(index);
++  cSubMenuNode *destNode = GetAbsNode(toIndex);
++
++  if (where == cSubMenu::INTO && destNode->GetType() != cSubMenuNode::MENU)
++     return;
++
++  if (where == cSubMenu::INTO) {
++     if (destNode->GetType() == cSubMenuNode::MENU) {
++        srcNode->GetCurrentMenu()->Del(srcNode, false);
++        srcNode->SetLevel(destNode->GetLevel()+1);
++        srcNode->SetParentMenu(destNode->GetCurrentMenu());
++        srcNode->SetCurrentMenu(destNode->GetSubMenus());
++
++        destNode->GetSubMenus()->Add(srcNode);
++        reloadNodeArray();
++        }
++     }
++  else {
++     srcNode->GetCurrentMenu()->Del(srcNode, false);
++     srcNode->SetLevel(destNode->GetLevel());
++     srcNode->SetParentMenu(destNode->GetParentMenu());
++     srcNode->SetCurrentMenu(destNode->GetCurrentMenu());
++
++     if (where == cSubMenu::BEHIND) {
++        destNode->GetCurrentMenu()->Add(srcNode, GetAbsNode(toIndex));
++        reloadNodeArray();
++        }
++     else {
++        destNode->GetCurrentMenu()->Ins(srcNode, GetAbsNode(toIndex));
++        reloadNodeArray();
++        }
++     }
++}
++
++/**
++ * Create a new Menu Entry
++ * @param index index of destination
++ * @param menuTitle  Titel of new Menu entry
++ */
++void cSubMenu::CreateMenu(int index, const char *menuTitle)
++{
++  if (index >= 0 && index < _nrNodes) {
++     cSubMenuNode *srcNode  = GetAbsNode(index);
++     if (srcNode != NULL) {
++        cSubMenuNode *newNode = new cSubMenuNode(srcNode->GetParentMenu(), srcNode->GetCurrentMenu());
++        newNode->SetLevel(srcNode->GetLevel());
++        newNode->SetName(menuTitle);
++        newNode->SetType(cSubMenuNode::MENU);
++        newNode->SetParentMenu(srcNode->GetParentMenu());
++        newNode->SetCurrentMenu(srcNode->GetCurrentMenu());
++
++        srcNode->GetCurrentMenu()->Add(newNode, GetAbsNode(index));
++        reloadNodeArray();
++        }
++     }
++}
++
++/**
++ * delete the specified entry, or subtree if the specified entry is a menu
++ * @param index destion index 
++ */
++void cSubMenu::DeleteMenu(int index)
++{
++  if (index >= 0 && index < _nrNodes) {
++     cSubMenuNode *srcNode = GetAbsNode(index);
++     srcNode->GetCurrentMenu()->Del(srcNode, true);
++     reloadNodeArray();
++     }
++}
++
++
++// Private Methods
++
++int cSubMenu::countNodes(cSubMenuNodes *tree)
++{
++  int count = 0;
++  if (tree != NULL) {
++     for (cSubMenuNode *node = tree->First(); node; node = tree->Next(node)) {
++         count++;
++         if (node->HasSubMenus())
++            count += countNodes(node->GetSubMenus());
++         }
++     }
++  return(count);
++}
++
++
++void cSubMenu::tree2Array(cSubMenuNodes *tree, int &index)
++{
++  if (tree != NULL) {
++     for (cSubMenuNode *node = tree->First(); node; node = tree->Next(node)) {
++         _nodeArray[index++]=node;
++         if (node->HasSubMenus())
++            tree2Array(node->GetSubMenus(), index);
++         }
++     }
++
++}
++
++bool cSubMenu::IsPluginInMenu(const char *name)
++{
++  bool found = false;
++  for (int i = 0; i < _nrNodes && found == false; i++) {
++      cSubMenuNode *node = GetAbsNode(i);
++      if (node != NULL && node->GetType() == cSubMenuNode::PLUGIN && strcmp(name, node->GetName()) == 0)
++         found = true;
++      }
++  return(found);
++}
++
++/**
++ * Adds the given plugin to the Menu-Tree if not allready in List
++ * @param name specifies the name of the plugin
++ */
++void cSubMenu::AddPlugin(const char *name)
++{
++  if (! IsPluginInMenu(name)) {
++     cSubMenuNode *node = new  cSubMenuNode(&_menuTree, NULL);
++     node->SetName(name);
++     node->SetType("plugin");
++     node->SetPlugin();
++     _menuTree.Add(node);
++     }
++}
++
++void cSubMenu::addMissingPlugins()
++{
++  _nrNodes = GetNrOfNodes();
++  for (int i = 0; ; i++) {
++      cPlugin *p = cPluginManager::GetPlugin(i);
++      if (p)
++         AddPlugin(p->Name());
++      else
++         break;
++      }
++  reloadNodeArray();
++}
++
++/**
++ * Adds the given command to the Menu-Tree
++ * @param name specifies the name of the command
++ */
++void cSubMenu::CreateCommand(int index, const char *name, const char *execute, int confirm)
++{
++  if (index >= 0 && index < _nrNodes) {
++     cSubMenuNode *srcNode  = GetAbsNode(index);
++     if (srcNode != NULL) {
++        cSubMenuNode *newNode = new cSubMenuNode(srcNode->GetParentMenu(), srcNode->GetCurrentMenu());
++        newNode->SetLevel(srcNode->GetLevel());
++        newNode->SetName(name);
++        newNode->SetType("command");
++        newNode->SetCommand(execute);
++        newNode->SetCommandConfirm(confirm);
++        newNode->SetParentMenu(srcNode->GetParentMenu());
++        newNode->SetCurrentMenu(srcNode->GetCurrentMenu());
++
++        srcNode->GetCurrentMenu()->Add(newNode, GetAbsNode(index));
++        reloadNodeArray();
++        }
++     }
++}
++
++void cSubMenu::CreateThread(int index, const char *name, const char *execute, int confirm)
++{
++  if (index >= 0 && index < _nrNodes) {
++     cSubMenuNode *srcNode  = GetAbsNode(index);
++     if (srcNode != NULL) {
++        cSubMenuNode *newNode = new cSubMenuNode(srcNode->GetParentMenu(), srcNode->GetCurrentMenu());
++        newNode->SetLevel(srcNode->GetLevel());
++        newNode->SetName(name);
++        newNode->SetType("thread");
++        newNode->SetCommand(execute);
++        newNode->SetCommandConfirm(confirm);
++        newNode->SetParentMenu(srcNode->GetParentMenu());
++        newNode->SetCurrentMenu(srcNode->GetCurrentMenu());
++
++        srcNode->GetCurrentMenu()->Add(newNode, GetAbsNode(index));
++        reloadNodeArray();
++        }
++     }
++}
++
++/**
++ * reloads the internal Array of Nodes
++ */
++void cSubMenu::reloadNodeArray()
++{
++  if (_nrNodes > 0)
++     free(_nodeArray);
++  _nodeArray = NULL;
++  _nrNodes = 0;
++  _nrNodes = GetNrOfNodes();
++}
++
++/**
++ * remove Undefined Nodes
++ */
++void cSubMenu::removeUndefinedNodes()
++{
++  bool remove = false;
++
++  reloadNodeArray();
++  for (int i = 0; i < _nrNodes; i++) {
++      cSubMenuNode *node = GetAbsNode(i);
++      if (node != NULL && node->GetType() == cSubMenuNode::UNDEFINED) {
++         cSubMenuNodes *pMenu = node->GetCurrentMenu();
++         pMenu->Del(node, true);
++         remove = true;
++         }
++      }
++  if (remove)
++     reloadNodeArray();
++}
++
++
++/**
++* Retrieves the Menutitel of the parent Menu
++*/
++const char *cSubMenu::GetParentMenuTitel()
++{
++  const char *result = "";
++
++  if (_currentMenuTree != NULL && _currentParentMenuTree != NULL) {
++     cSubMenuNode *node = NULL;
++     for (int i = 0; (node = _currentParentMenuTree->Get(i)) != NULL; i++) {
++         if (_currentMenuTree == node->GetSubMenus()) {
++            result = node->GetName();
++            break;
++            }
++         }
++     }
++
++  return(result);
++}
++
++#endif
++#endif /* SETUP */
+diff -NaurwB vdr-1.7.10/submenu.h vdr-1.7.10-patched/submenu.h
+--- vdr-1.7.10/submenu.h       1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.10-patched/submenu.h       2009-12-18 06:25:25.000000000 +0100
+@@ -0,0 +1,159 @@
++#ifdef USE_SETUP
++/****************************************************************************
++ * DESCRIPTION:
++ *             Submenu
++ *
++ * $Id: vdr-1.3.44-Setup-0.3.0.diff,v 1.1 2006/03/04 09:58:47 ralf Exp $
++ *
++ * Contact:    ranga@teddycats.de
++ *
++ * Copyright (C) 2004, 2005 by Ralf Dotzert
++ *
++ * modified for the VDR Extensions Patch by zulu @vdr-portal
++ ****************************************************************************/
++
++#ifndef SUBMENU_H
++#define SUBMENU_H
++
++#include "thread.h"
++#include "tools.h"
++#include "tinystr.h"
++
++class cSubMenuNode;
++class cSubMenuNodes;
++class cSubMenu;
++
++
++class cSubMenuNodes : public cList<cSubMenuNode> {};
++
++// execute cmd thread
++class cExecCmdThread : public cThread {
++private:
++  cString ExecCmd;
++protected:
++  virtual void Action(void) {
++     if (system(ExecCmd) == 0)
++        esyslog("%s - finished", *ExecCmd);
++     delete(this);
++     };
++public:
++  cExecCmdThread(char *cmd) {
++     ExecCmd = cString::sprintf("%s", cmd);
++     }
++  cExecCmdThread(const char *cmd) {
++     ExecCmd = cString::sprintf("%s", cmd);
++     }
++  ~cExecCmdThread() {
++     };
++  };
++
++//################################################################################
++//# SubMenuNode
++//################################################################################
++class cSubMenuNode : public cListObject {
++public:
++  enum Type { UNDEFINED, SYSTEM, COMMAND, THREAD, PLUGIN, MENU };
++  cSubMenuNode(TiXmlElement *xml, int level, cSubMenuNodes *currentMenu, cSubMenuNodes *parentMenu);
++  cSubMenuNode(cSubMenuNodes *currentMenu, cSubMenuNodes *parentMenu);
++  ~cSubMenuNode();
++  bool SaveXml(TiXmlElement *root);
++  static cSubMenuNode::Type IsType(const char *name);
++  void  SetType(const char *name);
++  void  SetType(enum Type type);
++  void  SetPlugin();
++  cSubMenuNode::Type GetType();
++  const char *GetTypeAsString();
++  void SetCommand(const char *command);
++  bool CommandConfirm();
++  void SetCommandConfirm(int val);
++  const char *GetCommand();
++  void SetCustomTitle(const char *title);
++  const char *GetCustomTitle();
++  void SetName(const char *name);
++  const char*GetName();
++  int  GetLevel();
++  void SetLevel(int level);
++  int  GetPluginIndex();
++  void SetPluginIndex(int index);
++  void SetPluginMainMenuEntry(const char *mainMenuEntry);
++  const char *GetPluginMainMenuEntry();
++  cSubMenuNodes *GetParentMenu();
++  void SetParentMenu(cSubMenuNodes *parent);
++  cSubMenuNodes *GetCurrentMenu();
++  void SetCurrentMenu(cSubMenuNodes *current);
++  cSubMenuNodes *GetSubMenus();
++  bool HasSubMenus();
++  void Print(int index = 0);
++private:
++  Type _type;
++  int _level;
++  // Plugin Variables
++  int _pluginIndex;
++  const char *_pluginMainMenuEntry;
++  // common
++  const char *_name;
++  const char *_command;
++  bool _commandConfirm;
++  const char *_title;
++  cSubMenuNodes _subMenus;
++  cSubMenuNodes *_parentMenu;
++  cSubMenuNodes *_currentMenu;
++  void init();
++  };
++
++
++//################################################################################
++//# SubMenu Class
++//################################################################################
++class cSubMenu {
++public:
++  cSubMenu();
++  ~cSubMenu();
++  enum Where { BEFORE, BEHIND, INTO};
++  bool LoadXml(cString fname);
++  bool SaveXml(cString fname);
++  bool SaveXml();
++  cSubMenuNodes *GetMenuTree();
++  bool Up(int *ParentIndex);
++#ifdef USE_PINPLUGIN
++  bool Down(cSubMenuNode* node, int currentIndex);
++#else
++  bool Down(int index);
++#endif /* PINPLUGIN */
++  int  GetNrOfNodes();
++  cSubMenuNode* GetAbsNode(int index);
++  cSubMenuNode* GetNode(int index);
++  void PrintMenuTree();
++  bool IsPluginInMenu(const char *name);
++  void AddPlugin(const char *name);
++  void CreateCommand(int index, const char *name, const char *execute, int confirm);
++  void CreateThread(int index, const char *name, const char *execute, int confirm);
++  const char *ExecuteCommand(const char *command);
++  void MoveMenu(int index, int toindex, enum Where);
++  void CreateMenu(int index, const char *menuTitle);
++  void DeleteMenu(int index);
++  cString GetMenuSuffix() { return _menuSuffix; }
++  void SetMenuSuffix(char *suffix) { _menuSuffix = suffix; }
++  bool isTopMenu() { return (_currentParentMenuTree == NULL); }
++  const char *GetParentMenuTitel();
++private:
++  cSubMenuNodes _menuTree;
++  cSubMenuNodes *_currentMenuTree;
++  cSubMenuNodes *_currentParentMenuTree;
++#ifdef USE_PINPLUGIN
++  int _currentParentIndex;
++#endif /* PINPLUGIN */
++  cString _fname;
++  char *_commandResult;
++  int _nrNodes;
++  cSubMenuNode **_nodeArray;
++  cString _menuSuffix;
++  int countNodes(cSubMenuNodes *tree);
++  void tree2Array(cSubMenuNodes *tree, int &index);
++  void addMissingPlugins();
++  void reloadNodeArray();
++  void removeUndefinedNodes();
++  };
++
++#endif //__SUBMENU_H
++#endif /* SETUP */
+diff -NaurwB vdr-1.7.10/svdrp.c vdr-1.7.10-patched/svdrp.c
+--- vdr-1.7.10/svdrp.c 2009-10-18 16:08:58.000000000 +0200
++++ vdr-1.7.10-patched/svdrp.c 2009-12-18 06:27:38.000000000 +0100
+@@ -299,6 +299,10 @@
+   "REMO [ on | off ]\n"
+   "    Turns the remote control on or off. Without a parameter, the current\n"
+   "    status of the remote control is reported.",
++#ifdef USE_LIEMIEXT
++  "RENR <number> <new name>\n"
++  "    Rename recording. Number must be the Number as returned by LSTR command.",
++#endif /* LIEMIEXT */
+   "SCAN\n"
+   "    Forces an EPG scan. If this is a single DVB device system, the scan\n"
+   "    will be done on the primary device unless it is currently recording.",
+@@ -1486,6 +1490,38 @@
+   Reply(250, "EPG scan triggered");
+ }
++#ifdef USE_LIEMIEXT
++void cSVDRP::CmdRENR(const char *Option)
++{
++  bool recordings = Recordings.Update(true);
++  if (recordings) {
++     if (*Option) {
++        char *tail;
++        int n = strtol(Option, &tail, 10);
++        cRecording *recording = Recordings.Get(n - 1);
++        if (recording && tail && tail != Option) {
++           char *oldName = strdup(recording->Name());
++           tail = skipspace(tail);
++           if (recording->Rename(tail)) {
++              Reply(250, "Renamed \"%s\" to \"%s\"", oldName, recording->Name());
++              Recordings.ChangeState();
++              Recordings.TouchUpdate();
++              }
++           else
++              Reply(501, "Renaming \"%s\" to \"%s\" failed", oldName, tail);
++           free(oldName);
++           }
++        else
++          Reply(501, "Recording not found or wrong syntax");
++        }
++     else
++        Reply(501, "Missing Input settings");
++     }
++  else
++     Reply(550, "No recordings available");
++}
++#endif /* LIEMIEXT */
++
+ void cSVDRP::CmdSTAT(const char *Option)
+ {
+   if (*Option) {
+@@ -1601,6 +1637,9 @@
+   else if (CMD("PLUG"))  CmdPLUG(s);
+   else if (CMD("PUTE"))  CmdPUTE(s);
+   else if (CMD("REMO"))  CmdREMO(s);
++#ifdef USE_LIEMIEXT
++  else if (CMD("RENR"))  CmdRENR(s);
++#endif /* LIEMIEXT */
+   else if (CMD("SCAN"))  CmdSCAN(s);
+   else if (CMD("STAT"))  CmdSTAT(s);
+   else if (CMD("UPDT"))  CmdUPDT(s);
+diff -NaurwB vdr-1.7.10/svdrp.h vdr-1.7.10-patched/svdrp.h
+--- vdr-1.7.10/svdrp.h 2007-04-30 14:28:28.000000000 +0200
++++ vdr-1.7.10-patched/svdrp.h 2009-12-18 06:25:25.000000000 +0100
+@@ -79,6 +79,9 @@
+   void CmdPLUG(const char *Option);
+   void CmdPUTE(const char *Option);
+   void CmdREMO(const char *Option);
++#ifdef USE_LIEMIEXT
++  void CmdRENR(const char *Option);
++#endif /* LIEMIEXT */
+   void CmdSCAN(const char *Option);
+   void CmdSTAT(const char *Option);
+   void CmdUPDT(const char *Option);
+diff -NaurwB vdr-1.7.10/timers.c vdr-1.7.10-patched/timers.c
+--- vdr-1.7.10/timers.c        2009-08-09 14:43:20.000000000 +0200
++++ vdr-1.7.10-patched/timers.c        2009-12-18 06:29:49.000000000 +0100
+@@ -46,6 +46,9 @@
+      stop -= 2400;
+   priority = Pause ? Setup.PausePriority : Setup.DefaultPriority;
+   lifetime = Pause ? Setup.PauseLifetime : Setup.DefaultLifetime;
++#ifdef USE_PINPLUGIN
++  fskProtection = 0;
++#endif /* PINPLUGIN */
+   *file = 0;
+   aux = NULL;
+   event = NULL;
+@@ -84,6 +87,9 @@
+      stop -= 2400;
+   priority = Setup.DefaultPriority;
+   lifetime = Setup.DefaultLifetime;
++#ifdef USE_PINPLUGIN
++  fskProtection = 0;
++#endif /* PINPLUGIN */  
+   *file = 0;
+   const char *Title = Event->Title();
+   if (!isempty(Title))
+@@ -95,6 +101,9 @@
+      }
+   aux = NULL;
+   event = NULL; // let SetEvent() be called to get a log message
++#ifdef USE_PINPLUGIN
++  cStatus::MsgTimerCreation(this, Event);
++#endif /* PINPLUGIN */  
+ }
+ cTimer::cTimer(const cTimer &Timer)
+@@ -129,6 +138,9 @@
+      stop         = Timer.stop;
+      priority     = Timer.priority;
+      lifetime     = Timer.lifetime;
++#ifdef USE_PINPLUGIN
++     fskProtection = Timer.fskProtection;
++#endif /* PINPLUGIN */
+      strncpy(file, Timer.file, sizeof(file));
+      free(aux);
+      aux = Timer.aux ? strdup(Timer.aux) : NULL;
+@@ -323,6 +335,9 @@
+         result = false;
+         }
+      }
++#ifdef USE_PINPLUGIN
++  fskProtection = aux && strstr(aux, "<pin-plugin><protected>yes</protected></pin-plugin>");
++#endif /* PINPLUGIN */
+   free(channelbuffer);
+   free(daybuffer);
+   free(filebuffer);
+@@ -632,6 +647,35 @@
+   Matches(); // refresh start and end time
+ }
++#ifdef USE_PINPLUGIN
++void cTimer::SetFskProtection(int aFlag)
++{
++   char* p;
++   char* tmp = 0;
++
++   fskProtection = aFlag;
++
++   if (fskProtection && (!aux || !strstr(aux, "<pin-plugin><protected>yes</protected></pin-plugin>")))
++   {
++      // add protection info to aux
++
++      if (aux) { tmp = strdup(aux); free(aux); }
++      asprintf(&aux,"%s<pin-plugin><protected>yes</protected></pin-plugin>", tmp ? tmp : "");
++   }
++   else if (!fskProtection && aux && (p = strstr(aux, "<pin-plugin><protected>yes</protected></pin-plugin>")))
++   {
++      // remove protection info to aux
++
++      asprintf(&tmp, "%.*s%s", p-aux, aux, p+strlen("<pin-plugin><protected>yes</protected></pin-plugin>"));
++      free(aux);
++      aux = strdup(tmp);
++   }
++
++   if (tmp)
++      free(tmp);
++}
++#endif /* PINPLUGIN */
++
+ // --- cTimers ---------------------------------------------------------------
+ cTimers Timers;
+diff -NaurwB vdr-1.7.10/timers.h vdr-1.7.10-patched/timers.h
+--- vdr-1.7.10/timers.h        2008-02-16 15:33:23.000000000 +0100
++++ vdr-1.7.10-patched/timers.h        2009-12-18 06:25:25.000000000 +0100
+@@ -37,6 +37,9 @@
+   int start;
+   int stop;
+   int priority;
++#ifdef USE_PINPLUGIN
++  int fskProtection;
++#endif /* PINPLUGIN */
+   int lifetime;
+   mutable char file[MaxFileName];
+   char *aux;
+@@ -58,6 +61,9 @@
+   int Start(void) const { return start; }
+   int Stop(void) const { return stop; }
+   int Priority(void) const { return priority; }
++#ifdef USE_PINPLUGIN
++  int FskProtection(void) const { return fskProtection; }
++#endif /* PINPLUGIN */
+   int Lifetime(void) const { return lifetime; }
+   const char *File(void) const { return file; }
+   time_t FirstDay(void) const { return weekdays ? day : 0; }
+@@ -86,6 +92,9 @@
+   void SetInVpsMargin(bool InVpsMargin);
+   void SetPriority(int Priority);
+   void SetFlags(uint Flags);
++#ifdef USE_PINPLUGIN
++  void SetFskProtection(int aFlag);
++#endif /* PINPLUGIN */
+   void ClrFlags(uint Flags);
+   void InvFlags(uint Flags);
+   bool HasFlags(uint Flags) const;
+diff -NaurwB vdr-1.7.10/tinystr.c vdr-1.7.10-patched/tinystr.c
+--- vdr-1.7.10/tinystr.c       1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.10-patched/tinystr.c       2009-12-18 06:25:25.000000000 +0100
+@@ -0,0 +1,301 @@
++#ifdef USE_SETUP
++/*
++www.sourceforge.net/projects/tinyxml
++Original file by Yves Berquin.
++
++This software is provided 'as-is', without any express or implied 
++warranty. In no event will the authors be held liable for any 
++damages arising from the use of this software.
++
++Permission is granted to anyone to use this software for any 
++purpose, including commercial applications, and to alter it and 
++redistribute it freely, subject to the following restrictions:
++
++1. The origin of this software must not be misrepresented; you must 
++not claim that you wrote the original software. If you use this 
++software in a product, an acknowledgment in the product documentation 
++would be appreciated but is not required.
++
++2. Altered source versions must be plainly marked as such, and
++must not be misrepresented as being the original software.
++
++3. This notice may not be removed or altered from any source 
++distribution.
++*/
++
++#include "tinyxml.h"
++
++#ifndef TIXML_USE_STL
++
++
++#include <stdlib.h>
++#include <string.h>
++#include <ctype.h>
++
++#include "tinystr.h"
++
++// TiXmlString constructor, based on a C string
++TiXmlString::TiXmlString (const char* instring)
++{
++    unsigned newlen;
++    char * newstring;
++
++    if (!instring)
++    {
++        allocated = 0;
++        cstring = NULL;
++        current_length = 0;
++        return;
++    }
++    newlen = strlen (instring) + 1;
++    newstring = new char [newlen];
++    memcpy (newstring, instring, newlen);
++    // strcpy (newstring, instring);
++    allocated = newlen;
++    cstring = newstring;
++    current_length = newlen - 1;
++}
++
++// TiXmlString copy constructor
++TiXmlString::TiXmlString (const TiXmlString& copy)
++{
++    unsigned newlen;
++    char * newstring;
++
++      // Prevent copy to self!
++      if ( &copy == this )
++              return;
++
++    if (! copy . allocated)
++    {
++        allocated = 0;
++        cstring = NULL;
++        current_length = 0;
++        return;
++    }
++    newlen = copy . length () + 1;
++    newstring = new char [newlen];
++    // strcpy (newstring, copy . cstring);
++    memcpy (newstring, copy . cstring, newlen);
++    allocated = newlen;
++    cstring = newstring;
++    current_length = newlen - 1;
++}
++
++// TiXmlString = operator. Safe when assign own content
++void TiXmlString ::operator = (const char * content)
++{
++    unsigned newlen;
++    char * newstring;
++
++    if (! content)
++    {
++        empty_it ();
++        return;
++    }
++    newlen = strlen (content) + 1;
++    newstring = new char [newlen];
++    // strcpy (newstring, content);
++    memcpy (newstring, content, newlen);
++    empty_it ();
++    allocated = newlen;
++    cstring = newstring;
++    current_length = newlen - 1;
++}
++
++// = operator. Safe when assign own content
++void TiXmlString ::operator = (const TiXmlString & copy)
++{
++    unsigned newlen;
++    char * newstring;
++
++    if (! copy . length ())
++    {
++        empty_it ();
++        return;
++    }
++    newlen = copy . length () + 1;
++    newstring = new char [newlen];
++    // strcpy (newstring, copy . c_str ());
++    memcpy (newstring, copy . c_str (), newlen);
++    empty_it ();
++    allocated = newlen;
++    cstring = newstring;
++    current_length = newlen - 1;
++}
++
++
++// append a const char * to an existing TiXmlString
++void TiXmlString::append( const char* str, int len )
++{
++    char * new_string;
++    unsigned new_alloc, new_size, size_suffix;
++      
++      // don't use strlen - it can overrun the len passed in!
++      const char* p = str;
++      size_suffix = 0;
++
++      while ( *p && size_suffix < (unsigned)len )
++      {
++              ++p;
++              ++size_suffix;
++      }
++    if ( !size_suffix)
++        return;
++
++    new_size = length () + size_suffix + 1;
++    // check if we need to expand
++    if (new_size > allocated)
++    {
++        // compute new size
++        new_alloc = assign_new_size (new_size);
++
++        // allocate new buffer
++        new_string = new char [new_alloc];        
++        new_string [0] = 0;
++
++        // copy the previous allocated buffer into this one
++        if (allocated && cstring)
++            // strcpy (new_string, cstring);
++            memcpy (new_string, cstring, length ());
++
++        // append the suffix. It does exist, otherwize we wouldn't be expanding 
++        // strncat (new_string, str, len);
++        memcpy (new_string + length (), 
++                str,
++                size_suffix);
++
++        // return previsously allocated buffer if any
++        if (allocated && cstring)
++            delete [] cstring;
++
++        // update member variables
++        cstring = new_string;
++        allocated = new_alloc;
++    }
++    else
++    {
++        // we know we can safely append the new string
++        // strncat (cstring, str, len);
++        memcpy (cstring + length (), 
++                str,
++                size_suffix);
++    }
++    current_length = new_size - 1;
++    cstring [current_length] = 0;
++}
++
++
++// append a const char * to an existing TiXmlString
++void TiXmlString::append( const char * suffix )
++{
++    char * new_string;
++    unsigned new_alloc, new_size;
++
++    new_size = length () + strlen (suffix) + 1;
++    // check if we need to expand
++    if (new_size > allocated)
++    {
++        // compute new size
++        new_alloc = assign_new_size (new_size);
++
++        // allocate new buffer
++        new_string = new char [new_alloc];        
++        new_string [0] = 0;
++
++        // copy the previous allocated buffer into this one
++        if (allocated && cstring)
++            memcpy (new_string, cstring, 1 + length ());
++            // strcpy (new_string, cstring);
++
++        // append the suffix. It does exist, otherwize we wouldn't be expanding 
++        // strcat (new_string, suffix);
++        memcpy (new_string + length (), 
++                suffix,
++                strlen (suffix) + 1);
++
++        // return previsously allocated buffer if any
++        if (allocated && cstring)
++            delete [] cstring;
++
++        // update member variables
++        cstring = new_string;
++        allocated = new_alloc;
++    }
++    else
++    {
++        // we know we can safely append the new string
++        // strcat (cstring, suffix);
++        memcpy (cstring + length (), 
++                suffix, 
++                strlen (suffix) + 1);
++    }
++    current_length = new_size - 1;
++}
++
++// Check for TiXmlString equuivalence
++//bool TiXmlString::operator == (const TiXmlString & compare) const
++//{
++//    return (! strcmp (c_str (), compare . c_str ()));
++//}
++
++//unsigned TiXmlString::length () const
++//{
++//    if (allocated)
++//        // return strlen (cstring);
++//        return current_length;
++//    return 0;
++//}
++
++
++unsigned TiXmlString::find (char tofind, unsigned offset) const
++{
++    char * lookup;
++
++    if (offset >= length ())
++        return (unsigned) notfound;
++    for (lookup = cstring + offset; * lookup; lookup++)
++        if (* lookup == tofind)
++            return lookup - cstring;
++    return (unsigned) notfound;
++}
++
++
++bool TiXmlString::operator == (const TiXmlString & compare) const
++{
++      if ( allocated && compare.allocated )
++      {
++              assert( cstring );
++              assert( compare.cstring );
++              return ( strcmp( cstring, compare.cstring ) == 0 );
++      }
++      return false;
++}
++
++
++bool TiXmlString::operator < (const TiXmlString & compare) const
++{
++      if ( allocated && compare.allocated )
++      {
++              assert( cstring );
++              assert( compare.cstring );
++              return ( strcmp( cstring, compare.cstring ) > 0 );
++      }
++      return false;
++}
++
++
++bool TiXmlString::operator > (const TiXmlString & compare) const
++{
++      if ( allocated && compare.allocated )
++      {
++              assert( cstring );
++              assert( compare.cstring );
++              return ( strcmp( cstring, compare.cstring ) < 0 );
++      }
++      return false;
++}
++
++
++#endif        // TIXML_USE_STL
++#endif /* SETUP */
+diff -NaurwB vdr-1.7.10/tinystr.h vdr-1.7.10-patched/tinystr.h
+--- vdr-1.7.10/tinystr.h       1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.10-patched/tinystr.h       2009-12-18 06:25:25.000000000 +0100
+@@ -0,0 +1,244 @@
++#ifdef USE_SETUP
++/*
++www.sourceforge.net/projects/tinyxml
++Original file by Yves Berquin.
++
++This software is provided 'as-is', without any express or implied 
++warranty. In no event will the authors be held liable for any 
++damages arising from the use of this software.
++
++Permission is granted to anyone to use this software for any 
++purpose, including commercial applications, and to alter it and 
++redistribute it freely, subject to the following restrictions:
++
++1. The origin of this software must not be misrepresented; you must 
++not claim that you wrote the original software. If you use this 
++software in a product, an acknowledgment in the product documentation 
++would be appreciated but is not required.
++
++2. Altered source versions must be plainly marked as such, and
++must not be misrepresented as being the original software.
++
++3. This notice may not be removed or altered from any source 
++distribution.
++*/
++
++#include "tinyxml.h"
++
++
++#ifndef TIXML_USE_STL
++
++#ifndef TIXML_STRING_INCLUDED
++#define TIXML_STRING_INCLUDED
++
++#ifdef _MSC_VER
++#pragma warning( disable : 4786 )     // Debugger truncating names.
++#endif
++
++#include <assert.h>
++
++/*
++   TiXmlString is an emulation of the std::string template.
++   Its purpose is to allow compiling TinyXML on compilers with no or poor STL support.
++   Only the member functions relevant to the TinyXML project have been implemented.
++   The buffer allocation is made by a simplistic power of 2 like mechanism : if we increase
++   a string and there's no more room, we allocate a buffer twice as big as we need.
++*/
++class TiXmlString
++{
++  public :
++    // TiXmlString constructor, based on a string
++    TiXmlString (const char * instring);
++
++    // TiXmlString empty constructor
++    TiXmlString ()
++    {
++        allocated = 0;
++        cstring = NULL;
++        current_length = 0;
++    }
++
++    // TiXmlString copy constructor
++    TiXmlString (const TiXmlString& copy);
++
++    // TiXmlString destructor
++    ~ TiXmlString ()
++    {
++        empty_it ();
++    }
++
++    // Convert a TiXmlString into a classical char *
++    const char * c_str () const
++    {
++        if (allocated)
++            return cstring;
++        return "";
++    }
++
++    // Return the length of a TiXmlString
++    unsigned length () const
++      {
++              return ( allocated ) ? current_length : 0;
++      }
++
++    // TiXmlString = operator
++    void operator = (const char * content);
++
++    // = operator
++    void operator = (const TiXmlString & copy);
++
++    // += operator. Maps to append
++    TiXmlString& operator += (const char * suffix)
++    {
++        append (suffix);
++              return *this;
++    }
++
++    // += operator. Maps to append
++    TiXmlString& operator += (char single)
++    {
++        append (single);
++              return *this;
++    }
++
++    // += operator. Maps to append
++    TiXmlString& operator += (TiXmlString & suffix)
++    {
++        append (suffix);
++              return *this;
++    }
++    bool operator == (const TiXmlString & compare) const;
++    bool operator < (const TiXmlString & compare) const;
++    bool operator > (const TiXmlString & compare) const;
++
++    // Checks if a TiXmlString is empty
++    bool empty () const
++    {
++        return length () ? false : true;
++    }
++
++    // single char extraction
++    const char& at (unsigned index) const
++    {
++        assert( index < length ());
++        return cstring [index];
++    }
++
++    // find a char in a string. Return TiXmlString::notfound if not found
++    unsigned find (char lookup) const
++    {
++        return find (lookup, 0);
++    }
++
++    // find a char in a string from an offset. Return TiXmlString::notfound if not found
++    unsigned find (char tofind, unsigned offset) const;
++
++    /*        Function to reserve a big amount of data when we know we'll need it. Be aware that this
++              function clears the content of the TiXmlString if any exists.
++    */
++    void reserve (unsigned size)
++    {
++        empty_it ();
++        if (size)
++        {
++            allocated = size;
++            cstring = new char [size];
++            cstring [0] = 0;
++            current_length = 0;
++        }
++    }
++
++    // [] operator 
++    char& operator [] (unsigned index) const
++    {
++        assert( index < length ());
++        return cstring [index];
++    }
++
++    // Error value for find primitive 
++    enum {    notfound = 0xffffffff,
++            npos = notfound };
++
++    void append (const char *str, int len );
++
++  protected :
++
++    // The base string
++    char * cstring;
++    // Number of chars allocated
++    unsigned allocated;
++    // Current string size
++    unsigned current_length;
++
++    // New size computation. It is simplistic right now : it returns twice the amount
++    // we need
++    unsigned assign_new_size (unsigned minimum_to_allocate)
++    {
++        return minimum_to_allocate * 2;
++    }
++
++    // Internal function that clears the content of a TiXmlString
++    void empty_it ()
++    {
++        if (cstring)
++            delete [] cstring;
++        cstring = NULL;
++        allocated = 0;
++        current_length = 0;
++    }
++
++    void append (const char *suffix );
++
++    // append function for another TiXmlString
++    void append (const TiXmlString & suffix)
++    {
++        append (suffix . c_str ());
++    }
++
++    // append for a single char.
++    void append (char single)
++    {
++        if ( cstring && current_length < (allocated-1) )
++              {
++                      cstring[ current_length ] = single;
++                      ++current_length;
++                      cstring[ current_length ] = 0;
++              }
++              else
++              {
++                      char smallstr [2];
++                      smallstr [0] = single;
++                      smallstr [1] = 0;
++                      append (smallstr);
++              }
++    }
++
++} ;
++
++/* 
++   TiXmlOutStream is an emulation of std::ostream. It is based on TiXmlString.
++   Only the operators that we need for TinyXML have been developped.
++*/
++class TiXmlOutStream : public TiXmlString
++{
++public :
++    TiXmlOutStream () : TiXmlString () {}
++
++    // TiXmlOutStream << operator. Maps to TiXmlString::append
++    TiXmlOutStream & operator << (const char * in)
++    {
++        append (in);
++        return (* this);
++    }
++
++    // TiXmlOutStream << operator. Maps to TiXmlString::append
++    TiXmlOutStream & operator << (const TiXmlString & in)
++    {
++        append (in . c_str ());
++        return (* this);
++    }
++} ;
++
++#endif        // TIXML_STRING_INCLUDED
++#endif        // TIXML_USE_STL
++#endif /* SETUP */
+diff -NaurwB vdr-1.7.10/tinyxml.c vdr-1.7.10-patched/tinyxml.c
+--- vdr-1.7.10/tinyxml.c       1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.10-patched/tinyxml.c       2009-12-18 06:25:25.000000000 +0100
+@@ -0,0 +1,1429 @@
++#ifdef USE_SETUP
++/*
++www.sourceforge.net/projects/tinyxml
++Original code (2.0 and earlier )copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com)
++
++This software is provided 'as-is', without any express or implied
++warranty. In no event will the authors be held liable for any
++damages arising from the use of this software.
++
++Permission is granted to anyone to use this software for any
++purpose, including commercial applications, and to alter it and
++redistribute it freely, subject to the following restrictions:
++
++1. The origin of this software must not be misrepresented; you must
++not claim that you wrote the original software. If you use this
++software in a product, an acknowledgment in the product documentation
++would be appreciated but is not required.
++
++2. Altered source versions must be plainly marked as such, and
++must not be misrepresented as being the original software.
++
++3. This notice may not be removed or altered from any source
++distribution.
++*/
++
++#include <ctype.h>
++#include "tinyxml.h"
++
++#ifdef TIXML_USE_STL
++#include <sstream>
++#endif
++
++
++bool TiXmlBase::condenseWhiteSpace = true;
++
++void TiXmlBase::PutString( const TIXML_STRING& str, TIXML_OSTREAM* stream )
++{
++      TIXML_STRING buffer;
++      PutString( str, &buffer );
++      (*stream) << buffer;
++}
++
++void TiXmlBase::PutString( const TIXML_STRING& str, TIXML_STRING* outString )
++{
++      int i=0;
++
++      while( i<(int)str.length() )
++      {
++              unsigned char c = (unsigned char) str[i];
++
++              if (    c == '&' 
++                   && i < ( (int)str.length() - 2 )
++                       && str[i+1] == '#'
++                       && str[i+2] == 'x' )
++              {
++                      // Hexadecimal character reference.
++                      // Pass through unchanged.
++                      // &#xA9;       -- copyright symbol, for example.
++                      //
++                      // The -1 is a bug fix from Rob Laveaux. It keeps
++                      // an overflow from happening if there is no ';'.
++                      // There are actually 2 ways to exit this loop -
++                      // while fails (error case) and break (semicolon found).
++                      // However, there is no mechanism (currently) for
++                      // this function to return an error.
++                      while ( i<(int)str.length()-1 )
++                      {
++                              outString->append( str.c_str() + i, 1 );
++                              ++i;
++                              if ( str[i] == ';' )
++                                      break;
++                      }
++              }
++              else if ( c == '&' )
++              {
++                      outString->append( entity[0].str, entity[0].strLength );
++                      ++i;
++              }
++              else if ( c == '<' )
++              {
++                      outString->append( entity[1].str, entity[1].strLength );
++                      ++i;
++              }
++              else if ( c == '>' )
++              {
++                      outString->append( entity[2].str, entity[2].strLength );
++                      ++i;
++              }
++              else if ( c == '\"' )
++              {
++                      outString->append( entity[3].str, entity[3].strLength );
++                      ++i;
++              }
++              else if ( c == '\'' )
++              {
++                      outString->append( entity[4].str, entity[4].strLength );
++                      ++i;
++              }
++              else if ( c < 32 )
++              {
++                      // Easy pass at non-alpha/numeric/symbol
++                      // Below 32 is symbolic.
++                      char buf[ 32 ];
++                      sprintf( buf, "&#x%02X;", (unsigned) ( c & 0xff ) );
++                      outString->append( buf, strlen( buf ) );
++                      ++i;
++              }
++              else
++              {
++                      //char realc = (char) c;
++                      //outString->append( &realc, 1 );
++                      *outString += (char) c; // somewhat more efficient function call.
++                      ++i;
++              }
++      }
++}
++
++
++// <-- Strange class for a bug fix. Search for STL_STRING_BUG
++TiXmlBase::StringToBuffer::StringToBuffer( const TIXML_STRING& str )
++{
++      buffer = new char[ str.length()+1 ];
++      if ( buffer )
++      {
++              strcpy( buffer, str.c_str() );
++      }
++}
++
++
++TiXmlBase::StringToBuffer::~StringToBuffer()
++{
++      delete [] buffer;
++}
++// End strange bug fix. -->
++
++
++TiXmlNode::TiXmlNode( NodeType _type ) : TiXmlBase()
++{
++      parent = 0;
++      type = _type;
++      firstChild = 0;
++      lastChild = 0;
++      prev = 0;
++      next = 0;
++}
++
++
++TiXmlNode::~TiXmlNode()
++{
++      TiXmlNode* node = firstChild;
++      TiXmlNode* temp = 0;
++
++      while ( node )
++      {
++              temp = node;
++              node = node->next;
++              delete temp;
++      }       
++}
++
++
++void TiXmlNode::CopyTo( TiXmlNode* target ) const
++{
++      target->SetValue (value.c_str() );
++      target->userData = userData; 
++}
++
++
++void TiXmlNode::Clear()
++{
++      TiXmlNode* node = firstChild;
++      TiXmlNode* temp = 0;
++
++      while ( node )
++      {
++              temp = node;
++              node = node->next;
++              delete temp;
++      }       
++
++      firstChild = 0;
++      lastChild = 0;
++}
++
++
++TiXmlNode* TiXmlNode::LinkEndChild( TiXmlNode* node )
++{
++      node->parent = this;
++
++      node->prev = lastChild;
++      node->next = 0;
++
++      if ( lastChild )
++              lastChild->next = node;
++      else
++              firstChild = node;                      // it was an empty list.
++
++      lastChild = node;
++      return node;
++}
++
++
++TiXmlNode* TiXmlNode::InsertEndChild( const TiXmlNode& addThis )
++{
++      TiXmlNode* node = addThis.Clone();
++      if ( !node )
++              return 0;
++
++      return LinkEndChild( node );
++}
++
++
++TiXmlNode* TiXmlNode::InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis )
++{     
++      if ( !beforeThis || beforeThis->parent != this )
++              return 0;
++
++      TiXmlNode* node = addThis.Clone();
++      if ( !node )
++              return 0;
++      node->parent = this;
++
++      node->next = beforeThis;
++      node->prev = beforeThis->prev;
++      if ( beforeThis->prev )
++      {
++              beforeThis->prev->next = node;
++      }
++      else
++      {
++              assert( firstChild == beforeThis );
++              firstChild = node;
++      }
++      beforeThis->prev = node;
++      return node;
++}
++
++
++TiXmlNode* TiXmlNode::InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis )
++{
++      if ( !afterThis || afterThis->parent != this )
++              return 0;
++
++      TiXmlNode* node = addThis.Clone();
++      if ( !node )
++              return 0;
++      node->parent = this;
++
++      node->prev = afterThis;
++      node->next = afterThis->next;
++      if ( afterThis->next )
++      {
++              afterThis->next->prev = node;
++      }
++      else
++      {
++              assert( lastChild == afterThis );
++              lastChild = node;
++      }
++      afterThis->next = node;
++      return node;
++}
++
++
++TiXmlNode* TiXmlNode::ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis )
++{
++      if ( replaceThis->parent != this )
++              return 0;
++
++      TiXmlNode* node = withThis.Clone();
++      if ( !node )
++              return 0;
++
++      node->next = replaceThis->next;
++      node->prev = replaceThis->prev;
++
++      if ( replaceThis->next )
++              replaceThis->next->prev = node;
++      else
++              lastChild = node;
++
++      if ( replaceThis->prev )
++              replaceThis->prev->next = node;
++      else
++              firstChild = node;
++
++      delete replaceThis;
++      node->parent = this;
++      return node;
++}
++
++
++bool TiXmlNode::RemoveChild( TiXmlNode* removeThis )
++{
++      if ( removeThis->parent != this )
++      {       
++              assert( 0 );
++              return false;
++      }
++
++      if ( removeThis->next )
++              removeThis->next->prev = removeThis->prev;
++      else
++              lastChild = removeThis->prev;
++
++      if ( removeThis->prev )
++              removeThis->prev->next = removeThis->next;
++      else
++              firstChild = removeThis->next;
++
++      delete removeThis;
++      return true;
++}
++
++TiXmlNode* TiXmlNode::FirstChild( const char * _value ) const
++{
++      TiXmlNode* node;
++      for ( node = firstChild; node; node = node->next )
++      {
++              if ( node->SValue() == TIXML_STRING( _value ))
++                      return node;
++      }
++      return 0;
++}
++
++TiXmlNode* TiXmlNode::LastChild( const char * _value ) const
++{
++      TiXmlNode* node;
++      for ( node = lastChild; node; node = node->prev )
++      {
++              if ( node->SValue() == TIXML_STRING (_value))
++                      return node;
++      }
++      return 0;
++}
++
++TiXmlNode* TiXmlNode::IterateChildren( TiXmlNode* previous ) const
++{
++      if ( !previous )
++      {
++              return FirstChild();
++      }
++      else
++      {
++              assert( previous->parent == this );
++              return previous->NextSibling();
++      }
++}
++
++TiXmlNode* TiXmlNode::IterateChildren( const char * val, TiXmlNode* previous ) const
++{
++      if ( !previous )
++      {
++              return FirstChild( val );
++      }
++      else
++      {
++              assert( previous->parent == this );
++              return previous->NextSibling( val );
++      }
++}
++
++TiXmlNode* TiXmlNode::NextSibling( const char * _value ) const
++{
++      TiXmlNode* node;
++      for ( node = next; node; node = node->next )
++      {
++              if ( node->SValue() == TIXML_STRING (_value))
++                      return node;
++      }
++      return 0;
++}
++
++
++TiXmlNode* TiXmlNode::PreviousSibling( const char * _value ) const
++{
++      TiXmlNode* node;
++      for ( node = prev; node; node = node->prev )
++      {
++              if ( node->SValue() == TIXML_STRING (_value))
++                      return node;
++      }
++      return 0;
++}
++
++void TiXmlElement::RemoveAttribute( const char * name )
++{
++      TiXmlAttribute* node = attributeSet.Find( name );
++      if ( node )
++      {
++              attributeSet.Remove( node );
++              delete node;
++      }
++}
++
++TiXmlElement* TiXmlNode::FirstChildElement() const
++{
++      TiXmlNode* node;
++
++      for (   node = FirstChild();
++                      node;
++                      node = node->NextSibling() )
++      {
++              if ( node->ToElement() )
++                      return node->ToElement();
++      }
++      return 0;
++}
++
++TiXmlElement* TiXmlNode::FirstChildElement( const char * _value ) const
++{
++      TiXmlNode* node;
++
++      for (   node = FirstChild( _value );
++                      node;
++                      node = node->NextSibling( _value ) )
++      {
++              if ( node->ToElement() )
++                      return node->ToElement();
++      }
++      return 0;
++}
++
++
++TiXmlElement* TiXmlNode::NextSiblingElement() const
++{
++      TiXmlNode* node;
++
++      for (   node = NextSibling();
++      node;
++      node = node->NextSibling() )
++      {
++              if ( node->ToElement() )
++                      return node->ToElement();
++      }
++      return 0;
++}
++
++TiXmlElement* TiXmlNode::NextSiblingElement( const char * _value ) const
++{
++      TiXmlNode* node;
++
++      for (   node = NextSibling( _value );
++      node;
++      node = node->NextSibling( _value ) )
++      {
++              if ( node->ToElement() )
++                      return node->ToElement();
++      }
++      return 0;
++}
++
++
++
++TiXmlDocument* TiXmlNode::GetDocument() const
++{
++      const TiXmlNode* node;
++
++      for( node = this; node; node = node->parent )
++      {
++              if ( node->ToDocument() )
++                      return node->ToDocument();
++      }
++      return 0;
++}
++
++
++TiXmlElement::TiXmlElement (const char * _value)
++      : TiXmlNode( TiXmlNode::ELEMENT )
++{
++      firstChild = lastChild = 0;
++      value = _value;
++}
++
++
++#ifdef TIXML_USE_STL
++TiXmlElement::TiXmlElement( const std::string& _value ) 
++      : TiXmlNode( TiXmlNode::ELEMENT )
++{
++      firstChild = lastChild = 0;
++      value = _value;
++}
++#endif
++
++
++TiXmlElement::TiXmlElement( const TiXmlElement& copy)
++      : TiXmlNode( TiXmlNode::ELEMENT )
++{
++      firstChild = lastChild = 0;
++      copy.CopyTo( this );    
++}
++
++
++void TiXmlElement::operator=( const TiXmlElement& base )
++{
++      ClearThis();
++      base.CopyTo( this );
++}
++
++
++TiXmlElement::~TiXmlElement()
++{
++      ClearThis();
++}
++
++
++void TiXmlElement::ClearThis()
++{
++      Clear();
++      while( attributeSet.First() )
++      {
++              TiXmlAttribute* node = attributeSet.First();
++              attributeSet.Remove( node );
++              delete node;
++      }
++}
++
++
++const char * TiXmlElement::Attribute( const char * name ) const
++{
++      TiXmlAttribute* node = attributeSet.Find( name );
++
++      if ( node )
++              return node->Value();
++
++      return 0;
++}
++
++
++const char * TiXmlElement::Attribute( const char * name, int* i ) const
++{
++      const char * s = Attribute( name );
++      if ( i )
++      {
++              if ( s )
++                      *i = atoi( s );
++              else
++                      *i = 0;
++      }
++      return s;
++}
++
++
++const char * TiXmlElement::Attribute( const char * name, double* d ) const
++{
++      const char * s = Attribute( name );
++      if ( d )
++      {
++              if ( s )
++                      *d = atof( s );
++              else
++                      *d = 0;
++      }
++      return s;
++}
++
++
++int TiXmlElement::QueryIntAttribute( const char* name, int* ival ) const
++{
++      TiXmlAttribute* node = attributeSet.Find( name );
++      if ( !node )
++              return TIXML_NO_ATTRIBUTE;
++
++      return node->QueryIntValue( ival );
++}
++
++
++int TiXmlElement::QueryDoubleAttribute( const char* name, double* dval ) const
++{
++      TiXmlAttribute* node = attributeSet.Find( name );
++      if ( !node )
++              return TIXML_NO_ATTRIBUTE;
++
++      return node->QueryDoubleValue( dval );
++}
++
++
++void TiXmlElement::SetAttribute( const char * name, int val )
++{     
++      char buf[64];
++      sprintf( buf, "%d", val );
++      SetAttribute( name, buf );
++}
++
++
++void TiXmlElement::SetDoubleAttribute( const char * name, double val )
++{     
++      char buf[128];
++      sprintf( buf, "%f", val );
++      SetAttribute( name, buf );
++}
++
++
++void TiXmlElement::SetAttribute( const char * name, const char * _value )
++{
++      TiXmlAttribute* node = attributeSet.Find( name );
++      if ( node )
++      {
++              node->SetValue( _value );
++              return;
++      }
++
++      TiXmlAttribute* attrib = new TiXmlAttribute( name, _value );
++      if ( attrib )
++      {
++              attributeSet.Add( attrib );
++      }
++      else
++      {
++              TiXmlDocument* document = GetDocument();
++              if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN );
++      }
++}
++
++void TiXmlElement::Print( FILE* cfile, int depth ) const
++{
++      int i;
++      for ( i=0; i<depth; i++ )
++      {
++              fprintf( cfile, "    " );
++      }
++
++      fprintf( cfile, "<%s", value.c_str() );
++
++      TiXmlAttribute* attrib;
++      for ( attrib = attributeSet.First(); attrib; attrib = attrib->Next() )
++      {
++              fprintf( cfile, " " );
++              attrib->Print( cfile, depth );
++      }
++
++      // There are 3 different formatting approaches:
++      // 1) An element without children is printed as a <foo /> node
++      // 2) An element with only a text child is printed as <foo> text </foo>
++      // 3) An element with children is printed on multiple lines.
++      TiXmlNode* node;
++      if ( !firstChild )
++      {
++              fprintf( cfile, " />" );
++      }
++      else if ( firstChild == lastChild && firstChild->ToText() )
++      {
++              fprintf( cfile, ">" );
++              firstChild->Print( cfile, depth + 1 );
++              fprintf( cfile, "</%s>", value.c_str() );
++      }
++      else
++      {
++              fprintf( cfile, ">" );
++
++              for ( node = firstChild; node; node=node->NextSibling() )
++              {
++                      if ( !node->ToText() )
++                      {
++                              fprintf( cfile, "\n" );
++                      }
++                      node->Print( cfile, depth+1 );
++              }
++              fprintf( cfile, "\n" );
++              for( i=0; i<depth; ++i )
++              fprintf( cfile, "    " );
++              fprintf( cfile, "</%s>", value.c_str() );
++      }
++}
++
++void TiXmlElement::StreamOut( TIXML_OSTREAM * stream ) const
++{
++      (*stream) << "<" << value;
++
++      TiXmlAttribute* attrib;
++      for ( attrib = attributeSet.First(); attrib; attrib = attrib->Next() )
++      {       
++              (*stream) << " ";
++              attrib->StreamOut( stream );
++      }
++
++      // If this node has children, give it a closing tag. Else
++      // make it an empty tag.
++      TiXmlNode* node;
++      if ( firstChild )
++      {               
++              (*stream) << ">";
++
++              for ( node = firstChild; node; node=node->NextSibling() )
++              {
++                      node->StreamOut( stream );
++              }
++              (*stream) << "</" << value << ">";
++      }
++      else
++      {
++              (*stream) << " />";
++      }
++}
++
++
++void TiXmlElement::CopyTo( TiXmlElement* target ) const
++{
++      // superclass:
++      TiXmlNode::CopyTo( target );
++
++      // Element class: 
++      // Clone the attributes, then clone the children.
++      TiXmlAttribute* attribute = 0;
++      for(    attribute = attributeSet.First();
++      attribute;
++      attribute = attribute->Next() )
++      {
++              target->SetAttribute( attribute->Name(), attribute->Value() );
++      }
++
++      TiXmlNode* node = 0;
++      for ( node = firstChild; node; node = node->NextSibling() )
++      {
++              target->LinkEndChild( node->Clone() );
++      }
++}
++
++
++TiXmlNode* TiXmlElement::Clone() const
++{
++      TiXmlElement* clone = new TiXmlElement( Value() );
++      if ( !clone )
++              return 0;
++
++      CopyTo( clone );
++      return clone;
++}
++
++
++TiXmlDocument::TiXmlDocument() : TiXmlNode( TiXmlNode::DOCUMENT )
++{
++      tabsize = 4;
++      ClearError();
++}
++
++TiXmlDocument::TiXmlDocument( const char * documentName ) : TiXmlNode( TiXmlNode::DOCUMENT )
++{
++      tabsize = 4;
++      value = documentName;
++      ClearError();
++}
++
++
++#ifdef TIXML_USE_STL
++TiXmlDocument::TiXmlDocument( const std::string& documentName ) : TiXmlNode( TiXmlNode::DOCUMENT )
++{
++      tabsize = 4;
++    value = documentName;
++      ClearError();
++}
++#endif
++
++
++TiXmlDocument::TiXmlDocument( const TiXmlDocument& copy ) : TiXmlNode( TiXmlNode::DOCUMENT )
++{
++      copy.CopyTo( this );
++}
++
++
++void TiXmlDocument::operator=( const TiXmlDocument& copy )
++{
++      Clear();
++      copy.CopyTo( this );
++}
++
++
++bool TiXmlDocument::LoadFile( TiXmlEncoding encoding )
++{
++      // See STL_STRING_BUG below.
++      StringToBuffer buf( value );
++
++      if ( buf.buffer && LoadFile( buf.buffer, encoding ) )
++              return true;
++
++      return false;
++}
++
++
++bool TiXmlDocument::SaveFile() const
++{
++      // See STL_STRING_BUG below.
++      StringToBuffer buf( value );
++
++      if ( buf.buffer && SaveFile( buf.buffer ) )
++              return true;
++
++      return false;
++}
++
++bool TiXmlDocument::LoadFile( const char* filename, TiXmlEncoding encoding )
++{
++      // Delete the existing data:
++      Clear();
++      location.Clear();
++
++      // There was a really terrifying little bug here. The code:
++      //              value = filename
++      // in the STL case, cause the assignment method of the std::string to
++      // be called. What is strange, is that the std::string had the same
++      // address as it's c_str() method, and so bad things happen. Looks
++      // like a bug in the Microsoft STL implementation.
++      // See STL_STRING_BUG above.
++      // Fixed with the StringToBuffer class.
++      value = filename;
++
++      FILE* file = fopen( value.c_str (), "r" );
++
++      if ( file )
++      {
++              // Get the file size, so we can pre-allocate the string. HUGE speed impact.
++              long length = 0;
++              fseek( file, 0, SEEK_END );
++              length = ftell( file );
++              fseek( file, 0, SEEK_SET );
++
++              // Strange case, but good to handle up front.
++              if ( length == 0 )
++              {
++                      fclose( file );
++                      return false;
++              }
++
++              // If we have a file, assume it is all one big XML file, and read it in.
++              // The document parser may decide the document ends sooner than the entire file, however.
++              TIXML_STRING data;
++              data.reserve( length );
++
++              const int BUF_SIZE = 2048;
++              char buf[BUF_SIZE];
++
++              while( fgets( buf, BUF_SIZE, file ) )
++              {
++                      data += buf;
++              }
++              fclose( file );
++
++              Parse( data.c_str(), 0, encoding );
++
++              if (  Error() )
++            return false;
++        else
++                      return true;
++      }
++      SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN );
++      return false;
++}
++
++bool TiXmlDocument::SaveFile( const char * filename ) const
++{
++      // The old c stuff lives on...
++      FILE* fp = fopen( filename, "w" );
++      if ( fp )
++      {
++              Print( fp, 0 );
++              fclose( fp );
++              return true;
++      }
++      return false;
++}
++
++
++void TiXmlDocument::CopyTo( TiXmlDocument* target ) const
++{
++      TiXmlNode::CopyTo( target );
++
++      target->error = error;
++      target->errorDesc = errorDesc.c_str ();
++
++      TiXmlNode* node = 0;
++      for ( node = firstChild; node; node = node->NextSibling() )
++      {
++              target->LinkEndChild( node->Clone() );
++      }       
++}
++
++
++TiXmlNode* TiXmlDocument::Clone() const
++{
++      TiXmlDocument* clone = new TiXmlDocument();
++      if ( !clone )
++              return 0;
++
++      CopyTo( clone );
++      return clone;
++}
++
++
++void TiXmlDocument::Print( FILE* cfile, int depth ) const
++{
++      TiXmlNode* node;
++      for ( node=FirstChild(); node; node=node->NextSibling() )
++      {
++              node->Print( cfile, depth );
++              fprintf( cfile, "\n" );
++      }
++}
++
++void TiXmlDocument::StreamOut( TIXML_OSTREAM * out ) const
++{
++      TiXmlNode* node;
++      for ( node=FirstChild(); node; node=node->NextSibling() )
++      {
++              node->StreamOut( out );
++
++              // Special rule for streams: stop after the root element.
++              // The stream in code will only read one element, so don't
++              // write more than one.
++              if ( node->ToElement() )
++                      break;
++      }
++}
++
++
++TiXmlAttribute* TiXmlAttribute::Next() const
++{
++      // We are using knowledge of the sentinel. The sentinel
++      // have a value or name.
++      if ( next->value.empty() && next->name.empty() )
++              return 0;
++      return next;
++}
++
++
++TiXmlAttribute* TiXmlAttribute::Previous() const
++{
++      // We are using knowledge of the sentinel. The sentinel
++      // have a value or name.
++      if ( prev->value.empty() && prev->name.empty() )
++              return 0;
++      return prev;
++}
++
++
++void TiXmlAttribute::Print( FILE* cfile, int /*depth*/ ) const
++{
++      TIXML_STRING n, v;
++
++      PutString( name, &n );
++      PutString( value, &v );
++
++      if (value.find ('\"') == TIXML_STRING::npos)
++              fprintf (cfile, "%s=\"%s\"", n.c_str(), v.c_str() );
++      else
++              fprintf (cfile, "%s='%s'", n.c_str(), v.c_str() );
++}
++
++
++void TiXmlAttribute::StreamOut( TIXML_OSTREAM * stream ) const
++{
++      if (value.find( '\"' ) != TIXML_STRING::npos)
++      {
++              PutString( name, stream );
++              (*stream) << "=" << "'";
++              PutString( value, stream );
++              (*stream) << "'";
++      }
++      else
++      {
++              PutString( name, stream );
++              (*stream) << "=" << "\"";
++              PutString( value, stream );
++              (*stream) << "\"";
++      }
++}
++
++int TiXmlAttribute::QueryIntValue( int* ival ) const
++{
++      if ( sscanf( value.c_str(), "%d", ival ) == 1 )
++              return TIXML_SUCCESS;
++      return TIXML_WRONG_TYPE;
++}
++
++int TiXmlAttribute::QueryDoubleValue( double* dval ) const
++{
++      if ( sscanf( value.c_str(), "%lf", dval ) == 1 )
++              return TIXML_SUCCESS;
++      return TIXML_WRONG_TYPE;
++}
++
++void TiXmlAttribute::SetIntValue( int _value )
++{
++      char buf [64];
++      sprintf (buf, "%d", _value);
++      SetValue (buf);
++}
++
++void TiXmlAttribute::SetDoubleValue( double _value )
++{
++      char buf [64];
++      sprintf (buf, "%lf", _value);
++      SetValue (buf);
++}
++
++const int TiXmlAttribute::IntValue() const
++{
++      return atoi (value.c_str ());
++}
++
++const double  TiXmlAttribute::DoubleValue() const
++{
++      return atof (value.c_str ());
++}
++
++
++TiXmlComment::TiXmlComment( const TiXmlComment& copy ) : TiXmlNode( TiXmlNode::COMMENT )
++{
++      copy.CopyTo( this );
++}
++
++
++void TiXmlComment::operator=( const TiXmlComment& base )
++{
++      Clear();
++      base.CopyTo( this );
++}
++
++
++void TiXmlComment::Print( FILE* cfile, int depth ) const
++{
++      for ( int i=0; i<depth; i++ )
++      {
++              fputs( "    ", cfile );
++      }
++      fprintf( cfile, "<!--%s-->", value.c_str() );
++}
++
++void TiXmlComment::StreamOut( TIXML_OSTREAM * stream ) const
++{
++      (*stream) << "<!--";
++      //PutString( value, stream );
++      (*stream) << value;
++      (*stream) << "-->";
++}
++
++
++void TiXmlComment::CopyTo( TiXmlComment* target ) const
++{
++      TiXmlNode::CopyTo( target );
++}
++
++
++TiXmlNode* TiXmlComment::Clone() const
++{
++      TiXmlComment* clone = new TiXmlComment();
++
++      if ( !clone )
++              return 0;
++
++      CopyTo( clone );
++      return clone;
++}
++
++
++void TiXmlText::Print( FILE* cfile, int /*depth*/ ) const
++{
++      TIXML_STRING buffer;
++      PutString( value, &buffer );
++      fprintf( cfile, "%s", buffer.c_str() );
++}
++
++
++void TiXmlText::StreamOut( TIXML_OSTREAM * stream ) const
++{
++      PutString( value, stream );
++}
++
++
++void TiXmlText::CopyTo( TiXmlText* target ) const
++{
++      TiXmlNode::CopyTo( target );
++}
++
++
++TiXmlNode* TiXmlText::Clone() const
++{     
++      TiXmlText* clone = 0;
++      clone = new TiXmlText( "" );
++
++      if ( !clone )
++              return 0;
++
++      CopyTo( clone );
++      return clone;
++}
++
++
++TiXmlDeclaration::TiXmlDeclaration( const char * _version,
++                                                                      const char * _encoding,
++                                                                      const char * _standalone )
++      : TiXmlNode( TiXmlNode::DECLARATION )
++{
++      version = _version;
++      encoding = _encoding;
++      standalone = _standalone;
++}
++
++
++#ifdef TIXML_USE_STL
++TiXmlDeclaration::TiXmlDeclaration(   const std::string& _version,
++                                                                      const std::string& _encoding,
++                                                                      const std::string& _standalone )
++      : TiXmlNode( TiXmlNode::DECLARATION )
++{
++      version = _version;
++      encoding = _encoding;
++      standalone = _standalone;
++}
++#endif
++
++
++TiXmlDeclaration::TiXmlDeclaration( const TiXmlDeclaration& copy )
++      : TiXmlNode( TiXmlNode::DECLARATION )
++{
++      copy.CopyTo( this );    
++}
++
++
++void TiXmlDeclaration::operator=( const TiXmlDeclaration& copy )
++{
++      Clear();
++      copy.CopyTo( this );
++}
++
++
++void TiXmlDeclaration::Print( FILE* cfile, int /*depth*/ ) const
++{
++      fprintf (cfile, "<?xml ");
++
++      if ( !version.empty() )
++              fprintf (cfile, "version=\"%s\" ", version.c_str ());
++      if ( !encoding.empty() )
++              fprintf (cfile, "encoding=\"%s\" ", encoding.c_str ());
++      if ( !standalone.empty() )
++              fprintf (cfile, "standalone=\"%s\" ", standalone.c_str ());
++      fprintf (cfile, "?>");
++}
++
++void TiXmlDeclaration::StreamOut( TIXML_OSTREAM * stream ) const
++{
++      (*stream) << "<?xml ";
++
++      if ( !version.empty() )
++      {
++              (*stream) << "version=\"";
++              PutString( version, stream );
++              (*stream) << "\" ";
++      }
++      if ( !encoding.empty() )
++      {
++              (*stream) << "encoding=\"";
++              PutString( encoding, stream );
++              (*stream ) << "\" ";
++      }
++      if ( !standalone.empty() )
++      {
++              (*stream) << "standalone=\"";
++              PutString( standalone, stream );
++              (*stream) << "\" ";
++      }
++      (*stream) << "?>";
++}
++
++
++void TiXmlDeclaration::CopyTo( TiXmlDeclaration* target ) const
++{
++      TiXmlNode::CopyTo( target );
++
++      target->version = version;
++      target->encoding = encoding;
++      target->standalone = standalone;
++}
++
++
++TiXmlNode* TiXmlDeclaration::Clone() const
++{     
++      TiXmlDeclaration* clone = new TiXmlDeclaration();
++
++      if ( !clone )
++              return 0;
++
++      CopyTo( clone );
++      return clone;
++}
++
++
++void TiXmlUnknown::Print( FILE* cfile, int depth ) const
++{
++      for ( int i=0; i<depth; i++ )
++              fprintf( cfile, "    " );
++      fprintf( cfile, "<%s>", value.c_str() );
++}
++
++
++void TiXmlUnknown::StreamOut( TIXML_OSTREAM * stream ) const
++{
++      (*stream) << "<" << value << ">";               // Don't use entities here! It is unknown.
++}
++
++
++void TiXmlUnknown::CopyTo( TiXmlUnknown* target ) const
++{
++      TiXmlNode::CopyTo( target );
++}
++
++
++TiXmlNode* TiXmlUnknown::Clone() const
++{
++      TiXmlUnknown* clone = new TiXmlUnknown();
++
++      if ( !clone )
++              return 0;
++
++      CopyTo( clone );
++      return clone;
++}
++
++
++TiXmlAttributeSet::TiXmlAttributeSet()
++{
++      sentinel.next = &sentinel;
++      sentinel.prev = &sentinel;
++}
++
++
++TiXmlAttributeSet::~TiXmlAttributeSet()
++{
++      assert( sentinel.next == &sentinel );
++      assert( sentinel.prev == &sentinel );
++}
++
++
++void TiXmlAttributeSet::Add( TiXmlAttribute* addMe )
++{
++      assert( !Find( addMe->Name() ) );       // Shouldn't be multiply adding to the set.
++
++      addMe->next = &sentinel;
++      addMe->prev = sentinel.prev;
++
++      sentinel.prev->next = addMe;
++      sentinel.prev      = addMe;
++}
++
++void TiXmlAttributeSet::Remove( TiXmlAttribute* removeMe )
++{
++      TiXmlAttribute* node;
++
++      for( node = sentinel.next; node != &sentinel; node = node->next )
++      {
++              if ( node == removeMe )
++              {
++                      node->prev->next = node->next;
++                      node->next->prev = node->prev;
++                      node->next = 0;
++                      node->prev = 0;
++                      return;
++              }
++      }
++      assert( 0 );            // we tried to remove a non-linked attribute.
++}
++
++TiXmlAttribute*       TiXmlAttributeSet::Find( const char * name ) const
++{
++      TiXmlAttribute* node;
++
++      for( node = sentinel.next; node != &sentinel; node = node->next )
++      {
++              if ( node->name == name )
++                      return node;
++      }
++      return 0;
++}
++
++
++#ifdef TIXML_USE_STL  
++TIXML_ISTREAM & operator >> (TIXML_ISTREAM & in, TiXmlNode & base)
++{
++      TIXML_STRING tag;
++      tag.reserve( 8 * 1000 );
++      base.StreamIn( &in, &tag );
++
++      base.Parse( tag.c_str(), 0, TIXML_DEFAULT_ENCODING );
++      return in;
++}
++#endif
++
++
++TIXML_OSTREAM & operator<< (TIXML_OSTREAM & out, const TiXmlNode & base)
++{
++      base.StreamOut (& out);
++      return out;
++}
++
++
++#ifdef TIXML_USE_STL  
++std::string & operator<< (std::string& out, const TiXmlNode& base )
++{
++   std::ostringstream os_stream( std::ostringstream::out );
++   base.StreamOut( &os_stream );
++   
++   out.append( os_stream.str() );
++   return out;
++}
++#endif
++
++
++TiXmlHandle TiXmlHandle::FirstChild() const
++{
++      if ( node )
++      {
++              TiXmlNode* child = node->FirstChild();
++              if ( child )
++                      return TiXmlHandle( child );
++      }
++      return TiXmlHandle( 0 );
++}
++
++
++TiXmlHandle TiXmlHandle::FirstChild( const char * value ) const
++{
++      if ( node )
++      {
++              TiXmlNode* child = node->FirstChild( value );
++              if ( child )
++                      return TiXmlHandle( child );
++      }
++      return TiXmlHandle( 0 );
++}
++
++
++TiXmlHandle TiXmlHandle::FirstChildElement() const
++{
++      if ( node )
++      {
++              TiXmlElement* child = node->FirstChildElement();
++              if ( child )
++                      return TiXmlHandle( child );
++      }
++      return TiXmlHandle( 0 );
++}
++
++
++TiXmlHandle TiXmlHandle::FirstChildElement( const char * value ) const
++{
++      if ( node )
++      {
++              TiXmlElement* child = node->FirstChildElement( value );
++              if ( child )
++                      return TiXmlHandle( child );
++      }
++      return TiXmlHandle( 0 );
++}
++
++
++TiXmlHandle TiXmlHandle::Child( int count ) const
++{
++      if ( node )
++      {
++              int i;
++              TiXmlNode* child = node->FirstChild();
++              for (   i=0;
++                              child && i<count;
++                              child = child->NextSibling(), ++i )
++              {
++                      // nothing
++              }
++              if ( child )
++                      return TiXmlHandle( child );
++      }
++      return TiXmlHandle( 0 );
++}
++
++
++TiXmlHandle TiXmlHandle::Child( const char* value, int count ) const
++{
++      if ( node )
++      {
++              int i;
++              TiXmlNode* child = node->FirstChild( value );
++              for (   i=0;
++                              child && i<count;
++                              child = child->NextSibling( value ), ++i )
++              {
++                      // nothing
++              }
++              if ( child )
++                      return TiXmlHandle( child );
++      }
++      return TiXmlHandle( 0 );
++}
++
++
++TiXmlHandle TiXmlHandle::ChildElement( int count ) const
++{
++      if ( node )
++      {
++              int i;
++              TiXmlElement* child = node->FirstChildElement();
++              for (   i=0;
++                              child && i<count;
++                              child = child->NextSiblingElement(), ++i )
++              {
++                      // nothing
++              }
++              if ( child )
++                      return TiXmlHandle( child );
++      }
++      return TiXmlHandle( 0 );
++}
++
++
++TiXmlHandle TiXmlHandle::ChildElement( const char* value, int count ) const
++{
++      if ( node )
++      {
++              int i;
++              TiXmlElement* child = node->FirstChildElement( value );
++              for (   i=0;
++                              child && i<count;
++                              child = child->NextSiblingElement( value ), ++i )
++              {
++                      // nothing
++              }
++              if ( child )
++                      return TiXmlHandle( child );
++      }
++      return TiXmlHandle( 0 );
++}
++#endif /* SETUP */
+diff -NaurwB vdr-1.7.10/tinyxmlerror.c vdr-1.7.10-patched/tinyxmlerror.c
+--- vdr-1.7.10/tinyxmlerror.c  1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.10-patched/tinyxmlerror.c  2009-12-18 06:25:25.000000000 +0100
+@@ -0,0 +1,53 @@
++#ifdef USE_SETUP
++/*
++www.sourceforge.net/projects/tinyxml
++Original code (2.0 and earlier )copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com)
++
++This software is provided 'as-is', without any express or implied 
++warranty. In no event will the authors be held liable for any 
++damages arising from the use of this software.
++
++Permission is granted to anyone to use this software for any 
++purpose, including commercial applications, and to alter it and 
++redistribute it freely, subject to the following restrictions:
++
++1. The origin of this software must not be misrepresented; you must
++not claim that you wrote the original software. If you use this
++software in a product, an acknowledgment in the product documentation
++would be appreciated but is not required.
++
++2. Altered source versions must be plainly marked as such, and
++must not be misrepresented as being the original software.
++
++3. This notice may not be removed or altered from any source
++distribution.
++*/
++
++#include "tinyxml.h"
++
++// The goal of the seperate error file is to make the first
++// step towards localization. tinyxml (currently) only supports
++// latin-1, but at least the error messages could now be translated.
++//
++// It also cleans up the code a bit.
++//
++
++const char* TiXmlBase::errorString[ TIXML_ERROR_STRING_COUNT ] =
++{
++      "No error",
++      "Error",
++      "Failed to open file",
++      "Memory allocation failed.",
++      "Error parsing Element.",
++      "Failed to read Element name",
++      "Error reading Element value.",
++      "Error reading Attributes.",
++      "Error: empty tag.",
++      "Error reading end tag.",
++      "Error parsing Unknown.",
++      "Error parsing Comment.",
++      "Error parsing Declaration.",
++      "Error document empty.",
++      "Error null (0) or unexpected EOF found in input stream.",
++};
++#endif /* SETUP */
+diff -NaurwB vdr-1.7.10/tinyxml.h vdr-1.7.10-patched/tinyxml.h
+--- vdr-1.7.10/tinyxml.h       1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.10-patched/tinyxml.h       2009-12-18 06:25:25.000000000 +0100
+@@ -0,0 +1,1372 @@
++#ifdef USE_SETUP
++/*
++www.sourceforge.net/projects/tinyxml
++Original code (2.0 and earlier )copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com)
++
++This software is provided 'as-is', without any express or implied
++warranty. In no event will the authors be held liable for any
++damages arising from the use of this software.
++
++Permission is granted to anyone to use this software for any
++purpose, including commercial applications, and to alter it and
++redistribute it freely, subject to the following restrictions:
++
++1. The origin of this software must not be misrepresented; you must
++not claim that you wrote the original software. If you use this
++software in a product, an acknowledgment in the product documentation
++would be appreciated but is not required.
++
++2. Altered source versions must be plainly marked as such, and
++must not be misrepresented as being the original software.
++
++3. This notice may not be removed or altered from any source
++distribution.
++*/
++
++
++#ifndef TINYXML_INCLUDED
++#define TINYXML_INCLUDED
++
++#ifdef _MSC_VER
++#pragma warning( disable : 4530 )
++#pragma warning( disable : 4786 )
++#endif
++
++#include <ctype.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <assert.h>
++
++// Help out windows:
++#if defined( _DEBUG ) && !defined( DEBUG )
++#define DEBUG
++#endif
++
++#if defined( DEBUG ) && defined( _MSC_VER )
++#include <windows.h>
++#define TIXML_LOG OutputDebugString
++#else
++#define TIXML_LOG printf
++#endif
++
++#ifdef TIXML_USE_STL
++      #include <string>
++      #include <iostream>
++      #define TIXML_STRING    std::string
++      #define TIXML_ISTREAM   std::istream
++      #define TIXML_OSTREAM   std::ostream
++#else
++      #include "tinystr.h"
++      #define TIXML_STRING    TiXmlString
++      #define TIXML_OSTREAM   TiXmlOutStream
++#endif
++
++class TiXmlDocument;
++class TiXmlElement;
++class TiXmlComment;
++class TiXmlUnknown;
++class TiXmlAttribute;
++class TiXmlText;
++class TiXmlDeclaration;
++class TiXmlParsingData;
++
++const int TIXML_MAJOR_VERSION = 2;
++const int TIXML_MINOR_VERSION = 3;
++const int TIXML_PATCH_VERSION = 2;
++
++/*    Internal structure for tracking location of items 
++      in the XML file.
++*/
++struct TiXmlCursor
++{
++      TiXmlCursor()           { Clear(); }
++      void Clear()            { row = col = -1; }
++
++      int row;        // 0 based.
++      int col;        // 0 based.
++};
++
++
++// Only used by Attribute::Query functions
++enum 
++{ 
++      TIXML_SUCCESS,
++      TIXML_NO_ATTRIBUTE,
++      TIXML_WRONG_TYPE
++};
++
++
++// Used by the parsing routines.
++enum TiXmlEncoding
++{
++      TIXML_ENCODING_UNKNOWN,
++      TIXML_ENCODING_UTF8,
++      TIXML_ENCODING_LEGACY
++};
++
++const TiXmlEncoding TIXML_DEFAULT_ENCODING = TIXML_ENCODING_UNKNOWN;
++
++/** TiXmlBase is a base class for every class in TinyXml.
++      It does little except to establish that TinyXml classes
++      can be printed and provide some utility functions.
++
++      In XML, the document and elements can contain
++      other elements and other types of nodes.
++
++      @verbatim
++      A Document can contain: Element (container or leaf)
++                                                      Comment (leaf)
++                                                      Unknown (leaf)
++                                                      Declaration( leaf )
++
++      An Element can contain: Element (container or leaf)
++                                                      Text    (leaf)
++                                                      Attributes (not on tree)
++                                                      Comment (leaf)
++                                                      Unknown (leaf)
++
++      A Decleration contains: Attributes (not on tree)
++      @endverbatim
++*/
++class TiXmlBase
++{
++      friend class TiXmlNode;
++      friend class TiXmlElement;
++      friend class TiXmlDocument;
++
++public:
++      TiXmlBase()     :       userData(0) {}
++      virtual ~TiXmlBase()                                    {}
++
++      /**     All TinyXml classes can print themselves to a filestream.
++              This is a formatted print, and will insert tabs and newlines.
++              
++              (For an unformatted stream, use the << operator.)
++      */
++      virtual void Print( FILE* cfile, int depth ) const = 0;
++
++      /**     The world does not agree on whether white space should be kept or
++              not. In order to make everyone happy, these global, static functions
++              are provided to set whether or not TinyXml will condense all white space
++              into a single space or not. The default is to condense. Note changing this
++              values is not thread safe.
++      */
++      static void SetCondenseWhiteSpace( bool condense )              { condenseWhiteSpace = condense; }
++
++      /// Return the current white space setting.
++      static bool IsWhiteSpaceCondensed()                                             { return condenseWhiteSpace; }
++
++      /** Return the position, in the original source file, of this node or attribute.
++              The row and column are 1-based. (That is the first row and first column is
++              1,1). If the returns values are 0 or less, then the parser does not have
++              a row and column value.
++
++              Generally, the row and column value will be set when the TiXmlDocument::Load(),
++              TiXmlDocument::LoadFile(), or any TiXmlNode::Parse() is called. It will NOT be set
++              when the DOM was created from operator>>.
++
++              The values reflect the initial load. Once the DOM is modified programmatically
++              (by adding or changing nodes and attributes) the new values will NOT update to
++              reflect changes in the document.
++
++              There is a minor performance cost to computing the row and column. Computation
++              can be disabled if TiXmlDocument::SetTabSize() is called with 0 as the value.
++
++              @sa TiXmlDocument::SetTabSize()
++      */
++      int Row() const                 { return location.row + 1; }
++      int Column() const              { return location.col + 1; }    ///< See Row()
++
++      void  SetUserData( void* user )                 { userData = user; }
++      void* GetUserData()                                             { return userData; }
++
++      // Table that returs, for a given lead byte, the total number of bytes
++      // in the UTF-8 sequence.
++      static const int utf8ByteTable[256];
++
++      virtual const char* Parse(      const char* p, 
++                                                              TiXmlParsingData* data, 
++                                                              TiXmlEncoding encoding /*= TIXML_ENCODING_UNKNOWN */ ) = 0;
++
++protected:
++
++      // See STL_STRING_BUG
++      // Utility class to overcome a bug.
++      class StringToBuffer
++      {
++        public:
++              StringToBuffer( const TIXML_STRING& str );
++              ~StringToBuffer();
++              char* buffer;
++      };
++
++      static const char*      SkipWhiteSpace( const char*, TiXmlEncoding encoding );
++      inline static bool      IsWhiteSpace( char c )          
++      { 
++              return ( isspace( (unsigned char) c ) || c == '\n' || c == '\r' ); 
++      }
++
++      virtual void StreamOut (TIXML_OSTREAM *) const = 0;
++
++      #ifdef TIXML_USE_STL
++          static bool StreamWhiteSpace( TIXML_ISTREAM * in, TIXML_STRING * tag );
++          static bool StreamTo( TIXML_ISTREAM * in, int character, TIXML_STRING * tag );
++      #endif
++
++      /*      Reads an XML name into the string provided. Returns
++              a pointer just past the last character of the name,
++              or 0 if the function has an error.
++      */
++      static const char* ReadName( const char* p, TIXML_STRING* name, TiXmlEncoding encoding );
++
++      /*      Reads text. Returns a pointer past the given end tag.
++              Wickedly complex options, but it keeps the (sensitive) code in one place.
++      */
++      static const char* ReadText(    const char* in,                         // where to start
++                                                                      TIXML_STRING* text,                     // the string read
++                                                                      bool ignoreWhiteSpace,          // whether to keep the white space
++                                                                      const char* endTag,                     // what ends this text
++                                                                      bool ignoreCase,                        // whether to ignore case in the end tag
++                                                                      TiXmlEncoding encoding );       // the current encoding
++
++      // If an entity has been found, transform it into a character.
++      static const char* GetEntity( const char* in, char* value, int* length, TiXmlEncoding encoding );
++
++      // Get a character, while interpreting entities.
++      // The length can be from 0 to 4 bytes.
++      inline static const char* GetChar( const char* p, char* _value, int* length, TiXmlEncoding encoding )
++      {
++              assert( p );
++              if ( encoding == TIXML_ENCODING_UTF8 )
++              {
++                      *length = utf8ByteTable[ *((unsigned char*)p) ];
++                      assert( *length >= 0 && *length < 5 );
++              }
++              else
++              {
++                      *length = 1;
++              }
++
++              if ( *length == 1 )
++              {
++                      if ( *p == '&' )
++                              return GetEntity( p, _value, length, encoding );
++                      *_value = *p;
++                      return p+1;
++              }
++              else if ( *length )
++              {
++                      strncpy( _value, p, *length );
++                      return p + (*length);
++              }
++              else
++              {
++                      // Not valid text.
++                      return 0;
++              }
++      }
++
++      // Puts a string to a stream, expanding entities as it goes.
++      // Note this should not contian the '<', '>', etc, or they will be transformed into entities!
++      static void PutString( const TIXML_STRING& str, TIXML_OSTREAM* out );
++
++      static void PutString( const TIXML_STRING& str, TIXML_STRING* out );
++
++      // Return true if the next characters in the stream are any of the endTag sequences.
++      // Ignore case only works for english, and should only be relied on when comparing
++      // to Engilish words: StringEqual( p, "version", true ) is fine.
++      static bool StringEqual(        const char* p,
++                                                              const char* endTag,
++                                                              bool ignoreCase,
++                                                              TiXmlEncoding encoding );
++
++
++      enum
++      {
++              TIXML_NO_ERROR = 0,
++              TIXML_ERROR,
++              TIXML_ERROR_OPENING_FILE,
++              TIXML_ERROR_OUT_OF_MEMORY,
++              TIXML_ERROR_PARSING_ELEMENT,
++              TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME,
++              TIXML_ERROR_READING_ELEMENT_VALUE,
++              TIXML_ERROR_READING_ATTRIBUTES,
++              TIXML_ERROR_PARSING_EMPTY,
++              TIXML_ERROR_READING_END_TAG,
++              TIXML_ERROR_PARSING_UNKNOWN,
++              TIXML_ERROR_PARSING_COMMENT,
++              TIXML_ERROR_PARSING_DECLARATION,
++              TIXML_ERROR_DOCUMENT_EMPTY,
++              TIXML_ERROR_EMBEDDED_NULL,
++
++              TIXML_ERROR_STRING_COUNT
++      };
++      static const char* errorString[ TIXML_ERROR_STRING_COUNT ];
++
++      TiXmlCursor location;
++
++    /// Field containing a generic user pointer
++      void*                   userData;
++      
++      // None of these methods are reliable for any language except English.
++      // Good for approximation, not great for accuracy.
++      static int IsAlpha( unsigned char anyByte, TiXmlEncoding encoding );
++      static int IsAlphaNum( unsigned char anyByte, TiXmlEncoding encoding );
++      inline static int ToLower( int v, TiXmlEncoding encoding )
++      {
++              if ( encoding == TIXML_ENCODING_UTF8 )
++              {
++                      if ( v < 128 ) return tolower( v );
++                      return v;
++              }
++              else
++              {
++                      return tolower( v );
++              }
++      }
++      static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length );
++
++private:
++      TiXmlBase( const TiXmlBase& );                          // not implemented.
++      void operator=( const TiXmlBase& base );        // not allowed.
++
++      struct Entity
++      {
++              const char*     str;
++              unsigned int    strLength;
++              char                chr;
++      };
++      enum
++      {
++              NUM_ENTITY = 5,
++              MAX_ENTITY_LENGTH = 6
++
++      };
++      static Entity entity[ NUM_ENTITY ];
++      static bool condenseWhiteSpace;
++};
++
++
++/** The parent class for everything in the Document Object Model.
++      (Except for attributes).
++      Nodes have siblings, a parent, and children. A node can be
++      in a document, or stand on its own. The type of a TiXmlNode
++      can be queried, and it can be cast to its more defined type.
++*/
++class TiXmlNode : public TiXmlBase
++{
++      friend class TiXmlDocument;
++      friend class TiXmlElement;
++
++public:
++      #ifdef TIXML_USE_STL    
++
++          /** An input stream operator, for every class. Tolerant of newlines and
++                  formatting, but doesn't expect them.
++          */
++          friend std::istream& operator >> (std::istream& in, TiXmlNode& base);
++
++          /** An output stream operator, for every class. Note that this outputs
++                  without any newlines or formatting, as opposed to Print(), which
++                  includes tabs and new lines.
++
++                  The operator<< and operator>> are not completely symmetric. Writing
++                  a node to a stream is very well defined. You'll get a nice stream
++                  of output, without any extra whitespace or newlines.
++                  
++                  But reading is not as well defined. (As it always is.) If you create
++                  a TiXmlElement (for example) and read that from an input stream,
++                  the text needs to define an element or junk will result. This is
++                  true of all input streams, but it's worth keeping in mind.
++
++                  A TiXmlDocument will read nodes until it reads a root element, and
++                      all the children of that root element.
++          */  
++          friend std::ostream& operator<< (std::ostream& out, const TiXmlNode& base);
++
++              /// Appends the XML node or attribute to a std::string.
++              friend std::string& operator<< (std::string& out, const TiXmlNode& base );
++
++      #else
++          // Used internally, not part of the public API.
++          friend TIXML_OSTREAM& operator<< (TIXML_OSTREAM& out, const TiXmlNode& base);
++      #endif
++
++      /** The types of XML nodes supported by TinyXml. (All the
++                      unsupported types are picked up by UNKNOWN.)
++      */
++      enum NodeType
++      {
++              DOCUMENT,
++              ELEMENT,
++              COMMENT,
++              UNKNOWN,
++              TEXT,
++              DECLARATION,
++              TYPECOUNT
++      };
++
++      virtual ~TiXmlNode();
++
++      /** The meaning of 'value' changes for the specific type of
++              TiXmlNode.
++              @verbatim
++              Document:       filename of the xml file
++              Element:        name of the element
++              Comment:        the comment text
++              Unknown:        the tag contents
++              Text:           the text string
++              @endverbatim
++
++              The subclasses will wrap this function.
++      */
++      const char * Value() const { return value.c_str (); }
++
++      /** Changes the value of the node. Defined as:
++              @verbatim
++              Document:       filename of the xml file
++              Element:        name of the element
++              Comment:        the comment text
++              Unknown:        the tag contents
++              Text:           the text string
++              @endverbatim
++      */
++      void SetValue(const char * _value) { value = _value;}
++
++    #ifdef TIXML_USE_STL
++      /// STL std::string form.
++      void SetValue( const std::string& _value )    
++      {         
++              StringToBuffer buf( _value );
++              SetValue( buf.buffer ? buf.buffer : "" );       
++      }       
++      #endif
++
++      /// Delete all the children of this node. Does not affect 'this'.
++      void Clear();
++
++      /// One step up the DOM.
++      TiXmlNode* Parent() const                                       { return parent; }
++
++      TiXmlNode* FirstChild() const   { return firstChild; }          ///< The first child of this node. Will be null if there are no children.
++      TiXmlNode* FirstChild( const char * value ) const;                      ///< The first child of this node with the matching 'value'. Will be null if none found.
++
++      TiXmlNode* LastChild() const    { return lastChild; }           /// The last child of this node. Will be null if there are no children.
++      TiXmlNode* LastChild( const char * value ) const;                       /// The last child of this node matching 'value'. Will be null if there are no children.
++
++    #ifdef TIXML_USE_STL
++      TiXmlNode* FirstChild( const std::string& _value ) const        {       return FirstChild (_value.c_str ());    }       ///< STL std::string form.
++      TiXmlNode* LastChild( const std::string& _value ) const         {       return LastChild (_value.c_str ());     }       ///< STL std::string form.
++      #endif
++
++      /** An alternate way to walk the children of a node.
++              One way to iterate over nodes is:
++              @verbatim
++                      for( child = parent->FirstChild(); child; child = child->NextSibling() )
++              @endverbatim
++
++              IterateChildren does the same thing with the syntax:
++              @verbatim
++                      child = 0;
++                      while( child = parent->IterateChildren( child ) )
++              @endverbatim
++
++              IterateChildren takes the previous child as input and finds
++              the next one. If the previous child is null, it returns the
++              first. IterateChildren will return null when done.
++      */
++      TiXmlNode* IterateChildren( TiXmlNode* previous ) const;
++
++      /// This flavor of IterateChildren searches for children with a particular 'value'
++      TiXmlNode* IterateChildren( const char * value, TiXmlNode* previous ) const;
++
++    #ifdef TIXML_USE_STL
++      TiXmlNode* IterateChildren( const std::string& _value, TiXmlNode* previous ) const      {       return IterateChildren (_value.c_str (), previous);     }       ///< STL std::string form.
++      #endif
++
++      /** Add a new node related to this. Adds a child past the LastChild.
++              Returns a pointer to the new object or NULL if an error occured.
++      */
++      TiXmlNode* InsertEndChild( const TiXmlNode& addThis );
++
++
++      /** Add a new node related to this. Adds a child past the LastChild.
++
++              NOTE: the node to be added is passed by pointer, and will be
++              henceforth owned (and deleted) by tinyXml. This method is efficient
++              and avoids an extra copy, but should be used with care as it
++              uses a different memory model than the other insert functions.
++
++              @sa InsertEndChild
++      */
++      TiXmlNode* LinkEndChild( TiXmlNode* addThis );
++
++      /** Add a new node related to this. Adds a child before the specified child.
++              Returns a pointer to the new object or NULL if an error occured.
++      */
++      TiXmlNode* InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis );
++
++      /** Add a new node related to this. Adds a child after the specified child.
++              Returns a pointer to the new object or NULL if an error occured.
++      */
++      TiXmlNode* InsertAfterChild(  TiXmlNode* afterThis, const TiXmlNode& addThis );
++
++      /** Replace a child of this node.
++              Returns a pointer to the new object or NULL if an error occured.
++      */
++      TiXmlNode* ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis );
++
++      /// Delete a child of this node.
++      bool RemoveChild( TiXmlNode* removeThis );
++
++      /// Navigate to a sibling node.
++      TiXmlNode* PreviousSibling() const                      { return prev; }
++
++      /// Navigate to a sibling node.
++      TiXmlNode* PreviousSibling( const char * ) const;
++
++    #ifdef TIXML_USE_STL
++      TiXmlNode* PreviousSibling( const std::string& _value ) const   {       return PreviousSibling (_value.c_str ());       }       ///< STL std::string form.
++      TiXmlNode* NextSibling( const std::string& _value) const                {       return NextSibling (_value.c_str ());   }       ///< STL std::string form.
++      #endif
++
++      /// Navigate to a sibling node.
++      TiXmlNode* NextSibling() const                          { return next; }
++
++      /// Navigate to a sibling node with the given 'value'.
++      TiXmlNode* NextSibling( const char * ) const;
++
++      /** Convenience function to get through elements.
++              Calls NextSibling and ToElement. Will skip all non-Element
++              nodes. Returns 0 if there is not another element.
++      */
++      TiXmlElement* NextSiblingElement() const;
++
++      /** Convenience function to get through elements.
++              Calls NextSibling and ToElement. Will skip all non-Element
++              nodes. Returns 0 if there is not another element.
++      */
++      TiXmlElement* NextSiblingElement( const char * ) const;
++
++    #ifdef TIXML_USE_STL
++      TiXmlElement* NextSiblingElement( const std::string& _value) const      {       return NextSiblingElement (_value.c_str ());    }       ///< STL std::string form.
++      #endif
++
++      /// Convenience function to get through elements.
++      TiXmlElement* FirstChildElement()       const;
++
++      /// Convenience function to get through elements.
++      TiXmlElement* FirstChildElement( const char * value ) const;
++
++    #ifdef TIXML_USE_STL
++      TiXmlElement* FirstChildElement( const std::string& _value ) const      {       return FirstChildElement (_value.c_str ());     }       ///< STL std::string form.
++      #endif
++
++      /** Query the type (as an enumerated value, above) of this node.
++              The possible types are: DOCUMENT, ELEMENT, COMMENT,
++                                                              UNKNOWN, TEXT, and DECLARATION.
++      */
++      virtual int Type() const        { return type; }
++
++      /** Return a pointer to the Document this node lives in.
++              Returns null if not in a document.
++      */
++      TiXmlDocument* GetDocument() const;
++
++      /// Returns true if this node has no children.
++      bool NoChildren() const                                         { return !firstChild; }
++
++      TiXmlDocument* ToDocument()     const           { return ( this && type == DOCUMENT ) ? (TiXmlDocument*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type.
++      TiXmlElement*  ToElement() const                { return ( this && type == ELEMENT  ) ? (TiXmlElement*)  this : 0; } ///< Cast to a more defined type. Will return null not of the requested type.
++      TiXmlComment*  ToComment() const                { return ( this && type == COMMENT  ) ? (TiXmlComment*)  this : 0; } ///< Cast to a more defined type. Will return null not of the requested type.
++      TiXmlUnknown*  ToUnknown() const                { return ( this && type == UNKNOWN  ) ? (TiXmlUnknown*)  this : 0; } ///< Cast to a more defined type. Will return null not of the requested type.
++      TiXmlText*         ToText()    const            { return ( this && type == TEXT     ) ? (TiXmlText*)     this : 0; } ///< Cast to a more defined type. Will return null not of the requested type.
++      TiXmlDeclaration* ToDeclaration() const { return ( this && type == DECLARATION ) ? (TiXmlDeclaration*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type.
++
++      /** Create an exact duplicate of this node and return it. The memory must be deleted
++              by the caller. 
++      */
++      virtual TiXmlNode* Clone() const = 0;
++
++protected:
++      TiXmlNode( NodeType _type );
++
++      // Copy to the allocated object. Shared functionality between Clone, Copy constructor,
++      // and the assignment operator.
++      void CopyTo( TiXmlNode* target ) const;
++
++      #ifdef TIXML_USE_STL
++          // The real work of the input operator.
++          virtual void StreamIn( TIXML_ISTREAM* in, TIXML_STRING* tag ) = 0;
++      #endif
++
++      // Figure out what is at *p, and parse it. Returns null if it is not an xml node.
++      TiXmlNode* Identify( const char* start, TiXmlEncoding encoding );
++
++      // Internal Value function returning a TIXML_STRING
++      const TIXML_STRING& SValue() const      { return value ; }
++
++      TiXmlNode*              parent;
++      NodeType                type;
++
++      TiXmlNode*              firstChild;
++      TiXmlNode*              lastChild;
++
++      TIXML_STRING    value;
++
++      TiXmlNode*              prev;
++      TiXmlNode*              next;
++
++private:
++      TiXmlNode( const TiXmlNode& );                          // not implemented.
++      void operator=( const TiXmlNode& base );        // not allowed.
++};
++
++
++/** An attribute is a name-value pair. Elements have an arbitrary
++      number of attributes, each with a unique name.
++
++      @note The attributes are not TiXmlNodes, since they are not
++                part of the tinyXML document object model. There are other
++                suggested ways to look at this problem.
++*/
++class TiXmlAttribute : public TiXmlBase
++{
++      friend class TiXmlAttributeSet;
++
++public:
++      /// Construct an empty attribute.
++      TiXmlAttribute() : TiXmlBase()
++      {
++              document = 0;
++              prev = next = 0;
++      }
++
++      #ifdef TIXML_USE_STL
++      /// std::string constructor.
++      TiXmlAttribute( const std::string& _name, const std::string& _value )
++      {
++              name = _name;
++              value = _value;
++              document = 0;
++              prev = next = 0;
++      }
++      #endif
++
++      /// Construct an attribute with a name and value.
++      TiXmlAttribute( const char * _name, const char * _value )
++      {
++              name = _name;
++              value = _value;
++              document = 0;
++              prev = next = 0;
++      }
++
++      const char*             Name()  const           { return name.c_str (); }               ///< Return the name of this attribute.
++      const char*             Value() const           { return value.c_str (); }              ///< Return the value of this attribute.
++      const int       IntValue() const;                                                                       ///< Return the value of this attribute, converted to an integer.
++      const double    DoubleValue() const;                                                            ///< Return the value of this attribute, converted to a double.
++
++      /** QueryIntValue examines the value string. It is an alternative to the
++              IntValue() method with richer error checking.
++              If the value is an integer, it is stored in 'value' and 
++              the call returns TIXML_SUCCESS. If it is not
++              an integer, it returns TIXML_WRONG_TYPE.
++
++              A specialized but useful call. Note that for success it returns 0,
++              which is the opposite of almost all other TinyXml calls.
++      */
++      int QueryIntValue( int* value ) const;
++      /// QueryDoubleValue examines the value string. See QueryIntValue().
++      int QueryDoubleValue( double* value ) const;
++
++      void SetName( const char* _name )       { name = _name; }                               ///< Set the name of this attribute.
++      void SetValue( const char* _value )     { value = _value; }                             ///< Set the value.
++
++      void SetIntValue( int value );                                                                          ///< Set the value from an integer.
++      void SetDoubleValue( double value );                                                            ///< Set the value from a double.
++
++    #ifdef TIXML_USE_STL
++      /// STL std::string form.
++      void SetName( const std::string& _name )        
++      {       
++              StringToBuffer buf( _name );
++              SetName ( buf.buffer ? buf.buffer : "error" );  
++      }
++      /// STL std::string form.       
++      void SetValue( const std::string& _value )      
++      {       
++              StringToBuffer buf( _value );
++              SetValue( buf.buffer ? buf.buffer : "error" );  
++      }
++      #endif
++
++      /// Get the next sibling attribute in the DOM. Returns null at end.
++      TiXmlAttribute* Next() const;
++      /// Get the previous sibling attribute in the DOM. Returns null at beginning.
++      TiXmlAttribute* Previous() const;
++
++      bool operator==( const TiXmlAttribute& rhs ) const { return rhs.name == name; }
++      bool operator<( const TiXmlAttribute& rhs )      const { return name < rhs.name; }
++      bool operator>( const TiXmlAttribute& rhs )  const { return name > rhs.name; }
++
++      /*      Attribute parsing starts: first letter of the name
++                                               returns: the next char after the value end quote
++      */
++      virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
++
++      // Prints this Attribute to a FILE stream.
++      virtual void Print( FILE* cfile, int depth ) const;
++
++      virtual void StreamOut( TIXML_OSTREAM * out ) const;
++      // [internal use]
++      // Set the document pointer so the attribute can report errors.
++      void SetDocument( TiXmlDocument* doc )  { document = doc; }
++
++private:
++      TiXmlAttribute( const TiXmlAttribute& );                                // not implemented.
++      void operator=( const TiXmlAttribute& base );   // not allowed.
++
++      TiXmlDocument*  document;       // A pointer back to a document, for error reporting.
++      TIXML_STRING name;
++      TIXML_STRING value;
++      TiXmlAttribute* prev;
++      TiXmlAttribute* next;
++};
++
++
++/*    A class used to manage a group of attributes.
++      It is only used internally, both by the ELEMENT and the DECLARATION.
++      
++      The set can be changed transparent to the Element and Declaration
++      classes that use it, but NOT transparent to the Attribute
++      which has to implement a next() and previous() method. Which makes
++      it a bit problematic and prevents the use of STL.
++
++      This version is implemented with circular lists because:
++              - I like circular lists
++              - it demonstrates some independence from the (typical) doubly linked list.
++*/
++class TiXmlAttributeSet
++{
++public:
++      TiXmlAttributeSet();
++      ~TiXmlAttributeSet();
++
++      void Add( TiXmlAttribute* attribute );
++      void Remove( TiXmlAttribute* attribute );
++
++      TiXmlAttribute* First() const   { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; }
++      TiXmlAttribute* Last()  const   { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; }
++      TiXmlAttribute* Find( const char * name ) const;
++
++private:
++      TiXmlAttribute sentinel;
++};
++
++
++/** The element is a container class. It has a value, the element name,
++      and can contain other elements, text, comments, and unknowns.
++      Elements also contain an arbitrary number of attributes.
++*/
++class TiXmlElement : public TiXmlNode
++{
++public:
++      /// Construct an element.
++      TiXmlElement (const char * in_value);
++
++      #ifdef TIXML_USE_STL
++      /// std::string constructor.
++      TiXmlElement( const std::string& _value );
++      #endif
++
++      TiXmlElement( const TiXmlElement& );
++
++      void operator=( const TiXmlElement& base );
++
++      virtual ~TiXmlElement();
++
++      /** Given an attribute name, Attribute() returns the value
++              for the attribute of that name, or null if none exists.
++      */
++      const char* Attribute( const char* name ) const;
++
++      /** Given an attribute name, Attribute() returns the value
++              for the attribute of that name, or null if none exists.
++              If the attribute exists and can be converted to an integer,
++              the integer value will be put in the return 'i', if 'i'
++              is non-null.
++      */
++      const char* Attribute( const char* name, int* i ) const;
++
++      /** Given an attribute name, Attribute() returns the value
++              for the attribute of that name, or null if none exists.
++              If the attribute exists and can be converted to an double,
++              the double value will be put in the return 'd', if 'd'
++              is non-null.
++      */
++      const char* Attribute( const char* name, double* d ) const;
++
++      /** QueryIntAttribute examines the attribute - it is an alternative to the
++              Attribute() method with richer error checking.
++              If the attribute is an integer, it is stored in 'value' and 
++              the call returns TIXML_SUCCESS. If it is not
++              an integer, it returns TIXML_WRONG_TYPE. If the attribute
++              does not exist, then TIXML_NO_ATTRIBUTE is returned.
++      */      
++      int QueryIntAttribute( const char* name, int* value ) const;
++      /// QueryDoubleAttribute examines the attribute - see QueryIntAttribute().
++      int QueryDoubleAttribute( const char* name, double* value ) const;
++
++      /** Sets an attribute of name to a given value. The attribute
++              will be created if it does not exist, or changed if it does.
++      */
++      void SetAttribute( const char* name, const char * value );
++
++    #ifdef TIXML_USE_STL
++      const char* Attribute( const std::string& name ) const                          { return Attribute( name.c_str() ); }
++      const char* Attribute( const std::string& name, int* i ) const          { return Attribute( name.c_str(), i ); }
++      const char* Attribute( const std::string& name, double* d ) const       { return Attribute( name.c_str(), d ); }
++      int QueryIntAttribute( const std::string& name, int* value ) const      { return QueryIntAttribute( name.c_str(), value ); }
++      int QueryDoubleAttribute( const std::string& name, double* value ) const { return QueryDoubleAttribute( name.c_str(), value ); }
++
++      /// STL std::string form.
++      void SetAttribute( const std::string& name, const std::string& _value ) 
++      {       
++              StringToBuffer n( name );
++              StringToBuffer v( _value );
++              if ( n.buffer && v.buffer )
++                      SetAttribute (n.buffer, v.buffer );     
++      }       
++      ///< STL std::string form.
++      void SetAttribute( const std::string& name, int _value )        
++      {       
++              StringToBuffer n( name );
++              if ( n.buffer )
++                      SetAttribute (n.buffer, _value);        
++      }       
++      #endif
++
++      /** Sets an attribute of name to a given value. The attribute
++              will be created if it does not exist, or changed if it does.
++      */
++      void SetAttribute( const char * name, int value );
++
++      /** Sets an attribute of name to a given value. The attribute
++              will be created if it does not exist, or changed if it does.
++      */
++      void SetDoubleAttribute( const char * name, double value );
++
++      /** Deletes an attribute with the given name.
++      */
++      void RemoveAttribute( const char * name );
++    #ifdef TIXML_USE_STL
++      void RemoveAttribute( const std::string& name ) {       RemoveAttribute (name.c_str ());        }       ///< STL std::string form.
++      #endif
++
++      TiXmlAttribute* FirstAttribute() const  { return attributeSet.First(); }                ///< Access the first attribute in this element.
++      TiXmlAttribute* LastAttribute() const   { return attributeSet.Last(); }         ///< Access the last attribute in this element.
++
++      /// Creates a new Element and returns it - the returned element is a copy.
++      virtual TiXmlNode* Clone() const;
++      // Print the Element to a FILE stream.
++      virtual void Print( FILE* cfile, int depth ) const;
++
++      /*      Attribtue parsing starts: next char past '<'
++                                               returns: next char past '>'
++      */
++      virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
++
++protected:
++
++      void CopyTo( TiXmlElement* target ) const;
++      void ClearThis();       // like clear, but initializes 'this' object as well
++
++      // Used to be public [internal use]
++      #ifdef TIXML_USE_STL
++          virtual void StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag );
++      #endif
++      virtual void StreamOut( TIXML_OSTREAM * out ) const;
++
++      /*      [internal use]
++              Reads the "value" of the element -- another element, or text.
++              This should terminate with the current end tag.
++      */
++      const char* ReadValue( const char* in, TiXmlParsingData* prevData, TiXmlEncoding encoding );
++
++private:
++
++      TiXmlAttributeSet attributeSet;
++};
++
++
++/**   An XML comment.
++*/
++class TiXmlComment : public TiXmlNode
++{
++public:
++      /// Constructs an empty comment.
++      TiXmlComment() : TiXmlNode( TiXmlNode::COMMENT ) {}
++      TiXmlComment( const TiXmlComment& );
++      void operator=( const TiXmlComment& base );
++
++      virtual ~TiXmlComment() {}
++
++      /// Returns a copy of this Comment.
++      virtual TiXmlNode* Clone() const;
++      /// Write this Comment to a FILE stream.
++      virtual void Print( FILE* cfile, int depth ) const;
++
++      /*      Attribtue parsing starts: at the ! of the !--
++                                               returns: next char past '>'
++      */
++      virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
++
++protected:
++      void CopyTo( TiXmlComment* target ) const;
++
++      // used to be public
++      #ifdef TIXML_USE_STL
++          virtual void StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag );
++      #endif
++      virtual void StreamOut( TIXML_OSTREAM * out ) const;
++
++private:
++
++};
++
++
++/** XML text. Contained in an element.
++*/
++class TiXmlText : public TiXmlNode
++{
++      friend class TiXmlElement;
++public:
++      /// Constructor.
++      TiXmlText (const char * initValue) : TiXmlNode (TiXmlNode::TEXT)
++      {
++              SetValue( initValue );
++      }
++      virtual ~TiXmlText() {}
++
++      #ifdef TIXML_USE_STL
++      /// Constructor.
++      TiXmlText( const std::string& initValue ) : TiXmlNode (TiXmlNode::TEXT)
++      {
++              SetValue( initValue );
++      }
++      #endif
++
++      TiXmlText( const TiXmlText& copy ) : TiXmlNode( TiXmlNode::TEXT )       { copy.CopyTo( this ); }
++      void operator=( const TiXmlText& base )                                                         { base.CopyTo( this ); }
++
++      /// Write this text object to a FILE stream.
++      virtual void Print( FILE* cfile, int depth ) const;
++
++      virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
++
++protected :
++      ///  [internal use] Creates a new Element and returns it.
++      virtual TiXmlNode* Clone() const;
++      void CopyTo( TiXmlText* target ) const;
++
++      virtual void StreamOut ( TIXML_OSTREAM * out ) const;
++      bool Blank() const;     // returns true if all white space and new lines
++      // [internal use]
++      #ifdef TIXML_USE_STL
++          virtual void StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag );
++      #endif
++
++private:
++};
++
++
++/** In correct XML the declaration is the first entry in the file.
++      @verbatim
++              <?xml version="1.0" standalone="yes"?>
++      @endverbatim
++
++      TinyXml will happily read or write files without a declaration,
++      however. There are 3 possible attributes to the declaration:
++      version, encoding, and standalone.
++
++      Note: In this version of the code, the attributes are
++      handled as special cases, not generic attributes, simply
++      because there can only be at most 3 and they are always the same.
++*/
++class TiXmlDeclaration : public TiXmlNode
++{
++public:
++      /// Construct an empty declaration.
++      TiXmlDeclaration()   : TiXmlNode( TiXmlNode::DECLARATION ) {}
++
++#ifdef TIXML_USE_STL
++      /// Constructor.
++      TiXmlDeclaration(       const std::string& _version,
++                                              const std::string& _encoding,
++                                              const std::string& _standalone );
++#endif
++
++      /// Construct.
++      TiXmlDeclaration(       const char* _version,
++                                              const char* _encoding,
++                                              const char* _standalone );
++
++      TiXmlDeclaration( const TiXmlDeclaration& copy );
++      void operator=( const TiXmlDeclaration& copy );
++
++      virtual ~TiXmlDeclaration()     {}
++
++      /// Version. Will return an empty string if none was found.
++      const char *Version() const                     { return version.c_str (); }
++      /// Encoding. Will return an empty string if none was found.
++      const char *Encoding() const            { return encoding.c_str (); }
++      /// Is this a standalone document?
++      const char *Standalone() const          { return standalone.c_str (); }
++
++      /// Creates a copy of this Declaration and returns it.
++      virtual TiXmlNode* Clone() const;
++      /// Print this declaration to a FILE stream.
++      virtual void Print( FILE* cfile, int depth ) const;
++
++      virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
++
++protected:
++      void CopyTo( TiXmlDeclaration* target ) const;
++      // used to be public
++      #ifdef TIXML_USE_STL
++          virtual void StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag );
++      #endif
++      virtual void StreamOut ( TIXML_OSTREAM * out) const;
++
++private:
++
++      TIXML_STRING version;
++      TIXML_STRING encoding;
++      TIXML_STRING standalone;
++};
++
++
++/** Any tag that tinyXml doesn't recognize is saved as an
++      unknown. It is a tag of text, but should not be modified.
++      It will be written back to the XML, unchanged, when the file
++      is saved.
++
++      DTD tags get thrown into TiXmlUnknowns.
++*/
++class TiXmlUnknown : public TiXmlNode
++{
++public:
++      TiXmlUnknown() : TiXmlNode( TiXmlNode::UNKNOWN )        {}
++      virtual ~TiXmlUnknown() {}
++
++      TiXmlUnknown( const TiXmlUnknown& copy ) : TiXmlNode( TiXmlNode::UNKNOWN )              { copy.CopyTo( this ); }
++      void operator=( const TiXmlUnknown& copy )                                                                              { copy.CopyTo( this ); }
++
++      /// Creates a copy of this Unknown and returns it.
++      virtual TiXmlNode* Clone() const;
++      /// Print this Unknown to a FILE stream.
++      virtual void Print( FILE* cfile, int depth ) const;
++
++      virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
++
++protected:
++      void CopyTo( TiXmlUnknown* target ) const;
++
++      #ifdef TIXML_USE_STL
++          virtual void StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag );
++      #endif
++      virtual void StreamOut ( TIXML_OSTREAM * out ) const;
++
++private:
++
++};
++
++
++/** Always the top level node. A document binds together all the
++      XML pieces. It can be saved, loaded, and printed to the screen.
++      The 'value' of a document node is the xml file name.
++*/
++class TiXmlDocument : public TiXmlNode
++{
++public:
++      /// Create an empty document, that has no name.
++      TiXmlDocument();
++      /// Create a document with a name. The name of the document is also the filename of the xml.
++      TiXmlDocument( const char * documentName );
++
++      #ifdef TIXML_USE_STL
++      /// Constructor.
++      TiXmlDocument( const std::string& documentName );
++      #endif
++
++      TiXmlDocument( const TiXmlDocument& copy );
++      void operator=( const TiXmlDocument& copy );
++
++      virtual ~TiXmlDocument() {}
++
++      /** Load a file using the current document value.
++              Returns true if successful. Will delete any existing
++              document data before loading.
++      */
++      bool LoadFile( TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );
++      /// Save a file using the current document value. Returns true if successful.
++      bool SaveFile() const;
++      /// Load a file using the given filename. Returns true if successful.
++      bool LoadFile( const char * filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );
++      /// Save a file using the given filename. Returns true if successful.
++      bool SaveFile( const char * filename ) const;
++
++      #ifdef TIXML_USE_STL
++      bool LoadFile( const std::string& filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING )                   ///< STL std::string version.
++      {
++              StringToBuffer f( filename );
++              return ( f.buffer && LoadFile( f.buffer, encoding ));
++      }
++      bool SaveFile( const std::string& filename ) const              ///< STL std::string version.
++      {
++              StringToBuffer f( filename );
++              return ( f.buffer && SaveFile( f.buffer ));
++      }
++      #endif
++
++      /** Parse the given null terminated block of xml data. Passing in an encoding to this
++              method (either TIXML_ENCODING_LEGACY or TIXML_ENCODING_UTF8 will force TinyXml
++              to use that encoding, regardless of what TinyXml might otherwise try to detect.
++      */
++      virtual const char* Parse( const char* p, TiXmlParsingData* data = 0, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );
++
++      /** Get the root element -- the only top level element -- of the document.
++              In well formed XML, there should only be one. TinyXml is tolerant of
++              multiple elements at the document level.
++      */
++      TiXmlElement* RootElement() const               { return FirstChildElement(); }
++
++      /** If an error occurs, Error will be set to true. Also,
++              - The ErrorId() will contain the integer identifier of the error (not generally useful)
++              - The ErrorDesc() method will return the name of the error. (very useful)
++              - The ErrorRow() and ErrorCol() will return the location of the error (if known)
++      */      
++      bool Error() const                                              { return error; }
++
++      /// Contains a textual (english) description of the error if one occurs.
++      const char * ErrorDesc() const  { return errorDesc.c_str (); }
++
++      /** Generally, you probably want the error string ( ErrorDesc() ). But if you
++              prefer the ErrorId, this function will fetch it.
++      */
++      const int ErrorId()     const                           { return errorId; }
++
++      /** Returns the location (if known) of the error. The first column is column 1, 
++              and the first row is row 1. A value of 0 means the row and column wasn't applicable
++              (memory errors, for example, have no row/column) or the parser lost the error. (An
++              error in the error reporting, in that case.)
++
++              @sa SetTabSize, Row, Column
++      */
++      int ErrorRow()  { return errorLocation.row+1; }
++      int ErrorCol()  { return errorLocation.col+1; } ///< The column where the error occured. See ErrorRow()
++
++      /** By calling this method, with a tab size
++              greater than 0, the row and column of each node and attribute is stored
++              when the file is loaded. Very useful for tracking the DOM back in to
++              the source file.
++
++              The tab size is required for calculating the location of nodes. If not
++              set, the default of 4 is used. The tabsize is set per document. Setting
++              the tabsize to 0 disables row/column tracking.
++
++              Note that row and column tracking is not supported when using operator>>.
++
++              The tab size needs to be enabled before the parse or load. Correct usage:
++              @verbatim
++              TiXmlDocument doc;
++              doc.SetTabSize( 8 );
++              doc.Load( "myfile.xml" );
++              @endverbatim
++
++              @sa Row, Column
++      */
++      void SetTabSize( int _tabsize )         { tabsize = _tabsize; }
++
++      int TabSize() const     { return tabsize; }
++
++      /** If you have handled the error, it can be reset with this call. The error
++              state is automatically cleared if you Parse a new XML block.
++      */
++      void ClearError()                                               {       error = false; 
++                                                                                              errorId = 0; 
++                                                                                              errorDesc = ""; 
++                                                                                              errorLocation.row = errorLocation.col = 0; 
++                                                                                              //errorLocation.last = 0; 
++                                                                                      }
++
++      /** Dump the document to standard out. */
++      void Print() const                                              { Print( stdout, 0 ); }
++
++      /// Print this Document to a FILE stream.
++      virtual void Print( FILE* cfile, int depth = 0 ) const;
++      // [internal use]
++      void SetError( int err, const char* errorLocation, TiXmlParsingData* prevData, TiXmlEncoding encoding );
++
++protected :
++      virtual void StreamOut ( TIXML_OSTREAM * out) const;
++      // [internal use]
++      virtual TiXmlNode* Clone() const;
++      #ifdef TIXML_USE_STL
++          virtual void StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag );
++      #endif
++
++private:
++      void CopyTo( TiXmlDocument* target ) const;
++
++      bool error;
++      int  errorId;
++      TIXML_STRING errorDesc;
++      int tabsize;
++      TiXmlCursor errorLocation;
++};
++
++
++/**
++      A TiXmlHandle is a class that wraps a node pointer with null checks; this is
++      an incredibly useful thing. Note that TiXmlHandle is not part of the TinyXml
++      DOM structure. It is a separate utility class.
++
++      Take an example:
++      @verbatim
++      <Document>
++              <Element attributeA = "valueA">
++                      <Child attributeB = "value1" />
++                      <Child attributeB = "value2" />
++              </Element>
++      <Document>
++      @endverbatim
++
++      Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very 
++      easy to write a *lot* of code that looks like:
++
++      @verbatim
++      TiXmlElement* root = document.FirstChildElement( "Document" );
++      if ( root )
++      {
++              TiXmlElement* element = root->FirstChildElement( "Element" );
++              if ( element )
++              {
++                      TiXmlElement* child = element->FirstChildElement( "Child" );
++                      if ( child )
++                      {
++                              TiXmlElement* child2 = child->NextSiblingElement( "Child" );
++                              if ( child2 )
++                              {
++                                      // Finally do something useful.
++      @endverbatim
++
++      And that doesn't even cover "else" cases. TiXmlHandle addresses the verbosity
++      of such code. A TiXmlHandle checks for null     pointers so it is perfectly safe 
++      and correct to use:
++
++      @verbatim
++      TiXmlHandle docHandle( &document );
++      TiXmlElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", 1 ).Element();
++      if ( child2 )
++      {
++              // do something useful
++      @endverbatim
++
++      Which is MUCH more concise and useful.
++
++      It is also safe to copy handles - internally they are nothing more than node pointers.
++      @verbatim
++      TiXmlHandle handleCopy = handle;
++      @endverbatim
++
++      What they should not be used for is iteration:
++
++      @verbatim
++      int i=0; 
++      while ( true )
++      {
++              TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", i ).Element();
++              if ( !child )
++                      break;
++              // do something
++              ++i;
++      }
++      @endverbatim
++
++      It seems reasonable, but it is in fact two embedded while loops. The Child method is 
++      a linear walk to find the element, so this code would iterate much more than it needs 
++      to. Instead, prefer:
++
++      @verbatim
++      TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).FirstChild( "Child" ).Element();
++
++      for( child; child; child=child->NextSiblingElement() )
++      {
++              // do something
++      }
++      @endverbatim
++*/
++class TiXmlHandle
++{
++public:
++      /// Create a handle from any node (at any depth of the tree.) This can be a null pointer.
++      TiXmlHandle( TiXmlNode* node )                                  { this->node = node; }
++      /// Copy constructor
++      TiXmlHandle( const TiXmlHandle& ref )                   { this->node = ref.node; }
++      TiXmlHandle operator=( const TiXmlHandle& ref ) { this->node = ref.node; return *this; }
++
++      /// Return a handle to the first child node.
++      TiXmlHandle FirstChild() const;
++      /// Return a handle to the first child node with the given name.
++      TiXmlHandle FirstChild( const char * value ) const;
++      /// Return a handle to the first child element.
++      TiXmlHandle FirstChildElement() const;
++      /// Return a handle to the first child element with the given name.
++      TiXmlHandle FirstChildElement( const char * value ) const;
++
++      /** Return a handle to the "index" child with the given name. 
++              The first child is 0, the second 1, etc.
++      */
++      TiXmlHandle Child( const char* value, int index ) const;
++      /** Return a handle to the "index" child. 
++              The first child is 0, the second 1, etc.
++      */
++      TiXmlHandle Child( int index ) const;
++      /** Return a handle to the "index" child element with the given name. 
++              The first child element is 0, the second 1, etc. Note that only TiXmlElements
++              are indexed: other types are not counted.
++      */
++      TiXmlHandle ChildElement( const char* value, int index ) const;
++      /** Return a handle to the "index" child element. 
++              The first child element is 0, the second 1, etc. Note that only TiXmlElements
++              are indexed: other types are not counted.
++      */
++      TiXmlHandle ChildElement( int index ) const;
++
++      #ifdef TIXML_USE_STL
++      TiXmlHandle FirstChild( const std::string& _value ) const                               { return FirstChild( _value.c_str() ); }
++      TiXmlHandle FirstChildElement( const std::string& _value ) const                { return FirstChildElement( _value.c_str() ); }
++
++      TiXmlHandle Child( const std::string& _value, int index ) const                 { return Child( _value.c_str(), index ); }
++      TiXmlHandle ChildElement( const std::string& _value, int index ) const  { return ChildElement( _value.c_str(), index ); }
++      #endif
++
++      /// Return the handle as a TiXmlNode. This may return null.
++      TiXmlNode* Node() const                 { return node; } 
++      /// Return the handle as a TiXmlElement. This may return null.
++      TiXmlElement* Element() const   { return ( ( node && node->ToElement() ) ? node->ToElement() : 0 ); }
++      /// Return the handle as a TiXmlText. This may return null.
++      TiXmlText* Text() const                 { return ( ( node && node->ToText() ) ? node->ToText() : 0 ); }
++      /// Return the handle as a TiXmlUnknown. This may return null;
++      TiXmlUnknown* Unknown() const                   { return ( ( node && node->ToUnknown() ) ? node->ToUnknown() : 0 ); }
++
++private:
++      TiXmlNode* node;
++};
++
++
++#endif
++#endif /* SETUP */
+diff -NaurwB vdr-1.7.10/tinyxmlparser.c vdr-1.7.10-patched/tinyxmlparser.c
+--- vdr-1.7.10/tinyxmlparser.c 1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.10-patched/tinyxmlparser.c 2009-12-18 06:25:25.000000000 +0100
+@@ -0,0 +1,1494 @@
++#ifdef USE_SETUP
++/*
++www.sourceforge.net/projects/tinyxml
++Original code (2.0 and earlier )copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com)
++
++This software is provided 'as-is', without any express or implied 
++warranty. In no event will the authors be held liable for any 
++damages arising from the use of this software.
++
++Permission is granted to anyone to use this software for any 
++purpose, including commercial applications, and to alter it and 
++redistribute it freely, subject to the following restrictions:
++
++1. The origin of this software must not be misrepresented; you must 
++not claim that you wrote the original software. If you use this
++software in a product, an acknowledgment in the product documentation
++would be appreciated but is not required.
++
++2. Altered source versions must be plainly marked as such, and 
++must not be misrepresented as being the original software.
++
++3. This notice may not be removed or altered from any source 
++distribution.
++*/
++
++#include "tinyxml.h"
++#include <ctype.h>
++
++//#define DEBUG_PARSER
++
++// Note tha "PutString" hardcodes the same list. This
++// is less flexible than it appears. Changing the entries
++// or order will break putstring.     
++TiXmlBase::Entity TiXmlBase::entity[ NUM_ENTITY ] = 
++{
++      { "&amp;",  5, '&' },
++      { "&lt;",   4, '<' },
++      { "&gt;",   4, '>' },
++      { "&quot;", 6, '\"' },
++      { "&apos;", 6, '\'' }
++};
++
++// Bunch of unicode info at:
++//            http://www.unicode.org/faq/utf_bom.html
++// Including the basic of this table, which determines the #bytes in the
++// sequence from the lead byte. 1 placed for invalid sequences --
++// although the result will be junk, pass it through as much as possible.
++// Beware of the non-characters in UTF-8:     
++//                            ef bb bf (Microsoft "lead bytes")
++//                            ef bf be
++//                            ef bf bf 
++
++
++
++const int TiXmlBase::utf8ByteTable[256] = 
++{
++      //      0       1       2       3       4       5       6       7       8       9       a       b       c       d       e       f
++              1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      // 0x00
++              1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      // 0x10
++              1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      // 0x20
++              1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      // 0x30
++              1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      // 0x40
++              1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      // 0x50
++              1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      // 0x60
++              1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      // 0x70 End of ASCII range
++              1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      // 0x80 0x80 to 0xc1 invalid
++              1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      // 0x90 
++              1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      // 0xa0 
++              1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      // 0xb0 
++              1,      1,      2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      // 0xc0 0xc2 to 0xdf 2 byte
++              2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      2,      // 0xd0
++              3,      3,      3,      3,      3,      3,      3,      3,      3,      3,      3,      3,      3,      3,      3,      3,      // 0xe0 0xe0 to 0xef 3 byte
++              4,      4,      4,      4,      4,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1,      1       // 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid
++};
++
++
++void TiXmlBase::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length )
++{
++      const unsigned long BYTE_MASK = 0xBF;
++      const unsigned long BYTE_MARK = 0x80;
++      const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
++
++      if (input < 0x80) 
++              *length = 1;
++      else if ( input < 0x800 )
++              *length = 2;
++      else if ( input < 0x10000 )
++              *length = 3;
++      else if ( input < 0x200000 )
++              *length = 4;
++      else
++              { *length = 0; return; }        // This code won't covert this correctly anyway.
++
++      output += *length;
++
++      // Scary scary fall throughs.
++      switch (*length) 
++      {
++              case 4:
++                      --output; 
++                      *output = (char)((input | BYTE_MARK) & BYTE_MASK); 
++                      input >>= 6;
++              case 3:
++                      --output; 
++                      *output = (char)((input | BYTE_MARK) & BYTE_MASK); 
++                      input >>= 6;
++              case 2:
++                      --output; 
++                      *output = (char)((input | BYTE_MARK) & BYTE_MASK); 
++                      input >>= 6;
++              case 1:
++                      --output; 
++                      *output = (char)(input | FIRST_BYTE_MARK[*length]);
++      }
++}
++
++
++/*static*/ int TiXmlBase::IsAlpha( unsigned char anyByte, TiXmlEncoding encoding )
++{
++      // This will only work for low-ascii, everything else is assumed to be a valid
++      // letter. I'm not sure this is the best approach, but it is quite tricky trying
++      // to figure out alhabetical vs. not across encoding. So take a very 
++      // conservative approach.
++
++//    if ( encoding == TIXML_ENCODING_UTF8 )
++//    {
++              if ( anyByte < 127 )
++                      return isalpha( anyByte );
++              else
++                      return 1;       // What else to do? The unicode set is huge...get the english ones right.
++//    }
++//    else
++//    {
++//            return isalpha( anyByte );
++//    }
++}
++
++
++/*static*/ int TiXmlBase::IsAlphaNum( unsigned char anyByte, TiXmlEncoding encoding )
++{
++      // This will only work for low-ascii, everything else is assumed to be a valid
++      // letter. I'm not sure this is the best approach, but it is quite tricky trying
++      // to figure out alhabetical vs. not across encoding. So take a very 
++      // conservative approach.
++
++//    if ( encoding == TIXML_ENCODING_UTF8 )
++//    {
++              if ( anyByte < 127 )
++                      return isalnum( anyByte );
++              else
++                      return 1;       // What else to do? The unicode set is huge...get the english ones right.
++//    }
++//    else
++//    {
++//            return isalnum( anyByte );
++//    }
++}
++
++
++class TiXmlParsingData
++{
++      friend class TiXmlDocument;
++  public:
++      void Stamp( const char* now, TiXmlEncoding encoding );
++
++      const TiXmlCursor& Cursor()     { return cursor; }
++
++  private:
++      // Only used by the document!
++      TiXmlParsingData( const char* start, int _tabsize, int row, int col )
++      {
++              assert( start );
++              stamp = start;
++              tabsize = _tabsize;
++              cursor.row = row;
++              cursor.col = col;
++      }
++
++      TiXmlCursor             cursor;
++      const char*             stamp;
++      int                             tabsize;
++};
++
++
++void TiXmlParsingData::Stamp( const char* now, TiXmlEncoding encoding )
++{
++      assert( now );
++
++      // Do nothing if the tabsize is 0.
++      if ( tabsize < 1 )
++      {
++              return;
++      }
++
++      // Get the current row, column.
++      int row = cursor.row;
++      int col = cursor.col;
++      const char* p = stamp;
++      assert( p );
++
++      while ( p < now )
++      {
++              // Code contributed by Fletcher Dunn: (modified by lee)
++              switch (*p) {
++                      case 0:
++                              // We *should* never get here, but in case we do, don't
++                              // advance past the terminating null character, ever
++                              return;
++
++                      case '\r':
++                              // bump down to the next line
++                              ++row;
++                              col = 0;                                
++                              // Eat the character
++                              ++p;
++
++                              // Check for \r\n sequence, and treat this as a single character
++                              if (*p == '\n') {
++                                      ++p;
++                              }
++                              break;
++
++                      case '\n':
++                              // bump down to the next line
++                              ++row;
++                              col = 0;
++
++                              // Eat the character
++                              ++p;
++
++                              // Check for \n\r sequence, and treat this as a single
++                              // character.  (Yes, this bizarre thing does occur still
++                              // on some arcane platforms...)
++                              if (*p == '\r') {
++                                      ++p;
++                              }
++                              break;
++
++                      case '\t':
++                              // Eat the character
++                              ++p;
++
++                              // Skip to next tab stop
++                              col = (col / tabsize + 1) * tabsize;
++                              break;
++
++                      case (char)(0xef):
++                              if ( encoding == TIXML_ENCODING_UTF8 )
++                              {
++                                      if ( *(p+1) && *(p+2) )
++                                      {
++                                              // In these cases, don't advance the column. These are
++                                              // 0-width spaces.
++                                              if ( *(p+1)==(char)(0xbb) && *(p+2)==(char)(0xbf) )
++                                                      p += 3; 
++                                              else if ( *(p+1)==(char)(0xbf) && *(p+2)==(char)(0xbe) )
++                                                      p += 3; 
++                                              else if ( *(p+1)==(char)(0xbf) && *(p+2)==(char)(0xbf) )
++                                                      p += 3; 
++                                              else
++                                                      { p +=3; ++col; }       // A normal character.
++                                      }
++                              }
++                              else
++                              {
++                                      ++p;
++                                      ++col;
++                              }
++                              break;
++
++                      default:
++                              if ( encoding == TIXML_ENCODING_UTF8 )
++                              {
++                                      // Eat the 1 to 4 byte utf8 character.
++                                      int step = TiXmlBase::utf8ByteTable[*((unsigned char*)p)];
++                                      if ( step == 0 )
++                                              step = 1;               // Error case from bad encoding, but handle gracefully.
++                                      p += step;
++
++                                      // Just advance one column, of course.
++                                      ++col;
++                              }
++                              else
++                              {
++                                      ++p;
++                                      ++col;
++                              }
++                              break;
++              }
++      }
++      cursor.row = row;
++      cursor.col = col;
++      assert( cursor.row >= -1 );
++      assert( cursor.col >= -1 );
++      stamp = p;
++      assert( stamp );
++}
++
++
++const char* TiXmlBase::SkipWhiteSpace( const char* p, TiXmlEncoding encoding )
++{
++      if ( !p || !*p )
++      {
++              return 0;
++      }
++      if ( encoding == TIXML_ENCODING_UTF8 )
++      {
++              while ( *p )
++              {
++                      // Skip the stupid Microsoft UTF-8 Byte order marks
++                      if (    *(p+0)==(char) 0xef 
++                               && *(p+1)==(char) 0xbb 
++                               && *(p+2)==(char) 0xbf )
++                      {
++                              p += 3;
++                              continue;
++                      }
++                      else if(*(p+0)==(char) 0xef
++                               && *(p+1)==(char) 0xbf
++                               && *(p+2)==(char) 0xbe )
++                      {
++                              p += 3;
++                              continue;
++                      }
++                      else if(*(p+0)==(char) 0xef
++                               && *(p+1)==(char) 0xbf
++                               && *(p+2)==(char) 0xbf )
++                      {
++                              p += 3;
++                              continue;
++                      }
++
++                      if ( IsWhiteSpace( *p ) || *p == '\n' || *p =='\r' )            // Still using old rules for white space.
++                              ++p;
++                      else
++                              break;
++              }
++      }
++      else
++      {
++              while ( *p && IsWhiteSpace( *p ) || *p == '\n' || *p =='\r' )
++                      ++p;
++      }
++
++      return p;
++}
++
++#ifdef TIXML_USE_STL
++/*static*/ bool TiXmlBase::StreamWhiteSpace( TIXML_ISTREAM * in, TIXML_STRING * tag )
++{
++      for( ;; )
++      {
++              if ( !in->good() ) return false;
++
++              int c = in->peek();
++              // At this scope, we can't get to a document. So fail silently.
++              if ( !IsWhiteSpace( c ) || c <= 0 )
++                      return true;
++
++              *tag += (char) in->get();
++      }
++}
++
++/*static*/ bool TiXmlBase::StreamTo( TIXML_ISTREAM * in, int character, TIXML_STRING * tag )
++{
++      //assert( character > 0 && character < 128 );   // else it won't work in utf-8
++      while ( in->good() )
++      {
++              int c = in->peek();
++              if ( c == character )
++                      return true;
++              if ( c <= 0 )           // Silent failure: can't get document at this scope
++                      return false;
++
++              in->get();
++              *tag += (char) c;
++      }
++      return false;
++}
++#endif
++
++const char* TiXmlBase::ReadName( const char* p, TIXML_STRING * name, TiXmlEncoding encoding )
++{
++      *name = "";
++      assert( p );
++
++      // Names start with letters or underscores.
++      // Of course, in unicode, tinyxml has no idea what a letter *is*. The
++      // algorithm is generous.
++      //
++      // After that, they can be letters, underscores, numbers,
++      // hyphens, or colons. (Colons are valid ony for namespaces,
++      // but tinyxml can't tell namespaces from names.)
++      if (    p && *p 
++               && ( IsAlpha( (unsigned char) *p, encoding ) || *p == '_' ) )
++      {
++              while(          p && *p
++                              &&      (               IsAlphaNum( (unsigned char ) *p, encoding ) 
++                                               || *p == '_'
++                                               || *p == '-'
++                                               || *p == '.'
++                                               || *p == ':' ) )
++              {
++                      (*name) += *p;
++                      ++p;
++              }
++              return p;
++      }
++      return 0;
++}
++
++const char* TiXmlBase::GetEntity( const char* p, char* value, int* length, TiXmlEncoding encoding )
++{
++      // Presume an entity, and pull it out.
++    TIXML_STRING ent;
++      int i;
++      *length = 0;
++
++      if ( *(p+1) && *(p+1) == '#' && *(p+2) )
++      {
++              unsigned long ucs = 0;
++              unsigned delta = 0;
++              unsigned mult = 1;
++
++              if ( *(p+2) == 'x' )
++              {
++                      // Hexadecimal.
++                      if ( !*(p+3) ) return 0;
++
++                      const char* q = p+3;
++                      q = strchr( q, ';' );
++
++                      if ( !q || !*q ) return 0;
++
++                      delta = q-p;
++                      --q;
++
++                      while ( *q != 'x' )
++                      {
++                              if ( *q >= '0' && *q <= '9' )
++                                      ucs += mult * (*q - '0');
++                              else if ( *q >= 'a' && *q <= 'f' )
++                                      ucs += mult * (*q - 'a' + 10);
++                              else if ( *q >= 'A' && *q <= 'F' )
++                                      ucs += mult * (*q - 'A' + 10 );
++                              else 
++                                      return 0;
++                              mult *= 16;
++                              --q;
++                      }
++              }
++              else
++              {
++                      // Decimal.
++                      if ( !*(p+2) ) return 0;
++
++                      const char* q = p+2;
++                      q = strchr( q, ';' );
++
++                      if ( !q || !*q ) return 0;
++
++                      delta = q-p;
++                      --q;
++
++                      while ( *q != '#' )
++                      {
++                              if ( *q >= '0' && *q <= '9' )
++                                      ucs += mult * (*q - '0');
++                              else 
++                                      return 0;
++                              mult *= 10;
++                              --q;
++                      }
++              }
++              if ( encoding == TIXML_ENCODING_UTF8 )
++              {
++                      // convert the UCS to UTF-8
++                      ConvertUTF32ToUTF8( ucs, value, length );
++              }
++              else
++              {
++                      *value = (char)ucs;
++                      *length = 1;
++              }
++              return p + delta + 1;
++      }
++
++      // Now try to match it.
++      for( i=0; i<NUM_ENTITY; ++i )
++      {
++              if ( strncmp( entity[i].str, p, entity[i].strLength ) == 0 )
++              {
++                      assert( strlen( entity[i].str ) == entity[i].strLength );
++                      *value = entity[i].chr;
++                      *length = 1;
++                      return ( p + entity[i].strLength );
++              }
++      }
++
++      // So it wasn't an entity, its unrecognized, or something like that.
++      *value = *p;    // Don't put back the last one, since we return it!
++      return p+1;
++}
++
++
++bool TiXmlBase::StringEqual( const char* p,
++                                                       const char* tag,
++                                                       bool ignoreCase,
++                                                       TiXmlEncoding encoding )
++{
++      assert( p );
++      assert( tag );
++      if ( !p || !*p )
++      {
++              assert( 0 );
++              return false;
++      }
++
++      const char* q = p;
++
++      if ( ignoreCase )
++      {
++              while ( *q && *tag && ToLower( *q, encoding ) == ToLower( *tag, encoding ) )
++              {
++                      ++q;
++                      ++tag;
++              }
++
++              if ( *tag == 0 )
++                      return true;
++      }
++      else
++      {
++              while ( *q && *tag && *q == *tag )
++              {
++                      ++q;
++                      ++tag;
++              }
++
++              if ( *tag == 0 )                // Have we found the end of the tag, and everything equal?
++                      return true;
++      }
++      return false;
++}
++
++const char* TiXmlBase::ReadText(      const char* p, 
++                                                                      TIXML_STRING * text, 
++                                                                      bool trimWhiteSpace, 
++                                                                      const char* endTag, 
++                                                                      bool caseInsensitive,
++                                                                      TiXmlEncoding encoding )
++{
++    *text = "";
++      if (    !trimWhiteSpace                 // certain tags always keep whitespace
++               || !condenseWhiteSpace )       // if true, whitespace is always kept
++      {
++              // Keep all the white space.
++              while (    p && *p
++                              && !StringEqual( p, endTag, caseInsensitive, encoding )
++                        )
++              {
++                      int len;
++                      char cArr[4] = { 0, 0, 0, 0 };
++                      p = GetChar( p, cArr, &len, encoding );
++                      text->append( cArr, len );
++              }
++      }
++      else
++      {
++              bool whitespace = false;
++
++              // Remove leading white space:
++              p = SkipWhiteSpace( p, encoding );
++              while (    p && *p
++                              && !StringEqual( p, endTag, caseInsensitive, encoding ) )
++              {
++                      if ( *p == '\r' || *p == '\n' )
++                      {
++                              whitespace = true;
++                              ++p;
++                      }
++                      else if ( IsWhiteSpace( *p ) )
++                      {
++                              whitespace = true;
++                              ++p;
++                      }
++                      else
++                      {
++                              // If we've found whitespace, add it before the
++                              // new character. Any whitespace just becomes a space.
++                              if ( whitespace )
++                              {
++                                      (*text) += ' ';
++                                      whitespace = false;
++                              }
++                              int len;
++                              char cArr[4] = { 0, 0, 0, 0 };
++                              p = GetChar( p, cArr, &len, encoding );
++                              if ( len == 1 )
++                                      (*text) += cArr[0];     // more efficient
++                              else
++                                      text->append( cArr, len );
++                      }
++              }
++      }
++      return p + strlen( endTag );
++}
++
++#ifdef TIXML_USE_STL
++
++void TiXmlDocument::StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag )
++{
++      // The basic issue with a document is that we don't know what we're
++      // streaming. Read something presumed to be a tag (and hope), then
++      // identify it, and call the appropriate stream method on the tag.
++      //
++      // This "pre-streaming" will never read the closing ">" so the
++      // sub-tag can orient itself.
++
++      if ( !StreamTo( in, '<', tag ) ) 
++      {
++              SetError( TIXML_ERROR_PARSING_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );
++              return;
++      }
++
++      while ( in->good() )
++      {
++              int tagIndex = (int) tag->length();
++              while ( in->good() && in->peek() != '>' )
++              {
++                      int c = in->get();
++                      if ( c <= 0 )
++                      {
++                              SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
++                              break;
++                      }
++                      (*tag) += (char) c;
++              }
++
++              if ( in->good() )
++              {
++                      // We now have something we presume to be a node of 
++                      // some sort. Identify it, and call the node to
++                      // continue streaming.
++                      TiXmlNode* node = Identify( tag->c_str() + tagIndex, TIXML_DEFAULT_ENCODING );
++
++                      if ( node )
++                      {
++                              node->StreamIn( in, tag );
++                              bool isElement = node->ToElement() != 0;
++                              delete node;
++                              node = 0;
++
++                              // If this is the root element, we're done. Parsing will be
++                              // done by the >> operator.
++                              if ( isElement )
++                              {
++                                      return;
++                              }
++                      }
++                      else
++                      {
++                              SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN );
++                              return;
++                      }
++              }
++      }
++      // We should have returned sooner.
++      SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN );
++}
++
++#endif
++
++const char* TiXmlDocument::Parse( const char* p, TiXmlParsingData* prevData, TiXmlEncoding encoding )
++{
++      ClearError();
++
++      // Parse away, at the document level. Since a document
++      // contains nothing but other tags, most of what happens
++      // here is skipping white space.
++      if ( !p || !*p )
++      {
++              SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );
++              return 0;
++      }
++
++      // Note that, for a document, this needs to come
++      // before the while space skip, so that parsing
++      // starts from the pointer we are given.
++      location.Clear();
++      if ( prevData )
++      {
++              location.row = prevData->cursor.row;
++              location.col = prevData->cursor.col;
++      }
++      else
++      {
++              location.row = 0;
++              location.col = 0;
++      }
++      TiXmlParsingData data( p, TabSize(), location.row, location.col );
++      location = data.Cursor();
++
++      if ( encoding == TIXML_ENCODING_UNKNOWN )
++      {
++              // Check for the Microsoft UTF-8 lead bytes.
++              if (    *(p+0) && *(p+0) == (char)(0xef)
++                       && *(p+1) && *(p+1) == (char)(0xbb)
++                       && *(p+2) && *(p+2) == (char)(0xbf) )
++              {
++                      encoding = TIXML_ENCODING_UTF8;
++              }
++      }
++
++    p = SkipWhiteSpace( p, encoding );
++      if ( !p )
++      {
++              SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );
++              return 0;
++      }
++
++      while ( p && *p )
++      {
++              TiXmlNode* node = Identify( p, encoding );
++              if ( node )
++              {
++                      p = node->Parse( p, &data, encoding );
++                      LinkEndChild( node );
++              }
++              else
++              {
++                      break;
++              }
++
++              // Did we get encoding info?
++              if (    encoding == TIXML_ENCODING_UNKNOWN
++                       && node->ToDeclaration() )
++              {
++                      TiXmlDeclaration* dec = node->ToDeclaration();
++                      const char* enc = dec->Encoding();
++                      assert( enc );
++
++                      if ( *enc == 0 )
++                              encoding = TIXML_ENCODING_UTF8;
++                      else if ( StringEqual( enc, "UTF-8", true, TIXML_ENCODING_UNKNOWN ) )
++                              encoding = TIXML_ENCODING_UTF8;
++                      else if ( StringEqual( enc, "UTF8", true, TIXML_ENCODING_UNKNOWN ) )
++                              encoding = TIXML_ENCODING_UTF8; // incorrect, but be nice
++                      else 
++                              encoding = TIXML_ENCODING_LEGACY;
++              }
++
++              p = SkipWhiteSpace( p, encoding );
++      }
++
++      // All is well.
++      return p;
++}
++
++void TiXmlDocument::SetError( int err, const char* pError, TiXmlParsingData* data, TiXmlEncoding encoding )
++{     
++      // The first error in a chain is more accurate - don't set again!
++      if ( error )
++              return;
++
++      assert( err > 0 && err < TIXML_ERROR_STRING_COUNT );
++      error   = true;
++      errorId = err;
++      errorDesc = errorString[ errorId ];
++
++      errorLocation.Clear();
++      if ( pError && data )
++      {
++              //TiXmlParsingData data( pError, prevData );
++              data->Stamp( pError, encoding );
++              errorLocation = data->Cursor();
++      }
++}
++
++
++TiXmlNode* TiXmlNode::Identify( const char* p, TiXmlEncoding encoding )
++{
++      TiXmlNode* returnNode = 0;
++
++      p = SkipWhiteSpace( p, encoding );
++      if( !p || !*p || *p != '<' )
++      {
++              return 0;
++      }
++
++      TiXmlDocument* doc = GetDocument();
++      p = SkipWhiteSpace( p, encoding );
++
++      if ( !p || !*p )
++      {
++              return 0;
++      }
++
++      // What is this thing? 
++      // - Elements start with a letter or underscore, but xml is reserved.
++      // - Comments: <!--
++      // - Decleration: <?xml
++      // - Everthing else is unknown to tinyxml.
++      //
++
++      const char* xmlHeader = { "<?xml" };
++      const char* commentHeader = { "<!--" };
++      const char* dtdHeader = { "<!" };
++
++      if ( StringEqual( p, xmlHeader, true, encoding ) )
++      {
++              #ifdef DEBUG_PARSER
++                      TIXML_LOG( "XML parsing Declaration\n" );
++              #endif
++              returnNode = new TiXmlDeclaration();
++      }
++      else if ( StringEqual( p, commentHeader, false, encoding ) )
++      {
++              #ifdef DEBUG_PARSER
++                      TIXML_LOG( "XML parsing Comment\n" );
++              #endif
++              returnNode = new TiXmlComment();
++      }
++      else if ( StringEqual( p, dtdHeader, false, encoding ) )
++      {
++              #ifdef DEBUG_PARSER
++                      TIXML_LOG( "XML parsing Unknown(1)\n" );
++              #endif
++              returnNode = new TiXmlUnknown();
++      }
++      else if (    IsAlpha( *(p+1), encoding )
++                        || *(p+1) == '_' )
++      {
++              #ifdef DEBUG_PARSER
++                      TIXML_LOG( "XML parsing Element\n" );
++              #endif
++              returnNode = new TiXmlElement( "" );
++      }
++      else
++      {
++              #ifdef DEBUG_PARSER
++                      TIXML_LOG( "XML parsing Unknown(2)\n" );
++              #endif
++              returnNode = new TiXmlUnknown();
++      }
++
++      if ( returnNode )
++      {
++              // Set the parent, so it can report errors
++              returnNode->parent = this;
++      }
++      else
++      {
++              if ( doc )
++                      doc->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN );
++      }
++      return returnNode;
++}
++
++#ifdef TIXML_USE_STL
++
++void TiXmlElement::StreamIn (TIXML_ISTREAM * in, TIXML_STRING * tag)
++{
++      // We're called with some amount of pre-parsing. That is, some of "this"
++      // element is in "tag". Go ahead and stream to the closing ">"
++      while( in->good() )
++      {
++              int c = in->get();
++              if ( c <= 0 )
++              {
++                      TiXmlDocument* document = GetDocument();
++                      if ( document )
++                              document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
++                      return;
++              }
++              (*tag) += (char) c ;
++              
++              if ( c == '>' )
++                      break;
++      }
++
++      if ( tag->length() < 3 ) return;
++
++      // Okay...if we are a "/>" tag, then we're done. We've read a complete tag.
++      // If not, identify and stream.
++
++      if (    tag->at( tag->length() - 1 ) == '>' 
++               && tag->at( tag->length() - 2 ) == '/' )
++      {
++              // All good!
++              return;
++      }
++      else if ( tag->at( tag->length() - 1 ) == '>' )
++      {
++              // There is more. Could be:
++              //              text
++              //              closing tag
++              //              another node.
++              for ( ;; )
++              {
++                      StreamWhiteSpace( in, tag );
++
++                      // Do we have text?
++                      if ( in->good() && in->peek() != '<' ) 
++                      {
++                              // Yep, text.
++                              TiXmlText text( "" );
++                              text.StreamIn( in, tag );
++
++                              // What follows text is a closing tag or another node.
++                              // Go around again and figure it out.
++                              continue;
++                      }
++
++                      // We now have either a closing tag...or another node.
++                      // We should be at a "<", regardless.
++                      if ( !in->good() ) return;
++                      assert( in->peek() == '<' );
++                      int tagIndex = tag->length();
++
++                      bool closingTag = false;
++                      bool firstCharFound = false;
++
++                      for( ;; )
++                      {
++                              if ( !in->good() )
++                                      return;
++
++                              int c = in->peek();
++                              if ( c <= 0 )
++                              {
++                                      TiXmlDocument* document = GetDocument();
++                                      if ( document )
++                                              document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
++                                      return;
++                              }
++                              
++                              if ( c == '>' )
++                                      break;
++
++                              *tag += (char) c;
++                              in->get();
++
++                              if ( !firstCharFound && c != '<' && !IsWhiteSpace( c ) )
++                              {
++                                      firstCharFound = true;
++                                      if ( c == '/' )
++                                              closingTag = true;
++                              }
++                      }
++                      // If it was a closing tag, then read in the closing '>' to clean up the input stream.
++                      // If it was not, the streaming will be done by the tag.
++                      if ( closingTag )
++                      {
++                              if ( !in->good() )
++                                      return;
++
++                              int c = in->get();
++                              if ( c <= 0 )
++                              {
++                                      TiXmlDocument* document = GetDocument();
++                                      if ( document )
++                                              document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
++                                      return;
++                              }
++                              assert( c == '>' );
++                              *tag += (char) c;
++
++                              // We are done, once we've found our closing tag.
++                              return;
++                      }
++                      else
++                      {
++                              // If not a closing tag, id it, and stream.
++                              const char* tagloc = tag->c_str() + tagIndex;
++                              TiXmlNode* node = Identify( tagloc, TIXML_DEFAULT_ENCODING );
++                              if ( !node )
++                                      return;
++                              node->StreamIn( in, tag );
++                              delete node;
++                              node = 0;
++
++                              // No return: go around from the beginning: text, closing tag, or node.
++                      }
++              }
++      }
++}
++#endif
++
++const char* TiXmlElement::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
++{
++      p = SkipWhiteSpace( p, encoding );
++      TiXmlDocument* document = GetDocument();
++
++      if ( !p || !*p )
++      {
++              if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, 0, 0, encoding );
++              return 0;
++      }
++
++//    TiXmlParsingData data( p, prevData );
++      if ( data )
++      {
++              data->Stamp( p, encoding );
++              location = data->Cursor();
++      }
++
++      if ( *p != '<' )
++      {
++              if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, p, data, encoding );
++              return 0;
++      }
++
++      p = SkipWhiteSpace( p+1, encoding );
++
++      // Read the name.
++      const char* pErr = p;
++
++    p = ReadName( p, &value, encoding );
++      if ( !p || !*p )
++      {
++              if ( document ) document->SetError( TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, pErr, data, encoding );
++              return 0;
++      }
++
++    TIXML_STRING endTag ("</");
++      endTag += value;
++      endTag += ">";
++
++      // Check for and read attributes. Also look for an empty
++      // tag or an end tag.
++      while ( p && *p )
++      {
++              pErr = p;
++              p = SkipWhiteSpace( p, encoding );
++              if ( !p || !*p )
++              {
++                      if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding );
++                      return 0;
++              }
++              if ( *p == '/' )
++              {
++                      ++p;
++                      // Empty tag.
++                      if ( *p  != '>' )
++                      {
++                              if ( document ) document->SetError( TIXML_ERROR_PARSING_EMPTY, p, data, encoding );             
++                              return 0;
++                      }
++                      return (p+1);
++              }
++              else if ( *p == '>' )
++              {
++                      // Done with attributes (if there were any.)
++                      // Read the value -- which can include other
++                      // elements -- read the end tag, and return.
++                      ++p;
++                      p = ReadValue( p, data, encoding );             // Note this is an Element method, and will set the error if one happens.
++                      if ( !p || !*p )
++                              return 0;
++
++                      // We should find the end tag now
++                      if ( StringEqual( p, endTag.c_str(), false, encoding ) )
++                      {
++                              p += endTag.length();
++                              return p;
++                      }
++                      else
++                      {
++                              if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding );
++                              return 0;
++                      }
++              }
++              else
++              {
++                      // Try to read an attribute:
++                      TiXmlAttribute* attrib = new TiXmlAttribute();
++                      if ( !attrib )
++                      {
++                              if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, pErr, data, encoding );
++                              return 0;
++                      }
++
++                      attrib->SetDocument( document );
++                      const char* pErr = p;
++                      p = attrib->Parse( p, data, encoding );
++
++                      if ( !p || !*p )
++                      {
++                              if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, pErr, data, encoding );
++                              delete attrib;
++                              return 0;
++                      }
++
++                      // Handle the strange case of double attributes:
++                      TiXmlAttribute* node = attributeSet.Find( attrib->Name() );
++                      if ( node )
++                      {
++                              node->SetValue( attrib->Value() );
++                              delete attrib;
++                              return 0;
++                      }
++
++                      attributeSet.Add( attrib );
++              }
++      }
++      return p;
++}
++
++
++const char* TiXmlElement::ReadValue( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
++{
++      TiXmlDocument* document = GetDocument();
++
++      const char* pWithWhiteSpace = p;
++      // Read in text and elements in any order.
++      p = SkipWhiteSpace( p, encoding );
++      while ( p && *p )
++      {
++              if ( *p != '<' )
++              {
++                      // Take what we have, make a text element.
++                      TiXmlText* textNode = new TiXmlText( "" );
++
++                      if ( !textNode )
++                      {
++                              if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, encoding );
++                                  return 0;
++                      }
++
++                      if ( TiXmlBase::IsWhiteSpaceCondensed() )
++                      {
++                              p = textNode->Parse( p, data, encoding );
++                      }
++                      else
++                      {
++                              // Special case: we want to keep the white space
++                              // so that leading spaces aren't removed.
++                              p = textNode->Parse( pWithWhiteSpace, data, encoding );
++                      }
++
++                      if ( !textNode->Blank() )
++                              LinkEndChild( textNode );
++                      else
++                              delete textNode;
++              } 
++              else 
++              {
++                      // We hit a '<'
++                      // Have we hit a new element or an end tag?
++                      if ( StringEqual( p, "</", false, encoding ) )
++                      {
++                              return p;
++                      }
++                      else
++                      {
++                              TiXmlNode* node = Identify( p, encoding );
++                              if ( node )
++                              {
++                                      p = node->Parse( p, data, encoding );
++                                      LinkEndChild( node );
++                              }                               
++                              else
++                              {
++                                      return 0;
++                              }
++                      }
++              }
++              p = SkipWhiteSpace( p, encoding );
++      }
++
++      if ( !p )
++      {
++              if ( document ) document->SetError( TIXML_ERROR_READING_ELEMENT_VALUE, 0, 0, encoding );
++      }       
++      return p;
++}
++
++
++#ifdef TIXML_USE_STL
++void TiXmlUnknown::StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag )
++{
++      while ( in->good() )
++      {
++              int c = in->get();      
++              if ( c <= 0 )
++              {
++                      TiXmlDocument* document = GetDocument();
++                      if ( document )
++                              document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
++                      return;
++              }
++              (*tag) += (char) c;
++
++              if ( c == '>' )
++              {
++                      // All is well.
++                      return;         
++              }
++      }
++}
++#endif
++
++
++const char* TiXmlUnknown::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
++{
++      TiXmlDocument* document = GetDocument();
++      p = SkipWhiteSpace( p, encoding );
++
++//    TiXmlParsingData data( p, prevData );
++      if ( data )
++      {
++              data->Stamp( p, encoding );
++              location = data->Cursor();
++      }
++      if ( !p || !*p || *p != '<' )
++      {
++              if ( document ) document->SetError( TIXML_ERROR_PARSING_UNKNOWN, p, data, encoding );
++              return 0;
++      }
++      ++p;
++    value = "";
++
++      while ( p && *p && *p != '>' )
++      {
++              value += *p;
++              ++p;
++      }
++
++      if ( !p )
++      {
++              if ( document ) document->SetError( TIXML_ERROR_PARSING_UNKNOWN, 0, 0, encoding );
++      }
++      if ( *p == '>' )
++              return p+1;
++      return p;
++}
++
++#ifdef TIXML_USE_STL
++void TiXmlComment::StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag )
++{
++      while ( in->good() )
++      {
++              int c = in->get();      
++              if ( c <= 0 )
++              {
++                      TiXmlDocument* document = GetDocument();
++                      if ( document )
++                              document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
++                      return;
++              }
++
++              (*tag) += (char) c;
++
++              if ( c == '>' 
++                       && tag->at( tag->length() - 2 ) == '-'
++                       && tag->at( tag->length() - 3 ) == '-' )
++              {
++                      // All is well.
++                      return;         
++              }
++      }
++}
++#endif
++
++
++const char* TiXmlComment::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
++{
++      TiXmlDocument* document = GetDocument();
++      value = "";
++
++      p = SkipWhiteSpace( p, encoding );
++
++//    TiXmlParsingData data( p, prevData );
++      if ( data )
++      {
++              data->Stamp( p, encoding );
++              location = data->Cursor();
++      }
++      const char* startTag = "<!--";
++      const char* endTag   = "-->";
++
++      if ( !StringEqual( p, startTag, false, encoding ) )
++      {
++              document->SetError( TIXML_ERROR_PARSING_COMMENT, p, data, encoding );
++              return 0;
++      }
++      p += strlen( startTag );
++      p = ReadText( p, &value, false, endTag, false, encoding );
++      return p;
++}
++
++
++const char* TiXmlAttribute::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
++{
++      p = SkipWhiteSpace( p, encoding );
++      if ( !p || !*p ) return 0;
++
++      int tabsize = 4;
++      if ( document )
++              tabsize = document->TabSize();
++
++//    TiXmlParsingData data( p, prevData );
++      if ( data )
++      {
++              data->Stamp( p, encoding );
++              location = data->Cursor();
++      }
++      // Read the name, the '=' and the value.
++      const char* pErr = p;
++      p = ReadName( p, &name, encoding );
++      if ( !p || !*p )
++      {
++              if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding );
++              return 0;
++      }
++      p = SkipWhiteSpace( p, encoding );
++      if ( !p || !*p || *p != '=' )
++      {
++              if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding );
++              return 0;
++      }
++
++      ++p;    // skip '='
++      p = SkipWhiteSpace( p, encoding );
++      if ( !p || !*p )
++      {
++              if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding );
++              return 0;
++      }
++      
++      const char* end;
++
++      if ( *p == '\'' )
++      {
++              ++p;
++              end = "\'";
++              p = ReadText( p, &value, false, end, false, encoding );
++      }
++      else if ( *p == '"' )
++      {
++              ++p;
++              end = "\"";
++              p = ReadText( p, &value, false, end, false, encoding );
++      }
++      else
++      {
++              // All attribute values should be in single or double quotes.
++              // But this is such a common error that the parser will try
++              // its best, even without them.
++              value = "";
++              while (    p && *p                                                                              // existence
++                              && !IsWhiteSpace( *p ) && *p != '\n' && *p != '\r'      // whitespace
++                              && *p != '/' && *p != '>' )                                             // tag end
++              {
++                      value += *p;
++                      ++p;
++              }
++      }
++      return p;
++}
++
++#ifdef TIXML_USE_STL
++void TiXmlText::StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag )
++{
++      while ( in->good() )
++      {
++              int c = in->peek();     
++              if ( c == '<' )
++                      return;
++              if ( c <= 0 )
++              {
++                      TiXmlDocument* document = GetDocument();
++                      if ( document )
++                              document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
++                      return;
++              }
++
++              (*tag) += (char) c;
++              in->get();
++      }
++}
++#endif
++
++const char* TiXmlText::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
++{
++      value = "";
++//    TiXmlParsingData data( p, prevData );
++      if ( data )
++      {
++              data->Stamp( p, encoding );
++              location = data->Cursor();
++      }
++      bool ignoreWhite = true;
++
++      const char* end = "<";
++      p = ReadText( p, &value, ignoreWhite, end, false, encoding );
++      if ( p )
++              return p-1;     // don't truncate the '<'
++      return 0;
++}
++
++#ifdef TIXML_USE_STL
++void TiXmlDeclaration::StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag )
++{
++      while ( in->good() )
++      {
++              int c = in->get();
++              if ( c <= 0 )
++              {
++                      TiXmlDocument* document = GetDocument();
++                      if ( document )
++                              document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
++                      return;
++              }
++              (*tag) += (char) c;
++
++              if ( c == '>' )
++              {
++                      // All is well.
++                      return;
++              }
++      }
++}
++#endif
++
++const char* TiXmlDeclaration::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding _encoding )
++{
++      p = SkipWhiteSpace( p, _encoding );
++      // Find the beginning, find the end, and look for
++      // the stuff in-between.
++      TiXmlDocument* document = GetDocument();
++      if ( !p || !*p || !StringEqual( p, "<?xml", true, _encoding ) )
++      {
++              if ( document ) document->SetError( TIXML_ERROR_PARSING_DECLARATION, 0, 0, _encoding );
++              return 0;
++      }
++//    TiXmlParsingData data( p, prevData );
++      if ( data )
++      {
++              data->Stamp( p, _encoding );
++              location = data->Cursor();
++      }
++      p += 5;
++
++      version = "";
++      encoding = "";
++      standalone = "";
++
++      while ( p && *p )
++      {
++              if ( *p == '>' )
++              {
++                      ++p;
++                      return p;
++              }
++
++              p = SkipWhiteSpace( p, _encoding );
++              if ( StringEqual( p, "version", true, _encoding ) )
++              {
++                      TiXmlAttribute attrib;
++                      p = attrib.Parse( p, data, _encoding );         
++                      version = attrib.Value();
++              }
++              else if ( StringEqual( p, "encoding", true, _encoding ) )
++              {
++                      TiXmlAttribute attrib;
++                      p = attrib.Parse( p, data, _encoding );         
++                      encoding = attrib.Value();
++              }
++              else if ( StringEqual( p, "standalone", true, _encoding ) )
++              {
++                      TiXmlAttribute attrib;
++                      p = attrib.Parse( p, data, _encoding );         
++                      standalone = attrib.Value();
++              }
++              else
++              {
++                      // Read over whatever it is.
++                      while( p && *p && *p != '>' && !IsWhiteSpace( *p ) )
++                              ++p;
++              }
++      }
++      return 0;
++}
++
++bool TiXmlText::Blank() const
++{
++      for ( unsigned i=0; i<value.length(); i++ )
++              if ( !IsWhiteSpace( value[i] ) )
++                      return false;
++      return true;
++}
++#endif /* SETUP */
+diff -NaurwB vdr-1.7.10/vdr.c vdr-1.7.10-patched/vdr.c
+--- vdr-1.7.10/vdr.c   2009-10-25 15:45:47.000000000 +0100
++++ vdr-1.7.10-patched/vdr.c   2009-12-18 06:29:28.000000000 +0100
+@@ -139,10 +139,17 @@
+   return true;
+ }
++#ifdef USE_SETTIME
++char *SetTime=NULL;
++#endif /* SETTIME */
++
+ static void SignalHandler(int signum)
+ {
+   switch (signum) {
+     case SIGPIPE:
++#ifdef USE_EM84XX
++    case SIGINT:
++#endif /* EM84XX */
+          break;
+     case SIGHUP:
+          LastSignal = signum;
+@@ -163,6 +170,10 @@
+   exit(1);
+ }
++#ifdef USE_CMDRECCMDI18N
++const char *ConfigDirectory = NULL;
++#endif /* CMDRECCMDI18N */
++
+ int main(int argc, char *argv[])
+ {
+   // Save terminal settings:
+@@ -187,8 +198,11 @@
+   bool UserDump = false;
+   int SVDRPport = DEFAULTSVDRPPORT;
+   const char *AudioCommand = NULL;
++#ifndef USE_CMDRECCMDI18N
+   const char *ConfigDirectory = NULL;
++#endif  /* CMDRECCMDI18N */
+   const char *EpgDataFileName = DEFAULTEPGDATAFILENAME;
++  bool DisplayExtensions = false;
+   bool DisplayHelp = false;
+   bool DisplayVersion = false;
+   bool DaemonMode = false;
+@@ -221,6 +235,7 @@
+       { "daemon",   no_argument,       NULL, 'd' },
+       { "device",   required_argument, NULL, 'D' },
+       { "epgfile",  required_argument, NULL, 'E' },
++      { "extensions",no_argument,      NULL, 'e' | 0x100 },
+       { "grab",     required_argument, NULL, 'g' },
+       { "help",     no_argument,       NULL, 'h' },
+       { "instance", required_argument, NULL, 'i' },
+@@ -236,6 +251,9 @@
+       { "record",   required_argument, NULL, 'r' },
+       { "shutdown", required_argument, NULL, 's' },
+       { "terminal", required_argument, NULL, 't' },
++#ifdef USE_SETTIME
++      { "timeset",  required_argument, NULL, 'T' },
++#endif /* SETTIME */
+       { "user",     required_argument, NULL, 'u' },
+       { "userdump", no_argument,       NULL, 'u' | 0x100 },
+       { "version",  no_argument,       NULL, 'V' },
+@@ -246,7 +264,11 @@
+     };
+   int c;
++#ifdef USE_SETTIME
++  while ((c = getopt_long(argc, argv, "a:c:dD:E:g:hi:l:L:mp:P:r:s:t:T:u:v:Vw:", long_options, NULL)) != -1) {
++#else
+   while ((c = getopt_long(argc, argv, "a:c:dD:E:g:hi:l:L:mp:P:r:s:t:u:v:Vw:", long_options, NULL)) != -1) {
++#endif /* SETTIME */
+         switch (c) {
+           case 'a': AudioCommand = optarg;
+                     break;
+@@ -263,6 +285,9 @@
+                     fprintf(stderr, "vdr: invalid DVB device number: %s\n", optarg);
+                     return 2;
+                     break;
++          case 'e' | 0x100:
++                    DisplayExtensions = true;
++                    break;
+           case 'E': EpgDataFileName = (*optarg != '-' ? optarg : NULL);
+                     break;
+           case 'g': cSVDRP::SetGrabImageDir(*optarg != '-' ? optarg : NULL);
+@@ -341,6 +366,10 @@
+                     break;
+           case 's': ShutdownHandler.SetShutdownCommand(optarg);
+                     break;
++#ifdef USE_SETTIME
++          case 'T': SetTime = strdup(optarg);
++                    break;
++#endif /* SETTIME */
+           case 't': Terminal = optarg;
+                     if (access(Terminal, R_OK | W_OK) < 0) {
+                        fprintf(stderr, "vdr: can't access terminal: %s\n", Terminal);
+@@ -406,6 +435,7 @@
+                "  -D NUM,   --device=NUM   use only the given DVB device (NUM = 0, 1, 2...)\n"
+                "                           there may be several -D options (default: all DVB\n"
+                "                           devices will be used)\n"
++               "            --extensions   print patchlevel and exit\n"
+                "  -E FILE,  --epgfile=FILE write the EPG data into the given FILE (default is\n"
+                "                           '%s' in the video directory)\n"
+                "                           '-E-' disables this\n"
+@@ -437,6 +467,9 @@
+                "  -r CMD,   --record=CMD   call CMD before and after a recording\n"
+                "  -s CMD,   --shutdown=CMD call CMD to shutdown the computer\n"
+                "  -t TTY,   --terminal=TTY controlling tty\n"
++#ifdef USE_SETTIME
++               "  -T CMD,   --timeset=CMD  call CMD to set the system time\n"
++#endif /* SETTIME */
+                "  -u USER,  --user=USER    run as user USER; only applicable if started as\n"
+                "                           root\n"
+                "            --userdump     allow coredumps if -u is given (debugging)\n"
+@@ -480,6 +513,197 @@
+      return 0;
+      }
++  if (DisplayExtensions) {
++     printf("VDR %s\n", VDRVERSION);
++     printf("VDREXTENSIONS %d\n", VDREXTENSIONS);
++
++#ifdef USE_ANALOGTV
++     printf("  ANALOGTV\n");
++#endif /* ANALOGTV */
++
++#ifdef USE_ATSC
++     printf("  ATSC\n");
++#endif /* ATSC */
++
++#ifdef USE_CHANNELSCAN
++     printf("  CHANNELSCAN\n");
++#endif /* CHANNELSCAN */
++
++#ifdef USE_CMDRECCMDI18N
++     printf("  CMDRECCMDI18N\n");
++#endif /* CMDRECCMDI18N */
++
++#ifdef USE_CMDSUBMENU
++     printf("  CMDSUBMENU %d\n", CMDSUBMENUVERSNUM);
++#endif /* CMDSUBMENU */
++
++#ifdef USE_CUTTERLIMIT
++     printf("  CUTTERLIMIT\n");
++#endif /* CUTTERLIMIT */
++
++#ifdef USE_CUTTERQUEUE
++     printf("  CUTTERQUEUE\n");
++#endif /* CUTTERQUEUE */
++
++#ifdef USE_CUTTIME
++     printf("  CUTTIME\n");
++#endif /* CUTTIME */
++
++#ifdef USE_DDEPGENTRY
++     printf("  DDEPGENTRY\n");
++#endif /* DDEPGENTRY */
++
++#ifdef USE_DELTIMESHIFTREC
++     printf("  DELTIMESHIFTREC\n");
++#endif /* DELTIMESHIFTREC */
++
++#ifdef USE_DOLBYINREC
++     printf("  DOLBYINREC\n");
++#endif /* DOLBYINREC */
++
++#ifdef USE_DVBSETUP
++     printf("  DVBSETUP\n");
++#endif /* DVBSETUP */
++
++#ifdef USE_DVDARCHIVE
++     printf("  DVDARCHIVE\n");
++#endif /* DVDARCHIVE */
++
++#ifdef USE_DVDCHAPJUMP
++     printf("  DVDCHAPJUMP\n");
++#endif /* DVDCHAPJUMP */
++
++#ifdef USE_DVLRECSCRIPTADDON
++     printf("  DVLRECSCRIPTADDON\n");
++#endif /* DVLRECSCRIPTADDON */
++
++#ifdef USE_DVLVIDPREFER
++     printf("  DVLVIDPREFER\n");
++#endif /* DVLVIDPREFER */
++
++#ifdef USE_DVLFRIENDLYFNAMES
++     printf("  DVLFRIENDLYFNAMES\n");
++#endif /* DVLFRIENDLYFNAMES */
++
++#ifdef USE_EM84XX
++     printf("  EM84XX\n");
++#endif /* EM84XX */
++
++#ifdef USE_GRAPHTFT
++     printf("  GRAPHTFT\n");
++#endif /* GRAPHTFT */
++
++#ifdef USE_HARDLINKCUTTER
++     printf("  HARDLINKCUTTER\n");
++#endif /* HARDLINKCUTTER */
++
++#ifdef USE_JUMPPLAY
++     printf("  JUMPPLAY %d\n", JUMPPLAYVERSNUM);
++#endif /* JUMPPLAY */
++
++#ifdef USE_LIEMIEXT
++     printf("  LIEMIEXT %d\n", LIEMIKUUTIO);
++#endif /* LIEMIEXT */
++
++#ifdef USE_LIRCSETTINGS
++     printf("  LIRCSETTINGS\n");
++#endif /* LIRCSETTINGS */
++
++#ifdef USE_LNBSHARE
++     printf("  LNBSHARE\n");
++#endif /* LNBSHARE */
++
++#ifdef USE_MAINMENUHOOKS
++     printf("  MAINMENUHOOKS %3.1f\n", MAINMENUHOOKSVERSNUM);
++#endif /* MAINMENUHOOKS */
++
++#ifdef USE_MENUORG
++     printf("  MENUORG\n");
++#endif /* MENUORG */
++
++#ifdef USE_NOEPG
++     printf("  NOEPG\n");
++#endif /* NOEPG */
++
++#ifdef USE_OSDMAXITEMS
++     printf("  OSDMAXITEMS\n");
++#endif /* OSDMAXITEMS */
++
++#ifdef USE_PARENTALRATING
++     printf("  PARENTALRATING\n");
++#endif /* PARENTALRATING */
++
++#ifdef USE_PINPLUGIN
++     printf("  PINPLUGIN %d\n", PIN_PLUGIN_PATCH);
++#endif /* PINPLUGIN */
++
++#ifdef USE_PLUGINAPI
++     printf("  PLUGINAPI\n");
++#endif /* PLUGINAPI */
++
++#ifdef USE_PLUGINMISSING
++     printf("  PLUGINMISSING\n");
++#endif /* PLUGINMISSING */
++
++#ifdef USE_PLUGINPARAM
++     printf("  PLUGINPARAM %d\n", PLUGINPARAMPATCHVERSNUM);
++#endif /* PLUGINPARAM */
++
++#ifdef USE_ROTOR
++     printf("  ROTOR\n");
++#endif /* ROTOR */
++
++#ifdef USE_SETTIME
++     printf("  SETTIME\n");
++#endif /* SETTIME */
++
++#ifdef USE_SETUP
++     printf("  SETUP\n");
++#endif /* SETUP */
++
++#ifdef USE_SOFTOSD
++     printf("  SOFTOSD\n");
++#endif /* SOFTOSD */
++
++#ifdef USE_SOURCECAPS
++     printf("  SOURCECAPS\n");
++#endif /* SOURCECAPS */
++
++#ifdef USE_SORTRECORDS
++     printf("  SORTRECORDS\n");
++#endif /* SORTRECORDS */
++
++#ifdef USE_TIMERCMD
++     printf("  TIMERCMD\n");
++#endif /* TIMERCMD */
++
++#ifdef USE_TIMERINFO
++     printf("  TIMERINFO\n");
++#endif /* TIMERINFO */
++
++#ifdef USE_TTXTSUBS
++     printf("  TTXTSUBS\n");
++#endif /* TTXTSUBS */
++
++#ifdef USE_VALIDINPUT
++     printf("  VALIDINPUT\n");
++#endif /* VALIDINPUT */
++
++#ifdef USE_VOLCTRL
++     printf("  VOLCTRL\n");
++#endif /* VOLCTRL */
++
++#ifdef USE_WAREAGLEICON
++     printf("  WAREAGLEICON\n");
++#endif /* WAREAGLEICON */
++
++#ifdef USE_YAEPG
++     printf("  YAEPG\n");
++#endif /* YAEPG */
++
++     return 0;
++     }
++
+   // Log file:
+   if (SysLogLevel > 0)
+@@ -574,8 +798,20 @@
+   Diseqcs.Load(AddDirectory(ConfigDirectory, "diseqc.conf"), true, Setup.DiSEqC);
+   Channels.Load(AddDirectory(ConfigDirectory, "channels.conf"), false, true);
+   Timers.Load(AddDirectory(ConfigDirectory, "timers.conf"));
++#ifdef USE_CMDRECCMDI18N
++  LoadCommandsI18n(Commands, AddDirectory(ConfigDirectory, "commands.conf"), true);
++  LoadCommandsI18n(RecordingCommands, AddDirectory(ConfigDirectory, "reccmds.conf"), true);
++#else
+   Commands.Load(AddDirectory(ConfigDirectory, "commands.conf"), true);
+   RecordingCommands.Load(AddDirectory(ConfigDirectory, "reccmds.conf"), true);
++#endif /* CMDRECCMDI18N */
++#ifdef USE_TIMERCMD
++#ifdef USE_CMDRECCMDI18N
++  LoadCommandsI18n(TimerCommands, AddDirectory(ConfigDirectory, "timercmds.conf"), true);
++#else
++  TimerCommands.Load(AddDirectory(ConfigDirectory, "timercmds.conf"), true);
++#endif /* CMDRECCMDI18N */
++#endif /* TIMERCMD */
+   SVDRPhosts.Load(AddDirectory(ConfigDirectory, "svdrphosts.conf"), true);
+   Keys.Load(AddDirectory(ConfigDirectory, "remote.conf"));
+   KeyMacros.Load(AddDirectory(ConfigDirectory, "keymacros.conf"), true);
+@@ -736,7 +972,11 @@
+         // Make sure we have a visible programme in case device usage has changed:
+         if (!EITScanner.Active() && cDevice::PrimaryDevice()->HasDecoder() && !cDevice::PrimaryDevice()->HasProgramme()) {
+            static time_t lastTime = 0;
++#ifdef USE_CHANNELSCAN
++           if (!scanning_on_receiving_device && (!Menu || CheckHasProgramme) && Now - lastTime > MINCHANNELWAIT) {
++#else
+            if ((!Menu || CheckHasProgramme) && Now - lastTime > MINCHANNELWAIT) { // !Menu to avoid interfering with the CAM if a CAM menu is open
++#endif /* CHANNELSCAN */
+               cChannel *Channel = Channels.GetByNumber(cDevice::CurrentChannel());
+               if (Channel && (Channel->Vpid() || Channel->Apid(0))) {
+                  if (!Channels.SwitchTo(cDevice::CurrentChannel()) // try to switch to the original channel...
+@@ -919,6 +1159,9 @@
+         cOsdObject *Interact = Menu ? Menu : cControl::Control();
+         eKeys key = Interface->GetKey(!Interact || !Interact->NeedsFastResponse());
+         if (ISREALKEY(key)) {
++#ifdef USE_PINPLUGIN
++           cStatus::MsgUserAction(key, Interact);
++#endif /* PINPLUGIN */
+            EITScanner.Activity();
+            // Cancel shutdown countdown:
+            if (ShutdownHandler.countdown)
+@@ -991,10 +1234,16 @@
+                      cControl::Control()->Hide();
+                   cPlugin *plugin = cPluginManager::GetPlugin(PluginName);
+                   if (plugin) {
++#ifdef USE_PINPLUGIN
++                  if (!cStatus::MsgPluginProtected(plugin)) {
++#endif /* PINPLUGIN */
+                      Menu = plugin->MainMenuAction();
+                      if (Menu)
+                         Menu->Show();
+                      }
++#ifdef USE_PINPLUGIN
++                     }
++#endif /* PINPLUGIN */
+                   else
+                      esyslog("ERROR: unknown plugin '%s'", PluginName);
+                   }
+@@ -1184,13 +1433,26 @@
+                   Channels.SwitchTo(PreviousChannel[PreviousChannelIndex ^= 1]);
+                   break;
+                   }
++#ifdef USE_VOLCTRL
++             // Left/Right volume control
++#else
+              // Direct Channel Select:
+              case k1 ... k9:
+              // Left/Right rotates through channel groups:
++#endif /* VOLCTRL */
+              case kLeft|k_Repeat:
+              case kLeft:
+              case kRight|k_Repeat:
+              case kRight:
++#ifdef USE_VOLCTRL
++                  if (Setup.LRVolumeControl && Setup.LRChannelGroups < 2) {
++                     cRemote::Put(NORMALKEY(key) == kLeft ? kVolDn : kVolUp, true);
++                     break;
++                     }
++                  // else fall through
++             // Direct Channel Select:
++             case k1 ... k9:
++#endif /* VOLCTRL */
+              // Previous/Next rotates through channel groups:
+              case kPrev|k_Repeat:
+              case kPrev:
+@@ -1208,9 +1470,15 @@
+              // Instant resume of the last viewed recording:
+              case kPlay:
+                   if (cReplayControl::LastReplayed()) {
++#ifdef USE_PINPLUGIN
++                     if (cStatus::MsgReplayProtected(0, cReplayControl::LastReplayed(), 0, false) == false) {
++#endif /* PINPLUGIN */
+                      cControl::Shutdown();
+                      cControl::Launch(new cReplayControl);
+                      }
++#ifdef USE_PINPLUGIN
++                     }
++#endif /* PINPLUGIN */
+                   break;
+              default:    break;
+              }
+diff -NaurwB vdr-1.7.10/vdrttxtsubshooks.c vdr-1.7.10-patched/vdrttxtsubshooks.c
+--- vdr-1.7.10/vdrttxtsubshooks.c      1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.10-patched/vdrttxtsubshooks.c      2009-12-18 06:25:25.000000000 +0100
+@@ -0,0 +1,65 @@
++#ifdef USE_TTXTSUBS
++/*
++ * vdr-ttxtsubs - A plugin for the Linux Video Disk Recorder
++ * Copyright (c) 2003 - 2008 Ragnar Sundblad <ragge@nada.kth.se>
++ *
++ * 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 of the License, 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
++ * this program; if not, write to the Free Software Foundation, Inc.,
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
++ *
++ */
++
++#include <stdlib.h>
++#include <stdio.h>
++#include <stdint.h>
++
++#include "vdrttxtsubshooks.h"
++
++// XXX Really should be a list...
++static cVDRTtxtsubsHookListener *gListener;
++
++// ------ class cVDRTtxtsubsHookProxy ------
++
++class cVDRTtxtsubsHookProxy : public cVDRTtxtsubsHookListener
++{
++ public:
++  virtual void HideOSD(void) { if(gListener) gListener->HideOSD(); };
++  virtual void ShowOSD(void) { if(gListener) gListener->ShowOSD(); };
++  virtual void PlayerTeletextData(uint8_t *p, int length, bool IsPesRecording)
++    { if(gListener) gListener->PlayerTeletextData(p, length, IsPesRecording); };
++  virtual int ManualPageNumber(const cChannel *channel)
++    { if(gListener) return gListener->ManualPageNumber(channel); else return 0; };
++};
++
++
++// ------ class cVDRTtxtsubsHookListener ------
++
++cVDRTtxtsubsHookListener::~cVDRTtxtsubsHookListener()
++{
++  gListener = 0;
++}
++
++void cVDRTtxtsubsHookListener::HookAttach(void)
++{
++  gListener = this;
++  //printf("cVDRTtxtsubsHookListener::HookAttach\n");
++}
++
++static cVDRTtxtsubsHookProxy gProxy;
++
++cVDRTtxtsubsHookListener *cVDRTtxtsubsHookListener::Hook(void)
++{
++  return &gProxy;
++}
++#endif /* TTXTSUBS */
++
+diff -NaurwB vdr-1.7.10/vdrttxtsubshooks.h vdr-1.7.10-patched/vdrttxtsubshooks.h
+--- vdr-1.7.10/vdrttxtsubshooks.h      1970-01-01 01:00:00.000000000 +0100
++++ vdr-1.7.10-patched/vdrttxtsubshooks.h      2009-12-18 06:25:25.000000000 +0100
+@@ -0,0 +1,50 @@
++#ifdef USE_TTXTSUBS
++/*
++ * vdr-ttxtsubs - A plugin for the Linux Video Disk Recorder
++ * Copyright (c) 2003 - 2008 Ragnar Sundblad <ragge@nada.kth.se>
++ *
++ * 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 of the License, 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
++ * this program; if not, write to the Free Software Foundation, Inc.,
++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
++ *
++ */
++
++#ifndef __VDRTTXTSUBSHOOKS_H
++#define __VDRTTXTSUBSHOOKS_H
++
++#define TTXTSUBSVERSNUM 1
++
++class cDevice;
++class cChannel;
++
++#define VDRTTXTSUBSHOOKS
++
++class cVDRTtxtsubsHookListener {
++ public:
++  cVDRTtxtsubsHookListener(void) {};
++  virtual ~cVDRTtxtsubsHookListener();
++
++  void HookAttach(void);
++
++  virtual void HideOSD(void) {};
++  virtual void ShowOSD(void) {};
++  virtual void PlayerTeletextData(uint8_t *p, int length, bool IsPesRecording = true) {};
++  virtual int ManualPageNumber(const cChannel *channel)
++    { return 0; };
++
++  // used by VDR to call hook listeners
++  static cVDRTtxtsubsHookListener *Hook(void);
++};
++
++#endif
++#endif /* TTXTSUBS */
+diff -NaurwB vdr-1.7.10/videodir.c vdr-1.7.10-patched/videodir.c
+--- vdr-1.7.10/videodir.c      2008-02-16 14:00:03.000000000 +0100
++++ vdr-1.7.10-patched/videodir.c      2009-12-18 06:29:15.000000000 +0100
+@@ -19,6 +19,10 @@
+ #include "recording.h"
+ #include "tools.h"
++#ifdef USE_HARDLINKCUTTER
++//#define HARDLINK_TEST_ONLY
++#endif /* HARDLINKCUTTER */
++
+ const char *VideoDirectory = VIDEODIR;
+ class cVideoDirectory {
+@@ -36,6 +40,11 @@
+   bool Next(void);
+   void Store(void);
+   const char *Adjust(const char *FileName);
++#ifdef USE_DVLVIDPREFER
++  char *GetVidPath(int nVid);
++  bool GetPreferedVideoDir(void);
++  bool IsVidDirOK(int nVid, int *freeMB = NULL);
++#endif /* DVLVIDPREFER */
+   };
+ cVideoDirectory::cVideoDirectory(void)
+@@ -117,6 +126,9 @@
+   if ((Flags & O_CREAT) != 0) {
+      cVideoDirectory Dir;
+      if (Dir.IsDistributed()) {
++#ifdef USE_DVLVIDPREFER
++        if (Setup.UseVidPrefer == 0) {
++#endif /* DVLVIDPREFER */
+         // Find the directory with the most free space:
+         int MaxFree = Dir.FreeMB();
+         while (Dir.Next()) {
+@@ -126,14 +138,24 @@
+                  MaxFree = Free;
+                  }
+               }
++#ifdef USE_DVLVIDPREFER
++        }
++          else Dir.GetPreferedVideoDir();
++#endif /* DVLVIDPREFER */
+         if (Dir.Stored()) {
+            ActualFileName = Dir.Adjust(FileName);
+            if (!MakeDirs(ActualFileName, false))
+               return NULL; // errno has been set by MakeDirs()
++#ifdef USE_DVLVIDPREFER
++           if (strcmp(ActualFileName, FileName) != 0) {
++#endif /* DVLVIDPREFER */
+            if (symlink(ActualFileName, FileName) < 0) {
+               LOG_ERROR_STR(FileName);
+               return NULL;
+               }
++#ifdef USE_DVLVIDPREFER
++              }
++#endif /* DVLVIDPREFER */
+            ActualFileName = strdup(ActualFileName); // must survive Dir!
+            }
+         }
+@@ -168,6 +190,122 @@
+   return RemoveFileOrDir(FileName, true);
+ }
++#ifdef USE_HARDLINKCUTTER
++static bool StatNearestDir(const char *FileName, struct stat *Stat)
++{
++  cString Name(FileName);
++  char *p;
++  while ((p = strrchr((const char*)Name + 1, '/')) != NULL) {
++        *p = 0; // truncate at last '/'
++        if (stat(Name, Stat) == 0) {
++           isyslog("StatNearestDir: Stating %s", (const char*)Name);
++           return true;
++           }
++        }
++  return false;
++}
++
++bool HardLinkVideoFile(const char *OldName, const char *NewName)
++{
++  // Incoming name must be in base video directory:
++  if (strstr(OldName, VideoDirectory) != OldName) {
++     esyslog("ERROR: %s not in %s", OldName, VideoDirectory);
++     return false;
++     }
++  if (strstr(NewName, VideoDirectory) != NewName) {
++     esyslog("ERROR: %s not in %s", NewName, VideoDirectory);
++     return false;
++     }
++
++  const char *ActualNewName = NewName;
++  cString ActualOldName(ReadLink(OldName), true);
++
++  // Some safety checks:
++  struct stat StatOldName;
++  if (lstat(ActualOldName, &StatOldName) == 0) {
++     if (S_ISLNK(StatOldName.st_mode)) {
++        esyslog("HardLinkVideoFile: Failed to resolve symbolic link %s", (const char*)ActualOldName);
++        return false;
++        }
++     }
++  else {
++     esyslog("HardLinkVideoFile: lstat failed on %s", (const char*)ActualOldName);
++     return false;
++     }
++  isyslog("HardLinkVideoFile: %s is on %i", (const char*)ActualOldName, (int)StatOldName.st_dev);
++
++  // Find the video directory where ActualOldName is located
++
++  cVideoDirectory Dir;
++  struct stat StatDir;
++  if (!StatNearestDir(NewName, &StatDir)) {
++     esyslog("HardLinkVideoFile: stat failed on %s", NewName);
++     return false;
++     }
++
++  isyslog("HardLinkVideoFile: %s is on %i", NewName, (int)StatDir.st_dev);
++  if (StatDir.st_dev != StatOldName.st_dev) {
++     // Not yet found.
++
++     if (!Dir.IsDistributed()) {
++        esyslog("HardLinkVideoFile: No matching video folder to hard link %s", (const char*)ActualOldName);
++        return false;
++        }
++
++     // Search in video01 and upwards
++     bool found = false;
++     while (Dir.Next()) {
++           Dir.Store();
++           const char *TmpNewName = Dir.Adjust(NewName);
++           if (StatNearestDir(TmpNewName, &StatDir) && StatDir.st_dev == StatOldName.st_dev) {
++              isyslog("HardLinkVideoFile: %s is on %i (match)", TmpNewName, (int)StatDir.st_dev);
++              ActualNewName = TmpNewName;
++              found = true;
++              break;
++              }
++           isyslog("HardLinkVideoFile: %s is on %i", TmpNewName, (int)StatDir.st_dev);
++           }
++     if (ActualNewName == NewName) {
++        esyslog("HardLinkVideoFile: No matching video folder to hard link %s", (const char*)ActualOldName);
++        return false;
++        }
++
++     // Looking good, we have a match. Create necessary folders.
++     if (!MakeDirs(ActualNewName, false))
++        return false;
++     // There's no guarantee that the directory of ActualNewName 
++     // is on the same device as the dir that StatNearestDir found.
++     // But worst case is that the link fails.
++     }
++
++#ifdef HARDLINK_TEST_ONLY
++  // Do the hard link to *.vdr_ for testing only
++  char *name = NULL;
++  asprintf(&name, "%s_",ActualNewName);
++  link(ActualOldName, name); 
++  free(name);
++  return false;
++#endif // HARDLINK_TEST_ONLY
++
++  // Try creating the hard link
++  if (link(ActualOldName, ActualNewName) != 0) {
++     // Failed to hard link. Maybe not allowed on file system.
++     LOG_ERROR_STR(ActualNewName);
++     isyslog("HardLinkVideoFile: failed to hard link from %s to %s", (const char*)ActualOldName, ActualNewName);
++     return false;
++     }
++
++  if (ActualNewName != NewName) {
++     // video01 and up. Do the remaining symlink
++     if (symlink(ActualNewName, NewName) < 0) {
++        LOG_ERROR_STR(NewName);
++        return false;
++        }
++     }
++  return true;
++}
++#endif /* HARDLINKCUTTER */
++
+ bool VideoFileSpaceAvailable(int SizeMB)
+ {
+   cVideoDirectory Dir;
+@@ -232,6 +370,129 @@
+      } while (Dir.Next());
+ }
++#ifdef USE_DVLVIDPREFER
++// returns path to nVid'th video directory or NULL if not existing
++char *cVideoDirectory::GetVidPath(int nVid)
++{
++  char *b = strdup(VideoDirectory);
++  int l = strlen(b), di, n;
++
++  while (l-- > 0 && isdigit(b[ l ]));
++
++  l++;
++  di = strlen(b) - l;
++
++  // di == number of digits
++  n = atoi(&b[ l ]);
++  if (n != 0)
++     return NULL;
++
++  // add requested number to dir name
++  sprintf(&b[ l ], "%0*d", di, nVid);
++
++  if (DirectoryOk(b) == true)
++     return b;
++
++  free(b);
++  return NULL;
++}
++
++// checks if a video dir is 'valid'
++bool cVideoDirectory::IsVidDirOK(int nVid, int *freeMB)
++{
++  char *dn;
++  int fMB;
++
++  if (nVid >= Setup.nVidPrefer)
++     return false;
++
++  if (Setup.VidPreferSize[ nVid ] == -1)
++     return false;
++
++  dn = GetVidPath(nVid);
++  if (dn == NULL)
++     return false;
++
++  fMB = FreeDiskSpaceMB(dn, NULL);
++  if (freeMB != NULL)
++     *freeMB = fMB;
++
++  free(dn);
++
++  if (Setup.VidPreferSize[ nVid ] >= fMB)
++     return false;
++  return true;
++}
++
++
++// calculates which video dir to use
++bool cVideoDirectory::GetPreferedVideoDir(void)
++{
++  cVideoDirectory d;
++  int nDirs = 1,
++  vidUse = Setup.nVidPrefer;
++  int i, top, topFree, x;
++
++  if (name == NULL)
++     return(false);
++
++  // count available video dirs
++  while (d.Next() == true)
++        nDirs++;
++
++  if (vidUse > nDirs)
++     vidUse = nDirs;
++
++  // check for prefered video dir
++  for (i = 0, top = -1, topFree = 0; i < vidUse; i++) {
++      if (IsVidDirOK(i, &x) == true) {
++         if (top == -1) {
++            // nothing set yet, use first 'ok' dir
++            top = i;
++            topFree = x;
++            }
++         else {
++            // check if we got a higher priority
++            if (Setup.VidPreferPrio[ i ] >= Setup.VidPreferPrio[ top ]) {
++               top = i;
++               topFree = x;
++               }
++            // check if we got same priority but more space
++            else if (Setup.VidPreferPrio[ i ] == Setup.VidPreferPrio[ top ] && x >= topFree) {
++               top = i;
++               topFree = x;
++               }
++            }
++         }
++      }
++
++  if (top == -1) {
++     isyslog("VidPrefer: no prefered video directory could be determined!");
++
++     // something went wrong here...
++     // let VDR determine the video directory
++     int MaxFree = FreeMB();
++
++     while (Next()) {
++           int Free = FreeDiskSpaceMB(Name());
++
++           if (Free > MaxFree) {
++              Store();
++              MaxFree = Free;
++              }
++           }
++     }
++  else {
++     isyslog("VidPrefer: prefered video directory '%d' set.", top);
++     if (stored != NULL)
++        free(stored);
++     stored = GetVidPath(top);
++     }
++
++  return true;
++}
++#endif /* DVLVIDPREFER */
++
+ bool IsOnVideoDirectoryFileSystem(const char *FileName)
+ {
+   cVideoDirectory Dir;
+diff -NaurwB vdr-1.7.10/videodir.h vdr-1.7.10-patched/videodir.h
+--- vdr-1.7.10/videodir.h      2008-02-16 13:53:11.000000000 +0100
++++ vdr-1.7.10-patched/videodir.h      2009-12-18 06:25:25.000000000 +0100
+@@ -19,6 +19,9 @@
+ int CloseVideoFile(cUnbufferedFile *File);
+ bool RenameVideoFile(const char *OldName, const char *NewName);
+ bool RemoveVideoFile(const char *FileName);
++#ifdef USE_HARDLINKCUTTER
++bool HardLinkVideoFile(const char *OldName, const char *NewName);
++#endif /* HARDLINKCUTTER */
+ bool VideoFileSpaceAvailable(int SizeMB);
+ int VideoDiskSpace(int *FreeMB = NULL, int *UsedMB = NULL); // returns the used disk space in percent
+ cString PrefixVideoFileName(const char *FileName, char Prefix);
+diff -NaurwB vdr-1.7.10/Make.config.template vdr-1.7.10-patched/Make.config.template
+--- vdr-1.7.10/Make.config.template    2009-01-18 11:46:13.000000000 +0100
++++ vdr-1.7.10-patched/Make.config.template    2009-12-18 06:37:57.000000000 +0100
+@@ -42,8 +42,247 @@
+ ## Define if you want vdr to not run as root
+ #VDR_USER = vdr
++### VDR-Extensions:
++# Comment the patches you don't need
++# DVDCHAPJUMP needs DVDARCHIVE enabled
++# DVDARCHIVE needs LIEMIEXT enabled
++# SORTRECORDS needs LIEMIEXT enabled
++# you can only enable MENUORG or SETUP
++
++#ANALOGTV = 1
++#ATSC = 1
++#CHANNELSCAN = 1
++CMDRECCMDI18N = 1
++CMDSUBMENU = 1
++#CUTTERLIMIT = 1
++#CUTTERQUEUE = 1
++CUTTIME = 1
++DDEPGENTRY = 1
++#DELTIMESHIFTREC = 1
++DOLBYINREC = 1
++#DVBSETUP = 1
++#DVDARCHIVE = 1
++#DVDCHAPJUMP = 1
++#DVLFRIENDLYFNAMES = 1
++#DVLRECSCRIPTADDON = 1
++#DVLVIDPREFER = 1
++#EM84XX = 1
++#GRAPHTFT = 1
++#HARDLINKCUTTER = 1
++#JUMPPLAY = 1
++LIEMIEXT = 1
++#LIRCSETTINGS = 1
++#LNBSHARE = 1
++#MAINMENUHOOKS = 1
++#MENUORG = 1
++#NOEPG = 1
++#OSDMAXITEMS = 1
++PARENTALRATING = 1
++#PINPLUGIN = 1
++PLUGINAPI = 1
++PLUGINMISSING = 1
++#PLUGINPARAM = 1
++#ROTOR = 1
++SETTIME = 1
++#SETUP = 1
++#SOFTOSD = 1
++#SOURCECAPS = 1
++#SORTRECORDS = 1
++STATUS_EXTENSION = 1
++#TIMERCMD = 1
++#TIMERINFO = 1
++#TTXTSUBS = 1
++#VALIDINPUT = 1
++#VOLCTRL = 1
++WAREAGLEICON = 1
++#YAEPG = 1
++
+ ### You don't need to touch the following:
+ ifdef DVBDIR
+ INCLUDES += -I$(DVBDIR)/include
+ endif
++
++ifdef ANALOGTV
++DEFINES += -DUSE_ANALOGTV
++endif
++
++ifdef ATSC
++DEFINES += -DUSE_ATSC
++endif
++
++ifdef CHANNELSCAN
++DEFINES += -DUSE_CHANNELSCAN
++endif
++
++ifdef CMDRECCMDI18N
++DEFINES += -DUSE_CMDRECCMDI18N
++endif
++
++ifdef CMDSUBMENU
++DEFINES += -DUSE_CMDSUBMENU
++endif
++
++ifdef CUTTERLIMIT
++DEFINES += -DUSE_CUTTERLIMIT
++endif
++
++ifdef CUTTERQUEUE
++DEFINES += -DUSE_CUTTERQUEUE
++endif
++
++ifdef CUTTIME
++DEFINES += -DUSE_CUTTIME
++endif
++
++ifdef DDEPGENTRY
++DEFINES += -DUSE_DDEPGENTRY
++endif
++
++ifdef DELTIMESHIFTREC
++DEFINES += -DUSE_DELTIMESHIFTREC
++endif
++
++ifdef DOLBYINREC
++DEFINES += -DUSE_DOLBYINREC
++endif
++
++ifdef DVBSETUP
++DEFINES += -DUSE_DVBSETUP
++endif
++
++ifdef DVDARCHIVE
++ifdef LIEMIEXT
++DEFINES += -DUSE_DVDARCHIVE
++endif
++endif
++
++ifdef DVLRECSCRIPTADDON
++DEFINES += -DUSE_DVLRECSCRIPTADDON
++endif
++
++ifdef DVLVIDPREFER
++DEFINES += -DUSE_DVLVIDPREFER
++endif
++
++ifdef DVLFRIENDLYFNAMES
++DEFINES += -DUSE_DVLFRIENDLYFNAMES
++endif
++
++ifdef EM84XX
++DEFINES += -DUSE_EM84XX
++endif
++
++ifdef GRAPHTFT
++DEFINES += -DUSE_GRAPHTFT
++endif
++
++ifdef HARDLINKCUTTER
++DEFINES += -DUSE_HARDLINKCUTTER
++endif
++
++ifdef JUMPPLAY
++DEFINES += -DUSE_JUMPPLAY
++endif
++
++ifdef LIEMIEXT
++DEFINES += -DUSE_LIEMIEXT
++endif
++
++ifdef LIRCSETTINGS
++DEFINES += -DUSE_LIRCSETTINGS
++endif
++
++ifdef LNBSHARE
++DEFINES += -DUSE_LNBSHARE
++endif
++
++ifdef MAINMENUHOOKS
++DEFINES += -DUSE_MAINMENUHOOKS
++endif
++
++ifdef MENUORG
++DEFINES += -DUSE_MENUORG
++else
++ifdef SETUP
++DEFINES += -DUSE_SETUP
++endif
++endif
++
++ifdef NOEPG
++DEFINES += -DUSE_NOEPG
++endif
++
++ifdef OSDMAXITEMS
++DEFINES += -DUSE_OSDMAXITEMS
++endif
++
++ifdef PARENTALRATING
++DEFINES += -DUSE_PARENTALRATING
++endif
++
++ifdef PINPLUGIN
++DEFINES += -DUSE_PINPLUGIN
++endif
++
++ifdef PLUGINMISSING
++DEFINES += -DUSE_PLUGINMISSING
++endif
++
++ifdef PLUGINPARAM
++DEFINES += -DUSE_PLUGINPARAM
++endif
++
++ifdef ROTOR
++DEFINES += -DUSE_ROTOR
++endif
++
++ifdef SETTIME
++DEFINES += -DUSE_SETTIME
++endif
++
++ifdef STATUS_EXTENSION
++DEFINES += -DUSE_STATUS_EXTENSION
++endif
++
++ifdef SOFTOSD
++DEFINES += -DUSE_SOFTOSD
++endif
++
++ifdef SOURCECAPS
++DEFINES += -DUSE_SOURCECAPS
++endif
++
++ifdef SORTRECORDS
++ifdef LIEMIEXT
++DEFINES += -DUSE_SORTRECORDS
++endif
++endif
++
++ifdef TIMERCMD
++DEFINES += -DUSE_TIMERCMD
++endif
++
++ifdef TIMERINFO
++DEFINES += -DUSE_TIMERINFO
++endif
++
++ifdef TTXTSUBS
++DEFINES += -DUSE_TTXTSUBS
++endif
++
++ifdef VALIDINPUT
++DEFINES += -DUSE_VALIDINPUT
++endif
++
++ifdef VOLCTRL
++DEFINES += -DUSE_VOLCTRL
++endif
++
++ifdef WAREAGLEICON
++DEFINES += -DUSE_WAREAGLEICON
++endif
++
++ifdef YAEPG
++DEFINES += -DUSE_YAEPG
++endif
diff --git a/xbmc/pvrclients/vdr-streamdev/patches/vdr-1.7.7-ExtendetStatusMessage.diff b/xbmc/pvrclients/vdr-streamdev/patches/vdr-1.7.7-ExtendetStatusMessage.diff
new file mode 100644 (file)
index 0000000..04b3600
--- /dev/null
@@ -0,0 +1,113 @@
+diff -NaurwB vdr-1.7.7/skins.c vdr-1.7.7-patched/skins.c
+--- vdr-1.7.7/skins.c  2008-02-17 12:31:09.000000000 +0100
++++ vdr-1.7.7-patched/skins.c  2009-11-23 05:13:30.000000000 +0100
+@@ -237,7 +237,11 @@
+      }
+   cSkinDisplay::Current()->SetMessage(Type, s);
+   cSkinDisplay::Current()->Flush();
++#ifdef USE_STATUS_EXTENSION
++  cStatus::MsgOsdStatusMessage(Type, s);
++#else
+   cStatus::MsgOsdStatusMessage(s);
++#endif
+   eKeys k = kNone;
+   if (Type != mtStatus) {
+      k = Interface->Wait(Seconds);
+@@ -248,7 +252,11 @@
+         }
+      else {
+         cSkinDisplay::Current()->SetMessage(Type, NULL);
++#ifdef USE_STATUS_EXTENSION
++        cStatus::MsgOsdStatusMessage(Type, NULL);
++#else
+         cStatus::MsgOsdStatusMessage(NULL);
++#endif
+         }
+      }
+   else if (!s && displayMessage) {
+diff -NaurwB vdr-1.7.7/status.c vdr-1.7.7-patched/status.c
+--- vdr-1.7.7/status.c 2009-06-13 01:01:25.000000000 +0200
++++ vdr-1.7.7-patched/status.c 2009-11-23 04:49:48.000000000 +0100
+@@ -97,11 +97,22 @@
+       sm->OsdTitle(Title);
+ }
++#ifdef USE_STATUS_EXTENSION
++void cStatus::MsgOsdStatusMessage(eMessageType type, const char *Message)
++{
++  for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
++  {
++      sm->OsdStatusMessage(type, Message);
++      sm->OsdStatusMessage(Message); // For comaptibilty
++  }
++}
++#else
+ void cStatus::MsgOsdStatusMessage(const char *Message)
+ {
+   for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
+       sm->OsdStatusMessage(Message);
+ }
++#endif
+ void cStatus::MsgOsdHelpKeys(const char *Red, const char *Green, const char *Yellow, const char *Blue)
+ {
+diff -NaurwB vdr-1.7.7/status.h vdr-1.7.7-patched/status.h
+--- vdr-1.7.7/status.h 2009-06-13 01:01:25.000000000 +0200
++++ vdr-1.7.7-patched/status.h 2009-11-23 04:50:34.000000000 +0100
+@@ -17,6 +17,10 @@
+ #ifdef USE_PINPLUGIN
+ #include "plugin.h"
+ #endif /* PINPLUGIN */
++#ifdef USE_STATUS_EXTENSION
++#include "skins.h"
++#endif /* STATUS_EXTENSION */
++
+ enum eTimerChange { tcMod, tcAdd, tcDel };
+ #ifdef USE_STREAMDEVEXT
+@@ -80,6 +84,11 @@
+   virtual void OsdStatusMessage(const char *Message) {}
+                // Message has been displayed in the status line of the menu.
+                // If Message is NULL, the status line has been cleared.
++#ifdef USE_STATUS_EXTENSION
++  virtual void OsdStatusMessage(eMessageType type, const char *Message) {}
++               // Message has been displayed in the status line of the menu.
++               // If Message is NULL, the status line has been cleared.
++#endif
+   virtual void OsdHelpKeys(const char *Red, const char *Green, const char *Yellow, const char *Blue) {}
+                // The help keys have been set to the given values (may be NULL).
+   virtual void OsdItem(const char *Text, int Index) {}
+@@ -145,7 +154,11 @@
+   static void MsgSetSubtitleTrack(int Index, const char * const *Tracks);
+   static void MsgOsdClear(void);
+   static void MsgOsdTitle(const char *Title);
++#ifdef USE_STATUS_EXTENSION
++  static void MsgOsdStatusMessage(eMessageType type, const char *Message);
++#else
+   static void MsgOsdStatusMessage(const char *Message);
++#endif
+   static void MsgOsdHelpKeys(const char *Red, const char *Green, const char *Yellow, const char *Blue);
+   static void MsgOsdItem(const char *Text, int Index);
+   static void MsgOsdCurrentItem(const char *Text);
+diff -NaurwB vdr-1.7.7/Make.config.template vdr-1.7.7-patched/Make.config.template
+--- vdr-1.7.7/Make.config.template     2009-06-13 01:05:04.000000000 +0200
++++ vdr-1.7.7-patched/Make.config.template     2009-11-23 04:51:28.000000000 +0100
+@@ -87,6 +87,7 @@
+ #SOFTOSD = 1
+ #SOURCECAPS = 1
+ #SORTRECORDS = 1
++STATUS_EXTENSION = 1
+ #STREAMDEVEXT = 1
+ #TIMERCMD = 1
+ #TIMERINFO = 1
+@@ -240,6 +241,10 @@
+ DEFINES += -DUSE_SETTIME
+ endif
++ifdef STATUS_EXTENSION
++DEFINES += -DUSE_STATUS_EXTENSION
++endif
++
+ ifdef SOFTOSD
+ DEFINES += -DUSE_SOFTOSD
+ endif
diff --git a/xbmc/pvrclients/vdr-streamdev/patches/vdr-1.7.7-GenreToFromEpgDat.diff b/xbmc/pvrclients/vdr-streamdev/patches/vdr-1.7.7-GenreToFromEpgDat.diff
new file mode 100644 (file)
index 0000000..4ff0f85
--- /dev/null
@@ -0,0 +1,46 @@
+diff -NaurwB vdr-1.7.7/epg.c vdr-1.7.7-patched/epg.c
+--- vdr-1.7.7/epg.c    2009-06-13 01:01:25.000000000 +0200
++++ vdr-1.7.7-patched/epg.c    2009-11-07 20:40:34.000000000 +0100
+@@ -630,6 +630,17 @@
+             fprintf(f, "%sX %s\n", Prefix, *p->ToString());
+             }
+         }
++#ifdef USE_PARENTALRATING
++     if (!isempty(GetContentsString())) {
++       for (int i = 0; i < MAXEVCONTENTS; i++) {
++         if (!isempty(GetContentsString(i))) {
++            strreplace(description, '\n', '|');
++            fprintf(f, "%sG %i %i %s\n",Prefix, Contents(i) & 0xF0, Contents(i) & 0x0F, (const char *)GetContentsString(i));
++            strreplace(description, '|', '\n');
++           }
++         }
++       }
++#endif /* PARENTALRATING */
+      if (vps)
+         fprintf(f, "%sV %ld\n", Prefix, vps);
+      if (!InfoOnly)
+@@ -697,6 +708,24 @@
+                              }
+                           }
+                        break;
++#ifdef USE_PARENTALRATING
++             case 'G': if (Event) {
++                          unsigned int ContentID = 0;
++                          unsigned int ContentSubID = 0;
++                          int n = sscanf(t, "%u %u", &ContentID, &ContentSubID);
++                          if (n == 2) {
++                            if (ContentID != 0) {
++                              for (int i = 0; i < MAXEVCONTENTS; i++) {
++                                if (Event->Contents(i) == 0) {
++                                  Event->contents[i] = ContentID | ContentSubID;
++                                  break;
++                                  }
++                                }
++                              }
++                            }
++                          }
++                       break;
++#endif /* PARENTALRATING */
+              case 'e': if (Event && !Event->Title())
+                           Event->SetTitle(tr("No title"));
+                        Event = NULL;
diff --git a/xbmc/pvrclients/vdr-streamdev/project/VS2008Express/XBMC_VDR.sln b/xbmc/pvrclients/vdr-streamdev/project/VS2008Express/XBMC_VDR.sln
new file mode 100644 (file)
index 0000000..41a4731
--- /dev/null
@@ -0,0 +1,19 @@
+Microsoft Visual Studio Solution File, Format Version 10.00\r
+# Visual C++ Express 2008\r
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "XBMC_VDR", "XBMC_VDR.vcproj", "{838CDE27-AFDF-449F-9981-A78903C9C71E}"\r
+EndProject\r
+Global\r
+       GlobalSection(SolutionConfigurationPlatforms) = preSolution\r
+               Debug|Win32 = Debug|Win32\r
+               Release|Win32 = Release|Win32\r
+       EndGlobalSection\r
+       GlobalSection(ProjectConfigurationPlatforms) = postSolution\r
+               {838CDE27-AFDF-449F-9981-A78903C9C71E}.Debug|Win32.ActiveCfg = Debug|Win32\r
+               {838CDE27-AFDF-449F-9981-A78903C9C71E}.Debug|Win32.Build.0 = Debug|Win32\r
+               {838CDE27-AFDF-449F-9981-A78903C9C71E}.Release|Win32.ActiveCfg = Release|Win32\r
+               {838CDE27-AFDF-449F-9981-A78903C9C71E}.Release|Win32.Build.0 = Release|Win32\r
+       EndGlobalSection\r
+       GlobalSection(SolutionProperties) = preSolution\r
+               HideSolutionNode = FALSE\r
+       EndGlobalSection\r
+EndGlobal\r
diff --git a/xbmc/pvrclients/vdr-streamdev/project/VS2008Express/XBMC_VDR.vcproj b/xbmc/pvrclients/vdr-streamdev/project/VS2008Express/XBMC_VDR.vcproj
new file mode 100644 (file)
index 0000000..d7797ed
--- /dev/null
@@ -0,0 +1,335 @@
+<?xml version="1.0" encoding="Windows-1252"?>\r
+<VisualStudioProject\r
+       ProjectType="Visual C++"\r
+       Version="9,00"\r
+       Name="pvrclient_vdr"\r
+       ProjectGUID="{838CDE27-AFDF-449F-9981-A78903C9C71E}"\r
+       RootNamespace="XBMC_VDR"\r
+       Keyword="Win32Proj"\r
+       TargetFrameworkVersion="131072"\r
+       >\r
+       <Platforms>\r
+               <Platform\r
+                       Name="Win32"\r
+               />\r
+       </Platforms>\r
+       <ToolFiles>\r
+       </ToolFiles>\r
+       <Configurations>\r
+               <Configuration\r
+                       Name="Debug|Win32"\r
+                       OutputDirectory="Debug"\r
+                       IntermediateDirectory="Debug"\r
+                       ConfigurationType="2"\r
+                       InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"\r
+                       CharacterSet="2"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               Optimization="0"\r
+                               AdditionalIncludeDirectories="..\..\..\..\addons\include;.;..\..\pthread_win32;..\..\windows;..\..\"\r
+                               PreprocessorDefinitions="_WIN32;_DEBUG;_WINDOWS;VDR_EXPORTS;_WIN32PC;_WINSOCKAPI_;_USE_32BIT_TIME_T;_CRT_SECURE_NO_WARNINGS"\r
+                               MinimalRebuild="true"\r
+                               ExceptionHandling="1"\r
+                               BasicRuntimeChecks="3"\r
+                               RuntimeLibrary="3"\r
+                               UsePrecompiledHeader="0"\r
+                               WarningLevel="3"\r
+                               Detect64BitPortabilityProblems="false"\r
+                               DebugInformationFormat="3"\r
+                               DisableSpecificWarnings="4996"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               IgnoreImportLibrary="true"\r
+                               AdditionalDependencies="ws2_32.lib ..\..\pthread_win32\pthreadVC2.lib"\r
+                               OutputFile="../../../../../addons/pvr.vdr.streamdev/XBMC_VDR_win32.pvr"\r
+                               LinkIncremental="1"\r
+                               IgnoreAllDefaultLibraries="false"\r
+                               IgnoreDefaultLibraryNames=""\r
+                               GenerateDebugInformation="true"\r
+                               ProgramDatabaseFile="$(OutDir)/XBMC_VDR.pdb"\r
+                               SubSystem="2"\r
+                               LargeAddressAware="2"\r
+                               RandomizedBaseAddress="1"\r
+                               DataExecutionPrevention="0"\r
+                               TargetMachine="1"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManifestTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCAppVerifierTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                       />\r
+               </Configuration>\r
+               <Configuration\r
+                       Name="Release|Win32"\r
+                       OutputDirectory="Release"\r
+                       IntermediateDirectory="Release"\r
+                       ConfigurationType="2"\r
+                       InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"\r
+                       CharacterSet="2"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               Optimization="0"\r
+                               AdditionalIncludeDirectories="..\..\..\..\addons\include;.;..\..\pthread_win32;..\..\windows;..\..\"\r
+                               PreprocessorDefinitions="_WIN32;NDEBUG;_WINDOWS;VDR_EXPORTS;_WIN32PC;_WINSOCKAPI_;_USE_32BIT_TIME_T"\r
+                               ExceptionHandling="1"\r
+                               BasicRuntimeChecks="0"\r
+                               RuntimeLibrary="2"\r
+                               UsePrecompiledHeader="0"\r
+                               WarningLevel="0"\r
+                               Detect64BitPortabilityProblems="false"\r
+                               DebugInformationFormat="3"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               IgnoreImportLibrary="true"\r
+                               AdditionalDependencies="ws2_32.lib ..\..\pthread_win32\pthreadVC2.lib"\r
+                               OutputFile="../../../../../addons/pvr.vdr.streamdev/XBMC_VDR_win32.pvr"\r
+                               LinkIncremental="1"\r
+                               IgnoreDefaultLibraryNames=""\r
+                               GenerateDebugInformation="false"\r
+                               ProgramDatabaseFile="$(OutDir)/XBMC_VDR.pdb"\r
+                               SubSystem="2"\r
+                               LargeAddressAware="2"\r
+                               OptimizeReferences="0"\r
+                               EnableCOMDATFolding="0"\r
+                               RandomizedBaseAddress="1"\r
+                               DataExecutionPrevention="0"\r
+                               TargetMachine="1"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManifestTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCAppVerifierTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                       />\r
+               </Configuration>\r
+       </Configurations>\r
+       <References>\r
+       </References>\r
+       <Files>\r
+               <Filter\r
+                       Name="Source Files"\r
+                       Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"\r
+                       UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"\r
+                       >\r
+                       <File\r
+                               RelativePath="..\..\channels.cpp"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\channelscan.cpp"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\client.cpp"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\windows\dirent.cpp"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\..\..\addons\library.xbmc.addon\dlfcn-win32.cpp"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\epg.cpp"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\windows\getline.cpp"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\windows\pvrclient-vdr_os_windows.cpp"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\recordings.cpp"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\ringbuffer.cpp"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\select.cpp"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\thread.cpp"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\timers.cpp"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\tools.cpp"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\vtptransceiver.cpp"\r
+                               >\r
+                       </File>\r
+               </Filter>\r
+               <Filter\r
+                       Name="Header Files"\r
+                       Filter="h;hpp;hxx;hm;inl;inc;xsd"\r
+                       UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"\r
+                       >\r
+                       <File\r
+                               RelativePath="..\..\channels.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\channelscan.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\client.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\windows\dirent.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\epg.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\windows\getline.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\pvrclient-vdr_os.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\windows\pvrclient-vdr_os_windows.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\recordings.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\ringbuffer.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\select.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\StdString.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\thread.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\timers.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\tools.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\vtptransceiver.h"\r
+                               >\r
+                       </File>\r
+               </Filter>\r
+               <File\r
+                       RelativePath="..\..\README"\r
+                       >\r
+               </File>\r
+       </Files>\r
+       <Globals>\r
+       </Globals>\r
+</VisualStudioProject>\r
diff --git a/xbmc/pvrclients/vdr-streamdev/project/VS2008Express/inttypes.h b/xbmc/pvrclients/vdr-streamdev/project/VS2008Express/inttypes.h
new file mode 100644 (file)
index 0000000..41c3347
--- /dev/null
@@ -0,0 +1,305 @@
+// ISO C9x  compliant inttypes.h for Microsoft Visual Studio\r
+// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 \r
+// \r
+//  Copyright (c) 2006 Alexander Chemeris\r
+// \r
+// Redistribution and use in source and binary forms, with or without\r
+// modification, are permitted provided that the following conditions are met:\r
+// \r
+//   1. Redistributions of source code must retain the above copyright notice,\r
+//      this list of conditions and the following disclaimer.\r
+// \r
+//   2. Redistributions in binary form must reproduce the above copyright\r
+//      notice, this list of conditions and the following disclaimer in the\r
+//      documentation and/or other materials provided with the distribution.\r
+// \r
+//   3. The name of the author may be used to endorse or promote products\r
+//      derived from this software without specific prior written permission.\r
+// \r
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED\r
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\r
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO\r
+// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\r
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;\r
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, \r
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\r
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\r
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+// \r
+///////////////////////////////////////////////////////////////////////////////\r
+\r
+#ifndef _MSC_VER // [\r
+#error "Use this header only with Microsoft Visual C++ compilers!"\r
+#endif // _MSC_VER ]\r
+\r
+#ifndef _MSC_INTTYPES_H_ // [\r
+#define _MSC_INTTYPES_H_\r
+\r
+#if _MSC_VER > 1000\r
+#pragma once\r
+#endif\r
+\r
+#include <stdint.h>\r
+\r
+// 7.8 Format conversion of integer types\r
+\r
+typedef struct {\r
+   intmax_t quot;\r
+   intmax_t rem;\r
+} imaxdiv_t;\r
+\r
+// 7.8.1 Macros for format specifiers\r
+\r
+#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [   See footnote 185 at page 198\r
+\r
+// The fprintf macros for signed integers are:\r
+#define PRId8       "d"\r
+#define PRIi8       "i"\r
+#define PRIdLEAST8  "d"\r
+#define PRIiLEAST8  "i"\r
+#define PRIdFAST8   "d"\r
+#define PRIiFAST8   "i"\r
+\r
+#define PRId16       "hd"\r
+#define PRIi16       "hi"\r
+#define PRIdLEAST16  "hd"\r
+#define PRIiLEAST16  "hi"\r
+#define PRIdFAST16   "hd"\r
+#define PRIiFAST16   "hi"\r
+\r
+#define PRId32       "I32d"\r
+#define PRIi32       "I32i"\r
+#define PRIdLEAST32  "I32d"\r
+#define PRIiLEAST32  "I32i"\r
+#define PRIdFAST32   "I32d"\r
+#define PRIiFAST32   "I32i"\r
+\r
+#define PRId64       "I64d"\r
+#define PRIi64       "I64i"\r
+#define PRIdLEAST64  "I64d"\r
+#define PRIiLEAST64  "I64i"\r
+#define PRIdFAST64   "I64d"\r
+#define PRIiFAST64   "I64i"\r
+\r
+#define PRIdMAX     "I64d"\r
+#define PRIiMAX     "I64i"\r
+\r
+#define PRIdPTR     "Id"\r
+#define PRIiPTR     "Ii"\r
+\r
+// The fprintf macros for unsigned integers are:\r
+#define PRIo8       "o"\r
+#define PRIu8       "u"\r
+#define PRIx8       "x"\r
+#define PRIX8       "X"\r
+#define PRIoLEAST8  "o"\r
+#define PRIuLEAST8  "u"\r
+#define PRIxLEAST8  "x"\r
+#define PRIXLEAST8  "X"\r
+#define PRIoFAST8   "o"\r
+#define PRIuFAST8   "u"\r
+#define PRIxFAST8   "x"\r
+#define PRIXFAST8   "X"\r
+\r
+#define PRIo16       "ho"\r
+#define PRIu16       "hu"\r
+#define PRIx16       "hx"\r
+#define PRIX16       "hX"\r
+#define PRIoLEAST16  "ho"\r
+#define PRIuLEAST16  "hu"\r
+#define PRIxLEAST16  "hx"\r
+#define PRIXLEAST16  "hX"\r
+#define PRIoFAST16   "ho"\r
+#define PRIuFAST16   "hu"\r
+#define PRIxFAST16   "hx"\r
+#define PRIXFAST16   "hX"\r
+\r
+#define PRIo32       "I32o"\r
+#define PRIu32       "I32u"\r
+#define PRIx32       "I32x"\r
+#define PRIX32       "I32X"\r
+#define PRIoLEAST32  "I32o"\r
+#define PRIuLEAST32  "I32u"\r
+#define PRIxLEAST32  "I32x"\r
+#define PRIXLEAST32  "I32X"\r
+#define PRIoFAST32   "I32o"\r
+#define PRIuFAST32   "I32u"\r
+#define PRIxFAST32   "I32x"\r
+#define PRIXFAST32   "I32X"\r
+\r
+#define PRIo64       "I64o"\r
+#define PRIu64       "I64u"\r
+#define PRIx64       "I64x"\r
+#define PRIX64       "I64X"\r
+#define PRIoLEAST64  "I64o"\r
+#define PRIuLEAST64  "I64u"\r
+#define PRIxLEAST64  "I64x"\r
+#define PRIXLEAST64  "I64X"\r
+#define PRIoFAST64   "I64o"\r
+#define PRIuFAST64   "I64u"\r
+#define PRIxFAST64   "I64x"\r
+#define PRIXFAST64   "I64X"\r
+\r
+#define PRIoMAX     "I64o"\r
+#define PRIuMAX     "I64u"\r
+#define PRIxMAX     "I64x"\r
+#define PRIXMAX     "I64X"\r
+\r
+#define PRIoPTR     "Io"\r
+#define PRIuPTR     "Iu"\r
+#define PRIxPTR     "Ix"\r
+#define PRIXPTR     "IX"\r
+\r
+// The fscanf macros for signed integers are:\r
+#define SCNd8       "d"\r
+#define SCNi8       "i"\r
+#define SCNdLEAST8  "d"\r
+#define SCNiLEAST8  "i"\r
+#define SCNdFAST8   "d"\r
+#define SCNiFAST8   "i"\r
+\r
+#define SCNd16       "hd"\r
+#define SCNi16       "hi"\r
+#define SCNdLEAST16  "hd"\r
+#define SCNiLEAST16  "hi"\r
+#define SCNdFAST16   "hd"\r
+#define SCNiFAST16   "hi"\r
+\r
+#define SCNd32       "ld"\r
+#define SCNi32       "li"\r
+#define SCNdLEAST32  "ld"\r
+#define SCNiLEAST32  "li"\r
+#define SCNdFAST32   "ld"\r
+#define SCNiFAST32   "li"\r
+\r
+#define SCNd64       "I64d"\r
+#define SCNi64       "I64i"\r
+#define SCNdLEAST64  "I64d"\r
+#define SCNiLEAST64  "I64i"\r
+#define SCNdFAST64   "I64d"\r
+#define SCNiFAST64   "I64i"\r
+\r
+#define SCNdMAX     "I64d"\r
+#define SCNiMAX     "I64i"\r
+\r
+#ifdef _WIN64 // [\r
+#  define SCNdPTR     "I64d"\r
+#  define SCNiPTR     "I64i"\r
+#else  // _WIN64 ][\r
+#  define SCNdPTR     "ld"\r
+#  define SCNiPTR     "li"\r
+#endif  // _WIN64 ]\r
+\r
+// The fscanf macros for unsigned integers are:\r
+#define SCNo8       "o"\r
+#define SCNu8       "u"\r
+#define SCNx8       "x"\r
+#define SCNX8       "X"\r
+#define SCNoLEAST8  "o"\r
+#define SCNuLEAST8  "u"\r
+#define SCNxLEAST8  "x"\r
+#define SCNXLEAST8  "X"\r
+#define SCNoFAST8   "o"\r
+#define SCNuFAST8   "u"\r
+#define SCNxFAST8   "x"\r
+#define SCNXFAST8   "X"\r
+\r
+#define SCNo16       "ho"\r
+#define SCNu16       "hu"\r
+#define SCNx16       "hx"\r
+#define SCNX16       "hX"\r
+#define SCNoLEAST16  "ho"\r
+#define SCNuLEAST16  "hu"\r
+#define SCNxLEAST16  "hx"\r
+#define SCNXLEAST16  "hX"\r
+#define SCNoFAST16   "ho"\r
+#define SCNuFAST16   "hu"\r
+#define SCNxFAST16   "hx"\r
+#define SCNXFAST16   "hX"\r
+\r
+#define SCNo32       "lo"\r
+#define SCNu32       "lu"\r
+#define SCNx32       "lx"\r
+#define SCNX32       "lX"\r
+#define SCNoLEAST32  "lo"\r
+#define SCNuLEAST32  "lu"\r
+#define SCNxLEAST32  "lx"\r
+#define SCNXLEAST32  "lX"\r
+#define SCNoFAST32   "lo"\r
+#define SCNuFAST32   "lu"\r
+#define SCNxFAST32   "lx"\r
+#define SCNXFAST32   "lX"\r
+\r
+#define SCNo64       "I64o"\r
+#define SCNu64       "I64u"\r
+#define SCNx64       "I64x"\r
+#define SCNX64       "I64X"\r
+#define SCNoLEAST64  "I64o"\r
+#define SCNuLEAST64  "I64u"\r
+#define SCNxLEAST64  "I64x"\r
+#define SCNXLEAST64  "I64X"\r
+#define SCNoFAST64   "I64o"\r
+#define SCNuFAST64   "I64u"\r
+#define SCNxFAST64   "I64x"\r
+#define SCNXFAST64   "I64X"\r
+\r
+#define SCNoMAX     "I64o"\r
+#define SCNuMAX     "I64u"\r
+#define SCNxMAX     "I64x"\r
+#define SCNXMAX     "I64X"\r
+\r
+#ifdef _WIN64 // [\r
+#  define SCNoPTR     "I64o"\r
+#  define SCNuPTR     "I64u"\r
+#  define SCNxPTR     "I64x"\r
+#  define SCNXPTR     "I64X"\r
+#else  // _WIN64 ][\r
+#  define SCNoPTR     "lo"\r
+#  define SCNuPTR     "lu"\r
+#  define SCNxPTR     "lx"\r
+#  define SCNXPTR     "lX"\r
+#endif  // _WIN64 ]\r
+\r
+#endif // __STDC_FORMAT_MACROS ]\r
+\r
+// 7.8.2 Functions for greatest-width integer types\r
+\r
+// 7.8.2.1 The imaxabs function\r
+#define imaxabs _abs64\r
+\r
+// 7.8.2.2 The imaxdiv function\r
+\r
+// This is modified version of div() function from Microsoft's div.c found\r
+// in %MSVC.NET%\crt\src\div.c\r
+#ifdef STATIC_IMAXDIV // [\r
+static\r
+#else // STATIC_IMAXDIV ][\r
+_inline\r
+#endif // STATIC_IMAXDIV ]\r
+imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom)\r
+{\r
+   imaxdiv_t result;\r
+\r
+   result.quot = numer / denom;\r
+   result.rem = numer % denom;\r
+\r
+   if (numer < 0 && result.rem > 0) {\r
+      // did division wrong; must fix up\r
+      ++result.quot;\r
+      result.rem -= denom;\r
+   }\r
+\r
+   return result;\r
+}\r
+\r
+// 7.8.2.3 The strtoimax and strtoumax functions\r
+#define strtoimax _strtoi64\r
+#define strtoumax _strtoui64\r
+\r
+// 7.8.2.4 The wcstoimax and wcstoumax functions\r
+#define wcstoimax _wcstoi64\r
+#define wcstoumax _wcstoui64\r
+\r
+\r
+#endif // _MSC_INTTYPES_H_ ]\r
diff --git a/xbmc/pvrclients/vdr-streamdev/project/VS2008Express/stdint.h b/xbmc/pvrclients/vdr-streamdev/project/VS2008Express/stdint.h
new file mode 100644 (file)
index 0000000..d0daff3
--- /dev/null
@@ -0,0 +1,247 @@
+// ISO C9x  compliant stdint.h for Microsoft Visual Studio
+// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 
+// 
+//  Copyright (c) 2006-2008 Alexander Chemeris
+// 
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// 
+//   1. Redistributions of source code must retain the above copyright notice,
+//      this list of conditions and the following disclaimer.
+// 
+//   2. Redistributions in binary form must reproduce the above copyright
+//      notice, this list of conditions and the following disclaimer in the
+//      documentation and/or other materials provided with the distribution.
+// 
+//   3. The name of the author may be used to endorse or promote products
+//      derived from this software without specific prior written permission.
+// 
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// 
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef _MSC_VER // [
+#error "Use this header only with Microsoft Visual C++ compilers!"
+#endif // _MSC_VER ]
+
+#ifndef _MSC_STDINT_H_ // [
+#define _MSC_STDINT_H_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif
+
+#include <limits.h>
+
+// For Visual Studio 6 in C++ mode and for many Visual Studio versions when
+// compiling for ARM we should wrap <wchar.h> include with 'extern "C++" {}'
+// or compiler give many errors like this:
+//   error C2733: second C linkage of overloaded function 'wmemchr' not allowed
+#ifdef __cplusplus
+extern "C" {
+#endif
+#  include <wchar.h>
+#ifdef __cplusplus
+}
+#endif
+
+// Define _W64 macros to mark types changing their size, like intptr_t.
+#ifndef _W64
+#  if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300
+#     define _W64 __w64
+#  else
+#     define _W64
+#  endif
+#endif
+
+
+// 7.18.1 Integer types
+
+// 7.18.1.1 Exact-width integer types
+
+// Visual Studio 6 and Embedded Visual C++ 4 doesn't
+// realize that, e.g. char has the same size as __int8
+// so we give up on __intX for them.
+#if (_MSC_VER < 1300)
+   typedef char              int8_t;
+   typedef short             int16_t;
+   typedef int               int32_t;
+   typedef unsigned char     uint8_t;
+   typedef unsigned short    uint16_t;
+   typedef unsigned int      uint32_t;
+#else
+   typedef signed __int8     int8_t;
+   typedef __int16           int16_t;
+   typedef __int32           int32_t;
+   typedef unsigned __int8   uint8_t;
+   typedef unsigned __int16  uint16_t;
+   typedef unsigned __int32  uint32_t;
+#endif
+typedef __int64              int64_t;
+typedef unsigned __int64     uint64_t;
+
+
+// 7.18.1.2 Minimum-width integer types
+typedef int8_t    int_least8_t;
+typedef int16_t   int_least16_t;
+typedef int32_t   int_least32_t;
+typedef int64_t   int_least64_t;
+typedef uint8_t   uint_least8_t;
+typedef uint16_t  uint_least16_t;
+typedef uint32_t  uint_least32_t;
+typedef uint64_t  uint_least64_t;
+
+// 7.18.1.3 Fastest minimum-width integer types
+typedef int8_t    int_fast8_t;
+typedef int16_t   int_fast16_t;
+typedef int32_t   int_fast32_t;
+typedef int64_t   int_fast64_t;
+typedef uint8_t   uint_fast8_t;
+typedef uint16_t  uint_fast16_t;
+typedef uint32_t  uint_fast32_t;
+typedef uint64_t  uint_fast64_t;
+
+// 7.18.1.4 Integer types capable of holding object pointers
+#ifdef _WIN64 // [
+   typedef __int64           intptr_t;
+   typedef unsigned __int64  uintptr_t;
+#else // _WIN64 ][
+   typedef _W64 int               intptr_t;
+   typedef _W64 unsigned int      uintptr_t;
+#endif // _WIN64 ]
+
+// 7.18.1.5 Greatest-width integer types
+typedef int64_t   intmax_t;
+typedef uint64_t  uintmax_t;
+
+
+// 7.18.2 Limits of specified-width integer types
+
+#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [   See footnote 220 at page 257 and footnote 221 at page 259
+
+// 7.18.2.1 Limits of exact-width integer types
+#define INT8_MIN     ((int8_t)_I8_MIN)
+#define INT8_MAX     _I8_MAX
+#define INT16_MIN    ((int16_t)_I16_MIN)
+#define INT16_MAX    _I16_MAX
+#define INT32_MIN    ((int32_t)_I32_MIN)
+#define INT32_MAX    _I32_MAX
+#define INT64_MIN    ((int64_t)_I64_MIN)
+#define INT64_MAX    _I64_MAX
+#define UINT8_MAX    _UI8_MAX
+#define UINT16_MAX   _UI16_MAX
+#define UINT32_MAX   _UI32_MAX
+#define UINT64_MAX   _UI64_MAX
+
+// 7.18.2.2 Limits of minimum-width integer types
+#define INT_LEAST8_MIN    INT8_MIN
+#define INT_LEAST8_MAX    INT8_MAX
+#define INT_LEAST16_MIN   INT16_MIN
+#define INT_LEAST16_MAX   INT16_MAX
+#define INT_LEAST32_MIN   INT32_MIN
+#define INT_LEAST32_MAX   INT32_MAX
+#define INT_LEAST64_MIN   INT64_MIN
+#define INT_LEAST64_MAX   INT64_MAX
+#define UINT_LEAST8_MAX   UINT8_MAX
+#define UINT_LEAST16_MAX  UINT16_MAX
+#define UINT_LEAST32_MAX  UINT32_MAX
+#define UINT_LEAST64_MAX  UINT64_MAX
+
+// 7.18.2.3 Limits of fastest minimum-width integer types
+#define INT_FAST8_MIN    INT8_MIN
+#define INT_FAST8_MAX    INT8_MAX
+#define INT_FAST16_MIN   INT16_MIN
+#define INT_FAST16_MAX   INT16_MAX
+#define INT_FAST32_MIN   INT32_MIN
+#define INT_FAST32_MAX   INT32_MAX
+#define INT_FAST64_MIN   INT64_MIN
+#define INT_FAST64_MAX   INT64_MAX
+#define UINT_FAST8_MAX   UINT8_MAX
+#define UINT_FAST16_MAX  UINT16_MAX
+#define UINT_FAST32_MAX  UINT32_MAX
+#define UINT_FAST64_MAX  UINT64_MAX
+
+// 7.18.2.4 Limits of integer types capable of holding object pointers
+#ifdef _WIN64 // [
+#  define INTPTR_MIN   INT64_MIN
+#  define INTPTR_MAX   INT64_MAX
+#  define UINTPTR_MAX  UINT64_MAX
+#else // _WIN64 ][
+#  define INTPTR_MIN   INT32_MIN
+#  define INTPTR_MAX   INT32_MAX
+#  define UINTPTR_MAX  UINT32_MAX
+#endif // _WIN64 ]
+
+// 7.18.2.5 Limits of greatest-width integer types
+#define INTMAX_MIN   INT64_MIN
+#define INTMAX_MAX   INT64_MAX
+#define UINTMAX_MAX  UINT64_MAX
+
+// 7.18.3 Limits of other integer types
+
+#ifdef _WIN64 // [
+#  define PTRDIFF_MIN  _I64_MIN
+#  define PTRDIFF_MAX  _I64_MAX
+#else  // _WIN64 ][
+#  define PTRDIFF_MIN  _I32_MIN
+#  define PTRDIFF_MAX  _I32_MAX
+#endif  // _WIN64 ]
+
+#define SIG_ATOMIC_MIN  INT_MIN
+#define SIG_ATOMIC_MAX  INT_MAX
+
+#ifndef SIZE_MAX // [
+#  ifdef _WIN64 // [
+#     define SIZE_MAX  _UI64_MAX
+#  else // _WIN64 ][
+#     define SIZE_MAX  _UI32_MAX
+#  endif // _WIN64 ]
+#endif // SIZE_MAX ]
+
+// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h>
+#ifndef WCHAR_MIN // [
+#  define WCHAR_MIN  0
+#endif  // WCHAR_MIN ]
+#ifndef WCHAR_MAX // [
+#  define WCHAR_MAX  _UI16_MAX
+#endif  // WCHAR_MAX ]
+
+#define WINT_MIN  0
+#define WINT_MAX  _UI16_MAX
+
+#endif // __STDC_LIMIT_MACROS ]
+
+
+// 7.18.4 Limits of other integer types
+
+#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [   See footnote 224 at page 260
+
+// 7.18.4.1 Macros for minimum-width integer constants
+
+#define INT8_C(val)  val##i8
+#define INT16_C(val) val##i16
+#define INT32_C(val) val##i32
+#define INT64_C(val) val##i64
+
+#define UINT8_C(val)  val##ui8
+#define UINT16_C(val) val##ui16
+#define UINT32_C(val) val##ui32
+#define UINT64_C(val) val##ui64
+
+// 7.18.4.2 Macros for greatest-width integer constants
+#define INTMAX_C   INT64_C
+#define UINTMAX_C  UINT64_C
+
+#endif // __STDC_CONSTANT_MACROS ]
+
+
+#endif // _MSC_STDINT_H_ ]
diff --git a/xbmc/pvrclients/vdr-streamdev/project/VS2010Express/XBMC_VDR.vcxproj b/xbmc/pvrclients/vdr-streamdev/project/VS2010Express/XBMC_VDR.vcxproj
new file mode 100644 (file)
index 0000000..2e8d6ff
--- /dev/null
@@ -0,0 +1,157 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectName>pvrclient_vdr</ProjectName>
+    <ProjectGuid>{838CDE27-AFDF-449F-9981-A78903C9C71E}</ProjectGuid>
+    <RootNamespace>XBMC_VDR</RootNamespace>
+    <Keyword>Win32Proj</Keyword>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup>
+    <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\..\..\..\addons\pvr.vdr.streamdev\</OutDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Debug\</IntDir>
+    <IgnoreImportLibrary Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</IgnoreImportLibrary>
+    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</LinkIncremental>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\..\..\..\addons\pvr.vdr.streamdev\</OutDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Release\</IntDir>
+    <IgnoreImportLibrary Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</IgnoreImportLibrary>
+    <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental>
+    <TargetName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">XBMC_VDR_win32</TargetName>
+    <TargetExt Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.pvr</TargetExt>
+    <TargetName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">XBMC_VDR_win32</TargetName>
+    <TargetExt Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.pvr</TargetExt>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <Optimization>Disabled</Optimization>
+      <AdditionalIncludeDirectories>..\..\..\..\addons\include;.;..\..\pthread_win32;..\..\windows;..\..\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>_WIN32;_DEBUG;_WINDOWS;VDR_EXPORTS;_WIN32PC;_WINSOCKAPI_;_USE_32BIT_TIME_T;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <MinimalRebuild>true</MinimalRebuild>
+      <ExceptionHandling>Sync</ExceptionHandling>
+      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+      <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+    </ClCompile>
+    <Link>
+      <AdditionalDependencies>ws2_32.lib;..\..\pthread_win32\pthreadVC2.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <OutputFile>..\..\..\..\..\addons\pvr.vdr.streamdev\XBMC_VDR_win32.pvr</OutputFile>
+      <IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
+      <IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <ProgramDatabaseFile>$(OutDir)XBMC_VDR.pdb</ProgramDatabaseFile>
+      <SubSystem>Windows</SubSystem>
+      <LargeAddressAware>true</LargeAddressAware>
+      <RandomizedBaseAddress>false</RandomizedBaseAddress>
+      <DataExecutionPrevention>
+      </DataExecutionPrevention>
+      <TargetMachine>MachineX86</TargetMachine>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <Optimization>Disabled</Optimization>
+      <AdditionalIncludeDirectories>..\..\..\..\addons\include;.;..\..\pthread_win32;..\..\windows;..\..\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>_WIN32;NDEBUG;_WINDOWS;VDR_EXPORTS;_WIN32PC;_WINSOCKAPI_;_USE_32BIT_TIME_T;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ExceptionHandling>Sync</ExceptionHandling>
+      <BasicRuntimeChecks>Default</BasicRuntimeChecks>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>TurnOffAllWarnings</WarningLevel>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+    </ClCompile>
+    <Link>
+      <AdditionalDependencies>ws2_32.lib;..\..\pthread_win32\pthreadVC2.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <OutputFile>..\..\..\..\..\addons\pvr.vdr.streamdev\XBMC_VDR_win32.pvr</OutputFile>
+      <IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
+      <GenerateDebugInformation>false</GenerateDebugInformation>
+      <ProgramDatabaseFile>$(OutDir)XBMC_VDR.pdb</ProgramDatabaseFile>
+      <SubSystem>Windows</SubSystem>
+      <LargeAddressAware>true</LargeAddressAware>
+      <OptimizeReferences>
+      </OptimizeReferences>
+      <EnableCOMDATFolding>
+      </EnableCOMDATFolding>
+      <RandomizedBaseAddress>false</RandomizedBaseAddress>
+      <DataExecutionPrevention>
+      </DataExecutionPrevention>
+      <TargetMachine>MachineX86</TargetMachine>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\channels.cpp" />
+    <ClCompile Include="..\..\channelscan.cpp" />
+    <ClCompile Include="..\..\client.cpp" />
+    <ClCompile Include="..\..\windows\dirent.cpp" />
+    <ClCompile Include="..\..\..\..\..\addons\library.xbmc.addon\dlfcn-win32.cpp" />
+    <ClCompile Include="..\..\epg.cpp" />
+    <ClCompile Include="..\..\windows\getline.cpp" />
+    <ClCompile Include="..\..\windows\pvrclient-vdr_os_windows.cpp" />
+    <ClCompile Include="..\..\recordings.cpp" />
+    <ClCompile Include="..\..\ringbuffer.cpp" />
+    <ClCompile Include="..\..\select.cpp" />
+    <ClCompile Include="..\..\thread.cpp" />
+    <ClCompile Include="..\..\timers.cpp" />
+    <ClCompile Include="..\..\tools.cpp" />
+    <ClCompile Include="..\..\vtptransceiver.cpp" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\channels.h" />
+    <ClInclude Include="..\..\channelscan.h" />
+    <ClInclude Include="..\..\client.h" />
+    <ClInclude Include="..\..\windows\dirent.h" />
+    <ClInclude Include="..\..\epg.h" />
+    <ClInclude Include="..\..\windows\getline.h" />
+    <ClInclude Include="..\..\pvrclient-vdr_os.h" />
+    <ClInclude Include="..\..\windows\pvrclient-vdr_os_windows.h" />
+    <ClInclude Include="..\..\recordings.h" />
+    <ClInclude Include="..\..\ringbuffer.h" />
+    <ClInclude Include="..\..\select.h" />
+    <ClInclude Include="..\..\StdString.h" />
+    <ClInclude Include="..\..\thread.h" />
+    <ClInclude Include="..\..\timers.h" />
+    <ClInclude Include="..\..\tools.h" />
+    <ClInclude Include="..\..\vtptransceiver.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="..\..\README" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/xbmc/pvrclients/vdr-streamdev/project/VS2010Express/XBMC_VDR.vcxproj.filters b/xbmc/pvrclients/vdr-streamdev/project/VS2010Express/XBMC_VDR.vcxproj.filters
new file mode 100644 (file)
index 0000000..223ccf0
--- /dev/null
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\channels.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\channelscan.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\client.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\windows\dirent.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\..\..\addons\library.xbmc.addon\dlfcn-win32.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\epg.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\windows\getline.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\windows\pvrclient-vdr_os_windows.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\recordings.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\ringbuffer.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\select.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\thread.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\timers.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tools.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\vtptransceiver.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\channels.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\channelscan.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\client.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\windows\dirent.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\epg.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\windows\getline.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\pvrclient-vdr_os.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\windows\pvrclient-vdr_os_windows.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\recordings.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\ringbuffer.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\select.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\StdString.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\thread.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\timers.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\tools.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\vtptransceiver.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="..\..\README" />
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/xbmc/pvrclients/vdr-streamdev/pthread_win32/pthread.h b/xbmc/pvrclients/vdr-streamdev/pthread_win32/pthread.h
new file mode 100644 (file)
index 0000000..9f2868b
--- /dev/null
@@ -0,0 +1,1368 @@
+/* This is an implementation of the threads API of POSIX 1003.1-2001.\r
+ *\r
+ * --------------------------------------------------------------------------\r
+ *\r
+ *      Pthreads-win32 - POSIX Threads Library for Win32\r
+ *      Copyright(C) 1998 John E. Bossom\r
+ *      Copyright(C) 1999,2005 Pthreads-win32 contributors\r
+ * \r
+ *      Contact Email: rpj@callisto.canberra.edu.au\r
+ * \r
+ *      The current list of contributors is contained\r
+ *      in the file CONTRIBUTORS included with the source\r
+ *      code distribution. The list can also be seen at the\r
+ *      following World Wide Web location:\r
+ *      http://sources.redhat.com/pthreads-win32/contributors.html\r
+ * \r
+ *      This library is free software; you can redistribute it and/or\r
+ *      modify it under the terms of the GNU Lesser General Public\r
+ *      License as published by the Free Software Foundation; either\r
+ *      version 2 of the License, or (at your option) any later version.\r
+ * \r
+ *      This library is distributed in the hope that it will be useful,\r
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+ *      Lesser General Public License for more details.\r
+ * \r
+ *      You should have received a copy of the GNU Lesser General Public\r
+ *      License along with this library in the file COPYING.LIB;\r
+ *      if not, write to the Free Software Foundation, Inc.,\r
+ *      59 Temple Place - Suite 330, Boston, MA 02111-1307, USA\r
+ */\r
+\r
+#if !defined( PTHREAD_H )\r
+#define PTHREAD_H\r
+\r
+/*\r
+ * See the README file for an explanation of the pthreads-win32 version\r
+ * numbering scheme and how the DLL is named etc.\r
+ */\r
+#define PTW32_VERSION 2,8,0,0\r
+#define PTW32_VERSION_STRING "2, 8, 0, 0\0"\r
+\r
+/* There are three implementations of cancel cleanup.\r
+ * Note that pthread.h is included in both application\r
+ * compilation units and also internally for the library.\r
+ * The code here and within the library aims to work\r
+ * for all reasonable combinations of environments.\r
+ *\r
+ * The three implementations are:\r
+ *\r
+ *   WIN32 SEH\r
+ *   C\r
+ *   C++\r
+ *\r
+ * Please note that exiting a push/pop block via\r
+ * "return", "exit", "break", or "continue" will\r
+ * lead to different behaviour amongst applications\r
+ * depending upon whether the library was built\r
+ * using SEH, C++, or C. For example, a library built\r
+ * with SEH will call the cleanup routine, while both\r
+ * C++ and C built versions will not.\r
+ */\r
+\r
+/*\r
+ * Define defaults for cleanup code.\r
+ * Note: Unless the build explicitly defines one of the following, then\r
+ * we default to standard C style cleanup. This style uses setjmp/longjmp\r
+ * in the cancelation and thread exit implementations and therefore won't\r
+ * do stack unwinding if linked to applications that have it (e.g.\r
+ * C++ apps). This is currently consistent with most/all commercial Unix\r
+ * POSIX threads implementations.\r
+ */\r
+#if !defined( __CLEANUP_SEH ) && !defined( __CLEANUP_CXX ) && !defined( __CLEANUP_C )\r
+# define __CLEANUP_C\r
+#endif\r
+\r
+#if defined( __CLEANUP_SEH ) && ( !defined( _MSC_VER ) && !defined(PTW32_RC_MSC))\r
+#error ERROR [__FILE__, line __LINE__]: SEH is not supported for this compiler.\r
+#endif\r
+\r
+/*\r
+ * Stop here if we are being included by the resource compiler.\r
+ */\r
+#ifndef RC_INVOKED\r
+\r
+#undef PTW32_LEVEL\r
+\r
+#if defined(_POSIX_SOURCE)\r
+#define PTW32_LEVEL 0\r
+/* Early POSIX */\r
+#endif\r
+\r
+#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309\r
+#undef PTW32_LEVEL\r
+#define PTW32_LEVEL 1\r
+/* Include 1b, 1c and 1d */\r
+#endif\r
+\r
+#if defined(INCLUDE_NP)\r
+#undef PTW32_LEVEL\r
+#define PTW32_LEVEL 2\r
+/* Include Non-Portable extensions */\r
+#endif\r
+\r
+#define PTW32_LEVEL_MAX 3\r
+\r
+#if !defined(PTW32_LEVEL)\r
+#define PTW32_LEVEL PTW32_LEVEL_MAX\r
+/* Include everything */\r
+#endif\r
+\r
+#ifdef _UWIN\r
+#   define HAVE_STRUCT_TIMESPEC 1\r
+#   define HAVE_SIGNAL_H        1\r
+#   undef HAVE_CONFIG_H\r
+#   pragma comment(lib, "pthread")\r
+#endif\r
+\r
+/*\r
+ * -------------------------------------------------------------\r
+ *\r
+ *\r
+ * Module: pthread.h\r
+ *\r
+ * Purpose:\r
+ *      Provides an implementation of PThreads based upon the\r
+ *      standard:\r
+ *\r
+ *              POSIX 1003.1-2001\r
+ *  and\r
+ *    The Single Unix Specification version 3\r
+ *\r
+ *    (these two are equivalent)\r
+ *\r
+ *      in order to enhance code portability between Windows,\r
+ *  various commercial Unix implementations, and Linux.\r
+ *\r
+ *      See the ANNOUNCE file for a full list of conforming\r
+ *      routines and defined constants, and a list of missing\r
+ *      routines and constants not defined in this implementation.\r
+ *\r
+ * Authors:\r
+ *      There have been many contributors to this library.\r
+ *      The initial implementation was contributed by\r
+ *      John Bossom, and several others have provided major\r
+ *      sections or revisions of parts of the implementation.\r
+ *      Often significant effort has been contributed to\r
+ *      find and fix important bugs and other problems to\r
+ *      improve the reliability of the library, which sometimes\r
+ *      is not reflected in the amount of code which changed as\r
+ *      result.\r
+ *      As much as possible, the contributors are acknowledged\r
+ *      in the ChangeLog file in the source code distribution\r
+ *      where their changes are noted in detail.\r
+ *\r
+ *      Contributors are listed in the CONTRIBUTORS file.\r
+ *\r
+ *      As usual, all bouquets go to the contributors, and all\r
+ *      brickbats go to the project maintainer.\r
+ *\r
+ * Maintainer:\r
+ *      The code base for this project is coordinated and\r
+ *      eventually pre-tested, packaged, and made available by\r
+ *\r
+ *              Ross Johnson <rpj@callisto.canberra.edu.au>\r
+ *\r
+ * QA Testers:\r
+ *      Ultimately, the library is tested in the real world by\r
+ *      a host of competent and demanding scientists and\r
+ *      engineers who report bugs and/or provide solutions\r
+ *      which are then fixed or incorporated into subsequent\r
+ *      versions of the library. Each time a bug is fixed, a\r
+ *      test case is written to prove the fix and ensure\r
+ *      that later changes to the code don't reintroduce the\r
+ *      same error. The number of test cases is slowly growing\r
+ *      and therefore so is the code reliability.\r
+ *\r
+ * Compliance:\r
+ *      See the file ANNOUNCE for the list of implemented\r
+ *      and not-implemented routines and defined options.\r
+ *      Of course, these are all defined is this file as well.\r
+ *\r
+ * Web site:\r
+ *      The source code and other information about this library\r
+ *      are available from\r
+ *\r
+ *              http://sources.redhat.com/pthreads-win32/\r
+ *\r
+ * -------------------------------------------------------------\r
+ */\r
+\r
+/* Try to avoid including windows.h */\r
+#if defined(__MINGW32__) && defined(__cplusplus)\r
+#define PTW32_INCLUDE_WINDOWS_H\r
+#endif\r
+\r
+#ifdef PTW32_INCLUDE_WINDOWS_H\r
+#include <windows.h>\r
+#endif\r
+\r
+#if defined(_MSC_VER) && _MSC_VER < 1300 || defined(__DMC__)\r
+/*\r
+ * VC++6.0 or early compiler's header has no DWORD_PTR type.\r
+ */\r
+typedef unsigned long DWORD_PTR;\r
+#endif\r
+/*\r
+ * -----------------\r
+ * autoconf switches\r
+ * -----------------\r
+ */\r
+\r
+#if HAVE_CONFIG_H\r
+#include "config.h"\r
+#endif /* HAVE_CONFIG_H */\r
+\r
+#ifndef NEED_FTIME\r
+#include <time.h>\r
+#else /* NEED_FTIME */\r
+/* use native WIN32 time API */\r
+#endif /* NEED_FTIME */\r
+\r
+#if HAVE_SIGNAL_H\r
+#include <signal.h>\r
+#endif /* HAVE_SIGNAL_H */\r
+\r
+#include <setjmp.h>\r
+#include <limits.h>\r
+\r
+/*\r
+ * Boolean values to make us independent of system includes.\r
+ */\r
+enum {\r
+  PTW32_FALSE = 0,\r
+  PTW32_TRUE = (! PTW32_FALSE)\r
+};\r
+\r
+/*\r
+ * This is a duplicate of what is in the autoconf config.h,\r
+ * which is only used when building the pthread-win32 libraries.\r
+ */\r
+\r
+#ifndef PTW32_CONFIG_H\r
+#  if defined(WINCE)\r
+#    define NEED_ERRNO\r
+#    define NEED_SEM\r
+#  endif\r
+#  if defined(_UWIN) || defined(__MINGW32__)\r
+#    define HAVE_MODE_T\r
+#  endif\r
+#endif\r
+\r
+/*\r
+ *\r
+ */\r
+\r
+#if PTW32_LEVEL >= PTW32_LEVEL_MAX\r
+#ifdef NEED_ERRNO\r
+#include "need_errno.h"\r
+#else\r
+#include <errno.h>\r
+#endif\r
+#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */\r
+\r
+/*\r
+ * Several systems don't define some error numbers.\r
+ */\r
+#ifndef ENOTSUP\r
+#  define ENOTSUP 48   /* This is the value in Solaris. */\r
+#endif\r
+\r
+#ifndef ETIMEDOUT\r
+#  define ETIMEDOUT 10060     /* This is the value in winsock.h. */\r
+#endif\r
+\r
+#ifndef ENOSYS\r
+#  define ENOSYS 140     /* Semi-arbitrary value */\r
+#endif\r
+\r
+#ifndef EDEADLK\r
+#  ifdef EDEADLOCK\r
+#    define EDEADLK EDEADLOCK\r
+#  else\r
+#    define EDEADLK 36     /* This is the value in MSVC. */\r
+#  endif\r
+#endif\r
+\r
+#include <sched.h>\r
+\r
+/*\r
+ * To avoid including windows.h we define only those things that we\r
+ * actually need from it.\r
+ */\r
+#ifndef PTW32_INCLUDE_WINDOWS_H\r
+#ifndef HANDLE\r
+# define PTW32__HANDLE_DEF\r
+# define HANDLE void *\r
+#endif\r
+#ifndef DWORD\r
+# define PTW32__DWORD_DEF\r
+# define DWORD unsigned long\r
+#endif\r
+#endif\r
+\r
+#ifndef HAVE_STRUCT_TIMESPEC\r
+#define HAVE_STRUCT_TIMESPEC 1\r
+struct timespec {\r
+        long tv_sec;\r
+        long tv_nsec;\r
+};\r
+#endif /* HAVE_STRUCT_TIMESPEC */\r
+\r
+#ifndef SIG_BLOCK\r
+#define SIG_BLOCK 0\r
+#endif /* SIG_BLOCK */\r
+\r
+#ifndef SIG_UNBLOCK \r
+#define SIG_UNBLOCK 1\r
+#endif /* SIG_UNBLOCK */\r
+\r
+#ifndef SIG_SETMASK\r
+#define SIG_SETMASK 2\r
+#endif /* SIG_SETMASK */\r
+\r
+#ifdef __cplusplus\r
+extern "C"\r
+{\r
+#endif                          /* __cplusplus */\r
+\r
+/*\r
+ * -------------------------------------------------------------\r
+ *\r
+ * POSIX 1003.1-2001 Options\r
+ * =========================\r
+ *\r
+ * Options are normally set in <unistd.h>, which is not provided\r
+ * with pthreads-win32.\r
+ *\r
+ * For conformance with the Single Unix Specification (version 3), all of the\r
+ * options below are defined, and have a value of either -1 (not supported)\r
+ * or 200112L (supported).\r
+ *\r
+ * These options can neither be left undefined nor have a value of 0, because\r
+ * either indicates that sysconf(), which is not implemented, may be used at\r
+ * runtime to check the status of the option.\r
+ *\r
+ * _POSIX_THREADS (== 200112L)\r
+ *                      If == 200112L, you can use threads\r
+ *\r
+ * _POSIX_THREAD_ATTR_STACKSIZE (== 200112L)\r
+ *                      If == 200112L, you can control the size of a thread's\r
+ *                      stack\r
+ *                              pthread_attr_getstacksize\r
+ *                              pthread_attr_setstacksize\r
+ *\r
+ * _POSIX_THREAD_ATTR_STACKADDR (== -1)\r
+ *                      If == 200112L, you can allocate and control a thread's\r
+ *                      stack. If not supported, the following functions\r
+ *                      will return ENOSYS, indicating they are not\r
+ *                      supported:\r
+ *                              pthread_attr_getstackaddr\r
+ *                              pthread_attr_setstackaddr\r
+ *\r
+ * _POSIX_THREAD_PRIORITY_SCHEDULING (== -1)\r
+ *                      If == 200112L, you can use realtime scheduling.\r
+ *                      This option indicates that the behaviour of some\r
+ *                      implemented functions conforms to the additional TPS\r
+ *                      requirements in the standard. E.g. rwlocks favour\r
+ *                      writers over readers when threads have equal priority.\r
+ *\r
+ * _POSIX_THREAD_PRIO_INHERIT (== -1)\r
+ *                      If == 200112L, you can create priority inheritance\r
+ *                      mutexes.\r
+ *                              pthread_mutexattr_getprotocol +\r
+ *                              pthread_mutexattr_setprotocol +\r
+ *\r
+ * _POSIX_THREAD_PRIO_PROTECT (== -1)\r
+ *                      If == 200112L, you can create priority ceiling mutexes\r
+ *                      Indicates the availability of:\r
+ *                              pthread_mutex_getprioceiling\r
+ *                              pthread_mutex_setprioceiling\r
+ *                              pthread_mutexattr_getprioceiling\r
+ *                              pthread_mutexattr_getprotocol     +\r
+ *                              pthread_mutexattr_setprioceiling\r
+ *                              pthread_mutexattr_setprotocol     +\r
+ *\r
+ * _POSIX_THREAD_PROCESS_SHARED (== -1)\r
+ *                      If set, you can create mutexes and condition\r
+ *                      variables that can be shared with another\r
+ *                      process.If set, indicates the availability\r
+ *                      of:\r
+ *                              pthread_mutexattr_getpshared\r
+ *                              pthread_mutexattr_setpshared\r
+ *                              pthread_condattr_getpshared\r
+ *                              pthread_condattr_setpshared\r
+ *\r
+ * _POSIX_THREAD_SAFE_FUNCTIONS (== 200112L)\r
+ *                      If == 200112L you can use the special *_r library\r
+ *                      functions that provide thread-safe behaviour\r
+ *\r
+ * _POSIX_READER_WRITER_LOCKS (== 200112L)\r
+ *                      If == 200112L, you can use read/write locks\r
+ *\r
+ * _POSIX_SPIN_LOCKS (== 200112L)\r
+ *                      If == 200112L, you can use spin locks\r
+ *\r
+ * _POSIX_BARRIERS (== 200112L)\r
+ *                      If == 200112L, you can use barriers\r
+ *\r
+ *      + These functions provide both 'inherit' and/or\r
+ *        'protect' protocol, based upon these macro\r
+ *        settings.\r
+ *\r
+ * -------------------------------------------------------------\r
+ */\r
+\r
+/*\r
+ * POSIX Options\r
+ */\r
+#undef _POSIX_THREADS\r
+#define _POSIX_THREADS 200112L\r
+\r
+#undef _POSIX_READER_WRITER_LOCKS\r
+#define _POSIX_READER_WRITER_LOCKS 200112L\r
+\r
+#undef _POSIX_SPIN_LOCKS\r
+#define _POSIX_SPIN_LOCKS 200112L\r
+\r
+#undef _POSIX_BARRIERS\r
+#define _POSIX_BARRIERS 200112L\r
+\r
+#undef _POSIX_THREAD_SAFE_FUNCTIONS\r
+#define _POSIX_THREAD_SAFE_FUNCTIONS 200112L\r
+\r
+#undef _POSIX_THREAD_ATTR_STACKSIZE\r
+#define _POSIX_THREAD_ATTR_STACKSIZE 200112L\r
+\r
+/*\r
+ * The following options are not supported\r
+ */\r
+#undef _POSIX_THREAD_ATTR_STACKADDR\r
+#define _POSIX_THREAD_ATTR_STACKADDR -1\r
+\r
+#undef _POSIX_THREAD_PRIO_INHERIT\r
+#define _POSIX_THREAD_PRIO_INHERIT -1\r
+\r
+#undef _POSIX_THREAD_PRIO_PROTECT\r
+#define _POSIX_THREAD_PRIO_PROTECT -1\r
+\r
+/* TPS is not fully supported.  */\r
+#undef _POSIX_THREAD_PRIORITY_SCHEDULING\r
+#define _POSIX_THREAD_PRIORITY_SCHEDULING -1\r
+\r
+#undef _POSIX_THREAD_PROCESS_SHARED\r
+#define _POSIX_THREAD_PROCESS_SHARED -1\r
+\r
+\r
+/*\r
+ * POSIX 1003.1-2001 Limits\r
+ * ===========================\r
+ *\r
+ * These limits are normally set in <limits.h>, which is not provided with\r
+ * pthreads-win32.\r
+ *\r
+ * PTHREAD_DESTRUCTOR_ITERATIONS\r
+ *                      Maximum number of attempts to destroy\r
+ *                      a thread's thread-specific data on\r
+ *                      termination (must be at least 4)\r
+ *\r
+ * PTHREAD_KEYS_MAX\r
+ *                      Maximum number of thread-specific data keys\r
+ *                      available per process (must be at least 128)\r
+ *\r
+ * PTHREAD_STACK_MIN\r
+ *                      Minimum supported stack size for a thread\r
+ *\r
+ * PTHREAD_THREADS_MAX\r
+ *                      Maximum number of threads supported per\r
+ *                      process (must be at least 64).\r
+ *\r
+ * SEM_NSEMS_MAX\r
+ *                      The maximum number of semaphores a process can have.\r
+ *                      (must be at least 256)\r
+ *\r
+ * SEM_VALUE_MAX\r
+ *                      The maximum value a semaphore can have.\r
+ *                      (must be at least 32767)\r
+ *\r
+ */\r
+#undef _POSIX_THREAD_DESTRUCTOR_ITERATIONS\r
+#define _POSIX_THREAD_DESTRUCTOR_ITERATIONS     4\r
+\r
+#undef PTHREAD_DESTRUCTOR_ITERATIONS\r
+#define PTHREAD_DESTRUCTOR_ITERATIONS           _POSIX_THREAD_DESTRUCTOR_ITERATIONS\r
+\r
+#undef _POSIX_THREAD_KEYS_MAX\r
+#define _POSIX_THREAD_KEYS_MAX                  128\r
+\r
+#undef PTHREAD_KEYS_MAX\r
+#define PTHREAD_KEYS_MAX                        _POSIX_THREAD_KEYS_MAX\r
+\r
+#undef PTHREAD_STACK_MIN\r
+#define PTHREAD_STACK_MIN                       0\r
+\r
+#undef _POSIX_THREAD_THREADS_MAX\r
+#define _POSIX_THREAD_THREADS_MAX               64\r
+\r
+  /* Arbitrary value */\r
+#undef PTHREAD_THREADS_MAX\r
+#define PTHREAD_THREADS_MAX                     2019\r
+\r
+#undef _POSIX_SEM_NSEMS_MAX\r
+#define _POSIX_SEM_NSEMS_MAX                    256\r
+\r
+  /* Arbitrary value */\r
+#undef SEM_NSEMS_MAX\r
+#define SEM_NSEMS_MAX                           1024\r
+\r
+#undef _POSIX_SEM_VALUE_MAX\r
+#define _POSIX_SEM_VALUE_MAX                    32767\r
+\r
+#undef SEM_VALUE_MAX\r
+#define SEM_VALUE_MAX                           INT_MAX\r
+\r
+\r
+#if __GNUC__ && ! defined (__declspec)\r
+# error Please upgrade your GNU compiler to one that supports __declspec.\r
+#endif\r
+\r
+/*\r
+ * When building the DLL code, you should define PTW32_BUILD so that\r
+ * the variables/functions are exported correctly. When using the DLL,\r
+ * do NOT define PTW32_BUILD, and then the variables/functions will\r
+ * be imported correctly.\r
+ */\r
+#ifndef PTW32_STATIC_LIB\r
+#  ifdef PTW32_BUILD\r
+#    define PTW32_DLLPORT __declspec (dllexport)\r
+#  else\r
+#    define PTW32_DLLPORT __declspec (dllimport)\r
+#  endif\r
+#else\r
+#  define PTW32_DLLPORT\r
+#endif\r
+\r
+/*\r
+ * The Open Watcom C/C++ compiler uses a non-standard calling convention\r
+ * that passes function args in registers unless __cdecl is explicitly specified\r
+ * in exposed function prototypes.\r
+ *\r
+ * We force all calls to cdecl even though this could slow Watcom code down\r
+ * slightly. If you know that the Watcom compiler will be used to build both\r
+ * the DLL and application, then you can probably define this as a null string.\r
+ * Remember that pthread.h (this file) is used for both the DLL and application builds.\r
+ */\r
+#define PTW32_CDECL __cdecl\r
+\r
+#if defined(_UWIN) && PTW32_LEVEL >= PTW32_LEVEL_MAX\r
+#   include     <sys/types.h>\r
+#else\r
+/*\r
+ * Generic handle type - intended to extend uniqueness beyond\r
+ * that available with a simple pointer. It should scale for either\r
+ * IA-32 or IA-64.\r
+ */\r
+typedef struct {\r
+    void * p;                   /* Pointer to actual object */\r
+    unsigned int x;             /* Extra information - reuse count etc */\r
+} ptw32_handle_t;\r
+\r
+typedef ptw32_handle_t pthread_t;\r
+typedef struct pthread_attr_t_ * pthread_attr_t;\r
+typedef struct pthread_once_t_ pthread_once_t;\r
+typedef struct pthread_key_t_ * pthread_key_t;\r
+typedef struct pthread_mutex_t_ * pthread_mutex_t;\r
+typedef struct pthread_mutexattr_t_ * pthread_mutexattr_t;\r
+typedef struct pthread_cond_t_ * pthread_cond_t;\r
+typedef struct pthread_condattr_t_ * pthread_condattr_t;\r
+#endif\r
+typedef struct pthread_rwlock_t_ * pthread_rwlock_t;\r
+typedef struct pthread_rwlockattr_t_ * pthread_rwlockattr_t;\r
+typedef struct pthread_spinlock_t_ * pthread_spinlock_t;\r
+typedef struct pthread_barrier_t_ * pthread_barrier_t;\r
+typedef struct pthread_barrierattr_t_ * pthread_barrierattr_t;\r
+\r
+/*\r
+ * ====================\r
+ * ====================\r
+ * POSIX Threads\r
+ * ====================\r
+ * ====================\r
+ */\r
+\r
+enum {\r
+/*\r
+ * pthread_attr_{get,set}detachstate\r
+ */\r
+  PTHREAD_CREATE_JOINABLE       = 0,  /* Default */\r
+  PTHREAD_CREATE_DETACHED       = 1,\r
+\r
+/*\r
+ * pthread_attr_{get,set}inheritsched\r
+ */\r
+  PTHREAD_INHERIT_SCHED         = 0,\r
+  PTHREAD_EXPLICIT_SCHED        = 1,  /* Default */\r
+\r
+/*\r
+ * pthread_{get,set}scope\r
+ */\r
+  PTHREAD_SCOPE_PROCESS         = 0,\r
+  PTHREAD_SCOPE_SYSTEM          = 1,  /* Default */\r
+\r
+/*\r
+ * pthread_setcancelstate paramters\r
+ */\r
+  PTHREAD_CANCEL_ENABLE         = 0,  /* Default */\r
+  PTHREAD_CANCEL_DISABLE        = 1,\r
+\r
+/*\r
+ * pthread_setcanceltype parameters\r
+ */\r
+  PTHREAD_CANCEL_ASYNCHRONOUS   = 0,\r
+  PTHREAD_CANCEL_DEFERRED       = 1,  /* Default */\r
+\r
+/*\r
+ * pthread_mutexattr_{get,set}pshared\r
+ * pthread_condattr_{get,set}pshared\r
+ */\r
+  PTHREAD_PROCESS_PRIVATE       = 0,\r
+  PTHREAD_PROCESS_SHARED        = 1,\r
+\r
+/*\r
+ * pthread_barrier_wait\r
+ */\r
+  PTHREAD_BARRIER_SERIAL_THREAD = -1\r
+};\r
+\r
+/*\r
+ * ====================\r
+ * ====================\r
+ * Cancelation\r
+ * ====================\r
+ * ====================\r
+ */\r
+#define PTHREAD_CANCELED       ((void *) -1)\r
+\r
+\r
+/*\r
+ * ====================\r
+ * ====================\r
+ * Once Key\r
+ * ====================\r
+ * ====================\r
+ */\r
+#define PTHREAD_ONCE_INIT       { PTW32_FALSE, 0, 0, 0}\r
+\r
+struct pthread_once_t_\r
+{\r
+  int          done;        /* indicates if user function has been executed */\r
+  void *       lock;\r
+  int          reserved1;\r
+  int          reserved2;\r
+};\r
+\r
+\r
+/*\r
+ * ====================\r
+ * ====================\r
+ * Object initialisers\r
+ * ====================\r
+ * ====================\r
+ */\r
+#define PTHREAD_MUTEX_INITIALIZER ((pthread_mutex_t) -1)\r
+#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER ((pthread_mutex_t) -2)\r
+#define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER ((pthread_mutex_t) -3)\r
+\r
+/*\r
+ * Compatibility with LinuxThreads\r
+ */\r
+#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP PTHREAD_RECURSIVE_MUTEX_INITIALIZER\r
+#define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP PTHREAD_ERRORCHECK_MUTEX_INITIALIZER\r
+\r
+#define PTHREAD_COND_INITIALIZER ((pthread_cond_t) -1)\r
+\r
+#define PTHREAD_RWLOCK_INITIALIZER ((pthread_rwlock_t) -1)\r
+\r
+#define PTHREAD_SPINLOCK_INITIALIZER ((pthread_spinlock_t) -1)\r
+\r
+\r
+/*\r
+ * Mutex types.\r
+ */\r
+enum\r
+{\r
+  /* Compatibility with LinuxThreads */\r
+  PTHREAD_MUTEX_FAST_NP,\r
+  PTHREAD_MUTEX_RECURSIVE_NP,\r
+  PTHREAD_MUTEX_ERRORCHECK_NP,\r
+  PTHREAD_MUTEX_TIMED_NP = PTHREAD_MUTEX_FAST_NP,\r
+  PTHREAD_MUTEX_ADAPTIVE_NP = PTHREAD_MUTEX_FAST_NP,\r
+  /* For compatibility with POSIX */\r
+  PTHREAD_MUTEX_NORMAL = PTHREAD_MUTEX_FAST_NP,\r
+  PTHREAD_MUTEX_RECURSIVE = PTHREAD_MUTEX_RECURSIVE_NP,\r
+  PTHREAD_MUTEX_ERRORCHECK = PTHREAD_MUTEX_ERRORCHECK_NP,\r
+  PTHREAD_MUTEX_DEFAULT = PTHREAD_MUTEX_NORMAL\r
+};\r
+\r
+\r
+typedef struct ptw32_cleanup_t ptw32_cleanup_t;\r
+\r
+#if defined(_MSC_VER)\r
+/* Disable MSVC 'anachronism used' warning */\r
+#pragma warning( disable : 4229 )\r
+#endif\r
+\r
+typedef void (* PTW32_CDECL ptw32_cleanup_callback_t)(void *);\r
+\r
+#if defined(_MSC_VER)\r
+#pragma warning( default : 4229 )\r
+#endif\r
+\r
+struct ptw32_cleanup_t\r
+{\r
+  ptw32_cleanup_callback_t routine;\r
+  void *arg;\r
+  struct ptw32_cleanup_t *prev;\r
+};\r
+\r
+#ifdef __CLEANUP_SEH\r
+        /*\r
+         * WIN32 SEH version of cancel cleanup.\r
+         */\r
+\r
+#define pthread_cleanup_push( _rout, _arg ) \\r
+        { \\r
+            ptw32_cleanup_t     _cleanup; \\r
+            \\r
+        _cleanup.routine        = (ptw32_cleanup_callback_t)(_rout); \\r
+            _cleanup.arg        = (_arg); \\r
+            __try \\r
+              { \\r
+\r
+#define pthread_cleanup_pop( _execute ) \\r
+              } \\r
+            __finally \\r
+                { \\r
+                    if( _execute || AbnormalTermination()) \\r
+                      { \\r
+                          (*(_cleanup.routine))( _cleanup.arg ); \\r
+                      } \\r
+                } \\r
+        }\r
+\r
+#else /* __CLEANUP_SEH */\r
+\r
+#ifdef __CLEANUP_C\r
+\r
+        /*\r
+         * C implementation of PThreads cancel cleanup\r
+         */\r
+\r
+#define pthread_cleanup_push( _rout, _arg ) \\r
+        { \\r
+            ptw32_cleanup_t     _cleanup; \\r
+            \\r
+            ptw32_push_cleanup( &_cleanup, (ptw32_cleanup_callback_t) (_rout), (_arg) ); \\r
+\r
+#define pthread_cleanup_pop( _execute ) \\r
+            (void) ptw32_pop_cleanup( _execute ); \\r
+        }\r
+\r
+#else /* __CLEANUP_C */\r
+\r
+#ifdef __CLEANUP_CXX\r
+\r
+        /*\r
+         * C++ version of cancel cleanup.\r
+         * - John E. Bossom.\r
+         */\r
+\r
+        class PThreadCleanup {\r
+          /*\r
+           * PThreadCleanup\r
+           *\r
+           * Purpose\r
+           *      This class is a C++ helper class that is\r
+           *      used to implement pthread_cleanup_push/\r
+           *      pthread_cleanup_pop.\r
+           *      The destructor of this class automatically\r
+           *      pops the pushed cleanup routine regardless\r
+           *      of how the code exits the scope\r
+           *      (i.e. such as by an exception)\r
+           */\r
+      ptw32_cleanup_callback_t cleanUpRout;\r
+          void    *       obj;\r
+          int             executeIt;\r
+\r
+        public:\r
+          PThreadCleanup() :\r
+            cleanUpRout( 0 ),\r
+            obj( 0 ),\r
+            executeIt( 0 )\r
+            /*\r
+             * No cleanup performed\r
+             */\r
+            {\r
+            }\r
+\r
+          PThreadCleanup(\r
+             ptw32_cleanup_callback_t routine,\r
+                         void    *       arg ) :\r
+            cleanUpRout( routine ),\r
+            obj( arg ),\r
+            executeIt( 1 )\r
+            /*\r
+             * Registers a cleanup routine for 'arg'\r
+             */\r
+            {\r
+            }\r
+\r
+          ~PThreadCleanup()\r
+            {\r
+              if ( executeIt && ((void *) cleanUpRout != (void *) 0) )\r
+                {\r
+                  (void) (*cleanUpRout)( obj );\r
+                }\r
+            }\r
+\r
+          void execute( int exec )\r
+            {\r
+              executeIt = exec;\r
+            }\r
+        };\r
+\r
+        /*\r
+         * C++ implementation of PThreads cancel cleanup;\r
+         * This implementation takes advantage of a helper\r
+         * class who's destructor automatically calls the\r
+         * cleanup routine if we exit our scope weirdly\r
+         */\r
+#define pthread_cleanup_push( _rout, _arg ) \\r
+        { \\r
+            PThreadCleanup  cleanup((ptw32_cleanup_callback_t)(_rout), \\r
+                                    (void *) (_arg) );\r
+\r
+#define pthread_cleanup_pop( _execute ) \\r
+            cleanup.execute( _execute ); \\r
+        }\r
+\r
+#else\r
+\r
+#error ERROR [__FILE__, line __LINE__]: Cleanup type undefined.\r
+\r
+#endif /* __CLEANUP_CXX */\r
+\r
+#endif /* __CLEANUP_C */\r
+\r
+#endif /* __CLEANUP_SEH */\r
+\r
+/*\r
+ * ===============\r
+ * ===============\r
+ * Methods\r
+ * ===============\r
+ * ===============\r
+ */\r
+\r
+/*\r
+ * PThread Attribute Functions\r
+ */\r
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_init (pthread_attr_t * attr);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_destroy (pthread_attr_t * attr);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_getdetachstate (const pthread_attr_t * attr,\r
+                                         int *detachstate);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_getstackaddr (const pthread_attr_t * attr,\r
+                                       void **stackaddr);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_getstacksize (const pthread_attr_t * attr,\r
+                                       size_t * stacksize);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_setdetachstate (pthread_attr_t * attr,\r
+                                         int detachstate);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_setstackaddr (pthread_attr_t * attr,\r
+                                       void *stackaddr);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_setstacksize (pthread_attr_t * attr,\r
+                                       size_t stacksize);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_getschedparam (const pthread_attr_t *attr,\r
+                                        struct sched_param *param);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_setschedparam (pthread_attr_t *attr,\r
+                                        const struct sched_param *param);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_setschedpolicy (pthread_attr_t *,\r
+                                         int);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_getschedpolicy (pthread_attr_t *,\r
+                                         int *);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_setinheritsched(pthread_attr_t * attr,\r
+                                         int inheritsched);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_getinheritsched(pthread_attr_t * attr,\r
+                                         int * inheritsched);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_setscope (pthread_attr_t *,\r
+                                   int);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_attr_getscope (const pthread_attr_t *,\r
+                                   int *);\r
+\r
+/*\r
+ * PThread Functions\r
+ */\r
+PTW32_DLLPORT int PTW32_CDECL pthread_create (pthread_t * tid,\r
+                            const pthread_attr_t * attr,\r
+                            void *(*start) (void *),\r
+                            void *arg);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_detach (pthread_t tid);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_equal (pthread_t t1,\r
+                           pthread_t t2);\r
+\r
+PTW32_DLLPORT void PTW32_CDECL pthread_exit (void *value_ptr);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_join (pthread_t thread,\r
+                          void **value_ptr);\r
+\r
+PTW32_DLLPORT pthread_t PTW32_CDECL pthread_self (void);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_cancel (pthread_t thread);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_setcancelstate (int state,\r
+                                    int *oldstate);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_setcanceltype (int type,\r
+                                   int *oldtype);\r
+\r
+PTW32_DLLPORT void PTW32_CDECL pthread_testcancel (void);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_once (pthread_once_t * once_control,\r
+                          void (*init_routine) (void));\r
+\r
+#if PTW32_LEVEL >= PTW32_LEVEL_MAX\r
+PTW32_DLLPORT ptw32_cleanup_t * PTW32_CDECL ptw32_pop_cleanup (int execute);\r
+\r
+PTW32_DLLPORT void PTW32_CDECL ptw32_push_cleanup (ptw32_cleanup_t * cleanup,\r
+                                 void (*routine) (void *),\r
+                                 void *arg);\r
+#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */\r
+\r
+/*\r
+ * Thread Specific Data Functions\r
+ */\r
+PTW32_DLLPORT int PTW32_CDECL pthread_key_create (pthread_key_t * key,\r
+                                void (*destructor) (void *));\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_key_delete (pthread_key_t key);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_setspecific (pthread_key_t key,\r
+                                 const void *value);\r
+\r
+PTW32_DLLPORT void * PTW32_CDECL pthread_getspecific (pthread_key_t key);\r
+\r
+\r
+/*\r
+ * Mutex Attribute Functions\r
+ */\r
+PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_init (pthread_mutexattr_t * attr);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_destroy (pthread_mutexattr_t * attr);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_getpshared (const pthread_mutexattr_t\r
+                                          * attr,\r
+                                          int *pshared);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_setpshared (pthread_mutexattr_t * attr,\r
+                                          int pshared);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_settype (pthread_mutexattr_t * attr, int kind);\r
+PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_gettype (pthread_mutexattr_t * attr, int *kind);\r
+\r
+/*\r
+ * Barrier Attribute Functions\r
+ */\r
+PTW32_DLLPORT int PTW32_CDECL pthread_barrierattr_init (pthread_barrierattr_t * attr);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_barrierattr_destroy (pthread_barrierattr_t * attr);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_barrierattr_getpshared (const pthread_barrierattr_t\r
+                                            * attr,\r
+                                            int *pshared);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_barrierattr_setpshared (pthread_barrierattr_t * attr,\r
+                                            int pshared);\r
+\r
+/*\r
+ * Mutex Functions\r
+ */\r
+PTW32_DLLPORT int PTW32_CDECL pthread_mutex_init (pthread_mutex_t * mutex,\r
+                                const pthread_mutexattr_t * attr);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_mutex_destroy (pthread_mutex_t * mutex);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_mutex_lock (pthread_mutex_t * mutex);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_mutex_timedlock(pthread_mutex_t *mutex,\r
+                                    const struct timespec *abstime);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_mutex_trylock (pthread_mutex_t * mutex);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_mutex_unlock (pthread_mutex_t * mutex);\r
+\r
+/*\r
+ * Spinlock Functions\r
+ */\r
+PTW32_DLLPORT int PTW32_CDECL pthread_spin_init (pthread_spinlock_t * lock, int pshared);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_spin_destroy (pthread_spinlock_t * lock);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_spin_lock (pthread_spinlock_t * lock);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_spin_trylock (pthread_spinlock_t * lock);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_spin_unlock (pthread_spinlock_t * lock);\r
+\r
+/*\r
+ * Barrier Functions\r
+ */\r
+PTW32_DLLPORT int PTW32_CDECL pthread_barrier_init (pthread_barrier_t * barrier,\r
+                                  const pthread_barrierattr_t * attr,\r
+                                  unsigned int count);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_barrier_destroy (pthread_barrier_t * barrier);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_barrier_wait (pthread_barrier_t * barrier);\r
+\r
+/*\r
+ * Condition Variable Attribute Functions\r
+ */\r
+PTW32_DLLPORT int PTW32_CDECL pthread_condattr_init (pthread_condattr_t * attr);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_condattr_destroy (pthread_condattr_t * attr);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_condattr_getpshared (const pthread_condattr_t * attr,\r
+                                         int *pshared);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_condattr_setpshared (pthread_condattr_t * attr,\r
+                                         int pshared);\r
+\r
+/*\r
+ * Condition Variable Functions\r
+ */\r
+PTW32_DLLPORT int PTW32_CDECL pthread_cond_init (pthread_cond_t * cond,\r
+                               const pthread_condattr_t * attr);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_cond_destroy (pthread_cond_t * cond);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_cond_wait (pthread_cond_t * cond,\r
+                               pthread_mutex_t * mutex);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_cond_timedwait (pthread_cond_t * cond,\r
+                                    pthread_mutex_t * mutex,\r
+                                    const struct timespec *abstime);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_cond_signal (pthread_cond_t * cond);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_cond_broadcast (pthread_cond_t * cond);\r
+\r
+/*\r
+ * Scheduling\r
+ */\r
+PTW32_DLLPORT int PTW32_CDECL pthread_setschedparam (pthread_t thread,\r
+                                   int policy,\r
+                                   const struct sched_param *param);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_getschedparam (pthread_t thread,\r
+                                   int *policy,\r
+                                   struct sched_param *param);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_setconcurrency (int);\r
\r
+PTW32_DLLPORT int PTW32_CDECL pthread_getconcurrency (void);\r
+\r
+/*\r
+ * Read-Write Lock Functions\r
+ */\r
+PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_init(pthread_rwlock_t *lock,\r
+                                const pthread_rwlockattr_t *attr);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_destroy(pthread_rwlock_t *lock);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_tryrdlock(pthread_rwlock_t *);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_trywrlock(pthread_rwlock_t *);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_rdlock(pthread_rwlock_t *lock);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_timedrdlock(pthread_rwlock_t *lock,\r
+                                       const struct timespec *abstime);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_wrlock(pthread_rwlock_t *lock);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_timedwrlock(pthread_rwlock_t *lock,\r
+                                       const struct timespec *abstime);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_rwlock_unlock(pthread_rwlock_t *lock);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_rwlockattr_init (pthread_rwlockattr_t * attr);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_rwlockattr_destroy (pthread_rwlockattr_t * attr);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_rwlockattr_getpshared (const pthread_rwlockattr_t * attr,\r
+                                           int *pshared);\r
+\r
+PTW32_DLLPORT int PTW32_CDECL pthread_rwlockattr_setpshared (pthread_rwlockattr_t * attr,\r
+                                           int pshared);\r
+\r
+#if PTW32_LEVEL >= PTW32_LEVEL_MAX - 1\r
+\r
+/*\r
+ * Signal Functions. Should be defined in <signal.h> but MSVC and MinGW32\r
+ * already have signal.h that don't define these.\r
+ */\r
+PTW32_DLLPORT int PTW32_CDECL pthread_kill(pthread_t thread, int sig);\r
+\r
+/*\r
+ * Non-portable functions\r
+ */\r
+\r
+/*\r
+ * Compatibility with Linux.\r
+ */\r
+PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_setkind_np(pthread_mutexattr_t * attr,\r
+                                         int kind);\r
+PTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_getkind_np(pthread_mutexattr_t * attr,\r
+                                         int *kind);\r
+\r
+/*\r
+ * Possibly supported by other POSIX threads implementations\r
+ */\r
+PTW32_DLLPORT int PTW32_CDECL pthread_delay_np (struct timespec * interval);\r
+PTW32_DLLPORT int PTW32_CDECL pthread_num_processors_np(void);\r
+\r
+/*\r
+ * Useful if an application wants to statically link\r
+ * the lib rather than load the DLL at run-time.\r
+ */\r
+PTW32_DLLPORT int PTW32_CDECL pthread_win32_process_attach_np(void);\r
+PTW32_DLLPORT int PTW32_CDECL pthread_win32_process_detach_np(void);\r
+PTW32_DLLPORT int PTW32_CDECL pthread_win32_thread_attach_np(void);\r
+PTW32_DLLPORT int PTW32_CDECL pthread_win32_thread_detach_np(void);\r
+\r
+/*\r
+ * Features that are auto-detected at load/run time.\r
+ */\r
+PTW32_DLLPORT int PTW32_CDECL pthread_win32_test_features_np(int);\r
+enum ptw32_features {\r
+  PTW32_SYSTEM_INTERLOCKED_COMPARE_EXCHANGE = 0x0001, /* System provides it. */\r
+  PTW32_ALERTABLE_ASYNC_CANCEL              = 0x0002  /* Can cancel blocked threads. */\r
+};\r
+\r
+/*\r
+ * Register a system time change with the library.\r
+ * Causes the library to perform various functions\r
+ * in response to the change. Should be called whenever\r
+ * the application's top level window receives a\r
+ * WM_TIMECHANGE message. It can be passed directly to\r
+ * pthread_create() as a new thread if desired.\r
+ */\r
+PTW32_DLLPORT void * PTW32_CDECL pthread_timechange_handler_np(void *);\r
+\r
+#endif /*PTW32_LEVEL >= PTW32_LEVEL_MAX - 1 */\r
+\r
+#if PTW32_LEVEL >= PTW32_LEVEL_MAX\r
+\r
+/*\r
+ * Returns the Win32 HANDLE for the POSIX thread.\r
+ */\r
+PTW32_DLLPORT HANDLE PTW32_CDECL pthread_getw32threadhandle_np(pthread_t thread);\r
+\r
+\r
+/*\r
+ * Protected Methods\r
+ *\r
+ * This function blocks until the given WIN32 handle\r
+ * is signaled or pthread_cancel had been called.\r
+ * This function allows the caller to hook into the\r
+ * PThreads cancel mechanism. It is implemented using\r
+ *\r
+ *              WaitForMultipleObjects\r
+ *\r
+ * on 'waitHandle' and a manually reset WIN32 Event\r
+ * used to implement pthread_cancel. The 'timeout'\r
+ * argument to TimedWait is simply passed to\r
+ * WaitForMultipleObjects.\r
+ */\r
+PTW32_DLLPORT int PTW32_CDECL pthreadCancelableWait (HANDLE waitHandle);\r
+PTW32_DLLPORT int PTW32_CDECL pthreadCancelableTimedWait (HANDLE waitHandle,\r
+                                        DWORD timeout);\r
+\r
+#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */\r
+\r
+/*\r
+ * Thread-Safe C Runtime Library Mappings.\r
+ */\r
+#ifndef _UWIN\r
+#  if defined(NEED_ERRNO)\r
+     PTW32_DLLPORT int * PTW32_CDECL _errno( void );\r
+#  else\r
+#    ifndef errno\r
+#      if (defined(_MT) || defined(_DLL))\r
+         __declspec(dllimport) extern int * __cdecl _errno(void);\r
+#        define errno   (*_errno())\r
+#      endif\r
+#    endif\r
+#  endif\r
+#endif\r
+\r
+/*\r
+ * WIN32 C runtime library had been made thread-safe\r
+ * without affecting the user interface. Provide\r
+ * mappings from the UNIX thread-safe versions to\r
+ * the standard C runtime library calls.\r
+ * Only provide function mappings for functions that\r
+ * actually exist on WIN32.\r
+ */\r
+\r
+#if !defined(__MINGW32__)\r
+#define strtok_r( _s, _sep, _lasts ) \\r
+        ( *(_lasts) = strtok( (_s), (_sep) ) )\r
+#endif /* !__MINGW32__ */\r
+\r
+#define asctime_r( _tm, _buf ) \\r
+        ( strcpy( (_buf), asctime( (_tm) ) ), \\r
+          (_buf) )\r
+\r
+#define ctime_r( _clock, _buf ) \\r
+        ( strcpy( (_buf), ctime( (_clock) ) ),  \\r
+          (_buf) )\r
+\r
+#define gmtime_r( _clock, _result ) \\r
+        ( *(_result) = *gmtime( (_clock) ), \\r
+          (_result) )\r
+\r
+#define localtime_r( _clock, _result ) \\r
+        ( *(_result) = *localtime( (_clock) ), \\r
+          (_result) )\r
+\r
+#define rand_r( _seed ) \\r
+        ( _seed == _seed? rand() : rand() )\r
+\r
+\r
+/*\r
+ * Some compiler environments don't define some things.\r
+ */\r
+#if defined(__BORLANDC__)\r
+#  define _ftime ftime\r
+#  define _timeb timeb\r
+#endif\r
+\r
+#ifdef __cplusplus\r
+\r
+/*\r
+ * Internal exceptions\r
+ */\r
+class ptw32_exception {};\r
+class ptw32_exception_cancel : public ptw32_exception {};\r
+class ptw32_exception_exit   : public ptw32_exception {};\r
+\r
+#endif\r
+\r
+#if PTW32_LEVEL >= PTW32_LEVEL_MAX\r
+\r
+/* FIXME: This is only required if the library was built using SEH */\r
+/*\r
+ * Get internal SEH tag\r
+ */\r
+PTW32_DLLPORT DWORD PTW32_CDECL ptw32_get_exception_services_code(void);\r
+\r
+#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */\r
+\r
+#ifndef PTW32_BUILD\r
+\r
+#ifdef __CLEANUP_SEH\r
+\r
+/*\r
+ * Redefine the SEH __except keyword to ensure that applications\r
+ * propagate our internal exceptions up to the library's internal handlers.\r
+ */\r
+#define __except( E ) \\r
+        __except( ( GetExceptionCode() == ptw32_get_exception_services_code() ) \\r
+                 ? EXCEPTION_CONTINUE_SEARCH : ( E ) )\r
+\r
+#endif /* __CLEANUP_SEH */\r
+\r
+#ifdef __CLEANUP_CXX\r
+\r
+/*\r
+ * Redefine the C++ catch keyword to ensure that applications\r
+ * propagate our internal exceptions up to the library's internal handlers.\r
+ */\r
+#ifdef _MSC_VER\r
+        /*\r
+         * WARNING: Replace any 'catch( ... )' with 'PtW32CatchAll'\r
+         * if you want Pthread-Win32 cancelation and pthread_exit to work.\r
+         */\r
+\r
+#ifndef PtW32NoCatchWarn\r
+\r
+#pragma message("Specify \"/DPtW32NoCatchWarn\" compiler flag to skip this message.")\r
+#pragma message("------------------------------------------------------------------")\r
+#pragma message("When compiling applications with MSVC++ and C++ exception handling:")\r
+#pragma message("  Replace any 'catch( ... )' in routines called from POSIX threads")\r
+#pragma message("  with 'PtW32CatchAll' or 'CATCHALL' if you want POSIX thread")\r
+#pragma message("  cancelation and pthread_exit to work. For example:")\r
+#pragma message("")\r
+#pragma message("    #ifdef PtW32CatchAll")\r
+#pragma message("      PtW32CatchAll")\r
+#pragma message("    #else")\r
+#pragma message("      catch(...)")\r
+#pragma message("    #endif")\r
+#pragma message("        {")\r
+#pragma message("          /* Catchall block processing */")\r
+#pragma message("        }")\r
+#pragma message("------------------------------------------------------------------")\r
+\r
+#endif\r
+\r
+#define PtW32CatchAll \\r
+        catch( ptw32_exception & ) { throw; } \\r
+        catch( ... )\r
+\r
+#else /* _MSC_VER */\r
+\r
+#define catch( E ) \\r
+        catch( ptw32_exception & ) { throw; } \\r
+        catch( E )\r
+\r
+#endif /* _MSC_VER */\r
+\r
+#endif /* __CLEANUP_CXX */\r
+\r
+#endif /* ! PTW32_BUILD */\r
+\r
+#ifdef __cplusplus\r
+}                               /* End of extern "C" */\r
+#endif                          /* __cplusplus */\r
+\r
+#ifdef PTW32__HANDLE_DEF\r
+# undef HANDLE\r
+#endif\r
+#ifdef PTW32__DWORD_DEF\r
+# undef DWORD\r
+#endif\r
+\r
+#undef PTW32_LEVEL\r
+#undef PTW32_LEVEL_MAX\r
+\r
+#endif /* ! RC_INVOKED */\r
+\r
+#endif /* PTHREAD_H */\r
diff --git a/xbmc/pvrclients/vdr-streamdev/pthread_win32/pthreadVC2.lib b/xbmc/pvrclients/vdr-streamdev/pthread_win32/pthreadVC2.lib
new file mode 100644 (file)
index 0000000..bc36770
Binary files /dev/null and b/xbmc/pvrclients/vdr-streamdev/pthread_win32/pthreadVC2.lib differ
diff --git a/xbmc/pvrclients/vdr-streamdev/pthread_win32/pthreadVC2d.lib b/xbmc/pvrclients/vdr-streamdev/pthread_win32/pthreadVC2d.lib
new file mode 100644 (file)
index 0000000..0df71c7
Binary files /dev/null and b/xbmc/pvrclients/vdr-streamdev/pthread_win32/pthreadVC2d.lib differ
diff --git a/xbmc/pvrclients/vdr-streamdev/pthread_win32/sched.h b/xbmc/pvrclients/vdr-streamdev/pthread_win32/sched.h
new file mode 100644 (file)
index 0000000..10ecb5d
--- /dev/null
@@ -0,0 +1,178 @@
+/*\r
+ * Module: sched.h\r
+ *\r
+ * Purpose:\r
+ *      Provides an implementation of POSIX realtime extensions\r
+ *      as defined in \r
+ *\r
+ *              POSIX 1003.1b-1993      (POSIX.1b)\r
+ *\r
+ * --------------------------------------------------------------------------\r
+ *\r
+ *      Pthreads-win32 - POSIX Threads Library for Win32\r
+ *      Copyright(C) 1998 John E. Bossom\r
+ *      Copyright(C) 1999,2005 Pthreads-win32 contributors\r
+ * \r
+ *      Contact Email: rpj@callisto.canberra.edu.au\r
+ * \r
+ *      The current list of contributors is contained\r
+ *      in the file CONTRIBUTORS included with the source\r
+ *      code distribution. The list can also be seen at the\r
+ *      following World Wide Web location:\r
+ *      http://sources.redhat.com/pthreads-win32/contributors.html\r
+ * \r
+ *      This library is free software; you can redistribute it and/or\r
+ *      modify it under the terms of the GNU Lesser General Public\r
+ *      License as published by the Free Software Foundation; either\r
+ *      version 2 of the License, or (at your option) any later version.\r
+ * \r
+ *      This library is distributed in the hope that it will be useful,\r
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+ *      Lesser General Public License for more details.\r
+ * \r
+ *      You should have received a copy of the GNU Lesser General Public\r
+ *      License along with this library in the file COPYING.LIB;\r
+ *      if not, write to the Free Software Foundation, Inc.,\r
+ *      59 Temple Place - Suite 330, Boston, MA 02111-1307, USA\r
+ */\r
+#ifndef _SCHED_H\r
+#define _SCHED_H\r
+\r
+#undef PTW32_LEVEL\r
+\r
+#if defined(_POSIX_SOURCE)\r
+#define PTW32_LEVEL 0\r
+/* Early POSIX */\r
+#endif\r
+\r
+#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309\r
+#undef PTW32_LEVEL\r
+#define PTW32_LEVEL 1\r
+/* Include 1b, 1c and 1d */\r
+#endif\r
+\r
+#if defined(INCLUDE_NP)\r
+#undef PTW32_LEVEL\r
+#define PTW32_LEVEL 2\r
+/* Include Non-Portable extensions */\r
+#endif\r
+\r
+#define PTW32_LEVEL_MAX 3\r
+\r
+#if !defined(PTW32_LEVEL)\r
+#define PTW32_LEVEL PTW32_LEVEL_MAX\r
+/* Include everything */\r
+#endif\r
+\r
+\r
+#if __GNUC__ && ! defined (__declspec)\r
+# error Please upgrade your GNU compiler to one that supports __declspec.\r
+#endif\r
+\r
+/*\r
+ * When building the DLL code, you should define PTW32_BUILD so that\r
+ * the variables/functions are exported correctly. When using the DLL,\r
+ * do NOT define PTW32_BUILD, and then the variables/functions will\r
+ * be imported correctly.\r
+ */\r
+#ifndef PTW32_STATIC_LIB\r
+#  ifdef PTW32_BUILD\r
+#    define PTW32_DLLPORT __declspec (dllexport)\r
+#  else\r
+#    define PTW32_DLLPORT __declspec (dllimport)\r
+#  endif\r
+#else\r
+#  define PTW32_DLLPORT\r
+#endif\r
+\r
+/*\r
+ * This is a duplicate of what is in the autoconf config.h,\r
+ * which is only used when building the pthread-win32 libraries.\r
+ */\r
+\r
+#ifndef PTW32_CONFIG_H\r
+#  if defined(WINCE)\r
+#    define NEED_ERRNO\r
+#    define NEED_SEM\r
+#  endif\r
+#  if defined(_UWIN) || defined(__MINGW32__)\r
+#    define HAVE_MODE_T\r
+#  endif\r
+#endif\r
+\r
+/*\r
+ *\r
+ */\r
+\r
+#if PTW32_LEVEL >= PTW32_LEVEL_MAX\r
+#ifdef NEED_ERRNO\r
+#include "need_errno.h"\r
+#else\r
+#include <errno.h>\r
+#endif\r
+#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */\r
+\r
+#if defined(__MINGW32__) || defined(_UWIN)\r
+#if PTW32_LEVEL >= PTW32_LEVEL_MAX\r
+/* For pid_t */\r
+#  include <sys/types.h>\r
+/* Required by Unix 98 */\r
+#  include <time.h>\r
+#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */\r
+#else\r
+typedef int pid_t;\r
+#endif\r
+\r
+/* Thread scheduling policies */\r
+\r
+enum {\r
+  SCHED_OTHER = 0,\r
+  SCHED_FIFO,\r
+  SCHED_RR,\r
+  SCHED_MIN   = SCHED_OTHER,\r
+  SCHED_MAX   = SCHED_RR\r
+};\r
+\r
+struct sched_param {\r
+  int sched_priority;\r
+};\r
+\r
+#ifdef __cplusplus\r
+extern "C"\r
+{\r
+#endif                          /* __cplusplus */\r
+\r
+PTW32_DLLPORT int __cdecl sched_yield (void);\r
+\r
+PTW32_DLLPORT int __cdecl sched_get_priority_min (int policy);\r
+\r
+PTW32_DLLPORT int __cdecl sched_get_priority_max (int policy);\r
+\r
+PTW32_DLLPORT int __cdecl sched_setscheduler (pid_t pid, int policy);\r
+\r
+PTW32_DLLPORT int __cdecl sched_getscheduler (pid_t pid);\r
+\r
+/*\r
+ * Note that this macro returns ENOTSUP rather than\r
+ * ENOSYS as might be expected. However, returning ENOSYS\r
+ * should mean that sched_get_priority_{min,max} are\r
+ * not implemented as well as sched_rr_get_interval.\r
+ * This is not the case, since we just don't support\r
+ * round-robin scheduling. Therefore I have chosen to\r
+ * return the same value as sched_setscheduler when\r
+ * SCHED_RR is passed to it.\r
+ */\r
+#define sched_rr_get_interval(_pid, _interval) \\r
+  ( errno = ENOTSUP, (int) -1 )\r
+\r
+\r
+#ifdef __cplusplus\r
+}                               /* End of extern "C" */\r
+#endif                          /* __cplusplus */\r
+\r
+#undef PTW32_LEVEL\r
+#undef PTW32_LEVEL_MAX\r
+\r
+#endif                          /* !_SCHED_H */\r
+\r
diff --git a/xbmc/pvrclients/vdr-streamdev/pthread_win32/semaphore.h b/xbmc/pvrclients/vdr-streamdev/pthread_win32/semaphore.h
new file mode 100644 (file)
index 0000000..ea42ce3
--- /dev/null
@@ -0,0 +1,166 @@
+/*\r
+ * Module: semaphore.h\r
+ *\r
+ * Purpose:\r
+ *     Semaphores aren't actually part of the PThreads standard.\r
+ *     They are defined by the POSIX Standard:\r
+ *\r
+ *             POSIX 1003.1b-1993      (POSIX.1b)\r
+ *\r
+ * --------------------------------------------------------------------------\r
+ *\r
+ *      Pthreads-win32 - POSIX Threads Library for Win32\r
+ *      Copyright(C) 1998 John E. Bossom\r
+ *      Copyright(C) 1999,2005 Pthreads-win32 contributors\r
+ * \r
+ *      Contact Email: rpj@callisto.canberra.edu.au\r
+ * \r
+ *      The current list of contributors is contained\r
+ *      in the file CONTRIBUTORS included with the source\r
+ *      code distribution. The list can also be seen at the\r
+ *      following World Wide Web location:\r
+ *      http://sources.redhat.com/pthreads-win32/contributors.html\r
+ * \r
+ *      This library is free software; you can redistribute it and/or\r
+ *      modify it under the terms of the GNU Lesser General Public\r
+ *      License as published by the Free Software Foundation; either\r
+ *      version 2 of the License, or (at your option) any later version.\r
+ * \r
+ *      This library is distributed in the hope that it will be useful,\r
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+ *      Lesser General Public License for more details.\r
+ * \r
+ *      You should have received a copy of the GNU Lesser General Public\r
+ *      License along with this library in the file COPYING.LIB;\r
+ *      if not, write to the Free Software Foundation, Inc.,\r
+ *      59 Temple Place - Suite 330, Boston, MA 02111-1307, USA\r
+ */\r
+#if !defined( SEMAPHORE_H )\r
+#define SEMAPHORE_H\r
+\r
+#undef PTW32_LEVEL\r
+\r
+#if defined(_POSIX_SOURCE)\r
+#define PTW32_LEVEL 0\r
+/* Early POSIX */\r
+#endif\r
+\r
+#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309\r
+#undef PTW32_LEVEL\r
+#define PTW32_LEVEL 1\r
+/* Include 1b, 1c and 1d */\r
+#endif\r
+\r
+#if defined(INCLUDE_NP)\r
+#undef PTW32_LEVEL\r
+#define PTW32_LEVEL 2\r
+/* Include Non-Portable extensions */\r
+#endif\r
+\r
+#define PTW32_LEVEL_MAX 3\r
+\r
+#if !defined(PTW32_LEVEL)\r
+#define PTW32_LEVEL PTW32_LEVEL_MAX\r
+/* Include everything */\r
+#endif\r
+\r
+#if __GNUC__ && ! defined (__declspec)\r
+# error Please upgrade your GNU compiler to one that supports __declspec.\r
+#endif\r
+\r
+/*\r
+ * When building the DLL code, you should define PTW32_BUILD so that\r
+ * the variables/functions are exported correctly. When using the DLL,\r
+ * do NOT define PTW32_BUILD, and then the variables/functions will\r
+ * be imported correctly.\r
+ */\r
+#ifndef PTW32_STATIC_LIB\r
+#  ifdef PTW32_BUILD\r
+#    define PTW32_DLLPORT __declspec (dllexport)\r
+#  else\r
+#    define PTW32_DLLPORT __declspec (dllimport)\r
+#  endif\r
+#else\r
+#  define PTW32_DLLPORT\r
+#endif\r
+\r
+/*\r
+ * This is a duplicate of what is in the autoconf config.h,\r
+ * which is only used when building the pthread-win32 libraries.\r
+ */\r
+\r
+#ifndef PTW32_CONFIG_H\r
+#  if defined(WINCE)\r
+#    define NEED_ERRNO\r
+#    define NEED_SEM\r
+#  endif\r
+#  if defined(_UWIN) || defined(__MINGW32__)\r
+#    define HAVE_MODE_T\r
+#  endif\r
+#endif\r
+\r
+/*\r
+ *\r
+ */\r
+\r
+#if PTW32_LEVEL >= PTW32_LEVEL_MAX\r
+#ifdef NEED_ERRNO\r
+#include "need_errno.h"\r
+#else\r
+#include <errno.h>\r
+#endif\r
+#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */\r
+\r
+#define _POSIX_SEMAPHORES\r
+\r
+#ifdef __cplusplus\r
+extern "C"\r
+{\r
+#endif                         /* __cplusplus */\r
+\r
+#ifndef HAVE_MODE_T\r
+typedef unsigned int mode_t;\r
+#endif\r
+\r
+\r
+typedef struct sem_t_ * sem_t;\r
+\r
+PTW32_DLLPORT int __cdecl sem_init (sem_t * sem,\r
+                           int pshared,\r
+                           unsigned int value);\r
+\r
+PTW32_DLLPORT int __cdecl sem_destroy (sem_t * sem);\r
+\r
+PTW32_DLLPORT int __cdecl sem_trywait (sem_t * sem);\r
+\r
+PTW32_DLLPORT int __cdecl sem_wait (sem_t * sem);\r
+\r
+PTW32_DLLPORT int __cdecl sem_timedwait (sem_t * sem,\r
+                                const struct timespec * abstime);\r
+\r
+PTW32_DLLPORT int __cdecl sem_post (sem_t * sem);\r
+\r
+PTW32_DLLPORT int __cdecl sem_post_multiple (sem_t * sem,\r
+                                    int count);\r
+\r
+PTW32_DLLPORT int __cdecl sem_open (const char * name,\r
+                           int oflag,\r
+                           mode_t mode,\r
+                           unsigned int value);\r
+\r
+PTW32_DLLPORT int __cdecl sem_close (sem_t * sem);\r
+\r
+PTW32_DLLPORT int __cdecl sem_unlink (const char * name);\r
+\r
+PTW32_DLLPORT int __cdecl sem_getvalue (sem_t * sem,\r
+                               int * sval);\r
+\r
+#ifdef __cplusplus\r
+}                              /* End of extern "C" */\r
+#endif                         /* __cplusplus */\r
+\r
+#undef PTW32_LEVEL\r
+#undef PTW32_LEVEL_MAX\r
+\r
+#endif                         /* !SEMAPHORE_H */\r
diff --git a/xbmc/pvrclients/vdr-streamdev/pvrclient-vdr_os.h b/xbmc/pvrclients/vdr-streamdev/pvrclient-vdr_os.h
new file mode 100644 (file)
index 0000000..883cb17
--- /dev/null
@@ -0,0 +1,44 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef PVRCLIENT_VDR_OS_H
+#define PVRCLIENT_VDR_OS_H
+
+#if defined(_WIN32) || defined(_WIN64)
+#define __WINDOWS__
+#endif
+
+#if defined(__WINDOWS__)
+#include "windows/pvrclient-vdr_os_windows.h"
+#else
+#include "linux/pvrclient-vdr_os_posix.h"
+#endif
+
+#if !defined(TRUE)
+#define TRUE 1
+#endif
+
+#if !defined(FALSE)
+#define FALSE 0
+#endif
+
+#endif
diff --git a/xbmc/pvrclients/vdr-streamdev/recordings.cpp b/xbmc/pvrclients/vdr-streamdev/recordings.cpp
new file mode 100644 (file)
index 0000000..a91c891
--- /dev/null
@@ -0,0 +1,312 @@
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+/*
+ * This code is taken from recordings.c in the Video Disk Recorder ('VDR')
+ */
+
+#include "StdString.h"
+#include "recordings.h"
+#include "client.h"
+
+#define RESUME_NOT_INITIALIZED (-2)
+
+cRecording::cRecording()
+{
+  m_channelName     = NULL;
+  m_aux             = NULL;
+  m_title           = NULL;
+  m_shortText       = NULL;
+  m_description     = NULL;
+  m_fileName        = NULL;
+  m_directory       = NULL;
+  m_framesPerSecond = DEFAULTFRAMESPERSECOND;
+  m_priority        = MAXPRIORITY;
+  m_lifetime        = MAXLIFETIME;
+  m_EventID         = 0;
+  m_StartTime       = 0;
+  m_Duration        = 0;
+  m_TableID         = 0;
+  m_Version         = 0xFF;
+  m_vps             = 0;
+  m_Index           = -1;
+  m_isPesRecording  = false;
+  m_resume          = RESUME_NOT_INITIALIZED;
+  m_fileSizeMB      = -1; // unknown
+  m_deleted         = false;
+}
+
+cRecording::cRecording(const PVR_RECORDINGINFO *Recording)
+{
+
+}
+
+cRecording::cRecording(const char *FileName)
+{
+  m_channelName     = NULL;
+  m_aux             = NULL;
+  m_title           = NULL;
+  m_shortText       = NULL;
+  m_description     = NULL;
+  m_fileName        = NULL;
+  m_directory       = NULL;
+  m_resume          = RESUME_NOT_INITIALIZED;
+  m_fileSizeMB      = -1; // unknown
+  m_priority        = MAXPRIORITY;
+  m_lifetime        = MAXLIFETIME;
+  m_isPesRecording  = false;
+  m_framesPerSecond = DEFAULTFRAMESPERSECOND;
+  m_deleted         = false;
+
+  FileName = m_fileName = strdup(FileName);
+  if (*(m_fileName + strlen(m_fileName) - 1) == '/')
+     *(m_fileName + strlen(m_fileName) - 1) = 0;
+  FileName += g_szRecordingsDir.length() + 1;
+  const char *p = strrchr(FileName, '/');
+  if (p)
+  {
+    time_t now = time(NULL);
+    struct tm tm_r;
+    struct tm t = *localtime_r(&now, &tm_r); // this initializes the time zone in 't'
+    t.tm_isdst = -1; // makes sure mktime() will determine the correct DST setting
+    int channel;
+    int instanceId;
+    if (7 == sscanf(p + 1, DATAFORMATTS, &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &channel, &instanceId)
+        || 7 == sscanf(p + 1, DATAFORMATPES, &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &m_priority, &m_lifetime))
+    {
+      t.tm_year -= 1900;
+      t.tm_mon--;
+      t.tm_sec = 0;
+      m_StartTime = mktime(&t);
+      char *path = MALLOC(char, p - FileName + 1);
+      strncpy(path, FileName, p - FileName);
+      path[p - FileName] = 0;
+
+      /* Make some default title based upon directory names */
+      p = strrchr(path, '/');
+      if (p)
+        m_title = strcpyrealloc(m_title, p + 1);
+      else
+        m_title = strcpyrealloc(m_title, path);
+
+      m_directory = MALLOC(char, strlen(path) - strlen(m_title) + 1);
+      strncpy(m_directory, path, strlen(path) - strlen(m_title));
+      m_directory[strlen(path) - strlen(m_title)] = 0;
+
+      if (g_bCharsetConv)
+      {
+        CStdString str_result = m_directory;
+        XBMC->UnknownToUTF8(str_result);
+        m_directory = strcpyrealloc(m_directory, str_result.c_str());
+      }
+
+      m_isPesRecording = instanceId < 0;
+      m_stream_url.Format("%s/%s", m_fileName, m_isPesRecording ? "*.vdr" : "*.ts");
+      free(path);
+    }
+    else
+      return;
+    // read info file:
+    CStdString InfoFileName;
+    InfoFileName.Format("%s%s", m_fileName, m_isPesRecording ? INFOFILESUFFIX ".vdr" : INFOFILESUFFIX);
+    FILE *f = fopen(InfoFileName.c_str(), "r");
+    if (f)
+    {
+      if (!Read(f))
+        XBMC->Log(LOG_ERROR, "EPG data problem in file %s", InfoFileName.c_str());
+      fclose(f);
+    }
+    else if (errno != ENOENT)
+      XBMC->Log(LOG_ERROR, "ERROR (%s,%d,s): %m", __FILE__, __LINE__, InfoFileName.c_str());
+  }
+}
+
+cRecording::~cRecording()
+{
+  free(m_aux);
+  free(m_channelName);
+  free(m_title);
+  free(m_shortText);
+  free(m_description);
+  free(m_fileName);
+  free(m_directory);
+}
+
+bool cRecording::Read(FILE *f)
+{
+  cReadLine ReadLine;
+  char *s;
+  int line = 0;
+  while ((s = ReadLine.Read(f)) != NULL)
+  {
+    ++line;
+    CStdString str_result = s;
+    if (g_bCharsetConv)
+      XBMC->UnknownToUTF8(str_result);
+    if (!ParseLine(str_result.c_str()))
+      return false;
+  }
+  return true;
+}
+
+bool cRecording::ParseLine(const char *s)
+{
+  char *t = skipspace(s + 1);
+  switch (*s)
+  {
+    case 'C':
+      {
+        char *p = strchr(t, ' ');
+        if (p)
+        {
+          free(m_channelName);
+          m_channelName = strdup(compactspace(p));
+          *p = 0; // strips optional channel name
+        }
+  //      if (*t)
+  //        channelID = tChannelID::FromString(t);
+      }
+      return true;
+    case 'D':
+      strreplace(t, '|', '\n');
+      SetDescription(t);
+      return true;
+    case 'E':
+      {
+        unsigned int EventID;
+        time_t StartTime;
+        int Duration;
+        unsigned int TableID = 0;
+        unsigned int Version = 0xFF;
+        int n = sscanf(t, "%u %ld %d %X %X", &EventID, &StartTime, &Duration, &TableID, &Version);
+        if (n >= 3 && n <= 5)
+        {
+          m_EventID   = EventID;
+          m_StartTime = StartTime;
+          m_Duration  = Duration;
+          m_TableID   = TableID;
+          m_Version   = Version;
+        }
+      }
+      return true;
+    case 'F':
+      m_framesPerSecond = atof(t);
+      return true;
+    case 'L':
+      m_lifetime = atoi(t);
+      return true;
+    case 'P':
+      m_priority = atoi(t);
+      return true;
+    case 'S':
+      SetShortText(t);
+      return true;
+    case 'T':
+      SetTitle(t);
+      return true;
+    case 'V':
+      m_vps = atoi(t);
+      return true;
+    case '@':
+      free(m_aux);
+      m_aux = strdup(t);
+      return true;
+    case '#':
+      return true; // comments are ignored
+
+
+    default:
+      break;
+  }
+  return false;
+}
+
+bool cRecording::ParseEntryLine(const char *s)
+{
+  if (*s >= '0' && *s <= '9')
+  {
+    char recdate[256];
+    char rectime[256];
+    char rectext[1024];
+
+    if (sscanf(s, " %u %[^ ] %[^ ] %[^\n]", &m_Index, recdate, rectime, rectext) >= 3)
+    {
+      CStdString fileName = rectext;
+      fileName.Replace('/', '_');
+      fileName.Replace('\\', '_');
+      fileName.Replace('?', '_');
+#if defined(_WIN32) || defined(_WIN64)
+      // just filter out some illegal characters on windows
+      fileName.Replace(':', '_');
+      fileName.Replace('*', '_');
+      fileName.Replace('?', '_');
+      fileName.Replace('\"', '_');
+      fileName.Replace('<', '_');
+      fileName.Replace('>', '_');
+      fileName.Replace('|', '_');
+      fileName.TrimRight(".");
+      fileName.TrimRight(" ");
+#endif
+      size_t found = fileName.find_last_of("~");
+      if (found != CStdString::npos)
+      {
+        CStdString dir = fileName.substr(0,found);
+        dir.Replace('~','/');
+        m_directory = strcpyrealloc(m_directory, dir.c_str());
+        m_fileName = strcpyrealloc(m_fileName, fileName.substr(found+1).c_str());
+      }
+      else
+      {
+        m_fileName = strcpyrealloc(m_fileName, fileName.c_str());
+        m_directory = strcpyrealloc(m_directory, "");
+      }
+    }
+    return true;
+  }
+
+  return false;
+}
+
+//    case 'X': if (!components)
+//                 components = new cComponents;
+//              components->SetComponent(components->NumComponents(), t);
+//              break;
+
+
+void cRecording::SetFramesPerSecond(double FramesPerSecond)
+{
+  m_framesPerSecond = FramesPerSecond;
+}
+
+void cRecording::SetTitle(const char *Title)
+{
+  m_title = strcpyrealloc(m_title, Title);
+}
+
+void cRecording::SetShortText(const char *ShortText)
+{
+  m_shortText = strcpyrealloc(m_shortText, ShortText);
+}
+
+void cRecording::SetDescription(const char *Description)
+{
+  m_description = strcpyrealloc(m_description, Description);
+}
diff --git a/xbmc/pvrclients/vdr-streamdev/recordings.h b/xbmc/pvrclients/vdr-streamdev/recordings.h
new file mode 100644 (file)
index 0000000..b467500
--- /dev/null
@@ -0,0 +1,101 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef __RECORDINGS_H
+#define __RECORDINGS_H
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "tools.h"
+
+#define DEFAULTFRAMESPERSECOND 25.0
+#define MAXPRIORITY       99
+#define MAXLIFETIME       99
+#define RECEXT            ".rec"
+#define DELEXT            ".del"
+#define DATAFORMATPES     "%4d-%02d-%02d.%02d%*c%02d.%02d.%02d" RECEXT
+#define NAMEFORMATPES     "%s/%s/" "%4d-%02d-%02d.%02d.%02d.%02d.%02d" RECEXT
+#define DATAFORMATTS      "%4d-%02d-%02d.%02d.%02d.%d-%d" RECEXT
+#define NAMEFORMATTS      "%s/%s/" DATAFORMATTS
+#define RESUMEFILESUFFIX  "/resume%s%s"
+#define SUMMARYFILESUFFIX "/summary.vdr"
+#define INFOFILESUFFIX    "/info"
+#define MARKSFILESUFFIX   "/marks"
+
+class cRecording
+{
+private:
+  int m_Index;
+  int m_resume;
+  int m_fileSizeMB;
+  char *m_channelName;
+  char *m_fileName;
+  char *m_directory;
+  CStdString m_stream_url;
+  double m_framesPerSecond;
+  int m_priority;
+  int m_lifetime;
+  char *m_aux;
+  unsigned int m_EventID;
+  time_t m_StartTime;
+  int m_Duration;
+  unsigned int m_TableID;
+  unsigned int m_Version;
+  bool m_isPesRecording;
+  bool m_deleted;
+  char *m_title;             // Title of this event
+  char *m_shortText;         // Short description of this event (typically the episode name in case of a series)
+  char *m_description;       // Description of this event
+  time_t m_vps;              // Video Programming Service timestamp (VPS, aka "Programme Identification Label", PIL)
+  bool Read(FILE *f);
+
+public:
+  cRecording(const char *FileName);
+  cRecording(const PVR_RECORDINGINFO *Recording);
+  cRecording();
+  virtual ~cRecording();
+
+  bool ParseLine(const char *s);
+  bool ParseEntryLine(const char *s);
+  const char *ChannelName(void) const { return m_channelName; }
+  const char *Aux(void) const { return m_aux; }
+  double FramesPerSecond(void) const { return m_framesPerSecond; }
+  void SetFramesPerSecond(double m_FramesPerSecond);
+  int Index(void) const { return m_Index; }
+  int Priority(void) const { return m_priority; }
+  int Lifetime(void) const { return m_lifetime; }
+  time_t StartTime(void) const { return m_StartTime; }
+  time_t Duration(void) const { return m_Duration; }
+  time_t Vps(void) const { return m_vps; }
+  const char *Title(void) const { return m_title; }
+  const char *ShortText(void) const { return m_shortText; }
+  const char *Description(void) const { return m_description; }
+  const char *FileName(void) const { return m_fileName; }
+  const char *Directory(void) const { return m_directory; }
+  const char *StreamURL(void) const { return m_stream_url.c_str(); }
+  void SetTitle(const char *Title);
+  void SetShortText(const char *ShortText);
+  void SetDescription(const char *Description);
+};
+
+#endif //__RECORDINGS_H
diff --git a/xbmc/pvrclients/vdr-streamdev/ringbuffer.cpp b/xbmc/pvrclients/vdr-streamdev/ringbuffer.cpp
new file mode 100644 (file)
index 0000000..44c9bd6
--- /dev/null
@@ -0,0 +1,385 @@
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+/*
+ * Most of this code is taken from ringbuffer.c in the Video Disk Recorder ('VDR')
+ */
+
+#include "tools.h"
+#include "ringbuffer.h"
+#include <stdlib.h>
+
+// --- cRingBuffer -----------------------------------------------------------
+
+#define OVERFLOWREPORTDELTA 5 // seconds between reports
+#define PERCENTAGEDELTA     10
+#define PERCENTAGETHRESHOLD 70
+
+cRingBuffer::cRingBuffer(int Size, bool Statistics)
+{
+  size = Size;
+  statistics = Statistics;
+  getThreadTid = 0;
+  maxFill = 0;
+  lastPercent = 0;
+  putTimeout = getTimeout = 0;
+  lastOverflowReport = 0;
+  overflowCount = overflowBytes = 0;
+}
+
+cRingBuffer::~cRingBuffer()
+{
+  if (statistics)
+     XBMC->Log(LOG_DEBUG, "buffer stats: %d (%d%%) used", maxFill, maxFill * 100 / (size - 1));
+}
+
+void cRingBuffer::UpdatePercentage(int Fill)
+{
+  if (Fill > maxFill)
+     maxFill = Fill;
+  int percent = Fill * 100 / (Size() - 1) / PERCENTAGEDELTA * PERCENTAGEDELTA; // clamp down to nearest quantum
+  if (percent != lastPercent) {
+     if (percent >= PERCENTAGETHRESHOLD && percent > lastPercent || percent < PERCENTAGETHRESHOLD && lastPercent >= PERCENTAGETHRESHOLD) {
+        XBMC->Log(LOG_DEBUG, "buffer usage: %d%% (tid=%d)", percent, getThreadTid);
+        lastPercent = percent;
+        }
+     }
+}
+
+void cRingBuffer::WaitForPut(void)
+{
+  if (putTimeout)
+     readyForPut.Wait(putTimeout);
+}
+
+void cRingBuffer::WaitForGet(void)
+{
+  if (getTimeout)
+     readyForGet.Wait(getTimeout);
+}
+
+void cRingBuffer::EnablePut(void)
+{
+  if (putTimeout && Free() > Size() / 3)
+     readyForPut.Signal();
+}
+
+void cRingBuffer::EnableGet(void)
+{
+  if (getTimeout && Available() > Size() / 3)
+     readyForGet.Signal();
+}
+
+void cRingBuffer::SetTimeouts(int PutTimeout, int GetTimeout)
+{
+  putTimeout = PutTimeout;
+  getTimeout = GetTimeout;
+}
+
+void cRingBuffer::ReportOverflow(int Bytes)
+{
+  overflowCount++;
+  overflowBytes += Bytes;
+  if (time(NULL) - lastOverflowReport > OVERFLOWREPORTDELTA) {
+     XBMC->Log(LOG_ERROR, "ERROR: %d ring buffer overflow%s (%d bytes dropped)", overflowCount, overflowCount > 1 ? "s" : "", overflowBytes);
+     overflowCount = overflowBytes = 0;
+     lastOverflowReport = time(NULL);
+     }
+}
+
+// --- cRingBufferLinear -----------------------------------------------------
+
+#ifdef DEBUGRINGBUFFERS
+#define MAXRBLS 30
+#define DEBUGRBLWIDTH 45
+
+cRingBufferLinear *cRingBufferLinear::RBLS[MAXRBLS] = { NULL };
+
+void cRingBufferLinear::AddDebugRBL(cRingBufferLinear *RBL)
+{
+  for (int i = 0; i < MAXRBLS; i++) {
+      if (!RBLS[i]) {
+         RBLS[i] = RBL;
+         break;
+         }
+      }
+}
+
+void cRingBufferLinear::DelDebugRBL(cRingBufferLinear *RBL)
+{
+  for (int i = 0; i < MAXRBLS; i++) {
+      if (RBLS[i] == RBL) {
+         RBLS[i] = NULL;
+         break;
+         }
+      }
+}
+
+void cRingBufferLinear::PrintDebugRBL(void)
+{
+  bool printed = false;
+  for (int i = 0; i < MAXRBLS; i++) {
+      cRingBufferLinear *p = RBLS[i];
+      if (p) {
+         printed = true;
+         int lh = p->lastHead;
+         int lt = p->lastTail;
+         int h = lh * DEBUGRBLWIDTH / p->Size();
+         int t = lt * DEBUGRBLWIDTH / p->Size();
+         char buf[DEBUGRBLWIDTH + 10];
+         memset(buf, '-', DEBUGRBLWIDTH);
+         if (lt <= lh)
+            memset(buf + t, '*', max(h - t, 1));
+         else {
+            memset(buf, '*', h);
+            memset(buf + t, '*', DEBUGRBLWIDTH - t);
+            }
+         buf[t] = '<';
+         buf[h] = '>';
+         buf[DEBUGRBLWIDTH] = 0;
+         printf("%2d %s %8d %8d %s\n", i, buf, p->lastPut, p->lastGet, p->description);
+         }
+      }
+  if (printed)
+     printf("\n");
+  }
+#endif
+
+cRingBufferLinear::cRingBufferLinear(int Size, int Margin, bool Statistics, const char *Description)
+:cRingBuffer(Size, Statistics)
+{
+  description = Description ? strdup(Description) : NULL;
+  tail = head = margin = Margin;
+  gotten = 0;
+  buffer = NULL;
+  if (Size > 1) { // 'Size - 1' must not be 0!
+     if (Margin <= Size / 2) {
+        buffer = MALLOC(unsigned char, Size);
+        if (!buffer)
+           XBMC->Log(LOG_ERROR, "ERROR: can't allocate ring buffer (size=%d)", Size);
+        Clear();
+        }
+     else
+        XBMC->Log(LOG_ERROR, "ERROR: invalid margin for ring buffer (%d > %d)", Margin, Size / 2);
+     }
+  else
+     XBMC->Log(LOG_ERROR, "ERROR: invalid size for ring buffer (%d)", Size);
+#ifdef DEBUGRINGBUFFERS
+  lastHead = head;
+  lastTail = tail;
+  lastPut = lastGet = -1;
+  AddDebugRBL(this);
+#endif
+}
+
+cRingBufferLinear::~cRingBufferLinear()
+{
+#ifdef DEBUGRINGBUFFERS
+  DelDebugRBL(this);
+#endif
+  free(buffer);
+  free(description);
+}
+
+int cRingBufferLinear::DataReady(const unsigned char *Data, int Count)
+{
+  return Count >= margin ? Count : 0;
+}
+
+int cRingBufferLinear::Available(void)
+{
+  int diff = head - tail;
+  return (diff >= 0) ? diff : Size() + diff - margin;
+}
+
+void cRingBufferLinear::Clear(void)
+{
+  tail = head = margin;
+#ifdef DEBUGRINGBUFFERS
+  lastHead = head;
+  lastTail = tail;
+  lastPut = lastGet = -1;
+#endif
+  maxFill = 0;
+  EnablePut();
+}
+
+int cRingBufferLinear::Read(int FileHandle, int Max)
+{
+  int Tail = tail;
+  int diff = Tail - head;
+  int free = (diff > 0) ? diff - 1 : Size() - head;
+  if (Tail <= margin)
+     free--;
+  int Count = -1;
+  errno = EAGAIN;
+  if (free > 0) {
+     if (0 < Max && Max < free)
+        free = Max;
+     Count = safe_read(FileHandle, buffer + head, free);
+     if (Count > 0) {
+        int Head = head + Count;
+        if (Head >= Size())
+           Head = margin;
+        head = Head;
+        if (statistics) {
+           int fill = head - Tail;
+           if (fill < 0)
+              fill = Size() + fill;
+           else if (fill >= Size())
+              fill = Size() - 1;
+           UpdatePercentage(fill);
+           }
+        }
+     }
+#ifdef DEBUGRINGBUFFERS
+  lastHead = head;
+  lastPut = Count;
+#endif
+  EnableGet();
+  if (free == 0)
+     WaitForPut();
+  return Count;
+}
+
+int cRingBufferLinear::Read(cUnbufferedFile *File, int Max)
+{
+  int Tail = tail;
+  int diff = Tail - head;
+  int free = (diff > 0) ? diff - 1 : Size() - head;
+  if (Tail <= margin)
+     free--;
+  int Count = -1;
+  errno = EAGAIN;
+  if (free > 0) {
+     if (0 < Max && Max < free)
+        free = Max;
+     Count = File->Read(buffer + head, free);
+     if (Count > 0) {
+        int Head = head + Count;
+        if (Head >= Size())
+           Head = margin;
+        head = Head;
+        if (statistics) {
+           int fill = head - Tail;
+           if (fill < 0)
+              fill = Size() + fill;
+           else if (fill >= Size())
+              fill = Size() - 1;
+           UpdatePercentage(fill);
+           }
+        }
+     }
+#ifdef DEBUGRINGBUFFERS
+  lastHead = head;
+  lastPut = Count;
+#endif
+  EnableGet();
+  if (free == 0)
+     WaitForPut();
+  return Count;
+}
+
+int cRingBufferLinear::Put(const unsigned char *Data, int Count)
+{
+  if (Count > 0) {
+     int Tail = tail;
+     int rest = Size() - head;
+     int diff = Tail - head;
+     int free = ((Tail < margin) ? rest : (diff > 0) ? diff : Size() + diff - margin) - 1;
+     if (statistics) {
+        int fill = Size() - free - 1 + Count;
+        if (fill >= Size())
+           fill = Size() - 1;
+        UpdatePercentage(fill);
+        }
+     if (free > 0) {
+        if (free < Count)
+           Count = free;
+        if (Count >= rest) {
+           memcpy(buffer + head, Data, rest);
+           if (Count - rest)
+              memcpy(buffer + margin, Data + rest, Count - rest);
+           head = margin + Count - rest;
+           }
+        else {
+           memcpy(buffer + head, Data, Count);
+           head += Count;
+           }
+        }
+     else
+        Count = 0;
+#ifdef DEBUGRINGBUFFERS
+     lastHead = head;
+     lastPut = Count;
+#endif
+     EnableGet();
+     if (Count == 0)
+        WaitForPut();
+     }
+  return Count;
+}
+
+unsigned char *cRingBufferLinear::Get(int &Count)
+{
+  int Head = head;
+  if (getThreadTid <= 0)
+     getThreadTid = cThread::ThreadId();
+  int rest = Size() - tail;
+  if (rest < margin && Head < tail) {
+     int t = margin - rest;
+     memcpy(buffer + t, buffer + tail, rest);
+     tail = t;
+     rest = Head - tail;
+     }
+  int diff = Head - tail;
+  int cont = (diff >= 0) ? diff : Size() + diff - margin;
+  if (cont > rest)
+     cont = rest;
+  unsigned char *p = buffer + tail;
+  if ((cont = DataReady(p, cont)) > 0) {
+     Count = gotten = cont;
+     return p;
+     }
+  WaitForGet();
+  return NULL;
+}
+
+void cRingBufferLinear::Del(int Count)
+{
+  if (Count > gotten) {
+     XBMC->Log(LOG_ERROR, "ERROR: invalid Count in cRingBufferLinear::Del: %d (limited to %d)", Count, gotten);
+     Count = gotten;
+     }
+  if (Count > 0) {
+     int Tail = tail;
+     Tail += Count;
+     gotten -= Count;
+     if (Tail >= Size())
+        Tail = margin;
+     tail = Tail;
+     EnablePut();
+     }
+#ifdef DEBUGRINGBUFFERS
+  lastTail = tail;
+  lastGet = Count;
+#endif
+}
diff --git a/xbmc/pvrclients/vdr-streamdev/ringbuffer.h b/xbmc/pvrclients/vdr-streamdev/ringbuffer.h
new file mode 100644 (file)
index 0000000..198f48e
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef __RINGBUFFER_H
+#define __RINGBUFFER_H
+
+#include "thread.h"
+#include "tools.h"
+
+class cRingBuffer {
+private:
+  cCondWait readyForPut, readyForGet;
+  int putTimeout;
+  int getTimeout;
+  int size;
+  time_t lastOverflowReport;
+  int overflowCount;
+  int overflowBytes;
+protected:
+  tThreadId getThreadTid;
+  int maxFill;//XXX
+  int lastPercent;
+  bool statistics;//XXX
+  void UpdatePercentage(int Fill);
+  void WaitForPut(void);
+  void WaitForGet(void);
+  void EnablePut(void);
+  void EnableGet(void);
+  virtual void Clear(void) = 0;
+  virtual int Available(void) = 0;
+  virtual int Free(void) { return Size() - Available() - 1; }
+  int Size(void) { return size; }
+public:
+  cRingBuffer(int Size, bool Statistics = false);
+  virtual ~cRingBuffer();
+  void SetTimeouts(int PutTimeout, int GetTimeout);
+  void ReportOverflow(int Bytes);
+  };
+
+class cRingBufferLinear : public cRingBuffer {
+//#define DEBUGRINGBUFFERS
+#ifdef DEBUGRINGBUFFERS
+private:
+  int lastHead, lastTail;
+  int lastPut, lastGet;
+  static cRingBufferLinear *RBLS[];
+  static void AddDebugRBL(cRingBufferLinear *RBL);
+  static void DelDebugRBL(cRingBufferLinear *RBL);
+public:
+  static void PrintDebugRBL(void);
+#endif
+private:
+  int margin, head, tail;
+  int gotten;
+  unsigned char *buffer;
+  char *description;
+protected:
+  virtual int DataReady(const unsigned char *Data, int Count);
+    ///< By default a ring buffer has data ready as soon as there are at least
+    ///< 'margin' bytes available. A derived class can reimplement this function
+    ///< if it has other conditions that define when data is ready.
+    ///< The return value is either 0 if there is not yet enough data available,
+    ///< or the number of bytes from the beginning of Data that are "ready".
+public:
+  cRingBufferLinear(int Size, int Margin = 0, bool Statistics = false, const char *Description = NULL);
+    ///< Creates a linear ring buffer.
+    ///< The buffer will be able to hold at most Size-Margin-1 bytes of data, and will
+    ///< be guaranteed to return at least Margin bytes in one consecutive block.
+    ///< The optional Description is used for debugging only.
+  virtual ~cRingBufferLinear();
+  virtual int Available(void);
+  virtual int Free(void) { return Size() - Available() - 1 - margin; }
+  virtual void Clear(void);
+    ///< Immediately clears the ring buffer.
+  int Read(int FileHandle, int Max = 0);
+    ///< Reads at most Max bytes from FileHandle and stores them in the
+    ///< ring buffer. If Max is 0, reads as many bytes as possible.
+    ///< Only one actual read() call is done.
+    ///< \return Returns the number of bytes actually read and stored, or
+    ///< an error value from the actual read() call.
+  int Read(cUnbufferedFile *File, int Max = 0);
+    ///< Like Read(int FileHandle, int Max), but reads fom a cUnbufferedFile).
+  int Put(const unsigned char *Data, int Count);
+    ///< Puts at most Count bytes of Data into the ring buffer.
+    ///< \return Returns the number of bytes actually stored.
+  unsigned char *Get(int &Count);
+    ///< Gets data from the ring buffer.
+    ///< The data will remain in the buffer until a call to Del() deletes it.
+    ///< \return Returns a pointer to the data, and stores the number of bytes
+    ///< actually available in Count. If the returned pointer is NULL, Count has no meaning.
+  void Del(int Count);
+    ///< Deletes at most Count bytes from the ring buffer.
+    ///< Count must be less or equal to the number that was returned by a previous
+    ///< call to Get().
+  };
+
+#endif // __RINGBUFFER_H
diff --git a/xbmc/pvrclients/vdr-streamdev/select.cpp b/xbmc/pvrclients/vdr-streamdev/select.cpp
new file mode 100644 (file)
index 0000000..a963f96
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+ /* NOTE:
+  *
+  * The following code is taken and copied from VDR streamdev plugin.
+  */
+
+#include "select.h"
+#include "tools.h"
+#include <sys/types.h>
+#include <time.h>
+#include <errno.h>
+
+cTBSelect::cTBSelect(void)
+{
+       Clear();
+}
+
+cTBSelect::~cTBSelect()
+{
+}
+
+int cTBSelect::Select(unsigned int TimeoutMs)
+{
+       struct timeval tv;
+       ssize_t res = 0;
+       int ms;
+
+       tv.tv_usec = (TimeoutMs % 1000) * 1000;
+       tv.tv_sec = TimeoutMs / 1000;
+       memcpy(m_FdsResult, m_FdsQuery, sizeof(m_FdsResult));
+
+       if (TimeoutMs == 0)
+               return __select(m_MaxFiled + 1, &m_FdsResult[0], &m_FdsResult[1], NULL, &tv);
+
+       cTimeMs starttime;
+       ms = TimeoutMs;
+       while (ms > 0 && (res = __select(m_MaxFiled + 1, &m_FdsResult[0], &m_FdsResult[1], NULL, &tv)) == -1 && errno == EINTR)
+       {
+               ms = TimeoutMs - starttime.Elapsed();
+               tv.tv_usec = (ms % 1000) * 1000;
+               tv.tv_sec = ms / 1000;
+               memcpy(m_FdsResult, m_FdsQuery, sizeof(m_FdsResult));
+       }
+       if (ms <= 0 || res == 0)
+       {
+               errno = ETIMEDOUT;
+               return -1;
+       }
+       return res;
+}
+
+int cTBSelect::Select(void)
+{
+       ssize_t res;
+       do
+       {
+               memcpy(m_FdsResult, m_FdsQuery, sizeof(m_FdsResult));
+       } while ((res = __select(m_MaxFiled + 1, &m_FdsResult[0], &m_FdsResult[1], NULL, NULL)) == -1 && errno == EINTR);
+       return res;
+}
diff --git a/xbmc/pvrclients/vdr-streamdev/select.h b/xbmc/pvrclients/vdr-streamdev/select.h
new file mode 100644 (file)
index 0000000..dcce4de
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef TOOLBOX_SELECT_H
+#define TOOLBOX_SELECT_H
+
+#include "tools.h"
+
+#include <sys/types.h>
+
+/* cTBSelect provides an interface for polling UNIX-like file descriptors. */
+
+class cTBSelect
+{
+private:
+       int    m_MaxFiled;
+
+       fd_set m_FdsQuery[2];
+       fd_set m_FdsResult[2];
+
+public:
+       cTBSelect(void);
+       virtual ~cTBSelect();
+
+       /* Clear() resets the object for use in a new Select() call. All file
+          descriptors and their previous states are invalidated. */
+       virtual void Clear(void);
+
+       /* Add() adds a file descriptor to be polled in the next Select() call.
+          That call polls if the file is readable if Output is set to false,
+          writeable otherwise. */
+       virtual bool Add(int Filed, bool Output = false);
+
+       /* Select() polls all descriptors added by Add() and returns as soon as
+          one of those changes state (gets readable/writeable), or after
+          TimeoutMs milliseconds, whichever happens first. It returns the number
+          of filedescriptors that have changed state. On error, -1 is returned
+          and errno is set appropriately. */
+       virtual int Select(unsigned int TimeoutMs);
+
+       /* Select() polls all descriptors added by Add() and returns as soon as
+          one of those changes state (gets readable/writeable). It returns the
+          number of filedescriptors that have changed state. On error, -1 is
+          returned and errno is set appropriately. */
+       virtual int Select(void);
+
+       /* CanRead() returns true if the descriptor has changed to readable during
+          the last Select() call. Otherwise false is returned. */
+       virtual bool CanRead(int FileNo) const;
+
+       /* CanWrite() returns true if the descriptor has changed to writeable
+          during the last Select() call. Otherwise false is returned. */
+       virtual bool CanWrite(int FileNo) const;
+};
+
+inline void cTBSelect::Clear(void)
+{
+       FD_ZERO(&m_FdsQuery[0]);
+       FD_ZERO(&m_FdsQuery[1]);
+       m_MaxFiled = -1;
+}
+
+inline bool cTBSelect::Add(int Filed, bool Output /* = false */)
+{
+       if (Filed < 0) return false;
+       FD_SET(Filed, &m_FdsQuery[Output ? 1 : 0]);
+       if (Filed > m_MaxFiled) m_MaxFiled = Filed;
+       return true;
+}
+
+inline bool cTBSelect::CanRead(int FileNo) const
+{
+       if (FileNo < 0) return false;
+       return FD_ISSET(FileNo, &m_FdsResult[0]);
+}
+
+inline bool cTBSelect::CanWrite(int FileNo) const
+{
+       if (FileNo < 0) return false;
+       return FD_ISSET(FileNo, &m_FdsResult[1]);
+}
+
+#endif // TOOLBOX_SELECT_H
diff --git a/xbmc/pvrclients/vdr-streamdev/thread.cpp b/xbmc/pvrclients/vdr-streamdev/thread.cpp
new file mode 100644 (file)
index 0000000..708bf07
--- /dev/null
@@ -0,0 +1,409 @@
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+/*
+ * Most of this code is taken from thread.c in the Video Disk Recorder ('VDR')
+ */
+
+#include "tools.h"
+#include "thread.h"
+#include <errno.h>
+#ifndef __APPLE__
+#include <malloc.h>
+#endif
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include "StdString.h"
+
+static bool GetAbsTime(struct timespec *Abstime, int MillisecondsFromNow)
+{
+  struct timeval now;
+  if (gettimeofday(&now, NULL) == 0) {           // get current time
+     now.tv_sec  += MillisecondsFromNow / 1000;  // add full seconds
+     now.tv_usec += (MillisecondsFromNow % 1000) * 1000;  // add microseconds
+     if (now.tv_usec >= 1000000) {               // take care of an overflow
+        now.tv_sec++;
+        now.tv_usec -= 1000000;
+        }
+     Abstime->tv_sec = now.tv_sec;          // seconds
+     Abstime->tv_nsec = now.tv_usec * 1000; // nano seconds
+     return true;
+     }
+  return false;
+}
+
+// --- cCondWait -------------------------------------------------------------
+
+cCondWait::cCondWait(void)
+{
+  signaled = false;
+  pthread_mutex_init(&mutex, NULL);
+  pthread_cond_init(&cond, NULL);
+}
+
+cCondWait::~cCondWait()
+{
+  pthread_cond_broadcast(&cond); // wake up any sleepers
+  pthread_cond_destroy(&cond);
+  pthread_mutex_destroy(&mutex);
+}
+
+void cCondWait::SleepMs(int TimeoutMs)
+{
+  cCondWait w;
+  w.Wait(max(TimeoutMs, 3)); // making sure the time is >2ms to avoid a possible busy wait
+}
+
+bool cCondWait::Wait(int TimeoutMs)
+{
+  pthread_mutex_lock(&mutex);
+  if (!signaled) {
+     if (TimeoutMs) {
+        struct timespec abstime;
+        if (GetAbsTime(&abstime, TimeoutMs)) {
+           while (!signaled) {
+                 if (pthread_cond_timedwait(&cond, &mutex, &abstime) == ETIMEDOUT)
+                    break;
+                 }
+           }
+        }
+     else
+        pthread_cond_wait(&cond, &mutex);
+     }
+  bool r = signaled;
+  signaled = false;
+  pthread_mutex_unlock(&mutex);
+  return r;
+}
+
+void cCondWait::Signal(void)
+{
+  pthread_mutex_lock(&mutex);
+  signaled = true;
+  pthread_cond_broadcast(&cond);
+  pthread_mutex_unlock(&mutex);
+}
+
+// --- cCondVar --------------------------------------------------------------
+
+cCondVar::cCondVar(void)
+{
+  pthread_cond_init(&cond, 0);
+}
+
+cCondVar::~cCondVar()
+{
+  pthread_cond_broadcast(&cond); // wake up any sleepers
+  pthread_cond_destroy(&cond);
+}
+
+void cCondVar::Wait(cMutex &Mutex)
+{
+  if (Mutex.locked) {
+     int locked = Mutex.locked;
+     Mutex.locked = 0; // have to clear the locked count here, as pthread_cond_wait
+                       // does an implicit unlock of the mutex
+     pthread_cond_wait(&cond, &Mutex.mutex);
+     Mutex.locked = locked;
+     }
+}
+
+bool cCondVar::TimedWait(cMutex &Mutex, int TimeoutMs)
+{
+  bool r = true; // true = condition signaled, false = timeout
+
+  if (Mutex.locked) {
+     struct timespec abstime;
+     if (GetAbsTime(&abstime, TimeoutMs)) {
+        int locked = Mutex.locked;
+        Mutex.locked = 0; // have to clear the locked count here, as pthread_cond_timedwait
+                          // does an implicit unlock of the mutex.
+        if (pthread_cond_timedwait(&cond, &Mutex.mutex, &abstime) == ETIMEDOUT)
+           r = false;
+        Mutex.locked = locked;
+        }
+     }
+  return r;
+}
+
+void cCondVar::Broadcast(void)
+{
+  pthread_cond_broadcast(&cond);
+}
+
+// --- cMutex ----------------------------------------------------------------
+
+cMutex::cMutex(void)
+{
+  locked = 0;
+  pthread_mutexattr_t attr;
+  pthread_mutexattr_init(&attr);
+#ifndef __APPLE__
+  pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK_NP);
+#else
+  pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
+#endif
+  pthread_mutex_init(&mutex, &attr);
+}
+
+cMutex::~cMutex()
+{
+  pthread_mutex_destroy(&mutex);
+}
+
+void cMutex::Lock(void)
+{
+  pthread_mutex_lock(&mutex);
+  locked++;
+}
+
+void cMutex::Unlock(void)
+{
+ if (!--locked)
+    pthread_mutex_unlock(&mutex);
+}
+
+// --- cThread ---------------------------------------------------------------
+
+tThreadId cThread::mainThreadId = 0;
+
+cThread::cThread(const char *Description)
+{
+  active = running = false;
+#if !defined(__WINDOWS__)
+  childTid = 0;
+#endif
+  childThreadId = 0;
+  description = NULL;
+  if (Description)
+     SetDescription("%s", Description);
+}
+
+cThread::~cThread()
+{
+  Cancel(); // just in case the derived class didn't call it
+  free(description);
+}
+
+void cThread::SetPriority(int Priority)
+{
+#if !defined(__WINDOWS__)
+  if (setpriority(PRIO_PROCESS, 0, Priority) < 0)
+     XBMC->Log(LOG_ERROR, "ERROR (%s,%d): %m", __FILE__, __LINE__);
+#endif
+}
+
+void cThread::SetIOPriority(int Priority)
+{
+#if !defined(__WINDOWS__)
+#ifdef HAVE_LINUXIOPRIO
+  if (syscall(SYS_ioprio_set, 1, 0, (Priority & 0xff) | (2 << 13)) < 0) // best effort class
+     XBMC->Log(LOG_ERROR, "ERROR (%s,%d): %m", __FILE__, __LINE__);
+#endif
+#endif
+}
+
+void cThread::SetDescription(const char *Description, ...)
+{
+  free(description);
+  description = NULL;
+  if (Description)
+  {
+     va_list ap;
+     va_start(ap, Description);
+     CStdString desc;
+     desc.FormatV(Description, ap);
+     description = strdup(desc.c_str());
+     va_end(ap);
+  }
+}
+
+void *cThread::StartThread(cThread *Thread)
+{
+  Thread->childThreadId = ThreadId();
+  if (Thread->description) {
+     XBMC->Log(LOG_DEBUG, "%s thread started (pid=%d, tid=%d)", Thread->description, getpid(), Thread->childThreadId);
+#ifdef PR_SET_NAME
+     if (prctl(PR_SET_NAME, Thread->description, 0, 0, 0) < 0)
+        XBMC->Log(LOG_ERROR, "%s thread naming failed (pid=%d, tid=%d)", Thread->description, getpid(), Thread->childThreadId);
+#endif
+     }
+  Thread->Action();
+  if (Thread->description)
+     XBMC->Log(LOG_DEBUG, "%s thread ended (pid=%d, tid=%d)", Thread->description, getpid(), Thread->childThreadId);
+  Thread->running = false;
+  Thread->active = false;
+  return NULL;
+}
+
+#define THREAD_STOP_TIMEOUT  3000 // ms to wait for a thread to stop before newly starting it
+#define THREAD_STOP_SLEEP      30 // ms to sleep while waiting for a thread to stop
+
+bool cThread::Start(void)
+{
+  if (!running) {
+     if (active) {
+        // Wait until the previous incarnation of this thread has completely ended
+        // before starting it newly:
+        cTimeMs RestartTimeout;
+        while (!running && active && RestartTimeout.Elapsed() < THREAD_STOP_TIMEOUT)
+              cCondWait::SleepMs(THREAD_STOP_SLEEP);
+        }
+     if (!active) {
+        active = running = true;
+        if (pthread_create(&childTid, NULL, (void *(*) (void *))&StartThread, (void *)this) == 0) {
+           pthread_detach(childTid); // auto-reap
+           }
+        else {
+           XBMC->Log(LOG_ERROR, "ERROR (%s,%d): %m", __FILE__, __LINE__);
+           active = running = false;
+           return false;
+           }
+        }
+     }
+  return true;
+}
+
+bool cThread::Active(void)
+{
+  if (active) {
+     //
+     // Single UNIX Spec v2 says:
+     //
+     // The pthread_kill() function is used to request
+     // that a signal be delivered to the specified thread.
+     //
+     // As in kill(), if sig is zero, error checking is
+     // performed but no signal is actually sent.
+     //
+     int err;
+     if ((err = pthread_kill(childTid, 0)) != 0) {
+        if (err != ESRCH)
+           XBMC->Log(LOG_ERROR, "ERROR (%s,%d): %m", __FILE__, __LINE__);
+#if !defined(__WINDOWS__)
+        childTid = 0;
+#endif
+        active = running = false;
+        }
+     else
+        return true;
+     }
+  return false;
+}
+
+void cThread::Cancel(int WaitSeconds)
+{
+  running = false;
+  if (active && WaitSeconds > -1)
+  {
+    if (WaitSeconds > 0)
+    {
+      for (time_t t0 = time(NULL) + WaitSeconds; time(NULL) < t0; )
+      {
+        if (!Active())
+          return;
+        cCondWait::SleepMs(10);
+      }
+      XBMC->Log(LOG_ERROR, "ERROR: %s thread %d won't end (waited %d seconds) - canceling it...", description ? description : "", childThreadId, WaitSeconds);
+    }
+    pthread_cancel(childTid);
+#if !defined(__WINDOWS__)
+    childTid = 0;
+#endif
+    active = false;
+  }
+}
+
+tThreadId cThread::ThreadId(void)
+{
+#ifdef __APPLE__
+    return (int)pthread_self();
+#else
+#ifdef __WINDOWS__
+  return GetCurrentThreadId();
+#else
+  return syscall(__NR_gettid);
+#endif
+#endif
+}
+
+void cThread::SetMainThreadId(void)
+{
+  if (mainThreadId == 0)
+     mainThreadId = ThreadId();
+  else
+     XBMC->Log(LOG_ERROR, "ERROR: attempt to set main thread id to %d while it already is %d", ThreadId(), mainThreadId);
+}
+
+// --- cMutexLock ------------------------------------------------------------
+
+cMutexLock::cMutexLock(cMutex *Mutex)
+{
+  mutex = NULL;
+  locked = false;
+  Lock(Mutex);
+}
+
+cMutexLock::~cMutexLock()
+{
+  if (mutex && locked)
+    mutex->Unlock();
+}
+
+bool cMutexLock::Lock(cMutex *Mutex)
+{
+  if (Mutex && !mutex)
+  {
+    mutex = Mutex;
+    Mutex->Lock();
+    locked = true;
+    return true;
+  }
+  return false;
+}
+
+// --- cThreadLock -----------------------------------------------------------
+
+cThreadLock::cThreadLock(cThread *Thread)
+{
+  thread = NULL;
+  locked = false;
+  Lock(Thread);
+}
+
+cThreadLock::~cThreadLock()
+{
+  if (thread && locked)
+    thread->Unlock();
+}
+
+bool cThreadLock::Lock(cThread *Thread)
+{
+  if (Thread && !thread)
+  {
+  thread = Thread;
+  Thread->Lock();
+  locked = true;
+  return true;
+  }
+  return false;
+}
diff --git a/xbmc/pvrclients/vdr-streamdev/thread.h b/xbmc/pvrclients/vdr-streamdev/thread.h
new file mode 100644 (file)
index 0000000..960a6e9
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef __THREAD_H
+#define __THREAD_H
+
+#include <stdio.h>
+#include <sys/types.h>
+#include "tools.h"
+
+class cCondWait {
+private:
+  pthread_mutex_t mutex;
+  pthread_cond_t cond;
+  bool signaled;
+public:
+  cCondWait(void);
+  ~cCondWait();
+  static void SleepMs(int TimeoutMs);
+       ///< Creates a cCondWait object and uses it to sleep for TimeoutMs
+       ///< milliseconds, immediately giving up the calling thread's time
+       ///< slice and thus avoiding a "busy wait".
+       ///< In order to avoid a possible busy wait, TimeoutMs will be automatically
+       ///< limited to values >2.
+  bool Wait(int TimeoutMs = 0);
+       ///< Waits at most TimeoutMs milliseconds for a call to Signal(), or
+       ///< forever if TimeoutMs is 0.
+       ///< \return Returns true if Signal() has been called, false it the given
+       ///< timeout has expired.
+  void Signal(void);
+       ///< Signals a caller of Wait() that the condition it is waiting for is met.
+  };
+
+class cMutex;
+
+class cCondVar {
+private:
+  pthread_cond_t cond;
+public:
+  cCondVar(void);
+  ~cCondVar();
+  void Wait(cMutex &Mutex);
+  bool TimedWait(cMutex &Mutex, int TimeoutMs);
+  void Broadcast(void);
+  };
+
+class cMutex {
+  friend class cCondVar;
+private:
+  pthread_mutex_t mutex;
+  int locked;
+public:
+  cMutex(void);
+  ~cMutex();
+  void Lock(void);
+  void Unlock(void);
+  };
+
+typedef pid_t tThreadId;
+
+class cThread {
+  friend class cThreadLock;
+private:
+  bool active;
+  bool running;
+  pthread_t childTid;
+  tThreadId childThreadId;
+  cMutex mutex;
+  char *description;
+  static tThreadId mainThreadId;
+  static void *StartThread(cThread *Thread);
+protected:
+  void SetPriority(int Priority);
+  void SetIOPriority(int Priority);
+  void Lock(void) { mutex.Lock(); }
+  void Unlock(void) { mutex.Unlock(); }
+  virtual void Action(void) = 0;
+       ///< A derived cThread class must implement the code it wants to
+       ///< execute as a separate thread in this function. If this is
+       ///< a loop, it must check Running() repeatedly to see whether
+       ///< it's time to stop.
+  bool Running(void) { return running; }
+       ///< Returns false if a derived cThread object shall leave its Action()
+       ///< function.
+  void Cancel(int WaitSeconds = 0);
+       ///< Cancels the thread by first setting 'running' to false, so that
+       ///< the Action() loop can finish in an orderly fashion and then waiting
+       ///< up to WaitSeconds seconds for the thread to actually end. If the
+       ///< thread doesn't end by itself, it is killed.
+       ///< If WaitSeconds is -1, only 'running' is set to false and Cancel()
+       ///< returns immediately, without killing the thread.
+public:
+  cThread(const char *Description = NULL);
+       ///< Creates a new thread.
+       ///< If Description is present, a log file entry will be made when
+       ///< the thread starts and stops. The Start() function must be called
+       ///< to actually start the thread.
+  virtual ~cThread();
+#ifdef __WINDOWS__
+  void SetDescription(const char *Description, ...);
+#else
+  void SetDescription(const char *Description, ...) __attribute__ ((format (printf, 2, 3)));
+#endif
+  bool Start(void);
+       ///< Actually starts the thread.
+       ///< If the thread is already running, nothing happens.
+  bool Active(void);
+       ///< Checks whether the thread is still alive.
+  static tThreadId ThreadId(void);
+  static tThreadId IsMainThread(void) { return ThreadId() == mainThreadId; }
+  static void SetMainThreadId(void);
+  };
+
+// cMutexLock can be used to easily set a lock on mutex and make absolutely
+// sure that it will be unlocked when the block will be left. Several locks can
+// be stacked, so a function that makes many calls to another function which uses
+// cMutexLock may itself use a cMutexLock to make one longer lock instead of many
+// short ones.
+
+class cMutexLock {
+private:
+  cMutex *mutex;
+  bool locked;
+public:
+  cMutexLock(cMutex *Mutex = NULL);
+  ~cMutexLock();
+  bool Lock(cMutex *Mutex);
+  };
+
+// cThreadLock can be used to easily set a lock in a thread and make absolutely
+// sure that it will be unlocked when the block will be left. Several locks can
+// be stacked, so a function that makes many calls to another function which uses
+// cThreadLock may itself use a cThreadLock to make one longer lock instead of many
+// short ones.
+
+class cThreadLock {
+private:
+  cThread *thread;
+  bool locked;
+public:
+  cThreadLock(cThread *Thread = NULL);
+  ~cThreadLock();
+  bool Lock(cThread *Thread);
+  };
+
+#define LOCK_THREAD cThreadLock ThreadLock(this)
+
+#endif //__THREAD_H
diff --git a/xbmc/pvrclients/vdr-streamdev/timers.cpp b/xbmc/pvrclients/vdr-streamdev/timers.cpp
new file mode 100644 (file)
index 0000000..e23cb63
--- /dev/null
@@ -0,0 +1,394 @@
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+/*
+ * This code is taken from timers.c in the Video Disk Recorder ('VDR')
+ */
+
+#include "tools.h"
+#include "timers.h"
+
+#define SECSINDAY  86400
+
+cTimer::cTimer()
+{
+  startTime   = 0;
+  stopTime    = 0;
+  index       = 0;
+  recording   = false;
+  pending     = false;
+  inVpsMargin = false;
+  flags       = tfActive;
+  weekdays    = 0;
+  aux         = NULL;
+}
+
+cTimer::cTimer(const PVR_TIMERINFO *Timer)
+{
+  aux       = NULL;
+  index     = Timer->index;
+  startTime = Timer->starttime;
+  stopTime  = Timer->endtime;
+  flags     = Timer->active;
+  if (Timer->repeat)
+  {
+    if (Timer->firstday)
+      day = Timer->firstday;
+    weekdays  = Timer->repeatflags;
+  }
+  else
+  {
+    day = 0;
+    weekdays = 0;
+  }
+  priority  = Timer->priority;
+  channel   = Timer->channelNum;
+  lifetime  = Timer->lifetime;
+
+  CStdString directory = Timer->directory;
+  directory.Replace('/','~');
+  if (directory[directory.size()-1] != '~') // this is a file
+   directory += "~";
+  strn0cpy(dir, directory.c_str(), 256);
+  strn0cpy(name, Timer->title, 256);
+  directory += Timer->title;
+  strn0cpy(file, directory.c_str(), 256);
+
+  struct tm tm_r;
+  struct tm *time = localtime_r(&startTime, &tm_r);
+  day = SetTime(startTime, 0);
+  start = time->tm_hour * 100 + time->tm_min;
+  time = localtime_r(&stopTime, &tm_r);
+  stop = time->tm_hour * 100 + time->tm_min;
+}
+
+cTimer::~cTimer()
+{
+  free(aux);
+}
+
+bool cTimer::Parse(const char *s)
+{
+  char channelbuffer[256];
+  char daybuffer[256];
+  char filebuffer[512];
+  free(aux);
+  aux = (char*) malloc(256);
+
+  char *s2 = NULL;
+  int l2 = strlen(s);
+  while (l2 > 0 && isspace(s[l2 - 1]))
+    l2--;
+
+  if (s[l2 - 1] == ':')
+  {
+    s2 = (char *)malloc(sizeof(char) * (l2 + 3));
+    strcat(strn0cpy(s2, s, l2 + 1), " \n");
+    s = s2;
+  }
+  bool result = false;
+  if (9 <= sscanf(s, "%u %u :%[^:]:%[^:]:%d :%d :%d :%d :%[^:\n]:%[^\n]", &index, &flags, channelbuffer, daybuffer, &start, &stop, &priority, &lifetime, filebuffer, aux))
+  {
+    if (aux && !*skipspace(aux))
+    {
+      free(aux);
+      aux = NULL;
+    }
+    //TODO add more plausibility checks
+    result = ParseDay(daybuffer, day, weekdays);
+
+    CStdString fileName = filebuffer;
+    fileName.Replace('/', '_');
+    fileName.Replace('\\', '_');
+    fileName.Replace('?', '_');
+#if defined(_WIN32) || defined(_WIN64)
+    // just filter out some illegal characters on windows
+    fileName.Replace(':', '_');
+    fileName.Replace('*', '_');
+    fileName.Replace('?', '_');
+    fileName.Replace('\"', '_');
+    fileName.Replace('<', '_');
+    fileName.Replace('>', '_');
+    fileName.Replace('|', '_');
+    fileName.TrimRight(".");
+    fileName.TrimRight(" ");
+#endif
+    size_t found = fileName.find_last_of("~");
+    if (found != CStdString::npos)
+    {
+      CStdString directory = fileName.substr(0,found);
+      directory.Replace('~','/');
+      strn0cpy(dir, directory.c_str(), 256);
+      strn0cpy(name, fileName.substr(found+1).c_str(), 256);
+    }
+    else
+    {
+      dir[0] = 0;
+      strn0cpy(name, fileName.c_str(), 256);
+    }
+
+    strn0cpy(file, filebuffer, 256);
+    strreplace(file, '|', ':');
+
+    if (IsNumber(channelbuffer))
+      channel = atoi(channelbuffer);
+    else
+    {
+      XBMC->Log(LOG_ERROR, "PCRClient-vdr: channel not defined for timer");
+      result = false;
+    }
+  }
+  free(s2);
+  return result;
+}
+
+bool cTimer::ParseDay(const char *s, time_t &Day, int &WeekDays)
+{
+  // possible formats are:
+  // 19
+  // 2005-03-19
+  // MTWTFSS
+  // MTWTFSS@19
+  // MTWTFSS@2005-03-19
+
+  Day = 0;
+  WeekDays = 0;
+  s = skipspace(s);
+  if (!*s)
+    return false;
+  const char *a = strchr(s, '@');
+  const char *d = a ? a + 1 : isdigit(*s) ? s : NULL;
+  if (d)
+  {
+    if (strlen(d) == 10)
+    {
+      struct tm tm_r;
+      if (3 == sscanf(d, "%d-%d-%d", &tm_r.tm_year, &tm_r.tm_mon, &tm_r.tm_mday))
+      {
+        tm_r.tm_year -= 1900;
+        tm_r.tm_mon--;
+        tm_r.tm_hour = tm_r.tm_min = tm_r.tm_sec = 0;
+        tm_r.tm_isdst = -1; // makes sure mktime() will determine the correct DST setting
+        Day = mktime(&tm_r);
+      }
+      else
+        return false;
+    }
+    else
+    {
+      // handle "day of month" for compatibility with older versions:
+      char *tail = NULL;
+      int day = strtol(d, &tail, 10);
+      if (tail && *tail || day < 1 || day > 31)
+      return false;
+      time_t t = time(NULL);
+      int DaysToCheck = 61; // 61 to handle months with 31/30/31
+      for (int i = -1; i <= DaysToCheck; i++)
+      {
+        time_t t0 = IncDay(t, i);
+        if (GetMDay(t0) == day)
+        {
+          Day = SetTime(t0, 0);
+          break;
+        }
+      }
+    }
+  }
+  if (a || !isdigit(*s))
+  {
+    if ((a && a - s == 7) || strlen(s) == 7)
+    {
+      for (const char *p = s + 6; p >= s; p--)
+      {
+        WeekDays <<= 1;
+        WeekDays |= (*p != '-');
+      }
+    }
+    else
+      return false;
+  }
+  return true;
+}
+
+bool cTimer::IsSingleEvent(void) const
+{
+  return !weekdays;
+}
+
+int cTimer::GetMDay(time_t t)
+{
+  struct tm tm_r;
+  return localtime_r(&t, &tm_r)->tm_mday;
+}
+
+int cTimer::GetWDay(time_t t)
+{
+  struct tm tm_r;
+  int weekday = localtime_r(&t, &tm_r)->tm_wday;
+  return weekday == 0 ? 6 : weekday - 1; // we start with Monday==0!
+}
+
+bool cTimer::DayMatches(time_t t) const
+{
+  return IsSingleEvent() ? SetTime(t, 0) == day : (weekdays & (1 << GetWDay(t))) != 0;
+}
+
+time_t cTimer::IncDay(time_t t, int Days)
+{
+  struct tm tm_r;
+  tm tm = *localtime_r(&t, &tm_r);
+  tm.tm_mday += Days; // now tm_mday may be out of its valid range
+  int h = tm.tm_hour; // save original hour to compensate for DST change
+  tm.tm_isdst = -1;   // makes sure mktime() will determine the correct DST setting
+  t = mktime(&tm);    // normalize all values
+  tm.tm_hour = h;     // compensate for DST change
+  return mktime(&tm); // calculate final result
+}
+
+time_t cTimer::SetTime(time_t t, int SecondsFromMidnight)
+{
+  struct tm tm_r;
+  tm tm = *localtime_r(&t, &tm_r);
+  tm.tm_hour = SecondsFromMidnight / 3600;
+  tm.tm_min = (SecondsFromMidnight % 3600) / 60;
+  tm.tm_sec =  SecondsFromMidnight % 60;
+  tm.tm_isdst = -1; // makes sure mktime() will determine the correct DST setting
+  return mktime(&tm);
+}
+
+void cTimer::ClrFlags(unsigned int Flags)
+{
+  flags &= ~Flags;
+}
+
+void cTimer::InvFlags(unsigned int Flags)
+{
+  flags ^= Flags;
+}
+
+bool cTimer::HasFlags(unsigned int Flags) const
+{
+  return (flags & Flags) == Flags;
+}
+
+bool cTimer::Matches(time_t t, bool Directly, int Margin) const
+{
+  startTime = stopTime = 0;
+  if (t == 0)
+    t = time(NULL);
+
+  int begin  = TimeToInt(start); // seconds from midnight
+  int length = TimeToInt(stop) - begin;
+  if (length < 0)
+    length += SECSINDAY;
+
+  if (IsSingleEvent())
+  {
+    startTime = SetTime(day, begin);
+    stopTime = startTime + length;
+  }
+  else
+  {
+    for (int i = -1; i <= 7; i++)
+    {
+      time_t t0 = IncDay(day ? max(day, t) : t, i);
+      if (DayMatches(t0))
+      {
+        time_t a = SetTime(t0, begin);
+        time_t b = a + length;
+        if ((!day || a >= day) && t < b)
+        {
+          startTime = a;
+          stopTime = b;
+          break;
+        }
+      }
+    }
+    if (!startTime)
+      startTime = IncDay(t, 7); // just to have something that's more than a week in the future
+    else if (!Directly && (t > startTime || t > day + SECSINDAY + 3600)) // +3600 in case of DST change
+      day = 0;
+  }
+
+  if (HasFlags(tfActive))
+  {
+    return startTime <= t + Margin && t < stopTime; // must stop *before* stopTime to allow adjacent timers
+  }
+  return false;
+}
+
+time_t cTimer::StartTime(void) const
+{
+  if (!startTime)
+     Matches();
+  return startTime;
+}
+
+time_t cTimer::StopTime(void) const
+{
+  if (!stopTime)
+     Matches();
+  return stopTime;
+}
+
+int cTimer::TimeToInt(int t)
+{
+  return (t / 100 * 60 + t % 100) * 60;
+}
+
+CStdString cTimer::ToText() const
+{
+  strreplace(file, ':', '|');
+  CStdString buffer;
+  buffer.Format("%u:%u:%s:%04d:%04d:%d:%d:%s:%s", flags, channel, PrintDay(day, weekdays), start, stop, priority, lifetime, file, aux ? aux : "");
+  strreplace(file, '|', ':');
+  return buffer;
+}
+
+CStdString cTimer::PrintDay(time_t Day, int WeekDays)
+{
+#define DAYBUFFERSIZE 64
+  char buffer[DAYBUFFERSIZE];
+  char *b = buffer;
+  if (WeekDays)
+  {
+    const char *w = "MTWTFSS";
+    for (int i = 0; i < 7; i++)
+    {
+      if (WeekDays & 1)
+        *b++ = w[i];
+      else
+        *b++ = '-';
+      WeekDays >>= 1;
+    }
+    if (Day)
+      *b++ = '@';
+  }
+  if (Day)
+  {
+    struct tm tm_r;
+    localtime_r(&Day, &tm_r);
+    b += strftime(b, DAYBUFFERSIZE - (b - buffer), "%Y-%m-%d", &tm_r);
+  }
+  *b = 0;
+  return buffer;
+}
+
diff --git a/xbmc/pvrclients/vdr-streamdev/timers.h b/xbmc/pvrclients/vdr-streamdev/timers.h
new file mode 100644 (file)
index 0000000..91a2bf1
--- /dev/null
@@ -0,0 +1,100 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef __TIMERS_H
+#define __TIMERS_H
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "StdString.h"
+
+enum eTimerFlags { tfNone      = 0x0000,
+                   tfActive    = 0x0001,
+                   tfInstant   = 0x0002,
+                   tfVps       = 0x0004,
+                   tfRecording = 0x0008,
+                   tfAll       = 0xFFFF,
+                 };
+
+class cTimer
+{
+private:
+  mutable time_t startTime, stopTime;
+  time_t lastSetEvent;
+  bool recording, pending, inVpsMargin;
+  unsigned int flags;
+  mutable time_t day; ///< midnight of the day this timer shall hit, or of the first day it shall hit in case of a repeating timer
+  int weekdays;       ///< bitmask, lowest bits: SSFTWTM  (the 'M' is the LSB)
+  int start;
+  int stop;
+  int priority;
+  int channel;
+  int lifetime;
+  mutable char file[256];
+  mutable char name[256];
+  mutable char dir[256];
+  int index;
+  char *aux;
+
+public:
+  cTimer(const PVR_TIMERINFO *Timer);
+  cTimer();
+  virtual ~cTimer();
+
+  int Index(void) const { return index; }
+  bool Recording(void) const { return recording; }
+  bool Pending(void) const { return pending; }
+  bool InVpsMargin(void) const { return inVpsMargin; }
+  unsigned int Flags(void) const { return flags; }
+  unsigned int Channel(void) const { return channel; }
+  time_t Day(void) const { return day; }
+  int WeekDays(void) const { return weekdays; }
+  int Start(void) const { return start; }
+  int Stop(void) const { return stop; }
+  int Priority(void) const { return priority; }
+  int Lifetime(void) const { return lifetime; }
+  time_t FirstDay(void) const { return weekdays ? day : 0; }
+  const char *Aux(void) const { return aux; }
+  const char *File(void) const { return file; }
+  const char *Title(void) const { return name; }
+  const char *Dir(void) const { return dir; }
+  bool Matches(time_t t = 0, bool Directly = false, int Margin = 0) const;
+  time_t StartTime(void) const;
+  time_t StopTime(void) const;
+  bool Parse(const char *s);
+  bool IsSingleEvent(void) const;
+  static bool ParseDay(const char *s, time_t &Day, int &WeekDays);
+  static int GetMDay(time_t t);
+  static int GetWDay(time_t t);
+  bool DayMatches(time_t t) const;
+  static time_t IncDay(time_t t, int Days);
+  static time_t SetTime(time_t t, int SecondsFromMidnight);
+  void ClrFlags(unsigned int Flags);
+  void InvFlags(unsigned int Flags);
+  bool HasFlags(unsigned int Flags) const;
+  static int TimeToInt(int t);
+  CStdString ToText() const;
+  static CStdString PrintDay(time_t Day, int WeekDays);
+};
+
+#endif //__TIMERS_H
diff --git a/xbmc/pvrclients/vdr-streamdev/tools.cpp b/xbmc/pvrclients/vdr-streamdev/tools.cpp
new file mode 100644 (file)
index 0000000..0b2828d
--- /dev/null
@@ -0,0 +1,715 @@
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+/*
+ * Most of this code is taken from tools.c in the Video Disk Recorder ('VDR')
+ */
+
+#include "tools.h"
+
+ssize_t safe_read(int filedes, void *buffer, size_t size)
+{
+  for (;;) {
+      ssize_t p = __read(filedes, buffer, size);
+      if (p < 0 && errno == EINTR) {
+         XBMC->Log(LOG_DEBUG, "EINTR while reading from file handle %d - retrying", filedes);
+         continue;
+         }
+      return p;
+      }
+}
+
+ssize_t safe_write(int filedes, const void *buffer, size_t size)
+{
+  ssize_t p = 0;
+  ssize_t written = size;
+  const unsigned char *ptr = (const unsigned char *)buffer;
+  while (size > 0) {
+        p = __write(filedes, ptr, size);
+        if (p < 0) {
+           if (errno == EINTR) {
+              XBMC->Log(LOG_DEBUG, "EINTR while writing to file handle %d - retrying", filedes);
+              continue;
+              }
+           break;
+           }
+        ptr  += p;
+        size -= p;
+        }
+  return p < 0 ? p : written;
+}
+
+void writechar(int filedes, char c)
+{
+  safe_write(filedes, &c, sizeof(c));
+}
+
+int WriteAllOrNothing(int fd, const unsigned char *Data, int Length, int TimeoutMs, int RetryMs)
+{
+  int written = 0;
+  while (Length > 0) {
+        int w = write(fd, Data + written, Length);
+        if (w > 0) {
+           Length -= w;
+           written += w;
+           }
+        else if (written > 0 && !FATALERRNO) {
+           // we've started writing, so we must finish it!
+           cTimeMs t;
+           cPoller Poller(fd, true);
+           Poller.Poll(RetryMs);
+           if (TimeoutMs > 0 && (TimeoutMs -= t.Elapsed()) <= 0)
+              break;
+           }
+        else
+           // nothing written yet (or fatal error), so we can just return the error code:
+           return w;
+        }
+  return written;
+}
+
+char *strcpyrealloc(char *dest, const char *src)
+{
+  if (src) {
+     int l = max(dest ? strlen(dest) : 0, strlen(src)) + 1; // don't let the block get smaller!
+     dest = (char *)realloc(dest, l);
+     if (dest)
+        strcpy(dest, src);
+     else
+        XBMC->Log(LOG_ERROR, "ERROR: out of memory");
+     }
+  else {
+     free(dest);
+     dest = NULL;
+     }
+  return dest;
+}
+
+char *strn0cpy(char *dest, const char *src, size_t n)
+{
+  char *s = dest;
+  for ( ; --n && (*dest = *src) != 0; dest++, src++) ;
+  *dest = 0;
+  return s;
+}
+
+char *strreplace(char *s, char c1, char c2)
+{
+  if (s) {
+     char *p = s;
+     while (*p) {
+           if (*p == c1)
+              *p = c2;
+           p++;
+           }
+     }
+  return s;
+}
+
+char *strreplace(char *s, const char *s1, const char *s2)
+{
+  char *p = strstr(s, s1);
+  if (p) {
+     int of = p - s;
+     int l  = strlen(s);
+     int l1 = strlen(s1);
+     int l2 = strlen(s2);
+     if (l2 > l1)
+        s = (char *)realloc(s, l + l2 - l1 + 1);
+     char *sof = s + of;
+     if (l2 != l1)
+        memmove(sof + l2, sof + l1, l - of - l1 + 1);
+     strncpy(sof, s2, l2);
+     }
+  return s;
+}
+
+char *stripspace(char *s)
+{
+  if (s && *s) {
+     for (char *p = s + strlen(s) - 1; p >= s; p--) {
+         if (!isspace(*p))
+            break;
+         *p = 0;
+         }
+     }
+  return s;
+}
+
+char *compactspace(char *s)
+{
+  if (s && *s) {
+     char *t = stripspace(skipspace(s));
+     char *p = t;
+     while (p && *p) {
+           char *q = skipspace(p);
+           if (q - p > 1)
+              memmove(p + 1, q, strlen(q) + 1);
+           p++;
+           }
+     if (t != s)
+        memmove(s, t, strlen(t) + 1);
+     }
+  return s;
+}
+
+bool startswith(const char *s, const char *p)
+{
+  while (*p) {
+        if (*p++ != *s++)
+           return false;
+        }
+  return true;
+}
+
+bool endswith(const char *s, const char *p)
+{
+  const char *se = s + strlen(s) - 1;
+  const char *pe = p + strlen(p) - 1;
+  while (pe >= p) {
+        if (*pe-- != *se-- || (se < s && pe >= p))
+           return false;
+        }
+  return true;
+}
+
+bool isempty(const char *s)
+{
+  return !(s && *skipspace(s));
+}
+
+int numdigits(int n)
+{
+  int res = 1;
+  while (n >= 10) {
+        n /= 10;
+        res++;
+        }
+  return res;
+}
+
+bool IsNumber(const char *s)
+{
+  if (!*s)
+     return false;
+  do {
+     if (!isdigit(*s))
+        return false;
+     } while (*++s);
+  return true;
+}
+
+CStdString AddDirectory(const char *DirName, const char *FileName)
+{
+  CStdString ret;
+  ret.Format("%s/%s", DirName && *DirName ? DirName : ".", FileName);
+  return ret;
+}
+
+char *ReadLink(const char *FileName)
+{
+#if defined(__WINDOWS__)
+  return NULL;
+#else
+  if (!FileName)
+    return NULL;
+  char *TargetName = NULL;
+  char *res = realpath(FileName, TargetName);
+  if (!res)
+  {
+    if (errno == ENOENT) // file doesn't exist
+      TargetName = strdup(FileName);
+    else // some other error occurred
+      XBMC->Log(LOG_ERROR, "ERROR (%s,%d,s): %m", __FILE__, __LINE__, FileName);
+  }
+  return TargetName;
+#endif
+}
+
+
+// --- cTimeMs ---------------------------------------------------------------
+
+cTimeMs::cTimeMs(int Ms)
+{
+  Set(Ms);
+}
+
+uint64_t cTimeMs::Now(void)
+{
+#if _POSIX_TIMERS > 0 && defined(_POSIX_MONOTONIC_CLOCK)
+#define MIN_RESOLUTION 5 // ms
+  static bool initialized = false;
+  static bool monotonic = false;
+  struct timespec tp;
+  if (!initialized) {
+     // check if monotonic timer is available and provides enough accurate resolution:
+     if (clock_getres(CLOCK_MONOTONIC, &tp) == 0) {
+        long Resolution = tp.tv_nsec;
+        // require a minimum resolution:
+        if (tp.tv_sec == 0 && tp.tv_nsec <= MIN_RESOLUTION * 1000000) {
+           if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0) {
+              XBMC->Log(LOG_DEBUG, "cTimeMs: using monotonic clock (resolution is %ld ns)", Resolution);
+              monotonic = true;
+              }
+           else
+              XBMC->Log(LOG_ERROR, "cTimeMs: clock_gettime(CLOCK_MONOTONIC) failed");
+           }
+        else
+           XBMC->Log(LOG_DEBUG, "cTimeMs: not using monotonic clock - resolution is too bad (%ld s %ld ns)", tp.tv_sec, tp.tv_nsec);
+        }
+     else
+        XBMC->Log(LOG_ERROR, "cTimeMs: clock_getres(CLOCK_MONOTONIC) failed");
+     initialized = true;
+     }
+  if (monotonic) {
+     if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0)
+        return (uint64_t(tp.tv_sec)) * 1000 + tp.tv_nsec / 1000000;
+     XBMC->Log(LOG_ERROR, "cTimeMs: clock_gettime(CLOCK_MONOTONIC) failed");
+     monotonic = false;
+     // fall back to gettimeofday()
+     }
+#else
+#if !defined(__WINDOWS__)
+#  warning Posix monotonic clock not available
+#endif
+#endif
+  struct timeval t;
+  if (gettimeofday(&t, NULL) == 0)
+     return (uint64_t(t.tv_sec)) * 1000 + t.tv_usec / 1000;
+  return 0;
+}
+
+void cTimeMs::Set(int Ms)
+{
+  begin = Now() + Ms;
+}
+
+bool cTimeMs::TimedOut(void)
+{
+  return Now() >= begin;
+}
+
+uint64_t cTimeMs::Elapsed(void)
+{
+  return Now() - begin;
+}
+
+// --- cPoller ---------------------------------------------------------------
+
+cPoller::cPoller(int FileHandle, bool Out)
+{
+  numFileHandles = 0;
+  Add(FileHandle, Out);
+}
+
+bool cPoller::Add(int FileHandle, bool Out)
+{
+  if (FileHandle >= 0) {
+     for (int i = 0; i < numFileHandles; i++) {
+         if (pfd[i].fd == FileHandle && pfd[i].events == (Out ? POLLOUT : POLLIN))
+            return true;
+         }
+     if (numFileHandles < MaxPollFiles) {
+        pfd[numFileHandles].fd = FileHandle;
+        pfd[numFileHandles].events = Out ? POLLOUT : POLLIN;
+        pfd[numFileHandles].revents = 0;
+        numFileHandles++;
+        return true;
+        }
+     XBMC->Log(LOG_ERROR, "ERROR: too many file handles in cPoller");
+     }
+  return false;
+}
+
+bool cPoller::Poll(int TimeoutMs)
+{
+  if (numFileHandles) {
+     if (__poll(pfd, numFileHandles, TimeoutMs) != 0)
+        return true; // returns true even in case of an error, to let the caller
+                     // access the file and thus see the error code
+     }
+  return false;
+}
+
+// --- cFile -----------------------------------------------------------------
+
+bool cFile::files[FD_SETSIZE] = { false };
+int cFile::maxFiles = 0;
+
+cFile::cFile(void)
+{
+  f = -1;
+}
+
+cFile::~cFile()
+{
+  Close();
+}
+
+bool cFile::Open(const char *FileName, int Flags, mode_t Mode)
+{
+  if (!IsOpen())
+#ifdef __WINDOWS__
+    return Open(open(FileName, Flags, 0));
+#else
+    return Open(open(FileName, Flags, Mode));
+#endif
+  XBMC->Log(LOG_ERROR, "ERROR: attempt to re-open %s", FileName);
+  return false;
+}
+
+bool cFile::Open(int FileDes)
+{
+  if (FileDes >= 0) {
+     if (!IsOpen()) {
+        f = FileDes;
+        if (f >= 0) {
+           if (f < FD_SETSIZE) {
+              if (f >= maxFiles)
+                 maxFiles = f + 1;
+              if (!files[f])
+                 files[f] = true;
+              else
+                 XBMC->Log(LOG_ERROR, "ERROR: file descriptor %d already in files[]", f);
+              return true;
+              }
+           else
+              XBMC->Log(LOG_ERROR, "ERROR: file descriptor %d is larger than FD_SETSIZE (%d)", f, FD_SETSIZE);
+           }
+        }
+     else
+        XBMC->Log(LOG_ERROR, "ERROR: attempt to re-open file descriptor %d", FileDes);
+     }
+  return false;
+}
+
+void cFile::Close(void)
+{
+  if (f >= 0) {
+     close(f);
+     files[f] = false;
+     f = -1;
+     }
+}
+
+bool cFile::Ready(bool Wait)
+{
+  return f >= 0 && AnyFileReady(f, Wait ? 1000 : 0);
+}
+
+bool cFile::AnyFileReady(int FileDes, int TimeoutMs)
+{
+  fd_set set;
+  FD_ZERO(&set);
+  for (int i = 0; i < maxFiles; i++) {
+      if (files[i])
+         FD_SET(i, &set);
+      }
+  if (0 <= FileDes && FileDes < FD_SETSIZE && !files[FileDes])
+     FD_SET(FileDes, &set); // in case we come in with an arbitrary descriptor
+  if (TimeoutMs == 0)
+     TimeoutMs = 10; // load gets too heavy with 0
+  struct timeval timeout;
+  timeout.tv_sec  = TimeoutMs / 1000;
+  timeout.tv_usec = (TimeoutMs % 1000) * 1000;
+  return select(FD_SETSIZE, &set, NULL, NULL, &timeout) > 0 && (FileDes < 0 || FD_ISSET(FileDes, &set));
+}
+
+bool cFile::FileReady(int FileDes, int TimeoutMs)
+{
+  fd_set set;
+  struct timeval timeout;
+  FD_ZERO(&set);
+  FD_SET(FileDes, &set);
+  if (TimeoutMs >= 0) {
+     if (TimeoutMs < 100)
+        TimeoutMs = 100;
+     timeout.tv_sec  = TimeoutMs / 1000;
+     timeout.tv_usec = (TimeoutMs % 1000) * 1000;
+     }
+  return select(FD_SETSIZE, &set, NULL, NULL, (TimeoutMs >= 0) ? &timeout : NULL) > 0 && FD_ISSET(FileDes, &set);
+}
+
+bool cFile::FileReadyForWriting(int FileDes, int TimeoutMs)
+{
+  fd_set set;
+  struct timeval timeout;
+  FD_ZERO(&set);
+  FD_SET(FileDes, &set);
+  if (TimeoutMs < 100)
+     TimeoutMs = 100;
+  timeout.tv_sec  = 0;
+  timeout.tv_usec = TimeoutMs * 1000;
+  return select(FD_SETSIZE, NULL, &set, NULL, &timeout) > 0 && FD_ISSET(FileDes, &set);
+}
+
+
+// --- cUnbufferedFile -------------------------------------------------------
+
+#if !defined(__WINDOWS__)
+#if !defined(__APPLE__)
+#define USE_FADVISE
+#endif
+#endif
+
+#define WRITE_BUFFER KILOBYTE(800)
+
+cUnbufferedFile::cUnbufferedFile(void)
+{
+  fd = -1;
+}
+
+cUnbufferedFile::~cUnbufferedFile()
+{
+  Close();
+}
+
+int cUnbufferedFile::Open(const char *FileName, int Flags, mode_t Mode)
+{
+  Close();
+  fd = open(FileName, Flags, Mode);
+  curpos = 0;
+#ifdef USE_FADVISE
+  begin = lastpos = ahead = 0;
+  cachedstart = 0;
+  cachedend = 0;
+  readahead = KILOBYTE(128);
+  written = 0;
+  totwritten = 0;
+  if (fd >= 0)
+     posix_fadvise(fd, 0, 0, POSIX_FADV_RANDOM); // we could use POSIX_FADV_SEQUENTIAL, but we do our own readahead, disabling the kernel one.
+#endif
+  return fd;
+}
+
+int cUnbufferedFile::Close(void)
+{
+  if (fd >= 0) {
+#ifdef USE_FADVISE
+     if (totwritten)    // if we wrote anything make sure the data has hit the disk before
+        fdatasync(fd);  // calling fadvise, as this is our last chance to un-cache it.
+     posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED);
+#endif
+     int OldFd = fd;
+     fd = -1;
+     return close(OldFd);
+     }
+  errno = EBADF;
+  return -1;
+}
+
+// When replaying and going e.g. FF->PLAY the position jumps back 2..8M
+// hence we do not want to drop recently accessed data at once.
+// We try to handle the common cases such as PLAY->FF->PLAY, small
+// jumps, moving editing marks etc.
+
+#define FADVGRAN   KILOBYTE(4) // AKA fadvise-chunk-size; PAGE_SIZE or getpagesize(2) would also work.
+#define READCHUNK  MEGABYTE(8)
+
+void cUnbufferedFile::SetReadAhead(size_t ra)
+{
+  readahead = ra;
+}
+
+int cUnbufferedFile::FadviseDrop(off_t Offset, off_t Len)
+{
+#ifdef USE_FADVISE
+  // rounding up the window to make sure that not PAGE_SIZE-aligned data gets freed.
+  return posix_fadvise(fd, Offset - (FADVGRAN - 1), Len + (FADVGRAN - 1) * 2, POSIX_FADV_DONTNEED);
+#else
+  return 0;
+#endif
+}
+
+off_t cUnbufferedFile::Seek(off_t Offset, int Whence)
+{
+  if (Whence == SEEK_SET && Offset == curpos)
+     return curpos;
+  curpos = lseek(fd, Offset, Whence);
+  return curpos;
+}
+
+ssize_t cUnbufferedFile::Read(void *Data, size_t Size)
+{
+  if (fd >= 0) {
+#ifdef USE_FADVISE
+     off_t jumped = curpos-lastpos; // nonzero means we're not at the last offset
+     if ((cachedstart < cachedend) && (curpos < cachedstart || curpos > cachedend)) {
+        // current position is outside the cached window -- invalidate it.
+        FadviseDrop(cachedstart, cachedend-cachedstart);
+        cachedstart = curpos;
+        cachedend = curpos;
+        }
+     cachedstart = min(cachedstart, curpos);
+#endif
+     ssize_t bytesRead = safe_read(fd, Data, Size);
+#ifdef USE_FADVISE
+     if (bytesRead > 0) {
+        curpos += bytesRead;
+        cachedend = max(cachedend, curpos);
+
+        // Read ahead:
+        // no jump? (allow small forward jump still inside readahead window).
+        if (jumped >= 0 && jumped <= (off_t)readahead) {
+           // Trigger the readahead IO, but only if we've used at least
+           // 1/2 of the previously requested area. This avoids calling
+           // fadvise() after every read() call.
+           if (ahead - curpos < (off_t)(readahead / 2)) {
+              posix_fadvise(fd, curpos, readahead, POSIX_FADV_WILLNEED);
+              ahead = curpos + readahead;
+              cachedend = max(cachedend, ahead);
+              }
+           if (readahead < Size * 32) { // automagically tune readahead size.
+              readahead = Size * 32;
+              }
+           }
+        else
+           ahead = curpos; // jumped -> we really don't want any readahead, otherwise e.g. fast-rewind gets in trouble.
+        }
+
+     if (cachedstart < cachedend) {
+        if (curpos - cachedstart > READCHUNK * 2) {
+           // current position has moved forward enough, shrink tail window.
+           FadviseDrop(cachedstart, curpos - READCHUNK - cachedstart);
+           cachedstart = curpos - READCHUNK;
+           }
+        else if (cachedend > ahead && cachedend - curpos > READCHUNK * 2) {
+           // current position has moved back enough, shrink head window.
+           FadviseDrop(curpos + READCHUNK, cachedend - (curpos + READCHUNK));
+           cachedend = curpos + READCHUNK;
+           }
+        }
+     lastpos = curpos;
+#endif
+     return bytesRead;
+     }
+  return -1;
+}
+
+ssize_t cUnbufferedFile::Write(const void *Data, size_t Size)
+{
+  if (fd >=0) {
+     ssize_t bytesWritten = safe_write(fd, Data, Size);
+#ifdef USE_FADVISE
+     if (bytesWritten > 0) {
+        begin = min(begin, curpos);
+        curpos += bytesWritten;
+        written += bytesWritten;
+        lastpos = max(lastpos, curpos);
+        if (written > WRITE_BUFFER) {
+           if (lastpos > begin) {
+              // Now do three things:
+              // 1) Start writeback of begin..lastpos range
+              // 2) Drop the already written range (by the previous fadvise call)
+              // 3) Handle nonpagealigned data.
+              //    This is why we double the WRITE_BUFFER; the first time around the
+              //    last (partial) page might be skipped, writeback will start only after
+              //    second call; the third call will still include this page and finally
+              //    drop it from cache.
+              off_t headdrop = min(begin, off_t(WRITE_BUFFER * 2));
+              posix_fadvise(fd, begin - headdrop, lastpos - begin + headdrop, POSIX_FADV_DONTNEED);
+              }
+           begin = lastpos = curpos;
+           totwritten += written;
+           written = 0;
+           // The above fadvise() works when writing slowly (recording), but could
+           // leave cached data around when writing at a high rate, e.g. when cutting,
+           // because by the time we try to flush the cached pages (above) the data
+           // can still be dirty - we are faster than the disk I/O.
+           // So we do another round of flushing, just like above, but at larger
+           // intervals -- this should catch any pages that couldn't be released
+           // earlier.
+           if (totwritten > MEGABYTE(32)) {
+              // It seems in some setups, fadvise() does not trigger any I/O and
+              // a fdatasync() call would be required do all the work (reiserfs with some
+              // kind of write gathering enabled), but the syncs cause (io) load..
+              // Uncomment the next line if you think you need them.
+              //fdatasync(fd);
+              off_t headdrop = min(off_t(curpos - totwritten), off_t(totwritten * 2));
+              posix_fadvise(fd, curpos - totwritten - headdrop, totwritten + headdrop, POSIX_FADV_DONTNEED);
+              totwritten = 0;
+              }
+           }
+        }
+#endif
+     return bytesWritten;
+     }
+  return -1;
+}
+
+cUnbufferedFile *cUnbufferedFile::Create(const char *FileName, int Flags, mode_t Mode)
+{
+  cUnbufferedFile *File = new cUnbufferedFile;
+  if (File->Open(FileName, Flags, Mode) < 0) {
+     delete File;
+     File = NULL;
+     }
+  return File;
+}
+
+// --- cReadDir --------------------------------------------------------------
+
+cReadDir::cReadDir(const char *Directory)
+{
+  directory = opendir(Directory);
+}
+
+cReadDir::~cReadDir()
+{
+  if (directory)
+    closedir(directory);
+}
+
+struct dirent *cReadDir::Next(void)
+{
+  return directory && readdir_r(directory, &u.d, &result) == 0 ? result : NULL;
+}
+
+// --- cReadLine -------------------------------------------------------------
+
+cReadLine::cReadLine(void)
+{
+  size = 0;
+  buffer = NULL;
+}
+
+cReadLine::~cReadLine()
+{
+  free(buffer);
+}
+
+char *cReadLine::Read(FILE *f)
+{
+  int n = getline(&buffer, &size, f);
+  if (n > 0) {
+     n--;
+     if (buffer[n] == '\n') {
+        buffer[n] = 0;
+        if (n > 0) {
+           n--;
+           if (buffer[n] == '\r')
+              buffer[n] = 0;
+           }
+        }
+     return buffer;
+     }
+  return NULL;
+}
diff --git a/xbmc/pvrclients/vdr-streamdev/tools.h b/xbmc/pvrclients/vdr-streamdev/tools.h
new file mode 100644 (file)
index 0000000..a4d78bd
--- /dev/null
@@ -0,0 +1,214 @@
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef __TOOLS_H
+#define __TOOLS_H
+
+#include "pvrclient-vdr_os.h"
+#include "client.h"
+#include "StdString.h"
+#include <errno.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <dirent.h>
+#if !defined(__WINDOWS__)
+#include <sys/stat.h>
+#endif
+
+#ifdef __APPLE__
+#include <osx/OSXGNUReplacements.h>
+#endif
+
+#define TS_SYNC_BYTE          0x47
+#define TS_SIZE               188
+#define TS_ERROR              0x80
+#define TS_PAYLOAD_START      0x40
+#define TS_TRANSPORT_PRIORITY 0x20
+#define TS_PID_MASK_HI        0x1F
+#define TS_SCRAMBLING_CONTROL 0xC0
+#define TS_ADAPT_FIELD_EXISTS 0x20
+#define TS_PAYLOAD_EXISTS     0x10
+#define TS_CONT_CNT_MASK      0x0F
+#define TS_ADAPT_DISCONT      0x80
+#define TS_ADAPT_RANDOM_ACC   0x40 // would be perfect for detecting independent frames, but unfortunately not used by all broadcasters
+#define TS_ADAPT_ELEM_PRIO    0x20
+#define TS_ADAPT_PCR          0x10
+#define TS_ADAPT_OPCR         0x08
+#define TS_ADAPT_SPLICING     0x04
+#define TS_ADAPT_TP_PRIVATE   0x02
+#define TS_ADAPT_EXTENSION    0x01
+
+#define ERRNUL(e) {errno=e;return 0;}
+#define ERRSYS(e) {errno=e;return -1;}
+
+#define SECSINDAY  86400
+
+#define KILOBYTE(n) ((n) * 1024)
+#define MEGABYTE(n) ((n) * 1024LL * 1024LL)
+
+#define MALLOC(type, size)  (type *)malloc(sizeof(type) * (size))
+
+#define DELETENULL(p) (delete (p), p = NULL)
+//
+//#define CHECK(s) { if ((s) < 0) LOG_ERROR; } // used for 'ioctl()' calls
+#define FATALERRNO (errno && errno != EAGAIN && errno != EINTR)
+
+ssize_t safe_read(int filedes, void *buffer, size_t size);
+ssize_t safe_write(int filedes, const void *buffer, size_t size);
+void writechar(int filedes, char c);
+int WriteAllOrNothing(int fd, const unsigned char *Data, int Length, int TimeoutMs = 0, int RetryMs = 0);
+    ///< Writes either all Data to the given file descriptor, or nothing at all.
+    ///< If TimeoutMs is greater than 0, it will only retry for that long, otherwise
+    ///< it will retry forever. RetryMs defines the time between two retries.
+char *strcpyrealloc(char *dest, const char *src);
+char *strn0cpy(char *dest, const char *src, size_t n);
+char *strreplace(char *s, char c1, char c2);
+char *strreplace(char *s, const char *s1, const char *s2); ///< re-allocates 's' and deletes the original string if necessary!
+inline char *skipspace(const char *s)
+{
+  if ((unsigned char)*s > ' ') // most strings don't have any leading space, so handle this case as fast as possible
+     return (char *)s;
+  while (*s && (unsigned char)*s <= ' ') // avoiding isspace() here, because it is much slower
+        s++;
+  return (char *)s;
+}
+char *stripspace(char *s);
+char *compactspace(char *s);
+bool startswith(const char *s, const char *p);
+bool endswith(const char *s, const char *p);
+bool isempty(const char *s);
+int numdigits(int n);
+bool IsNumber(const char *s);
+CStdString AddDirectory(const char *DirName, const char *FileName);
+char *ReadLink(const char *FileName); ///< returns a new string allocated on the heap, which the caller must delete (or NULL in case of an error)
+
+class cTimeMs
+{
+private:
+  uint64_t begin;
+public:
+  cTimeMs(int Ms = 0);
+      ///< Creates a timer with ms resolution and an initial timeout of Ms.
+  static uint64_t Now(void);
+  void Set(int Ms = 0);
+  bool TimedOut(void);
+  uint64_t Elapsed(void);
+};
+
+class cPoller
+{
+private:
+  enum { MaxPollFiles = 16 };
+  pollfd pfd[MaxPollFiles];
+  int numFileHandles;
+public:
+  cPoller(int FileHandle = -1, bool Out = false);
+  bool Add(int FileHandle, bool Out);
+  bool Poll(int TimeoutMs = 0);
+};
+
+class cFile
+{
+private:
+  static bool files[];
+  static int maxFiles;
+  int f;
+public:
+  cFile(void);
+  ~cFile();
+  operator int () { return f; }
+  bool Open(const char *FileName, int Flags, mode_t Mode = DEFFILEMODE);
+  bool Open(int FileDes);
+  void Close(void);
+  bool IsOpen(void) { return f >= 0; }
+  bool Ready(bool Wait = true);
+  static bool AnyFileReady(int FileDes = -1, int TimeoutMs = 1000);
+  static bool FileReady(int FileDes, int TimeoutMs = 1000);
+  static bool FileReadyForWriting(int FileDes, int TimeoutMs = 1000);
+};
+
+/// cUnbufferedFile is used for large files that are mainly written or read
+/// in a streaming manner, and thus should not be cached.
+
+class cUnbufferedFile
+{
+private:
+  int fd;
+  off_t curpos;
+  off_t cachedstart;
+  off_t cachedend;
+  off_t begin;
+  off_t lastpos;
+  off_t ahead;
+  size_t readahead;
+  size_t written;
+  size_t totwritten;
+  int FadviseDrop(off_t Offset, off_t Len);
+public:
+  cUnbufferedFile(void);
+  ~cUnbufferedFile();
+  int Open(const char *FileName, int Flags, mode_t Mode = DEFFILEMODE);
+  int Close(void);
+  void SetReadAhead(size_t ra);
+  off_t Seek(off_t Offset, int Whence);
+  ssize_t Read(void *Data, size_t Size);
+  ssize_t Write(const void *Data, size_t Size);
+  static cUnbufferedFile *Create(const char *FileName, int Flags, mode_t Mode = DEFFILEMODE);
+};
+
+class cReadDir
+{
+private:
+  DIR *directory;
+  struct dirent *result;
+  union // according to "The GNU C Library Reference Manual"
+  {
+    struct dirent d;
+    char b[offsetof(struct dirent, d_name) + NAME_MAX + 1];
+  } u;
+public:
+  cReadDir(const char *Directory);
+  ~cReadDir();
+  bool Ok(void) { return directory != NULL; }
+  struct dirent *Next(void);
+};
+
+class cReadLine
+{
+private:
+  size_t size;
+  char *buffer;
+public:
+  cReadLine(void);
+  ~cReadLine();
+  char *Read(FILE *f);
+};
+
+inline int CompareStrings(const void *a, const void *b)
+{
+  return strcmp(*(const char **)a, *(const char **)b);
+}
+
+
+#endif //__TOOLS_H
diff --git a/xbmc/pvrclients/vdr-streamdev/vtptransceiver.cpp b/xbmc/pvrclients/vdr-streamdev/vtptransceiver.cpp
new file mode 100644 (file)
index 0000000..457a367
--- /dev/null
@@ -0,0 +1,1433 @@
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include "vtptransceiver.h"
+#include "select.h"
+#include "client.h"
+#include "timers.h"
+#include "channels.h"
+#include "recordings.h"
+#include "epg.h"
+#include "client.h"
+
+//#define DEBUG_VTP
+#define MINLOGREPEAT    10 //don't log connect failures too often (seconds)
+#define MAX_LINK_LEVEL  6
+
+using namespace std;
+
+CVTPTransceiver VTPTransceiver;
+
+CVTPTransceiver::CVTPTransceiver()
+  : m_VTPSocket(INVALID_SOCKET)
+{
+  memset(m_DataSockets, INVALID_SOCKET, sizeof(SOCKET) * si_Count);
+}
+
+CVTPTransceiver::~CVTPTransceiver()
+{
+  Reset();
+  if (IsOpen()) Quit();
+}
+
+void CVTPTransceiver::Reset(void)
+{
+  for (int it = 0; it < si_Count; ++it)
+  {
+    if (m_DataSockets[it] != INVALID_SOCKET)
+    {
+      close(m_DataSockets[it]);
+      m_DataSockets[it] = INVALID_SOCKET;
+    }
+  }
+}
+
+bool CVTPTransceiver::OpenStreamSocket(SOCKET& sock, struct sockaddr_in& address2)
+{
+  struct sockaddr_in address(address2);
+
+  sock = __socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+
+  if(sock == INVALID_SOCKET)
+  {
+    XBMC->Log(LOG_ERROR, "CVTPTransceiver::OpenStreamSocket - invalid socket");
+    return false;
+  }
+
+  address.sin_addr.s_addr = INADDR_ANY;
+  address.sin_port        = 0;
+
+  if(__bind(sock, (struct sockaddr*) &address, sizeof(address)) == SOCKET_ERROR)
+  {
+    XBMC->Log(LOG_ERROR, "CVTPTransceiver::OpenStreamSocket - bind failed");
+    return false;
+  }
+
+  socklen_t len = sizeof(address);
+  if(__getsockname(sock, (struct sockaddr*) &address, &len) == SOCKET_ERROR)
+  {
+    XBMC->Log(LOG_ERROR, "CVTPTransceiver::OpenStreamSocket - bind failed");
+    return false;
+  }
+
+  if(__listen(sock, 1) == SOCKET_ERROR)
+  {
+    XBMC->Log(LOG_ERROR, "CVTPTransceiver::OpenStreamSocket - listen failed");
+    return false;
+  }
+
+  address2.sin_port = address.sin_port;
+
+  XBMC->Log(LOG_DEBUG, "CVTPTransceiver::OpenStreamSocket - listening on %s:%d", inet_ntoa(address.sin_addr), address.sin_port);
+  return true;
+}
+
+bool CVTPTransceiver::AcceptStreamSocket(SOCKET& sock2)
+{
+  SOCKET sock;
+  sock = __accept(sock2, NULL, NULL);
+  if(sock == INVALID_SOCKET)
+  {
+    XBMC->Log(LOG_ERROR, "CVTPStream::Accept - failed to accept incomming connection");
+    return false;
+  }
+
+  __close(sock2);
+
+  const char sol=1;
+  // Ignore possible errors here, proceed as usual
+  __setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &sol, sizeof(sol));
+
+  sock2 = sock;
+  return true;
+}
+
+void CVTPTransceiver::Close()
+{
+  if(m_VTPSocket != INVALID_SOCKET)
+    __close(m_VTPSocket);
+}
+
+bool CVTPTransceiver::Connect(const string &host, int port)
+{
+  socklen_t       len;
+  struct hostent *hp;
+
+  if ((m_VTPSocket = __socket(PF_INET, SOCK_STREAM, IPPROTO_IP)) == INVALID_SOCKET)
+  {
+    XBMC->Log(LOG_ERROR, "CVTPTransceiver::Connect - Can't open socket '%s:%u'", host.c_str(), port);
+    return false;
+  }
+
+#ifdef _LINUX
+  int flags = fcntl(m_VTPSocket, F_GETFL);
+  if (__fcntl(m_VTPSocket, F_SETFL, O_NONBLOCK) == -1)
+#elif defined(_WIN32) || defined(_WIN64)
+  u_long iMode = 1;
+  if (__ioctlsocket(m_VTPSocket, FIONBIO, &iMode) == -1)
+#endif
+  {
+    XBMC->Log(LOG_ERROR, "CVTPTransceiver::Connect - Can't set socket to non blocking mode");
+    Close();
+    return false;
+  }
+
+  if ((hp = __gethostbyname(host.c_str())) == NULL)
+  {
+    XBMC->Log(LOG_ERROR, "CVTPTransceiver::Connect - failed to resolve hostname: %s", host.c_str());
+    Close();
+    return false;
+  }
+
+  m_LocalAddr.sin_family = AF_INET;
+  m_LocalAddr.sin_port   = 0;
+  m_LocalAddr.sin_addr.s_addr = INADDR_ANY;
+  if (__bind(m_VTPSocket, (struct sockaddr*)&m_LocalAddr, sizeof(m_LocalAddr)) == -1)
+  {
+    XBMC->Log(LOG_ERROR, "CVTPTransceiver::Connect - Can't bind requested socket '%s:%u'", host.c_str(), port);
+    Close();
+    return false;
+  }
+
+  m_RemoteAddr.sin_family = AF_INET;
+  m_RemoteAddr.sin_port   = htons(port);
+  memcpy(&m_RemoteAddr.sin_addr.s_addr, hp->h_addr_list[0], hp->h_length);
+  if (__connect(m_VTPSocket, (struct sockaddr*)&m_RemoteAddr, sizeof(m_RemoteAddr)) < 0)
+  {
+    fd_set         set_r, set_w, set_e;
+    timeval        timeout;
+
+    timeout.tv_sec  = g_iConnectTimeout;
+    timeout.tv_usec = 0;
+
+    // fill with new data
+    FD_ZERO(&set_r);
+    FD_ZERO(&set_w);
+    FD_ZERO(&set_e);
+    FD_SET(m_VTPSocket, &set_r);
+    FD_SET(m_VTPSocket, &set_w);
+    FD_SET(m_VTPSocket, &set_e);
+    int result = __select(FD_SETSIZE, &set_r, &set_w, &set_e, &timeout);
+    if (result < 0)
+    {
+      XBMC->Log(LOG_ERROR, "CVTPTransceiver::Connect - select failed '%s:%u'", host.c_str(), port);
+      Close();
+      return false;
+    }
+    else if (result == 0)
+    {
+      XBMC->Log(LOG_ERROR, "CVTPTransceiver::Connect - connect timed out '%s:%u'", host.c_str(), port);
+      Close();
+      return false;
+    }
+    else if (!IsConnected(m_VTPSocket, &set_r, &set_w, &set_e))
+    {
+      XBMC->Log(LOG_ERROR, "CVTPTransceiver::Connect - failed to connect to IP '%d.%d.%d.%d",
+                        (ntohl(m_RemoteAddr.sin_addr.s_addr) >> 24) & 0xff,
+                        (ntohl(m_RemoteAddr.sin_addr.s_addr) >> 16) & 0xff,
+                        (ntohl(m_RemoteAddr.sin_addr.s_addr) >> 8) & 0xff,
+                        (ntohl(m_RemoteAddr.sin_addr.s_addr) & 0xff));
+      Close();
+      return false;
+    }
+  }
+
+#ifdef _LINUX
+  if (__fcntl(m_VTPSocket, F_SETFL, flags) == -1)
+#elif defined(_WIN32) || defined(_WIN64)
+  iMode = 0;
+  if (__ioctlsocket(m_VTPSocket, FIONBIO, &iMode) == -1)
+#endif
+  {
+    XBMC->Log(LOG_ERROR, "CVTPTransceiver::Connect - Can't set socket initial condition");
+    Close();
+    return false;
+  }
+
+  len = sizeof(struct sockaddr_in);
+  if (__getpeername(m_VTPSocket, (struct sockaddr*)&m_RemoteAddr, &len) == -1)
+  {
+    XBMC->Log(LOG_ERROR, "CVTPTransceiver::Connect - Can't get the name of the peer socket '%s:%u'", host.c_str(), port);
+    Close();
+    return false;
+  }
+
+  len = sizeof(struct sockaddr_in);
+  if (__getsockname(m_VTPSocket, (struct sockaddr*)&m_LocalAddr, &len) == -1)
+  {
+    XBMC->Log(LOG_ERROR, "CVTPTransceiver::Connect - Can't get the socket name '%s:%u'", host.c_str(), port);
+    Close();
+    return false;
+  }
+
+  // VTP Server will send a greeting
+  string line;
+  int    code;
+  if (!ReadResponse(code, line))
+  {
+    XBMC->Log(LOG_ERROR, "CVTPTransceiver::Connect - Failed reading response");
+    Close();
+    return false;
+  }
+
+  XBMC->Log(LOG_INFO, "CVTPTransceiver::Connect - server greeting: %s", line.c_str());
+  return true;
+}
+
+#if defined(_WIN32) || defined(_WIN64)
+bool CVTPTransceiver::IsConnected(SOCKET socket, fd_set *rd, fd_set *wr, fd_set *ex)
+{
+  WSASetLastError(0);
+  if (!FD_ISSET(socket, rd) && !FD_ISSET(socket, wr))
+    return false;
+  if (FD_ISSET(socket, ex))
+    return false;
+  return true;
+}
+#else
+bool CVTPTransceiver::IsConnected(SOCKET socket, fd_set *rd, fd_set *wr, fd_set *ex)
+{
+  int err;
+  socklen_t len = sizeof(err);
+
+  errno = 0;             /* assume no error */
+  if (!FD_ISSET(socket, rd ) && !FD_ISSET(socket, wr))
+    return false;
+  if (__getsockopt(socket, SOL_SOCKET, SO_ERROR, &err, &len) < 0)
+    return false;
+  errno = err;           /* in case we're not connected */
+  return err == 0;
+}
+#endif
+
+bool CVTPTransceiver::ReadResponse(int &code, string &line)
+{
+  vector<string> lines;
+  if(ReadResponse(code, lines))
+  {
+    line = lines[lines.size()-1];
+    return true;
+  }
+  return false;
+}
+
+bool CVTPTransceiver::ReadResponse(int &code, vector<string> &lines)
+{
+  fd_set         set_r, set_e;
+  timeval        timeout;
+  int            result;
+  int            retries = 5;
+  char           buffer[2048];
+  char           cont = 0;
+  string         line;
+  size_t         pos1 = 0, pos2 = 0, pos3 = 0;
+
+  while(true)
+  {
+    while( (pos1 = line.find("\r\n", pos3)) != std::string::npos)
+    {
+      if(sscanf(line.c_str(), "%d%c", &code, &cont) != 2)
+      {
+        XBMC->Log(LOG_DEBUG, "CVTPTransceiver::ReadResponse - unknown line format: %s", line.c_str());
+        line.erase(0, pos1 + 2);
+        continue;
+      }
+
+      pos2 = line.find(cont);
+
+      lines.push_back(line.substr(pos2+1, pos1-pos2-1));
+
+      line.erase(0, pos1 + 2);
+      pos3 = 0;
+    }
+
+    // we only need to recheck 1 byte
+    if(line.size() > 0)
+      pos3 = line.size() - 1;
+    else
+      pos3 = 0;
+
+    if(cont == ' ')
+      break;
+
+    //TODO set 10 seconds timeout value??
+    timeout.tv_sec  = 10;
+    timeout.tv_usec = 0;
+
+    // fill with new data
+    FD_ZERO(&set_r);
+    FD_ZERO(&set_e);
+    FD_SET(m_VTPSocket, &set_r);
+    FD_SET(m_VTPSocket, &set_e);
+    result = __select(FD_SETSIZE, &set_r, NULL, &set_e, &timeout);
+    if(result < 0)
+    {
+      XBMC->Log(LOG_DEBUG, "CVTPTransceiver::ReadResponse - select failed");
+      m_VTPSocket = INVALID_SOCKET;
+      return false;
+    }
+
+    if(result == 0)
+    {
+      XBMC->Log(LOG_DEBUG, "CVTPTransceiver::ReadResponse - timeout waiting for response, retrying...");
+      if (retries != 0) {
+          retries--;
+      continue;
+    }
+      else {
+          m_VTPSocket = INVALID_SOCKET;
+          return false;
+      }
+    }
+
+    result = __recv(m_VTPSocket, buffer, sizeof(buffer) - 1, 0);
+    if(result < 0)
+    {
+      XBMC->Log(LOG_DEBUG, "CVTPTransceiver::ReadResponse - recv failed");
+      m_VTPSocket = INVALID_SOCKET;
+      return false;
+    }
+    buffer[result] = 0;
+
+    line.append(buffer);
+  }
+
+  return true;
+}
+
+bool CVTPTransceiver::SendCommand(const string &command)
+{
+  fd_set set_w, set_e;
+  struct timeval tv;
+  int  result;
+  char buffer[1024];
+  int  len;
+
+  len = sprintf(buffer, "%s\r\n", command.c_str());
+
+  // fill with new data
+  tv.tv_sec  = 0;
+  tv.tv_usec = 0;
+
+  FD_ZERO(&set_w);
+  FD_ZERO(&set_e);
+  FD_SET(m_VTPSocket, &set_w);
+  FD_SET(m_VTPSocket, &set_e);
+  result = __select(FD_SETSIZE, &set_w, NULL, &set_e, &tv);
+  if(result < 0)
+  {
+    XBMC->Log(LOG_ERROR, "CVTPTransceiver::SendCommand - select failed");
+    m_VTPSocket = INVALID_SOCKET;
+    return false;
+  }
+  if (FD_ISSET(m_VTPSocket, &set_w))
+  {
+    XBMC->Log(LOG_ERROR, "CVTPTransceiver::SendCommand - failed to send data");
+    m_VTPSocket = INVALID_SOCKET;
+    return false;
+  }
+  if(__send(m_VTPSocket, buffer, len, 0) != len)
+  {
+    XBMC->Log(LOG_ERROR, "CVTPTransceiver::SendCommand - failed to send data");
+    m_VTPSocket = INVALID_SOCKET;
+    return false;
+  }
+  return true;
+}
+
+bool CVTPTransceiver::SendCommand(const string &command, int &code, string line)
+{
+  vector<string> lines;
+  if(SendCommand(command, code, lines))
+  {
+    line = lines[lines.size()-1];
+    return true;
+  }
+  return false;
+}
+
+bool CVTPTransceiver::SendCommand(const string &command, int &code, vector<string> &lines)
+{
+  if(!SendCommand(command))
+    return false;
+
+  if(!ReadResponse(code, lines))
+    return false;
+
+  if(code < 200 || code > 299)
+  {
+    if (code == 550 && lines[lines.size()-1] == "No schedule found") // Ignore error for missing EPG
+      return true;
+
+    XBMC->Log(LOG_ERROR, "CVTPTransceiver::SendCommand - Failed with code: %d (%s)", code, lines[lines.size()-1].c_str());
+    return false;
+  }
+
+  return true;
+}
+
+bool CVTPTransceiver::CheckConnection()
+{
+  CMD_LOCK;
+
+  if (IsOpen())
+  {
+    cTBSelect select;
+
+#ifdef DEBUG_VTP
+    XBMC->Log(LOG_DEBUG, "connection open");
+#endif
+
+    // XXX+ check if connection is still alive (is there a better way?)
+    // There REALLY shouldn't be anything readable according to PROTOCOL here
+    // If there is, assume it's an eof signal (subseq. read would return 0)
+    select.Add(m_VTPSocket, false);
+    int res;
+    if ((res = select.Select(0)) == 0)
+    {
+#ifdef DEBUG_VTP
+      XBMC->Log(LOG_DEBUG, "select said nothing happened");
+#endif
+      return true;
+    }
+    XBMC->Log(LOG_DEBUG, "closing connection (res was %d)", res);
+    Close();
+  }
+
+  if (!Connect(g_szHostname, g_iPort))
+  {
+    static time_t lastTime = 0;
+    if (time(NULL) - lastTime > MINLOGREPEAT)
+    {
+      XBMC->Log(LOG_ERROR, "Couldn't connect to %s:%d: %s", g_szHostname.c_str(), g_iPort, strerror(errno));
+      lastTime = time(NULL);
+    }
+    return false;
+  }
+
+  string line;
+  int    code = 220;
+  if(!SendCommand("CAPS TS", code, line) || code != 220)
+  {
+    if (errno == 0)
+      XBMC->Log(LOG_ERROR, "Couldn't negotiate capabilities on %s:%d", g_szHostname.c_str(), g_iPort);
+    Close();
+    return false;
+  }
+
+  const char *Filters = "";
+  if(SendCommand("CAPS FILTERS", code, line) || code != 220)
+    Filters = ",FILTERS";
+
+  XBMC->Log(LOG_INFO, "Connected to server %s:%d using capabilities TS%s", g_szHostname.c_str(), g_iPort, Filters);
+  return true;
+}
+
+bool CVTPTransceiver::ProvidesChannel(unsigned int Channel, int Priority)
+{
+  if (!CheckConnection()) return false;
+
+  CMD_LOCK;
+
+  string line;
+  int    code;
+
+  CStdString command;
+  command.Format("PROV %i %d", Priority, Channel);
+  if(!SendCommand(command, code, line))
+  {
+    if (command != "560" && errno == 0)
+      XBMC->Log(LOG_ERROR, "Couldn't check if %s:%d provides channel %d", g_szHostname.c_str(), g_iPort, Channel);
+    return false;
+  }
+
+  return true;
+}
+
+bool CVTPTransceiver::CreateDataConnection(eSocketId Id)
+{
+  if (!CheckConnection()) return false;
+
+  if (m_DataSockets[Id] != INVALID_SOCKET)
+    close(m_DataSockets[Id]);
+
+  sockaddr_in address;
+  SOCKET      sock;
+  socklen_t   len = sizeof(address);
+  string      result;
+  int         code;
+
+  if(__getsockname(m_VTPSocket, (struct sockaddr*) &address, &len) == SOCKET_ERROR)
+  {
+    XBMC->Log(LOG_ERROR, "Couldn't get socket name: %s", strerror(errno));
+    return false;
+  }
+
+  XBMC->Log(LOG_DEBUG, "CVTPTransceiver::CreateDataConnection - local address %s:%d", inet_ntoa(address.sin_addr), ntohs(address.sin_port) );
+
+  if(!OpenStreamSocket(sock, address))
+  {
+    XBMC->Log(LOG_ERROR, "Couldn't create data connection: %s", strerror(errno));
+    return false;
+  }
+
+  int port = ntohs(address.sin_port);
+  int addr = ntohl(address.sin_addr.s_addr);
+
+  CStdString command;
+  command.Format("PORT %d %d,%d,%d,%d,%d,%d", Id
+                , (addr & 0xFF000000)>>24
+                , (addr & 0x00FF0000)>>16
+                , (addr & 0x0000FF00)>>8
+                , (addr & 0x000000FF)>>0
+                , (port & 0xFF00)>>8
+                , (port & 0x00FF)>>0);
+
+  CMD_LOCK;
+
+  if(!SendCommand(command, code, result) || code != 220)
+  {
+    XBMC->Log(LOG_DEBUG, "error: %m");
+    if (errno == 0)
+      XBMC->Log(LOG_ERROR, "Couldn't establish data connection to %s:%d", g_szHostname.c_str(), g_iPort);
+    return false;
+  }
+
+  if(!AcceptStreamSocket(sock))
+  {
+    XBMC->Log(LOG_ERROR, "Couldn't establish data connection to %s:%d%s%s", g_szHostname.c_str(), g_iPort, errno == 0 ? "" : ": ", errno == 0 ? "" : strerror(errno));
+    close(sock);
+    return false;
+  }
+
+  m_DataSockets[Id] = sock;
+  return true;
+}
+
+bool CVTPTransceiver::CloseDataConnection(eSocketId Id)
+{
+  //if (!CheckConnection()) return false;
+
+  CMD_LOCK;
+
+  if(Id == siLive || Id == siLiveFilter || Id == siReplay || Id == siDataRespond)
+  {
+    if (m_DataSockets[Id] != INVALID_SOCKET)
+    {
+      string line;
+      int    code;
+      CStdString command;
+      command.Format("ABRT %i", Id);
+      if (!SendCommand(command, code, line) || code != 220)
+      {
+        if (errno == 0)
+          XBMC->Log(LOG_ERROR, "Couldn't cleanly close data connection");
+        //return false;
+      }
+      close(m_DataSockets[Id]);
+      m_DataSockets[Id] = INVALID_SOCKET;
+    }
+  }
+  return true;
+}
+
+bool CVTPTransceiver::SetChannelDevice(unsigned int Channel)
+{
+  if (!CheckConnection()) return false;
+
+  CMD_LOCK;
+
+  string result;
+  int    code;
+  CStdString command;
+  command.Format("TUNE %d", Channel);
+  if(!SendCommand(command, code, result) || code != 220)
+  {
+    if (errno == 0)
+      XBMC->Log(LOG_ERROR, "Couldn't tune %s:%d to channel %d", g_szHostname.c_str(), g_iPort, Channel);
+    return false;
+       }
+       return true;
+}
+
+bool CVTPTransceiver::SetRecordingIndex(unsigned int Recording)
+{
+  if (!CheckConnection()) return false;
+
+  CMD_LOCK;
+
+  string result;
+  int    code;
+  CStdString command;
+  command.Format("PLAY %d", Recording);
+  if (!SendCommand(command, code, result) || code != 220)
+  {
+    if (errno == 0)
+      XBMC->Log(LOG_ERROR, "Couldn't open recording %d on %s:%d", Recording, g_szHostname.c_str(), g_iPort);
+    return false;
+  }
+  return true;
+}
+
+bool CVTPTransceiver::GetPlayingRecordingSize(uint64_t *size, uint32_t *frames)
+{
+  vector<string> lines;
+  int            code;
+
+  if (!CheckConnection()) return false;
+
+  CMD_LOCK;
+
+  if (!SendCommand("SIZE", code, lines) || code != 220)
+    return false;
+
+  vector<string>::iterator it = lines.begin();
+  string& data(*it);
+
+  *size = atoll(data.c_str());
+  *frames = atol(data.substr(data.find(" ") + 1).c_str());
+  return true;
+}
+
+uint64_t CVTPTransceiver::SeekRecordingPosition(uint64_t position)
+{
+  vector<string> lines;
+  int            code;
+
+  if (!CheckConnection()) return 0;
+
+  CMD_LOCK;
+
+  CStdString command;
+  command.Format("SEEK %llu", position);
+  if (!SendCommand(command, code, lines) || code != 220)
+  {
+    if (errno == 0)
+      XBMC->Log(LOG_ERROR, "Couldn't seek to position %llu on %s:%d", position, g_szHostname.c_str(), g_iPort);
+    return 0;
+  }
+
+  vector<string>::iterator it = lines.begin();
+  string& data(*it);
+  return atoll(data.c_str());;
+}
+
+CStdString CVTPTransceiver::GetBackendName()
+{
+  vector<string>  lines;
+  int             code;
+
+  if (!CheckConnection())
+    return "";
+
+  CMD_LOCK;
+
+  if (!SendCommand("STAT name", code, lines))
+    return "";
+
+  vector<string>::iterator it = lines.begin();
+  string& data(*it);
+  return data;
+}
+
+CStdString CVTPTransceiver::GetBackendVersion()
+{
+  vector<string>  lines;
+  int             code;
+
+  if (!CheckConnection())
+    return "";
+
+  CMD_LOCK;
+
+  if (!SendCommand("STAT version", code, lines))
+    return "";
+
+  vector<string>::iterator it = lines.begin();
+  string& data(*it);
+  return data;
+}
+
+PVR_ERROR CVTPTransceiver::GetDriveSpace(long long *total, long long *used)
+{
+  vector<string>  lines;
+  int             code;
+
+  if (!CheckConnection())
+    return PVR_ERROR_SERVER_ERROR;
+
+  CMD_LOCK;
+
+  if (!SendCommand("STAT disk", code, lines))
+    return PVR_ERROR_SERVER_ERROR;
+
+  vector<string>::iterator it = lines.begin();
+  string& data(*it);
+  size_t found = data.find("MB");
+  if (found != CStdString::npos)
+  {
+    *total = atol(data.c_str()) * 1024;
+    data.erase(0, found + 3);
+    *used = atol(data.c_str()) * 1024;
+  }
+
+  return PVR_ERROR_NO_ERROR;
+}
+
+PVR_ERROR CVTPTransceiver::GetBackendTime(time_t *localTime, int *gmtOffset)
+{
+  vector<string>  lines;
+  int             code;
+
+  if (!CheckConnection())
+    return PVR_ERROR_SERVER_ERROR;
+
+  CMD_LOCK;
+
+  if (!SendCommand("STAT time", code, lines))
+    return PVR_ERROR_SERVER_ERROR;
+
+  vector<string>::iterator it = lines.begin();
+  string& data(*it);
+
+  *localTime = atol(data.c_str());
+  *gmtOffset = atol(data.substr(data.find(" ") + 1).c_str());
+  return PVR_ERROR_NO_ERROR;
+}
+
+PVR_ERROR CVTPTransceiver::RequestEPGForChannel(const PVR_CHANNEL &channel, PVRHANDLE handle, time_t start, time_t end)
+{
+  vector<string> lines;
+  int            code;
+  cEpg           epg;
+
+  if (!CheckConnection()) return PVR_ERROR_SERVER_ERROR;
+
+  CMD_LOCK;
+
+  CStdString command;
+  if (start != 0)
+    command.Format("LSTE %d from %lu to %lu", channel.number, (long)start, (long)end);
+  else
+    command.Format("LSTE %d", channel.number);
+  while (!SendCommand(command, code, lines) || code != 215)
+  {
+    if (code == 550)
+      return PVR_ERROR_NO_ERROR;
+    else if (code != 451)
+      return PVR_ERROR_SERVER_ERROR;
+    Sleep(100);
+  }
+
+  for (vector<string>::iterator it = lines.begin(); it != lines.end(); it++)
+  {
+    string& data(*it);
+    CStdString str_result = data;
+
+    if (g_bCharsetConv)
+      XBMC->UnknownToUTF8(str_result);
+
+    bool isEnd = epg.ParseLine(str_result.c_str());
+    if (isEnd && epg.StartTime() != 0)
+    {
+      PVR_PROGINFO broadcast;
+      broadcast.channum         = channel.number;
+      broadcast.uid             = epg.UniqueId();
+      broadcast.title           = epg.Title();
+      broadcast.subtitle        = epg.ShortText();
+      broadcast.description     = epg.Description();
+      broadcast.starttime       = epg.StartTime();
+      broadcast.endtime         = epg.EndTime();
+      broadcast.genre_type      = epg.GenreType();
+      broadcast.genre_sub_type  = epg.GenreSubType();
+      broadcast.parental_rating = epg.ParentalRating();
+      PVR->TransferEpgEntry(handle, &broadcast);
+      epg.Reset();
+    }
+  }
+
+  return PVR_ERROR_NO_ERROR;
+}
+
+int CVTPTransceiver::GetNumChannels()
+{
+  vector<string>  lines;
+  int             code;
+
+  if (!CheckConnection()) return -1;
+
+  CMD_LOCK;
+
+  if (!SendCommand("STAT channels", code, lines))
+    return -1;
+
+  vector<string>::iterator it = lines.begin();
+  string& data(*it);
+  return atol(data.c_str());
+}
+
+PVR_ERROR CVTPTransceiver::RequestChannelList(PVRHANDLE handle, bool radio)
+{
+  vector<string> lines;
+  int            code;
+
+  if (!g_bRadioEnabled && radio) return PVR_ERROR_NO_ERROR;
+  if (!CheckConnection()) return PVR_ERROR_SERVER_ERROR;
+
+  CMD_LOCK;
+
+  while (!SendCommand("LSTC", code, lines))
+  {
+    if (code != 451)
+      return PVR_ERROR_SERVER_ERROR;
+    Sleep(10);
+  }
+
+  for (vector<string>::iterator it = lines.begin(); it < lines.end(); it++)
+  {
+    string& data(*it);
+    CStdString str_result = data;
+
+    if (g_bCharsetConv)
+      XBMC->UnknownToUTF8(str_result);
+
+    cChannel channel;
+    channel.Parse(str_result.c_str());
+
+    /* Ignore channels without streams */
+    if ((g_bNoBadChannels && channel.Vpid() == 0 && channel.Apid(0) == 0 && channel.Dpid(0) == 0) || (g_bOnlyFTA && channel.Ca() != 0))
+      continue;
+
+    PVR_CHANNEL tag;
+    tag.uid         = channel.Sid();
+    tag.number      = channel.Number();
+    tag.name        = channel.Name();
+    tag.callsign    = channel.Name();
+    tag.iconpath    = "";
+    tag.encryption  = channel.Ca();
+    tag.radio       = (channel.Vpid() == 0) && (channel.Apid(0) != 0) ? true : false;
+    tag.hide        = false;
+    tag.recording   = false;
+    tag.bouquet     = 0;
+    tag.multifeed   = false;
+    tag.input_format = "mpegts";
+    tag.stream_url  = "";
+
+    if (radio == tag.radio)
+      PVR->TransferChannelEntry(handle, &tag);
+  }
+
+  return PVR_ERROR_NO_ERROR;
+}
+
+int CVTPTransceiver::GetNumRecordings(void)
+{
+  vector<string>  lines;
+  int             code;
+
+  if (!CheckConnection()) return -1;
+
+  CMD_LOCK;
+
+  if (!SendCommand("STAT records", code, lines))
+    return -1;
+
+  vector<string>::iterator it = lines.begin();
+  string& data(*it);
+  return atol(data.c_str());
+}
+
+void CVTPTransceiver::ScanVideoDir(PVRHANDLE handle, const char *DirName, bool Deleted, int LinkLevel)
+{
+  cReadDir d(DirName);
+  struct dirent *e;
+  while ((e = d.Next()) != NULL)
+  {
+    if (strcmp(e->d_name, ".") && strcmp(e->d_name, ".."))
+    {
+      char *buffer = strdup(AddDirectory(DirName, e->d_name));
+      struct stat st;
+      if (stat(buffer, &st) == 0)
+      {
+        int Link = 0;
+        if (S_ISLNK(st.st_mode))
+        {
+          if (LinkLevel > MAX_LINK_LEVEL)
+          {
+            XBMC->Log(LOG_ERROR, "max link level exceeded - not scanning %s", buffer);
+            continue;
+          }
+          Link = 1;
+          char *old = buffer;
+          buffer = ReadLink(old);
+          free(old);
+          if (!buffer)
+            continue;
+          if (stat(buffer, &st) != 0)
+          {
+            free(buffer);
+            continue;
+          }
+        }
+        if (S_ISDIR(st.st_mode))
+        {
+          if (endswith(buffer, Deleted ? DELEXT : RECEXT))
+          {
+            cRecording recording(buffer);
+
+            PVR_RECORDINGINFO tag;
+            tag.index           = m_recIndex++;
+            tag.channel_name    = recording.ChannelName();
+            tag.lifetime        = recording.Lifetime();
+            tag.priority        = recording.Priority();
+            tag.recording_time  = recording.StartTime();
+            tag.duration        = recording.Duration();
+            tag.subtitle        = recording.ShortText();
+            tag.description     = recording.Description();
+            tag.title           = recording.Title();
+            tag.directory       = recording.Directory();
+            tag.stream_url      = recording.StreamURL();
+
+            PVR->TransferRecordingEntry(handle, &tag);
+          }
+          else
+            ScanVideoDir(handle, buffer, Deleted, LinkLevel + Link);
+        }
+      }
+      free(buffer);
+    }
+  }
+}
+
+PVR_ERROR CVTPTransceiver::RequestRecordingsList(PVRHANDLE handle)
+{
+  if (g_bUseRecordingsDir && g_szRecordingsDir != "")
+  {
+    m_recIndex = 1;
+    ScanVideoDir(handle, g_szRecordingsDir.c_str());
+  }
+  else
+  {
+    vector<string> linesShort;
+    int            code;
+
+    if (!CheckConnection()) return PVR_ERROR_SERVER_ERROR;
+
+    CMD_LOCK;
+
+    if (!SendCommand("LSTR", code, linesShort))
+      return PVR_ERROR_SERVER_ERROR;
+
+    for (vector<string>::iterator it = linesShort.begin(); it != linesShort.end(); it++)
+    {
+      string& data(*it);
+      CStdString str_result = data;
+
+      /* Convert to UTF8 string format */
+      if (g_bCharsetConv)
+        XBMC->UnknownToUTF8(str_result);
+
+      cRecording recording;
+      if (recording.ParseEntryLine(str_result.c_str()))
+      {
+        vector<string> linesDetails;
+
+        CStdString command;
+        command.Format("LSTR %i", recording.Index());
+        if (!SendCommand(command, code, linesDetails))
+          continue;
+
+        for (vector<string>::iterator it2 = linesDetails.begin(); it2 != linesDetails.end(); it2++)
+        {
+          string& data2(*it2);
+          CStdString str_details = data2;
+
+          /* Convert to UTF8 string format */
+          if (g_bCharsetConv)
+            XBMC->UnknownToUTF8(str_details);
+
+          recording.ParseLine(str_details.c_str());
+        }
+
+        PVR_RECORDINGINFO tag;
+        tag.index           = recording.Index();
+        tag.channel_name    = recording.ChannelName();
+        tag.lifetime        = recording.Lifetime();
+        tag.priority        = recording.Priority();
+        tag.recording_time  = recording.StartTime();
+        tag.duration        = recording.Duration();
+        tag.subtitle        = recording.ShortText();
+        tag.description     = recording.Description();
+        tag.stream_url      = "";
+        tag.title           = recording.FileName();
+        tag.directory       = recording.Directory();
+
+        PVR->TransferRecordingEntry(handle, &tag);
+      }
+    }
+  }
+
+  return PVR_ERROR_NO_ERROR;
+}
+
+PVR_ERROR CVTPTransceiver::DeleteRecording(const PVR_RECORDINGINFO &recinfo)
+{
+  vector<string> lines;
+  int            code;
+
+  if (!CheckConnection()) return PVR_ERROR_SERVER_ERROR;
+
+  CMD_LOCK;
+
+  CStdString command;
+  command.Format("LSTR %i", recinfo.index);
+  if (!VTPTransceiver.SendCommand(command, code, lines))
+    return PVR_ERROR_SERVER_ERROR;
+  if (code != 215)
+    return PVR_ERROR_NOT_SYNC;
+
+  command.Format("DELR %i", recinfo.index);
+  if (!SendCommand(command, code, lines))
+    return PVR_ERROR_SERVER_ERROR;
+  if (code != 250)
+    return PVR_ERROR_NOT_DELETED;
+
+  return PVR_ERROR_NO_ERROR;
+}
+
+PVR_ERROR CVTPTransceiver::RenameRecording(const PVR_RECORDINGINFO &recinfo, const char *newname)
+{
+  vector<string> lines;
+  int            code;
+
+  if (!CheckConnection()) return PVR_ERROR_SERVER_ERROR;
+
+  CMD_LOCK;
+
+  CStdString command;
+  command.Format("LSTR %i", recinfo.index);
+  if (!SendCommand(command, code, lines))
+    return PVR_ERROR_SERVER_ERROR;
+  if (code != 215)
+    return PVR_ERROR_NOT_SYNC;
+
+  CStdString renamedName = recinfo.directory;
+  if (renamedName != "" && renamedName[renamedName.size()-1] != '/')
+    renamedName += "/";
+  renamedName += newname;
+  renamedName.Replace('/','~');;
+
+  command.Format("RENR %d %s", recinfo.index, renamedName.c_str());
+  if (!SendCommand(command, code, lines))
+    return PVR_ERROR_SERVER_ERROR;
+  if (code != 250)
+    return PVR_ERROR_NOT_DELETED;
+
+  return PVR_ERROR_NO_ERROR;
+}
+
+int CVTPTransceiver::GetNumTimers(void)
+{
+  vector<string>  lines;
+  int             code;
+
+  if (!CheckConnection()) return -1;
+
+  CMD_LOCK;
+
+  if (!SendCommand("STAT timers", code, lines))
+    return -1;
+
+  vector<string>::iterator it = lines.begin();
+  string& data(*it);
+  return atol(data.c_str());
+}
+
+PVR_ERROR CVTPTransceiver::RequestTimerList(PVRHANDLE handle)
+{
+  vector<string> lines;
+  int            code;
+
+  if (!CheckConnection()) return PVR_ERROR_SERVER_ERROR;
+
+  CMD_LOCK;
+
+  if (!SendCommand("LSTT", code, lines))
+    return PVR_ERROR_SERVER_ERROR;
+
+  for (vector<string>::iterator it = lines.begin(); it != lines.end(); it++)
+  {
+    string& data(*it);
+    CStdString str_result = data;
+
+    /**
+     * VDR Format given by LSTT:
+     * 250-1 1:6:2008-10-27:0013:0055:50:99:Zeiglers wunderbare Welt des Fußballs:
+     * 250 2 0:15:2008-10-26:2000:2138:50:99:ZDFtheaterkanal:
+     * 250 3 1:6:MTWTFS-:2000:2129:50:99:WDR Köln:
+     */
+
+    if (g_bCharsetConv)
+      XBMC->UnknownToUTF8(str_result);
+
+    cTimer timer;
+    timer.Parse(str_result.c_str());
+
+    PVR_TIMERINFO tag;
+    tag.index       = timer.Index();
+    tag.active      = timer.HasFlags(tfActive);
+    tag.channelNum  = timer.Channel();
+    tag.firstday    = timer.FirstDay();
+    tag.starttime   = timer.StartTime();
+    tag.endtime     = timer.StopTime();
+    tag.recording   = timer.HasFlags(tfRecording) || timer.HasFlags(tfInstant);
+    tag.title       = timer.Title();
+    tag.directory   = timer.Dir();
+    tag.priority    = timer.Priority();
+    tag.lifetime    = timer.Lifetime();
+    tag.repeat      = timer.WeekDays() == 0 ? false : true;
+    tag.repeatflags = timer.WeekDays();
+
+    PVR->TransferTimerEntry(handle, &tag);
+  }
+
+  return PVR_ERROR_NO_ERROR;
+}
+
+PVR_ERROR CVTPTransceiver::GetTimerInfo(unsigned int timernumber, PVR_TIMERINFO &tag)
+{
+  vector<string>  lines;
+  int             code;
+
+  if (!VTPTransceiver.CheckConnection()) return PVR_ERROR_SERVER_ERROR;
+
+  CMD_LOCK;
+
+  CStdString command;
+  command.Format("LSTT %i", timernumber);
+  if (!VTPTransceiver.SendCommand(command, code, lines))
+    return PVR_ERROR_SERVER_ERROR;
+
+  vector<string>::iterator it = lines.begin();
+  string& data(*it);
+  CStdString str_result = data;
+
+  if (g_bCharsetConv)
+    XBMC->UnknownToUTF8(str_result);
+
+  cTimer timer;
+  timer.Parse(str_result.c_str());
+
+  tag.index       = timer.Index();
+  tag.active      = timer.HasFlags(tfActive);
+  tag.channelNum  = timer.Channel();
+  tag.firstday    = timer.FirstDay();
+  tag.starttime   = timer.StartTime();
+  tag.endtime     = timer.StopTime();
+  tag.recording   = timer.HasFlags(tfRecording) || timer.HasFlags(tfInstant);
+  tag.title       = timer.File();
+  tag.priority    = timer.Priority();
+  tag.lifetime    = timer.Lifetime();
+  tag.repeat      = timer.WeekDays() == 0 ? false : true;
+  tag.repeatflags = timer.WeekDays();
+
+  return PVR_ERROR_NO_ERROR;
+}
+
+PVR_ERROR CVTPTransceiver::AddTimer(const PVR_TIMERINFO &timerinfo)
+{
+  vector<string> lines;
+  int            code;
+
+  if (!CheckConnection())
+    return PVR_ERROR_SERVER_ERROR;
+
+  cTimer timer(&timerinfo);
+
+  CMD_LOCK;
+
+  CStdString command;
+  if (timerinfo.index == -1)
+  {
+    command.Format("NEWT %s", timer.ToText().c_str());
+    if (!SendCommand(command, code, lines))
+      return PVR_ERROR_NOT_SAVED;
+    if (code != 250)
+      return PVR_ERROR_NOT_SYNC;
+  }
+  else
+  {
+    // Modified timer
+    command.Format("LSTT %i", timerinfo.index);
+    if (!SendCommand(command, code, lines))
+      return PVR_ERROR_SERVER_ERROR;
+    if (code != 250)
+      return PVR_ERROR_NOT_SYNC;
+
+    command.Format("MODT %d %s", timerinfo.index, timer.ToText().c_str());
+    if (!SendCommand(command, code, lines))
+      return PVR_ERROR_NOT_SAVED;
+    if (code != 250)
+      return PVR_ERROR_NOT_SYNC;
+  }
+
+  return PVR_ERROR_NO_ERROR;
+}
+
+PVR_ERROR CVTPTransceiver::DeleteTimer(const PVR_TIMERINFO &timerinfo, bool force)
+{
+  vector<string> lines;
+  int            code;
+
+  if (!CheckConnection()) return PVR_ERROR_SERVER_ERROR;
+
+  CMD_LOCK;
+
+  CStdString command;
+  command.Format("LSTT %d", timerinfo.index);
+  if (!SendCommand(command, code, lines))
+    return PVR_ERROR_SERVER_ERROR;
+  if (code != 250)
+    return PVR_ERROR_NOT_SYNC;
+
+  lines.erase(lines.begin(), lines.end());
+
+  if (force)
+    command.Format("DELT %d FORCE", timerinfo.index);
+  else
+    command.Format("DELT %d", timerinfo.index);
+  if (!SendCommand(command, code, lines))
+  {
+    vector<string>::iterator it = lines.begin();
+    string& data(*it);
+    CStdString str_result = data;
+    if (str_result.find("is recording", 0) != std::string::npos)
+      return PVR_ERROR_RECORDING_RUNNING;
+    else
+      return PVR_ERROR_NOT_DELETED;
+  }
+  if (code != 250)
+    return PVR_ERROR_NOT_SYNC;
+
+  return PVR_ERROR_NO_ERROR;
+}
+
+PVR_ERROR CVTPTransceiver::RenameTimer(const PVR_TIMERINFO &timerinfo, const char *newname)
+{
+  PVR_TIMERINFO timerinfo1;
+  PVR_ERROR ret = GetTimerInfo(timerinfo.index, timerinfo1);
+  if (ret != PVR_ERROR_NO_ERROR)
+    return ret;
+
+  timerinfo1.title = newname;
+  return UpdateTimer(timerinfo1);
+}
+
+PVR_ERROR CVTPTransceiver::UpdateTimer(const PVR_TIMERINFO &timerinfo)
+{
+  vector<string> lines;
+  int            code;
+
+  if (!CheckConnection()) return PVR_ERROR_SERVER_ERROR;
+  if (timerinfo.index == -1) return PVR_ERROR_NOT_SAVED;
+
+  cTimer timer(&timerinfo);
+
+  CMD_LOCK;
+
+  CStdString command;
+  command.Format("LSTT %i", timerinfo.index);
+  if (!VTPTransceiver.SendCommand(command, code, lines))
+    return PVR_ERROR_SERVER_ERROR;
+  if (code != 250)
+    return PVR_ERROR_NOT_SYNC;
+
+  command.Format("MODT %d %s", timerinfo.index, timer.ToText().c_str());
+  if (!VTPTransceiver.SendCommand(command, code, lines))
+    return PVR_ERROR_NOT_SAVED;
+  if (code != 250)
+    return PVR_ERROR_NOT_SYNC;
+
+  return PVR_ERROR_NO_ERROR;
+}
+
+PVR_ERROR CVTPTransceiver::SignalQuality(PVR_SIGNALQUALITY &qualityinfo, unsigned int channel)
+{
+  vector<string> lines;
+  int            code;
+
+  if (!CheckConnection()) return PVR_ERROR_SERVER_ERROR;
+
+  CMD_LOCK;
+
+  CStdString command;
+  command.Format("LSTQ %i", channel);
+  if (!SendCommand(command, code, lines) || code != 215)
+    return PVR_ERROR_SERVER_ERROR;
+
+  for (vector<string>::iterator it = lines.begin(); it < lines.end(); it++)
+  {
+    string& data(*it);
+    const char *s = data.c_str();
+    if (!strncasecmp(s, "Device", 6))
+      strncpy(qualityinfo.frontend_name, s + 9, sizeof(qualityinfo.frontend_name));
+    else if (!strncasecmp(s, "Status", 6))
+      strncpy(qualityinfo.frontend_status, s + 9, sizeof(qualityinfo.frontend_status));
+    else if (!strncasecmp(s, "Signal", 6))
+      qualityinfo.signal = (uint16_t)strtol(s + 9, NULL, 16);
+    else if (!strncasecmp(s, "SNR", 3))
+      qualityinfo.snr = (uint16_t)strtol(s + 9, NULL, 16);
+    else if (!strncasecmp(s, "BER", 3))
+      qualityinfo.ber = (uint32_t)strtol(s + 9, NULL, 16);
+    else if (!strncasecmp(s, "UNC", 3))
+      qualityinfo.unc = (uint32_t)strtol(s + 9, NULL, 16);
+    else if (!strncasecmp(s, "Video", 5))
+      qualityinfo.video_bitrate = strtod(s + 9, NULL);
+    else if (!strncasecmp(s, "Audio", 5))
+      qualityinfo.audio_bitrate = strtod(s + 9, NULL);
+    else if (!strncasecmp(s, "Dolby", 5))
+      qualityinfo.dolby_bitrate = strtod(s + 9, NULL);
+  }
+
+  return PVR_ERROR_NO_ERROR;
+}
+
+int CVTPTransceiver::TransferRecordingToSocket(uint64_t position, int size)
+{
+  if (!CheckConnection()) return 0;
+
+  CMD_LOCK;
+
+  vector<string> lines;
+  int            code;
+
+  CStdString command;
+  command.Format("READ %llu %u", (unsigned long long)position, size);
+  if (!SendCommand(command, code, lines) || code != 220)
+    return 0;
+
+  vector<string>::iterator it = lines.begin();
+  string& data(*it);
+
+  return atol(data.c_str());
+}
+
+bool CVTPTransceiver::Quit(void)
+{
+  vector<string> lines;
+  int            code;
+  bool           ret;
+
+  if (!CheckConnection()) return false;
+
+  if (!(ret = SendCommand("QUIT", code, lines)) || code != 221)
+  {
+    if (errno == 0)
+      XBMC->Log(LOG_ERROR, "ERROR: Streamdev: Couldn't quit command connection to %s:%d", g_szHostname.c_str(), g_iPort);
+  }
+  Close();
+  return ret;
+
+}
+
+bool CVTPTransceiver::SuspendServer(void)
+{
+  vector<string> lines;
+  int            code;
+
+  if (!CheckConnection()) return 0;
+
+  CMD_LOCK;
+
+  if (!SendCommand("SUSP", code, lines) || code != 220)
+  {
+    XBMC->Log(LOG_ERROR, "Couldn't suspend server");
+    return false;
+  }
+  return true;
+}
+
+
diff --git a/xbmc/pvrclients/vdr-streamdev/vtptransceiver.h b/xbmc/pvrclients/vdr-streamdev/vtptransceiver.h
new file mode 100644 (file)
index 0000000..d586f8c
--- /dev/null
@@ -0,0 +1,104 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2008 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <vector>
+#include "StdString.h"
+#include "thread.h"
+#include "tools.h"
+
+#define CMD_LOCK cMutexLock CmdLock((cMutex*)&m_Mutex)
+
+enum eSocketId {
+  siLive,
+  siReplay,
+  siLiveFilter,
+  siDataRespond,
+  si_Count
+};
+
+class CVTPTransceiver
+{
+public:
+  bool ReadResponse(int &code, std::string &line);
+  bool ReadResponse(int &code, std::vector<std::string> &lines);
+
+  bool SendCommand(const std::string &command);
+  bool SendCommand(const std::string &command, int &code, std::string line);
+  bool SendCommand(const std::string &command, int &code, std::vector<std::string> &lines);
+
+private:
+       SOCKET m_DataSockets[si_Count];
+       SOCKET m_VTPSocket;
+       cMutex m_Mutex;
+       int    m_recIndex;
+
+  struct sockaddr_in m_LocalAddr;
+  struct sockaddr_in m_RemoteAddr;
+
+  bool OpenStreamSocket(SOCKET& socket, struct sockaddr_in& address);
+  bool AcceptStreamSocket(SOCKET& socket);
+  bool Connect(const std::string &host, int port);
+  void Close();
+  bool IsConnected(SOCKET socket, fd_set *rd, fd_set *wr, fd_set *ex);
+  void ScanVideoDir(PVRHANDLE handle, const char *DirName, bool Deleted = false, int LinkLevel = 0);
+
+public:
+  CVTPTransceiver();
+  ~CVTPTransceiver();
+
+  void Reset(void);
+
+  bool IsOpen(void) const { return m_VTPSocket != INVALID_SOCKET; }
+  bool CheckConnection();
+  bool ProvidesChannel(unsigned int Channel, int Priority);
+  bool CreateDataConnection(eSocketId Id);
+  bool CloseDataConnection(eSocketId Id);
+  SOCKET DataSocket(eSocketId Id) const { return m_DataSockets[Id]; }
+  bool SetChannelDevice(unsigned int Channel);
+  bool SetRecordingIndex(unsigned int Recording);
+  bool GetPlayingRecordingSize(uint64_t *size, uint32_t *frames);
+  uint64_t SeekRecordingPosition(uint64_t position);
+  CStdString GetBackendName();
+  CStdString GetBackendVersion();
+  PVR_ERROR GetDriveSpace(long long *total, long long *used);
+  PVR_ERROR GetBackendTime(time_t *localTime, int *gmtOffset);
+  PVR_ERROR RequestEPGForChannel(const PVR_CHANNEL &channel, PVRHANDLE handle, time_t start = NULL, time_t end = NULL);
+  int GetNumChannels(void);
+  PVR_ERROR RequestChannelList(PVRHANDLE handle, bool radio = false);
+  int GetNumRecordings(void);
+  PVR_ERROR RequestRecordingsList(PVRHANDLE handle);
+  PVR_ERROR DeleteRecording(const PVR_RECORDINGINFO &recinfo);
+  PVR_ERROR RenameRecording(const PVR_RECORDINGINFO &recinfo, const char *newname);
+  int GetNumTimers(void);
+  PVR_ERROR RequestTimerList(PVRHANDLE handle);
+  PVR_ERROR GetTimerInfo(unsigned int timernumber, PVR_TIMERINFO &tag);
+  PVR_ERROR AddTimer(const PVR_TIMERINFO &timerinfo);
+  PVR_ERROR DeleteTimer(const PVR_TIMERINFO &timerinfo, bool force = false);
+  PVR_ERROR RenameTimer(const PVR_TIMERINFO &timerinfo, const char *newname);
+  PVR_ERROR UpdateTimer(const PVR_TIMERINFO &timerinfo);
+  PVR_ERROR SignalQuality(PVR_SIGNALQUALITY &qualityinfo, unsigned int channel);
+  int TransferRecordingToSocket(uint64_t position, int size);
+  bool SuspendServer(void);
+  bool Quit(void);
+};
+
+extern class CVTPTransceiver VTPTransceiver;
diff --git a/xbmc/pvrclients/vdr-streamdev/windows/dirent.cpp b/xbmc/pvrclients/vdr-streamdev/windows/dirent.cpp
new file mode 100644 (file)
index 0000000..312b29f
--- /dev/null
@@ -0,0 +1,161 @@
+//#include <malloc.h>
+
+#include <windows.h>
+#include <io.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <direct.h>
+#include <string.h>
+#include <errno.h>
+
+#include "pvrclient-vdr_os.h"
+#include <dirent.h>
+
+/**********************************************************************
+ * Implement dirent-style opendir/readdir/rewinddir/closedir on Win32
+ *
+ * Functions defined are opendir(), readdir(), rewinddir() and
+ * closedir() with the same prototypes as the normal dirent.h
+ * implementation.
+ *
+ * Does not implement telldir(), seekdir(), or scandir().  The dirent
+ * struct is compatible with Unix, except that d_ino is always 1 and
+ * d_off is made up as we go along.
+ *
+ * The DIR typedef is not compatible with Unix.
+ **********************************************************************/
+
+DIR *opendir(const char *dir)
+{
+       DIR             *dirp;
+       char    *filespec;
+       long    handle;
+       int             index;
+
+       filespec = (char *)malloc(strlen(dir) + 2 + 1);
+       strcpy(filespec, dir);
+       index = (int)strlen(filespec) - 1;
+       if (index >= 0 && (filespec[index] == '/' || 
+          (filespec[index] == '\\' && !IsDBCSLeadByte(filespec[index-1]))))
+               filespec[index] = '\0';
+       strcat(filespec, "/*");
+
+       dirp = (DIR *) malloc(sizeof(DIR));
+       dirp->offset = 0;
+       dirp->finished = 0;
+
+       if ((handle = _findfirst(filespec, &(dirp->fileinfo))) < 0) 
+       {
+               if (errno == ENOENT || errno == EINVAL) 
+                       dirp->finished = 1;
+               else 
+               {
+                       free(dirp);
+                       free(filespec);
+                       return NULL;
+               }
+       }
+       dirp->dirname = strdup(dir);
+       dirp->handle = handle;
+       free(filespec);
+
+       return dirp;
+}
+
+int closedir(DIR *dp)
+{
+       int iret = -1;
+       if (!dp)
+               return iret;
+       iret = _findclose(dp->handle);
+       if (iret == 0 && dp->dirname)
+               free(dp->dirname);
+       if (iret == 0 && dp)
+               free(dp);
+
+       return iret;
+}
+
+struct dirent *readdir(DIR *dp)
+{
+       if (!dp || dp->finished)
+               return NULL;
+
+       if (dp->offset != 0) 
+       {
+               if (_findnext(dp->handle, &(dp->fileinfo)) < 0) 
+               {
+                       dp->finished = 1;
+                       return NULL;
+               }
+       }
+       dp->offset++;
+
+       strcpy(dp->dent.d_name, dp->fileinfo.name);/*, _MAX_FNAME+1);*/
+       dp->dent.d_ino = 1;
+       dp->dent.d_reclen = (unsigned short)strlen(dp->dent.d_name);
+       dp->dent.d_off = dp->offset;
+
+       return &(dp->dent);
+}
+
+int readdir_r(DIR *dp, struct dirent *entry, struct dirent **result)
+{
+       if (!dp || dp->finished) 
+       {
+               *result = NULL;
+               return -1;
+       }
+
+       if (dp->offset != 0) 
+       {
+               if (_findnext(dp->handle, &(dp->fileinfo)) < 0) 
+               {
+                       dp->finished = 1;
+                       *result = NULL;
+                       return -1;
+               }
+       }
+       dp->offset++;
+
+       strcpy(dp->dent.d_name, dp->fileinfo.name);/*, _MAX_FNAME+1);*/
+       dp->dent.d_ino = 1;
+       dp->dent.d_reclen = (unsigned short)strlen(dp->dent.d_name);
+       dp->dent.d_off = dp->offset;
+
+       memcpy(entry, &dp->dent, sizeof(*entry));
+
+       *result = &dp->dent;
+
+       return 0;
+}
+
+int rewinddir(DIR *dp)
+{
+       char    *filespec;
+       long    handle;
+       int             index;
+
+       _findclose(dp->handle);
+
+       dp->offset = 0;
+       dp->finished = 0;
+
+       filespec = (char *)malloc(strlen(dp->dirname) + 2 + 1);
+       strcpy(filespec, dp->dirname);
+       index = (int)(strlen(filespec) - 1);
+       if (index >= 0 && (filespec[index] == '/' || filespec[index] == '\\'))
+               filespec[index] = '\0';
+       strcat(filespec, "/*");
+
+       if ((handle = _findfirst(filespec, &(dp->fileinfo))) < 0) 
+       {
+               if (errno == ENOENT || errno == EINVAL)
+                       dp->finished = 1;
+       }
+       dp->handle = handle;
+       free(filespec);
+
+       return 0;
+}
diff --git a/xbmc/pvrclients/vdr-streamdev/windows/dirent.h b/xbmc/pvrclients/vdr-streamdev/windows/dirent.h
new file mode 100644 (file)
index 0000000..85db134
--- /dev/null
@@ -0,0 +1,103 @@
+/*****************************************************************************
+ * dirent.h - dirent API for Microsoft Visual Studio
+ *
+ * Copyright (C) 2006 Toni Ronkko
+ *
+ * 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 TONI RONKKO 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.
+ *
+ * Dec 15, 2009, John Cunningham
+ * Added rewinddir member function
+ *
+ * Jan 18, 2008, Toni Ronkko
+ * Using FindFirstFileA and WIN32_FIND_DATAA to avoid converting string
+ * between multi-byte and unicode representations.  This makes the
+ * code simpler and also allows the code to be compiled under MingW.  Thanks
+ * to Azriel Fasten for the suggestion.
+ *
+ * Mar 4, 2007, Toni Ronkko
+ * Bug fix: due to the strncpy_s() function this file only compiled in
+ * Visual Studio 2005.  Using the new string functions only when the
+ * compiler version allows.
+ *
+ * Nov  2, 2006, Toni Ronkko
+ * Major update: removed support for Watcom C, MS-DOS and Turbo C to
+ * simplify the file, updated the code to compile cleanly on Visual
+ * Studio 2005 with both unicode and multi-byte character strings,
+ * removed rewinddir() as it had a bug.
+ *
+ * Aug 20, 2006, Toni Ronkko
+ * Removed all remarks about MSVC 1.0, which is antiqued now.  Simplified
+ * comments by removing SGML tags.
+ *
+ * May 14 2002, Toni Ronkko
+ * Embedded the function definitions directly to the header so that no
+ * source modules need to be included in the Visual Studio project.  Removed
+ * all the dependencies to other projects so that this very header can be
+ * used independently.
+ *
+ * May 28 1998, Toni Ronkko
+ * First version.
+ *****************************************************************************/
+#ifndef DIRENT_H
+#define DIRENT_H
+
+#include <windows.h>
+#include <string.h>
+#include <assert.h>
+
+/* struct dirent - same as Unix dirent.h */
+struct dirent 
+{
+       long                            d_ino;                                          /* inode number (always 1 in WIN32) */
+       off_t                           d_off;                                  /* offset to this dirent */
+       unsigned short          d_reclen;               /* length of d_name */
+       char                            d_name[_MAX_FNAME + 1]; /* filename (null terminated) */
+       /*unsigned char         d_type;*/               /*type of file*/
+};
+
+
+/* def struct DIR - different from Unix DIR */
+typedef struct
+{
+       long                            handle;                                         /* _findfirst/_findnext handle */
+       short                           offset;                                         /* offset into directory */
+       short                           finished;                                               /* 1 if there are not more files */
+       struct _finddata_t      fileinfo;               /* from _findfirst/_findnext */
+       char                            *dirname;                                               /* the dir we are reading */
+       struct dirent           dent;                                   /* the dirent to return */
+} DIR;
+
+/* Function prototypes */
+DIR *opendir(const char *);
+struct dirent *readdir(DIR *);
+int readdir_r(DIR *, struct dirent *, struct dirent **);
+int closedir(DIR *);
+int rewinddir(DIR *);
+
+
+/* Use the new safe string functions introduced in Visual Studio 2005 */
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+# define STRNCPY(dest,src,size) strncpy_s((dest),(size),(src),_TRUNCATE)
+#else
+# define STRNCPY(dest,src,size) strncpy((dest),(src),(size))
+#endif
+
+
+#endif /*DIRENT_H*/
diff --git a/xbmc/pvrclients/vdr-streamdev/windows/getline.cpp b/xbmc/pvrclients/vdr-streamdev/windows/getline.cpp
new file mode 100644 (file)
index 0000000..c7daea5
--- /dev/null
@@ -0,0 +1,149 @@
+/* getline.c -- Replacement for GNU C library function getline
+
+Copyright (C) 1993 Free Software Foundation, Inc.
+
+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 of the
+License, 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.  */
+
+/* Written by Jan Brittenson, bson@gnu.ai.mit.edu.  */
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+#include "getline.h"
+
+/* Always add at least this many bytes when extending the buffer.  */
+#define MIN_CHUNK 64
+
+/* Read up to (and including) a TERMINATOR from STREAM into *LINEPTR
+   + OFFSET (and null-terminate it).  If LIMIT is non-negative, then
+   read no more than LIMIT chars.
+
+   *LINEPTR is a pointer returned from malloc (or NULL), pointing to
+   *N characters of space.  It is realloc'd as necessary.  
+
+   Return the number of characters read (not including the null
+   terminator), or -1 on error or EOF.  On a -1 return, the caller
+   should check feof(), if not then errno has been set to indicate the
+   error.  */
+
+int getstr(char **lineptr, size_t *n, FILE *stream, int terminator, int offset, int limit)
+{
+  int nchars_avail;            /* Allocated but unused chars in *LINEPTR.  */
+  char *read_pos;              /* Where we're reading into *LINEPTR. */
+  int ret;
+
+  if (!lineptr || !n || !stream)
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
+  if (!*lineptr)
+    {
+      *n = MIN_CHUNK;
+      *lineptr = (char *)malloc (*n);
+      if (!*lineptr)
+       {
+         errno = ENOMEM;
+         return -1;
+       }
+      *lineptr[0] = '\0';
+    }
+
+  nchars_avail = *n - offset;
+  read_pos = *lineptr + offset;
+
+  for (;;)
+    {
+      int save_errno;
+      register int c;
+
+      if (limit == 0)
+          break;
+      else
+      {
+          c = getc (stream);
+
+          /* If limit is negative, then we shouldn't pay attention to
+             it, so decrement only if positive. */
+          if (limit > 0)
+              limit--;
+      }
+
+      save_errno = errno;
+
+      /* We always want at least one char left in the buffer, since we
+        always (unless we get an error while reading the first char)
+        NUL-terminate the line buffer.  */
+
+      assert((*lineptr + *n) == (read_pos + nchars_avail));
+      if (nchars_avail < 2)
+       {
+         if (*n > MIN_CHUNK)
+           *n *= 2;
+         else
+           *n += MIN_CHUNK;
+
+         nchars_avail = *n + *lineptr - read_pos;
+         *lineptr = (char *)realloc (*lineptr, *n);
+         if (!*lineptr)
+           {
+             errno = ENOMEM;
+             return -1;
+           }
+         read_pos = *n - nchars_avail + *lineptr;
+         assert((*lineptr + *n) == (read_pos + nchars_avail));
+       }
+
+      if (ferror (stream))
+       {
+         /* Might like to return partial line, but there is no
+            place for us to store errno.  And we don't want to just
+            lose errno.  */
+         errno = save_errno;
+         return -1;
+       }
+
+      if (c == EOF)
+       {
+         /* Return partial line, if any.  */
+         if (read_pos == *lineptr)
+           return -1;
+         else
+           break;
+       }
+
+      *read_pos++ = c;
+      nchars_avail--;
+
+      if (c == terminator)
+       /* Return the line.  */
+       break;
+    }
+
+  /* Done - NUL terminate and return the number of chars read.  */
+  *read_pos = '\0';
+
+  ret = read_pos - (*lineptr + offset);
+  return ret;
+}
+
+int getline(char **lineptr, size_t *n, FILE *stream)
+{
+  return getstr (lineptr, n, stream, '\n', 0, GETLINE_NO_LIMIT);
+}
+
+int getline_safe(char **lineptr, size_t *n, FILE *stream, int limit)
+{
+  return getstr (lineptr, n, stream, '\n', 0, limit);
+}
diff --git a/xbmc/pvrclients/vdr-streamdev/windows/getline.h b/xbmc/pvrclients/vdr-streamdev/windows/getline.h
new file mode 100644 (file)
index 0000000..ce1b49c
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef _getline_h_
+#define _getline_h_
+
+#include <stdio.h>
+
+#define GETLINE_NO_LIMIT -1
+
+int getline(char **_lineptr, size_t *_n, FILE *_stream);
+int getline_safe(char **_lineptr, size_t *_n, FILE *_stream, int limit);
+int getstr(char **_lineptr, size_t *_n, FILE *_stream, int _terminator, int _offset, int limit);
+
+#endif /* _getline_h_ */
diff --git a/xbmc/pvrclients/vdr-streamdev/windows/pvrclient-vdr_os_windows.cpp b/xbmc/pvrclients/vdr-streamdev/windows/pvrclient-vdr_os_windows.cpp
new file mode 100644 (file)
index 0000000..aac13a1
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "pvrclient-vdr_os_windows.h"
+#include <sys/timeb.h>
+
+THREADLOCAL int ws32_result;
+THREADLOCAL int _so_err;
+THREADLOCAL int _so_err_siz = sizeof(int);
+
+int gettimeofday(struct timeval *pcur_time, struct timezone *tz)
+{
+  struct _timeb current;
+
+  _ftime(&current);
+
+  pcur_time->tv_sec = current.time;
+  pcur_time->tv_usec = current.millitm * 1000L;
+  if (tz)
+  {
+    tz->tz_minuteswest = current.timezone;     /* minutes west of Greenwich  */
+    tz->tz_dsttime = current.dstflag;  /* type of dst correction  */
+  }
+  return 0;
+}
diff --git a/xbmc/pvrclients/vdr-streamdev/windows/pvrclient-vdr_os_windows.h b/xbmc/pvrclients/vdr-streamdev/windows/pvrclient-vdr_os_windows.h
new file mode 100644 (file)
index 0000000..15037e9
--- /dev/null
@@ -0,0 +1,367 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef PVRCLIENT_VDR_OS_WIN_H
+#define PVRCLIENT_VDR_OS_WIN_H
+
+#if defined _FILE_OFFSET_BITS && _FILE_OFFSET_BITS == 64
+# define __USE_FILE_OFFSET64   1
+#endif
+
+#include "getline.h"
+
+typedef int ssize_t;
+typedef int mode_t;
+typedef int bool_t;
+typedef signed __int8 int8_t;
+typedef signed __int16 int16_t;
+typedef signed __int32 int32_t;
+typedef signed __int64 int64_t;
+typedef unsigned __int8 uint8_t;
+typedef unsigned __int16 uint16_t;
+typedef unsigned __int32 uint32_t;
+typedef unsigned __int64 uint64_t;
+
+#if defined __USE_FILE_OFFSET64
+typedef int64_t off_t;
+typedef uint64_t ino_t;
+#else
+typedef long off_t;
+#endif
+
+#define NAME_MAX         255   /* # chars in a file name */
+#define MAXPATHLEN       255
+
+#ifndef S_ISLNK
+# define S_ISLNK(x) 0
+#endif
+
+
+/* Some tricks for MS Compilers */
+#define THREADLOCAL __declspec(thread)
+
+#ifndef DEFFILEMODE
+#define DEFFILEMODE 0
+#endif
+
+#define alloca _alloca
+#define chdir _chdir
+#define dup _dup
+#define dup2 _dup2
+#define fdopen _fdopen
+#define fileno _fileno
+#define getcwd _getcwd
+#define getpid _getpid
+#define ioctl ioctlsocket
+#define mkdir(p) _mkdir(p)
+#define mktemp _mktemp
+#define open _open
+#define pclose _pclose
+#define popen _popen
+#define putenv _putenv
+#define setmode _setmode
+#define sleep(t) Sleep((t)*1000)
+#define usleep(t) Sleep((t)/1000)
+#define snprintf _snprintf
+#define strcasecmp _stricmp
+#define strdup _strdup
+#define strlwr _strlwr
+#define strncasecmp _strnicmp
+#define tempnam _tempnam
+#define umask _umask
+#define unlink _unlink
+#define close _close
+
+#define O_RDONLY        _O_RDONLY
+#define O_WRONLY        _O_WRONLY
+#define O_RDWR          _O_RDWR
+#define O_APPEND        _O_APPEND
+
+#define O_CREAT         _O_CREAT
+#define O_TRUNC         _O_TRUNC
+#define O_EXCL          _O_EXCL
+
+#define O_TEXT          _O_TEXT
+#define O_BINARY        _O_BINARY
+#define O_RAW           _O_BINARY
+#define O_TEMPORARY     _O_TEMPORARY
+#define O_NOINHERIT     _O_NOINHERIT
+#define O_SEQUENTIAL    _O_SEQUENTIAL
+#define O_RANDOM        _O_RANDOM
+#define O_NDELAY       0
+
+#define S_IRWXO 007
+#define        S_ISDIR(m) (((m) & _S_IFDIR) == _S_IFDIR)
+#define        S_ISREG(m) (((m) & _S_IFREG) == _S_IFREG)
+
+#ifndef SIGHUP
+#define        SIGHUP  1       /* hangup */
+#endif
+#ifndef SIGBUS
+#define        SIGBUS  7       /* bus error */
+#endif
+#ifndef SIGKILL
+#define        SIGKILL 9       /* kill (cannot be caught or ignored) */
+#endif
+#ifndef        SIGSEGV
+#define        SIGSEGV 11      /* segment violation */
+#endif
+#ifndef SIGPIPE
+#define        SIGPIPE 13      /* write on a pipe with no one to read it */
+#endif
+#ifndef SIGCHLD
+#define        SIGCHLD 20      /* to parent on child stop or exit */
+#endif
+#ifndef SIGUSR1
+#define SIGUSR1 30     /* user defined signal 1 */
+#endif
+#ifndef SIGUSR2
+#define SIGUSR2 31     /* user defined signal 2 */
+#endif
+
+typedef unsigned short in_port_t;
+typedef unsigned short int ushort;
+typedef unsigned int in_addr_t;
+typedef int socklen_t;
+typedef int uid_t;
+typedef int gid_t;
+
+#if defined __USE_FILE_OFFSET64
+#define stat _stati64
+#define lseek _lseeki64
+#define fstat _fstati64
+#define tell _telli64
+#else
+#define stat _stat
+#define lseek _lseek
+#define fstat _fstat
+#define tell _tell
+#endif
+
+#define atoll _atoi64
+#ifndef va_copy
+#define va_copy(x, y) x = y
+#endif
+
+#include <stddef.h>
+#include <process.h>
+#if defined(_MSC_VER) /* Microsoft C Compiler ONLY */
+#pragma warning (push)
+/* Hack to suppress compiler warnings on FD_SET() & FD_CLR() */
+#pragma warning (disable:4142)
+/* Suppress compiler warnings about double definition of _WINSOCKAPI_ */
+#pragma warning (disable:4005)
+#endif
+/* prevent inclusion of wingdi.h */
+#define NOGDI
+#include <ws2spi.h>
+#include <ws2ipdef.h>
+#if defined(_MSC_VER) /* Microsoft C Compiler ONLY */
+#pragma warning (pop)
+#endif
+#include <io.h>
+#include <stdlib.h>
+#include <errno.h>
+#include "pthread_win32/pthread.h"
+
+typedef char * caddr_t;
+
+#undef FD_CLOSE
+#undef FD_OPEN
+#undef FD_READ
+#undef FD_WRITE
+
+#if (_MSC_VER < 1600)
+// Not yet defined in errno.h under VS2008
+#define EISCONN WSAEISCONN
+#define EINPROGRESS WSAEINPROGRESS
+#define EWOULDBLOCK WSAEWOULDBLOCK
+#define EALREADY WSAEALREADY
+#ifndef ETIMEDOUT
+#define ETIMEDOUT WSAETIMEDOUT
+#endif
+#define ECONNABORTED WSAECONNABORTED
+#define ECONNREFUSED WSAECONNREFUSED
+#define ECONNRESET WSAECONNRESET
+#define ERESTART WSATRY_AGAIN
+#define ENOTCONN WSAENOTCONN
+#define ENOBUFS WSAENOBUFS
+#define EOVERFLOW 2006
+
+#define INT64_MAX _I64_MAX
+#define INT64_MIN _I64_MIN
+#endif
+
+#undef h_errno
+#define h_errno errno /* we'll set it ourselves */
+
+struct timezone
+{
+  int  tz_minuteswest; /* minutes west of Greenwich */
+  int  tz_dsttime;     /* type of dst correction */
+};
+
+extern int gettimeofday(struct timeval *, struct timezone *);
+
+/* Unix socket emulation macros */
+#define __close closesocket
+
+#undef FD_CLR
+#define FD_CLR(fd, set) do { \
+    u_int __i; \
+    SOCKET __sock = _get_osfhandle(fd); \
+    for (__i = 0; __i < ((fd_set FAR *)(set))->fd_count ; __i++) { \
+        if (((fd_set FAR *)(set))->fd_array[__i] == __sock) { \
+            while (__i < ((fd_set FAR *)(set))->fd_count-1) { \
+                ((fd_set FAR *)(set))->fd_array[__i] = \
+                    ((fd_set FAR *)(set))->fd_array[__i+1]; \
+                __i++; \
+            } \
+            ((fd_set FAR *)(set))->fd_count--; \
+            break; \
+        } \
+    } \
+} while(0)
+
+#undef FD_SET
+#define FD_SET(fd, set) do { \
+    u_int __i; \
+    SOCKET __sock = _get_osfhandle(fd); \
+    for (__i = 0; __i < ((fd_set FAR *)(set))->fd_count; __i++) { \
+        if (((fd_set FAR *)(set))->fd_array[__i] == (__sock)) { \
+            break; \
+        } \
+    } \
+    if (__i == ((fd_set FAR *)(set))->fd_count) { \
+        if (((fd_set FAR *)(set))->fd_count < FD_SETSIZE) { \
+            ((fd_set FAR *)(set))->fd_array[__i] = (__sock); \
+            ((fd_set FAR *)(set))->fd_count++; \
+        } \
+    } \
+} while(0)
+
+#undef FD_ISSET
+#define FD_ISSET(fd, set) __WSAFDIsSet((SOCKET)(_get_osfhandle(fd)), (fd_set FAR *)(set))
+
+extern THREADLOCAL int ws32_result;
+#define __poll(f,n,t) \
+       (SOCKET_ERROR == WSAPoll(f,n,t) ? \
+       (errno = WSAGetLastError()), -1 : 0)
+#define __socket(f,t,p) \
+       (INVALID_SOCKET == ((SOCKET)(ws32_result = (int)socket(f,t,p))) ? \
+       ((WSAEMFILE == (errno = WSAGetLastError()) ? errno = EMFILE : -1), -1) : \
+       (SOCKET)_open_osfhandle(ws32_result,0))
+#define __accept(s,a,l) \
+       (INVALID_SOCKET == ((SOCKET)(ws32_result = (int)accept(_get_osfhandle(s),a,l))) ? \
+       ((WSAEMFILE == (errno = WSAGetLastError()) ? errno = EMFILE : -1), -1) : \
+       (SOCKET)_open_osfhandle(ws32_result,0))
+#define __bind(s,n,l) \
+       ((SOCKET_ERROR == ::bind(_get_osfhandle(s),n,l)) ? \
+       (errno = WSAGetLastError()), -1 : 0)
+#define __connect(s,n,l) \
+       (SOCKET_ERROR == connect(_get_osfhandle(s),n,l) ? \
+       (WSAEMFILE == (errno = WSAGetLastError()) ? errno = EMFILE : -1, -1) : 0)
+#define __listen(s,b) \
+       (SOCKET_ERROR == listen(_get_osfhandle(s),b) ? \
+       (WSAEMFILE == (errno = WSAGetLastError()) ? errno = EMFILE : -1, -1) : 0)
+#define __shutdown(s,h) \
+       (SOCKET_ERROR == shutdown(_get_osfhandle(s),h) ? \
+       (errno = WSAGetLastError()), -1 : 0)
+#define __select(n,r,w,e,t) \
+       (SOCKET_ERROR == (ws32_result = select(n,r,w,e,t)) ? \
+       (errno = WSAGetLastError()), -1 : ws32_result)
+#define __recv(s,b,l,f) \
+       (SOCKET_ERROR == (ws32_result = recv(_get_osfhandle(s),b,l,f)) ? \
+  (errno = WSAGetLastError()), -1 : ws32_result)
+#define __recvfrom(s,b,l,f,fr,frl) \
+       (SOCKET_ERROR == (ws32_result = recvfrom(_get_osfhandle(s),b,l,f,fr,frl)) ? \
+       (errno = WSAGetLastError()), -1 : ws32_result)
+#define __send(s,b,l,f) \
+       (SOCKET_ERROR == (ws32_result = send(_get_osfhandle(s),b,l,f)) ? \
+       (errno = WSAGetLastError()), -1 : ws32_result)
+#define __sendto(s,b,l,f,t,tl) \
+       (SOCKET_ERROR == (ws32_result = sendto(_get_osfhandle(s),b,l,f,t,tl)) ? \
+       (errno = WSAGetLastError()), -1 : ws32_result)
+#define __getsockname(s,n,l) \
+       (SOCKET_ERROR == getsockname(_get_osfhandle(s),n,l) ? \
+       (errno = WSAGetLastError()), -1 : 0)
+#define __getpeername(s,n,l) \
+       (SOCKET_ERROR == getpeername(_get_osfhandle(s),n,l) ? \
+       (errno = WSAGetLastError()), -1 : 0)
+#define __getsockopt(s,l,o,v,n) \
+       (Sleep(1), SOCKET_ERROR == getsockopt(_get_osfhandle(s),l,o,(char*)v,n) ? \
+       (errno = WSAGetLastError()), -1 : 0)
+#define __setsockopt(s,l,o,v,n) \
+       (SOCKET_ERROR == setsockopt(_get_osfhandle(s),l,o,v,n) ? \
+       (errno = WSAGetLastError()), -1 : 0)
+#define __ioctlsocket(s,c,a) \
+       (SOCKET_ERROR == ioctlsocket(_get_osfhandle(s),c,a) ? \
+       (errno = WSAGetLastError()), -1 : 0)
+#define __gethostname(n,l) \
+       (SOCKET_ERROR == gethostname(n,l) ? \
+       (errno = WSAGetLastError()), -1 : 0)
+#define __gethostbyname(n) \
+       (NULL == ((HOSTENT FAR*)(ws32_result = (int)gethostbyname(n))) ? \
+       (errno = WSAGetLastError()), NULL : (HOSTENT FAR*)ws32_result)
+#define __getservbyname(n,p) \
+       (NULL == ((SERVENT FAR*)(ws32_result = (int)getservbyname(n,p))) ? \
+       (errno = WSAGetLastError()), NULL : (SERVENT FAR*)ws32_result)
+#define __gethostbyaddr(a,l,t) \
+       (NULL == ((HOSTENT FAR*)(ws32_result = (int)gethostbyaddr(a,l,t))) ? \
+       (errno = WSAGetLastError()), NULL : (HOSTENT FAR*)ws32_result)
+extern THREADLOCAL int _so_err;
+extern THREADLOCAL int _so_err_siz;
+#define __read(fd,buf,siz) \
+       (_so_err_siz = sizeof(_so_err), \
+       __getsockopt((fd),SOL_SOCKET,SO_ERROR,&_so_err,&_so_err_siz) \
+       == 0 ? __recv((fd),(char *)(buf),(siz),0) : _read((fd),(char *)(buf),(siz)))
+#define __write(fd,buf,siz) \
+       (_so_err_siz = sizeof(_so_err), \
+       __getsockopt((fd),SOL_SOCKET,SO_ERROR,&_so_err,&_so_err_siz) \
+       == 0 ? __send((fd),(const char *)(buf),(siz),0) : _write((fd),(const char *)(buf),(siz)))
+
+
+#if !defined(__MINGW32__)
+#define strtok_r( _s, _sep, _lasts ) \
+       ( *(_lasts) = strtok( (_s), (_sep) ) )
+#endif /* !__MINGW32__ */
+
+#define asctime_r( _tm, _buf ) \
+       ( strcpy( (_buf), asctime( (_tm) ) ), \
+         (_buf) )
+
+#define ctime_r( _clock, _buf ) \
+       ( strcpy( (_buf), ctime( (_clock) ) ),  \
+         (_buf) )
+
+#define gmtime_r( _clock, _result ) \
+       ( *(_result) = *gmtime( (_clock) ), \
+         (_result) )
+
+#define localtime_r( _clock, _result ) \
+       ( *(_result) = *localtime( (_clock) ), \
+         (_result) )
+
+#define rand_r( _seed ) \
+       ( _seed == _seed? rand() : rand() )
+
+#endif
diff --git a/xbmc/pvrclients/vdr-vnsi/COPYING b/xbmc/pvrclients/vdr-vnsi/COPYING
new file mode 100644 (file)
index 0000000..f90922e
--- /dev/null
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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 of the License, 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 this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/xbmc/pvrclients/vdr-vnsi/Makefile.in b/xbmc/pvrclients/vdr-vnsi/Makefile.in
new file mode 100644 (file)
index 0000000..0ce15b1
--- /dev/null
@@ -0,0 +1,28 @@
+#
+# Makefile for the XBMC Video Disk Recorder PVR AddOn
+#
+# See the README for copyright information and
+# how to reach the author.
+#
+
+LIBS   =-ldl
+LIBDIR = ../../../addons/pvr.vdr.vnsi
+LIB    = ../../../addons/pvr.vdr.vnsi/XBMC_VDR_vnsi.pvr
+
+SRCS   = client.cpp \
+       VNSIChannelScan.cpp \
+       VNSIData.cpp \
+       VNSIDemux.cpp \
+       VNSIRecording.cpp \
+       VNSISession.cpp \
+       recordings.cpp \
+       requestpacket.cpp \
+       responsepacket.cpp \
+       thread.cpp \
+       tools.cpp
+
+include ../Makefile.include
+
+ifeq ($(findstring Darwin,$(shell uname -a)), Darwin)
+DEFINES += -isysroot /Developer/SDKs/MacOSX10.4u.sdk -mmacosx-version-min=10.4 -fno-common
+endif
diff --git a/xbmc/pvrclients/vdr-vnsi/README b/xbmc/pvrclients/vdr-vnsi/README
new file mode 100644 (file)
index 0000000..c4a8676
--- /dev/null
@@ -0,0 +1,71 @@
+XBMC Video Disk Recorder ('VDR') PVR Add-on
+------------------------------------------
+
+THIS IS A PRELIMINARY README AND IS SUBJECT TO CHANGE!!!
+
+Written by:                  Alwin Esch (Team XBMC)
+
+Project's homepage:          
+
+Latest version available at: 
+
+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 of the License, or
+(at your option) any later version.
+See the file COPYING for more information.
+
+------------------------------------------
+
+This is a PVR Add-on for XBMC to add VDR (http://www.cadsoft.de/vdr) as a TV/PVR Backend to
+XBMC based upon the new VNSI Protocol.
+
+It want to add support for Live TV watching, replaying of Recordings, programming Timers and.
+EPG TV Guide to use on same computer or over the Network.
+
+The connection of this AddOn depend upon a installed VNSI-Server-Plugin on the VDR which
+is included inside the "vdr-plugin-vnsiserver" directory of this Addon.
+VDR itself need no patches or modification to use all current features.
+
+VDR Versions older as 1.6.0 are not supported by this plugin.
+
+------------------------------------------
+PLUGIN INSTALLATION INSTRUCTIONS:
+
+Copy the "vdr-plugin-vnsiserver" directory to your "VDR/plugins/src" directory and
+rename it to "vnsiserver".
+
+Run a "make plugins" and depented on your environment a "make install".
+
+And add "-P'vnsiserver'" to your startup options.
+
+
+------------------------------------------
+CHANNEL SCANNING
+
+For channel scan's a modified version of the wirbelscan plugin for VDR (Version dev-0.0.5-pre11e) is
+required. You can download the orginal source from here "http://wirbel.htpc-forum.de/wirbelscan/index2.html".
+
+The VNSI communicate with wirbelscan over VDR's plugin service interface, to add this feature
+you must patch wirbelscan with the file in "patches/vdr-wirbelscan-0.0.5-pre11e-AddServiceInterface.diff".
+
+Please load VDR with VNSI plugin together with the modified wirbelscan plugin and you can access the
+"Search Channels" option inside "Settings->TV->General"
+
+Scanning can take up to 50 minutes dependet on signal type and signal quality.
+    * DVB-T ~ 5 min
+    * DVB-C ~ 30 min (Symbolrate=AUTO, QAM=AUTO)
+    * DVB-S/S2 ~ 50 min (depend on Satellite, Beam, Hardware)
+    * Analog ~ 5 min
+
+Note: Please notice the warning on the wirbelscan plugin homepage:
+      "Development Version - kein Support. Benutzung auf eigene Verantwortung."
+      "Development Version - no support. Use at your own risk."
+      This means, the time how long the scan run and problems on VDR side are dependet on wirbelscan
+      and not part of VNSI.
+
+
+------------------------------------------
+
+Links:
+VDR:                     http://www.cadsoft.de/vdr
diff --git a/xbmc/pvrclients/vdr-vnsi/StdString.h b/xbmc/pvrclients/vdr-vnsi/StdString.h
new file mode 100644 (file)
index 0000000..8ee3060
--- /dev/null
@@ -0,0 +1,4333 @@
+#pragma once
+#include <string>
+#include <stdint.h>
+#if !defined(_LINUX)
+#include <windows.h>
+#include "pvrclient-vdr_os.h"
+#endif
+
+// =============================================================================
+//  FILE:  StdString.h
+//  AUTHOR:  Joe O'Leary (with outside help noted in comments)
+//
+//    If you find any bugs in this code, please let me know:
+//
+//        jmoleary@earthlink.net
+//        http://www.joeo.net/stdstring.htm (a bit outdated)
+//
+//      The latest version of this code should always be available at the
+//      following link:
+//
+//              http://www.joeo.net/code/StdString.zip (Dec 6, 2003)
+//
+//
+//  REMARKS:
+//    This header file declares the CStdStr template.  This template derives
+//    the Standard C++ Library basic_string<> template and add to it the
+//    the following conveniences:
+//      - The full MFC CString set of functions (including implicit cast)
+//      - writing to/reading from COM IStream interfaces
+//      - Functional objects for use in STL algorithms
+//
+//    From this template, we intstantiate two classes:  CStdStringA and
+//    CStdStringW.  The name "CStdString" is just a #define of one of these,
+//    based upone the UNICODE macro setting
+//
+//    This header also declares our own version of the MFC/ATL UNICODE-MBCS
+//    conversion macros.  Our version looks exactly like the Microsoft's to
+//    facilitate portability.
+//
+//  NOTE:
+//    If you you use this in an MFC or ATL build, you should include either
+//    afx.h or atlbase.h first, as appropriate.
+//
+//  PEOPLE WHO HAVE CONTRIBUTED TO THIS CLASS:
+//
+//    Several people have helped me iron out problems and othewise improve
+//    this class.  OK, this is a long list but in my own defense, this code
+//    has undergone two major rewrites.  Many of the improvements became
+//    necessary after I rewrote the code as a template.  Others helped me
+//    improve the CString facade.
+//
+//    Anyway, these people are (in chronological order):
+//
+//      - Pete the Plumber (???)
+//      - Julian Selman
+//      - Chris (of Melbsys)
+//      - Dave Plummer
+//      - John C Sipos
+//      - Chris Sells
+//      - Nigel Nunn
+//      - Fan Xia
+//      - Matthew Williams
+//      - Carl Engman
+//      - Mark Zeren
+//      - Craig Watson
+//      - Rich Zuris
+//      - Karim Ratib
+//      - Chris Conti
+//      - Baptiste Lepilleur
+//      - Greg Pickles
+//      - Jim Cline
+//      - Jeff Kohn
+//      - Todd Heckel
+//      - Ullrich Poll�hne
+//      - Joe Vitaterna
+//      - Joe Woodbury
+//      - Aaron (no last name)
+//      - Joldakowski (???)
+//      - Scott Hathaway
+//      - Eric Nitzche
+//      - Pablo Presedo
+//      - Farrokh Nejadlotfi
+//      - Jason Mills
+//      - Igor Kholodov
+//      - Mike Crusader
+//      - John James
+//      - Wang Haifeng
+//      - Tim Dowty
+//          - Arnt Witteveen
+//          - Glen Maynard
+//          - Paul DeMarco
+//          - Bagira (full name?)
+//          - Ronny Schulz
+//          - Jakko Van Hunen
+//      - Charles Godwin
+//      - Henk Demper
+//      - Greg Marr
+//      - Bill Carducci
+//      - Brian Groose
+//      - MKingman
+//      - Don Beusee
+//
+//  REVISION HISTORY
+//
+//    2005-JAN-10 - Thanks to Don Beusee for pointing out the danger in mapping
+//          length-checked formatting functions to non-length-checked
+//          CRT equivalents.  Also thanks to him for motivating me to
+//          optimize my implementation of Replace()
+//
+//    2004-APR-22 - A big, big thank you to "MKingman" (whoever you are) for
+//          finally spotting a silly little error in StdCodeCvt that
+//          has been causing me (and users of CStdString) problems for
+//          years in some relatively rare conversions.  I had reversed
+//          two length arguments.
+//
+//    2003-NOV-24 - Thanks to a bunch of people for helping me clean up many
+//          compiler warnings (and yes, even a couple of actual compiler
+//          errors).  These include Henk Demper for figuring out how
+//          to make the Intellisense work on with CStdString on VC6,
+//          something I was never able to do.  Greg Marr pointed out
+//          a compiler warning about an unreferenced symbol and a
+//          problem with my version of Load in MFC builds.  Bill
+//          Carducci took a lot of time with me to help me figure out
+//          why some implementations of the Standard C++ Library were
+//          returning error codes for apparently successful conversions
+//          between ASCII and UNICODE.  Finally thanks to Brian Groose
+//          for helping me fix compiler signed unsigned warnings in
+//          several functions.
+//
+//    2003-JUL-10 - Thanks to Charles Godwin for making me realize my 'FmtArg'
+//          fixes had inadvertently broken the DLL-export code (which is
+//                  normally commented out.  I had to move it up higher.  Also
+//          this helped me catch a bug in ssicoll that would prevent
+//                  compilation, otherwise.
+//
+//    2003-MAR-14 - Thanks to Jakko Van Hunen for pointing out a copy-and-paste
+//                  bug in one of the overloads of FmtArg.
+//
+//    2003-MAR-10 - Thanks to Ronny Schulz for (twice!) sending me some changes
+//                  to help CStdString build on SGI and for pointing out an
+//                  error in placement of my preprocessor macros for ssfmtmsg.
+//
+//    2002-NOV-26 - Thanks to Bagira for pointing out that my implementation of
+//                  SpanExcluding was not properly handling the case in which
+//                  the string did NOT contain any of the given characters
+//
+//    2002-OCT-21 - Many thanks to Paul DeMarco who was invaluable in helping me
+//                  get this code working with Borland's free compiler as well
+//                  as the Dev-C++ compiler (available free at SourceForge).
+//
+//    2002-SEP-13 - Thanks to Glen Maynard who helped me get rid of some loud
+//                  but harmless warnings that were showing up on g++.  Glen
+//                  also pointed out that some pre-declarations of FmtArg<>
+//                  specializations were unnecessary (and no good on G++)
+//
+//    2002-JUN-26 - Thanks to Arnt Witteveen for pointing out that I was using
+//                  static_cast<> in a place in which I should have been using
+//                  reinterpret_cast<> (the ctor for unsigned char strings).
+//                  That's what happens when I don't unit-test properly!
+//                  Arnt also noticed that CString was silently correcting the
+//                  'nCount' argument to Left() and Right() where CStdString was
+//                  not (and crashing if it was bad).  That is also now fixed!
+//
+//    2002-FEB-25 - Thanks to Tim Dowty for pointing out (and giving me the fix
+//          for) a conversion problem with non-ASCII MBCS characters.
+//          CStdString is now used in my favorite commercial MP3 player!
+//
+//    2001-DEC-06 - Thanks to Wang Haifeng for spotting a problem in one of the
+//          assignment operators (for _bstr_t) that would cause compiler
+//          errors when refcounting protection was turned off.
+//
+//    2001-NOV-27 - Remove calls to operator!= which involve reverse_iterators
+//          due to a conflict with the rel_ops operator!=.  Thanks to
+//          John James for pointing this out.
+//
+//    2001-OCT-29 - Added a minor range checking fix for the Mid function to
+//          make it as forgiving as CString's version is.  Thanks to
+//          Igor Kholodov for noticing this.
+//          - Added a specialization of std::swap for CStdString.  Thanks
+//          to Mike Crusader for suggesting this!  It's commented out
+//          because you're not supposed to inject your own code into the
+//          'std' namespace.  But if you don't care about that, it's
+//          there if you want it
+//          - Thanks to Jason Mills for catching a case where CString was
+//          more forgiving in the Delete() function than I was.
+//
+//    2001-JUN-06 - I was violating the Standard name lookup rules stated
+//          in [14.6.2(3)].  None of the compilers I've tried so
+//          far apparently caught this but HP-UX aCC 3.30 did.  The
+//          fix was to add 'this->' prefixes in many places.
+//          Thanks to Farrokh Nejadlotfi for this!
+//
+//    2001-APR-27 - StreamLoad was calculating the number of BYTES in one
+//          case, not characters.  Thanks to Pablo Presedo for this.
+//
+//    2001-FEB-23 - Replace() had a bug which caused infinite loops if the
+//          source string was empty.  Fixed thanks to Eric Nitzsche.
+//
+//    2001-FEB-23 - Scott Hathaway was a huge help in providing me with the
+//          ability to build CStdString on Sun Unix systems.  He
+//          sent me detailed build reports about what works and what
+//          does not.  If CStdString compiles on your Unix box, you
+//          can thank Scott for it.
+//
+//    2000-DEC-29 - Joldakowski noticed one overload of Insert failed to do a
+//          range check as CString's does.  Now fixed -- thanks!
+//
+//    2000-NOV-07 - Aaron pointed out that I was calling static member
+//          functions of char_traits via a temporary.  This was not
+//          technically wrong, but it was unnecessary and caused
+//          problems for poor old buggy VC5.  Thanks Aaron!
+//
+//    2000-JUL-11 - Joe Woodbury noted that the CString::Find docs don't match
+//          what the CString::Find code really ends up doing.   I was
+//          trying to match the docs.  Now I match the CString code
+//          - Joe also caught me truncating strings for GetBuffer() calls
+//          when the supplied length was less than the current length.
+//
+//    2000-MAY-25 - Better support for STLPORT's Standard library distribution
+//          - Got rid of the NSP macro - it interfered with Koenig lookup
+//          - Thanks to Joe Woodbury for catching a TrimLeft() bug that
+//          I introduced in January.  Empty strings were not getting
+//          trimmed
+//
+//    2000-APR-17 - Thanks to Joe Vitaterna for pointing out that ReverseFind
+//          is supposed to be a const function.
+//
+//    2000-MAR-07 - Thanks to Ullrich Poll�hne for catching a range bug in one
+//          of the overloads of assign.
+//
+//    2000-FEB-01 - You can now use CStdString on the Mac with CodeWarrior!
+//          Thanks to Todd Heckel for helping out with this.
+//
+//    2000-JAN-23 - Thanks to Jim Cline for pointing out how I could make the
+//          Trim() function more efficient.
+//          - Thanks to Jeff Kohn for prompting me to find and fix a typo
+//          in one of the addition operators that takes _bstr_t.
+//          - Got rid of the .CPP file -  you only need StdString.h now!
+//
+//    1999-DEC-22 - Thanks to Greg Pickles for helping me identify a problem
+//          with my implementation of CStdString::FormatV in which
+//          resulting string might not be properly NULL terminated.
+//
+//    1999-DEC-06 - Chris Conti pointed yet another basic_string<> assignment
+//          bug that MS has not fixed.  CStdString did nothing to fix
+//          it either but it does now!  The bug was: create a string
+//          longer than 31 characters, get a pointer to it (via c_str())
+//          and then assign that pointer to the original string object.
+//          The resulting string would be empty.  Not with CStdString!
+//
+//    1999-OCT-06 - BufferSet was erasing the string even when it was merely
+//          supposed to shrink it.  Fixed.  Thanks to Chris Conti.
+//          - Some of the Q172398 fixes were not checking for assignment-
+//          to-self.  Fixed.  Thanks to Baptiste Lepilleur.
+//
+//    1999-AUG-20 - Improved Load() function to be more efficient by using
+//          SizeOfResource().  Thanks to Rich Zuris for this.
+//          - Corrected resource ID constructor, again thanks to Rich.
+//          - Fixed a bug that occurred with UNICODE characters above
+//          the first 255 ANSI ones.  Thanks to Craig Watson.
+//          - Added missing overloads of TrimLeft() and TrimRight().
+//          Thanks to Karim Ratib for pointing them out
+//
+//    1999-JUL-21 - Made all calls to GetBuf() with no args check length first.
+//
+//    1999-JUL-10 - Improved MFC/ATL independence of conversion macros
+//          - Added SS_NO_REFCOUNT macro to allow you to disable any
+//          reference-counting your basic_string<> impl. may do.
+//          - Improved ReleaseBuffer() to be as forgiving as CString.
+//          Thanks for Fan Xia for helping me find this and to
+//          Matthew Williams for pointing it out directly.
+//
+//    1999-JUL-06 - Thanks to Nigel Nunn for catching a very sneaky bug in
+//          ToLower/ToUpper.  They should call GetBuf() instead of
+//          data() in order to ensure the changed string buffer is not
+//          reference-counted (in those implementations that refcount).
+//
+//    1999-JUL-01 - Added a true CString facade.  Now you can use CStdString as
+//          a drop-in replacement for CString.  If you find this useful,
+//          you can thank Chris Sells for finally convincing me to give
+//          in and implement it.
+//          - Changed operators << and >> (for MFC CArchive) to serialize
+//          EXACTLY as CString's do.  So now you can send a CString out
+//          to a CArchive and later read it in as a CStdString.   I have
+//          no idea why you would want to do this but you can.
+//
+//    1999-JUN-21 - Changed the CStdString class into the CStdStr template.
+//          - Fixed FormatV() to correctly decrement the loop counter.
+//          This was harmless bug but a bug nevertheless.  Thanks to
+//          Chris (of Melbsys) for pointing it out
+//          - Changed Format() to try a normal stack-based array before
+//          using to _alloca().
+//          - Updated the text conversion macros to properly use code
+//          pages and to fit in better in MFC/ATL builds.  In other
+//          words, I copied Microsoft's conversion stuff again.
+//          - Added equivalents of CString::GetBuffer, GetBufferSetLength
+//          - new sscpy() replacement of CStdString::CopyString()
+//          - a Trim() function that combines TrimRight() and TrimLeft().
+//
+//    1999-MAR-13 - Corrected the "NotSpace" functional object to use _istpace()
+//          instead of _isspace()   Thanks to Dave Plummer for this.
+//
+//    1999-FEB-26 - Removed errant line (left over from testing) that #defined
+//          _MFC_VER.  Thanks to John C Sipos for noticing this.
+//
+//    1999-FEB-03 - Fixed a bug in a rarely-used overload of operator+() that
+//          caused infinite recursion and stack overflow
+//          - Added member functions to simplify the process of
+//          persisting CStdStrings to/from DCOM IStream interfaces
+//          - Added functional objects (e.g. StdStringLessNoCase) that
+//          allow CStdStrings to be used as keys STL map objects with
+//          case-insensitive comparison
+//          - Added array indexing operators (i.e. operator[]).  I
+//          originally assumed that these were unnecessary and would be
+//          inherited from basic_string.  However, without them, Visual
+//          C++ complains about ambiguous overloads when you try to use
+//          them.  Thanks to Julian Selman to pointing this out.
+//
+//    1998-FEB-?? - Added overloads of assign() function to completely account
+//          for Q172398 bug.  Thanks to "Pete the Plumber" for this
+//
+//    1998-FEB-?? - Initial submission
+//
+// COPYRIGHT:
+//    2002 Joseph M. O'Leary.  This code is 100% free.  Use it anywhere you
+//      want.  Rewrite it, restructure it, whatever.  If you can write software
+//      that makes money off of it, good for you.  I kinda like capitalism.
+//      Please don't blame me if it causes your $30 billion dollar satellite
+//      explode in orbit.  If you redistribute it in any form, I'd appreciate it
+//      if you would leave this notice here.
+// =============================================================================
+
+// Avoid multiple inclusion
+
+#ifndef STDSTRING_H
+#define STDSTRING_H
+
+// When using VC, turn off browser references
+// Turn off unavoidable compiler warnings
+
+#if defined(_MSC_VER) && (_MSC_VER > 1100)
+  #pragma component(browser, off, references, "CStdString")
+  #pragma warning (disable : 4290) // C++ Exception Specification ignored
+  #pragma warning (disable : 4127) // Conditional expression is constant
+  #pragma warning (disable : 4097) // typedef name used as synonym for class name
+#endif
+
+// Borland warnings to turn off
+
+#ifdef __BORLANDC__
+    #pragma option push -w-inl
+//  #pragma warn -inl   // Turn off inline function warnings
+#endif
+
+// SS_IS_INTRESOURCE
+// -----------------
+//    A copy of IS_INTRESOURCE from VC7.  Because old VC6 version of winuser.h
+//    doesn't have this.
+
+#define SS_IS_INTRESOURCE(_r) (false)
+
+#if !defined (SS_ANSI) && defined(_MSC_VER)
+  #undef SS_IS_INTRESOURCE
+  #if defined(_WIN64)
+    #define SS_IS_INTRESOURCE(_r) (((unsigned __int64)(_r) >> 16) == 0)
+  #else
+    #define SS_IS_INTRESOURCE(_r) (((unsigned long)(_r) >> 16) == 0)
+  #endif
+#endif
+
+
+// MACRO: SS_UNSIGNED
+// ------------------
+//      This macro causes the addition of a constructor and assignment operator
+//      which take unsigned characters.  CString has such functions and in order
+//      to provide maximum CString-compatability, this code needs them as well.
+//      In practice you will likely never need these functions...
+
+//#define SS_UNSIGNED
+
+#ifdef SS_ALLOW_UNSIGNED_CHARS
+  #define SS_UNSIGNED
+#endif
+
+// MACRO: SS_SAFE_FORMAT
+// ---------------------
+//      This macro provides limited compatability with a questionable CString
+//      "feature".  You can define it in order to avoid a common problem that
+//      people encounter when switching from CString to CStdString.
+//
+//      To illustrate the problem -- With CString, you can do this:
+//
+//          CString sName("Joe");
+//          CString sTmp;
+//          sTmp.Format("My name is %s", sName);                    // WORKS!
+//
+//      However if you were to try this with CStdString, your program would
+//      crash.
+//
+//          CStdString sName("Joe");
+//          CStdString sTmp;
+//          sTmp.Format("My name is %s", sName);                    // CRASHES!
+//
+//      You must explicitly call c_str() or cast the object to the proper type
+//
+//          sTmp.Format("My name is %s", sName.c_str());            // WORKS!
+//          sTmp.Format("My name is %s", static_cast<PCSTR>(sName));// WORKS!
+//          sTmp.Format("My name is %s", (PCSTR)sName);        // WORKS!
+//
+//      This is because it is illegal to pass anything but a POD type as a
+//      variadic argument to a variadic function (i.e. as one of the "..."
+//      arguments).  The type const char* is a POD type.  The type CStdString
+//      is not.  Of course, neither is the type CString, but CString lets you do
+//      it anyway due to the way they laid out the class in binary.  I have no
+//      control over this in CStdString since I derive from whatever
+//      implementation of basic_string is available.
+//
+//      However if you have legacy code (which does this) that you want to take
+//      out of the MFC world and you don't want to rewrite all your calls to
+//      Format(), then you can define this flag and it will no longer crash.
+//
+//      Note however that this ONLY works for Format(), not sprintf, fprintf,
+//      etc.  If you pass a CStdString object to one of those functions, your
+//      program will crash.  Not much I can do to get around this, short of
+//      writing substitutes for those functions as well.
+
+#define SS_SAFE_FORMAT  // use new template style Format() function
+
+
+// MACRO: SS_NO_IMPLICIT_CAST
+// --------------------------
+//      Some people don't like the implicit cast to const char* (or rather to
+//      const CT*) that CStdString (and MFC's CString) provide.  That was the
+//      whole reason I created this class in the first place, but hey, whatever
+//      bakes your cake.  Just #define this macro to get rid of the the implicit
+//      cast.
+
+//#define SS_NO_IMPLICIT_CAST // gets rid of operator const CT*()
+
+
+// MACRO: SS_NO_REFCOUNT
+// ---------------------
+//    turns off reference counting at the assignment level.  Only needed
+//    for the version of basic_string<> that comes with Visual C++ versions
+//    6.0 or earlier, and only then in some heavily multithreaded scenarios.
+//    Uncomment it if you feel you need it.
+
+//#define SS_NO_REFCOUNT
+
+// MACRO: SS_WIN32
+// ---------------
+//      When this flag is set, we are building code for the Win32 platform and
+//      may use Win32 specific functions (such as LoadString).  This gives us
+//      a couple of nice extras for the code.
+//
+//      Obviously, Microsoft's is not the only compiler available for Win32 out
+//      there.  So I can't just check to see if _MSC_VER is defined to detect
+//      if I'm building on Win32.  So for now, if you use MS Visual C++ or
+//      Borland's compiler, I turn this on.  Otherwise you may turn it on
+//      yourself, if you prefer
+
+#if defined(_MSC_VER) || defined(__BORLANDC__) || defined(_WIN32)
+ #define SS_WIN32
+#endif
+
+// MACRO: SS_ANSI
+// --------------
+//      When this macro is defined, the code attempts only to use ANSI/ISO
+//      standard library functions to do it's work.  It will NOT attempt to use
+//      any Win32 of Visual C++ specific functions -- even if they are
+//      available.  You may define this flag yourself to prevent any Win32
+//      of VC++ specific functions from being called.
+
+// If we're not on Win32, we MUST use an ANSI build
+
+#ifndef SS_WIN32
+    #if !defined(SS_NO_ANSI)
+        #define SS_ANSI
+    #endif
+#endif
+
+// MACRO: SS_ALLOCA
+// ----------------
+//      Some implementations of the Standard C Library have a non-standard
+//      function known as alloca().  This functions allows one to allocate a
+//      variable amount of memory on the stack.  It is needed to implement
+//      the ASCII/MBCS conversion macros.
+//
+//      I wanted to find some way to determine automatically if alloca() is
+//    available on this platform via compiler flags but that is asking for
+//    trouble.  The crude test presented here will likely need fixing on
+//    other platforms.  Therefore I'll leave it up to you to fiddle with
+//    this test to determine if it exists.  Just make sure SS_ALLOCA is or
+//    is not defined as appropriate and you control this feature.
+
+#if defined(_MSC_VER) && !defined(SS_ANSI)
+  #define SS_ALLOCA
+#endif
+
+
+// MACRO: SS_MBCS
+// --------------
+//    Setting this macro means you are using MBCS characters.  In MSVC builds,
+//    this macro gets set automatically by detection of the preprocessor flag
+//    _MBCS.  For other platforms you may set it manually if you wish.  The
+//    only effect it currently has is to cause the allocation of more space
+//    for wchar_t --> char conversions.
+//    Note that MBCS does not mean UNICODE.
+//
+//  #define SS_MBCS
+//
+
+#ifdef _MBCS
+  #define SS_MBCS
+#endif
+
+
+// MACRO SS_NO_LOCALE
+// ------------------
+// If your implementation of the Standard C++ Library lacks the <locale> header,
+// you can #define this macro to make your code build properly.  Note that this
+// is some of my newest code and frankly I'm not very sure of it, though it does
+// pass my unit tests.
+
+// #define SS_NO_LOCALE
+
+
+// Compiler Error regarding _UNICODE and UNICODE
+// -----------------------------------------------
+// Microsoft header files are screwy.  Sometimes they depend on a preprocessor
+// flag named "_UNICODE".  Other times they check "UNICODE" (note the lack of
+// leading underscore in the second version".  In several places, they silently
+// "synchronize" these two flags this by defining one of the other was defined.
+// In older version of this header, I used to try to do the same thing.
+//
+// However experience has taught me that this is a bad idea.  You get weird
+// compiler errors that seem to indicate things like LPWSTR and LPTSTR not being
+// equivalent in UNICODE builds, stuff like that (when they MUST be in a proper
+// UNICODE  build).  You end up scratching your head and saying, "But that HAS
+// to compile!".
+//
+// So what should you do if you get this error?
+//
+// Make sure that both macros (_UNICODE and UNICODE) are defined before this
+// file is included.  You can do that by either
+//
+//    a) defining both yourself before any files get included
+//    b) including the proper MS headers in the proper order
+//    c) including this file before any other file, uncommenting
+//       the #defines below, and commenting out the #errors
+//
+//  Personally I recommend solution a) but it's your call.
+
+#ifdef _MSC_VER
+  #if defined (_UNICODE) && !defined (UNICODE)
+    #error UNICODE defined  but not UNICODE
+  //  #define UNICODE  // no longer silently fix this
+  #endif
+  #if defined (UNICODE) && !defined (_UNICODE)
+    #error Warning, UNICODE defined  but not _UNICODE
+  //  #define _UNICODE  // no longer silently fix this
+  #endif
+#endif
+
+
+// -----------------------------------------------------------------------------
+// MIN and MAX.  The Standard C++ template versions go by so many names (at
+// at least in the MS implementation) that you never know what's available
+// -----------------------------------------------------------------------------
+template<class Type>
+inline const Type& SSMIN(const Type& arg1, const Type& arg2)
+{
+  return arg2 < arg1 ? arg2 : arg1;
+}
+template<class Type>
+inline const Type& SSMAX(const Type& arg1, const Type& arg2)
+{
+  return arg2 > arg1 ? arg2 : arg1;
+}
+
+// If they have not #included W32Base.h (part of my W32 utility library) then
+// we need to define some stuff.  Otherwise, this is all defined there.
+
+#if !defined(W32BASE_H)
+
+  // If they want us to use only standard C++ stuff (no Win32 stuff)
+
+  #ifdef SS_ANSI
+
+    // On Win32 we have TCHAR.H so just include it.  This is NOT violating
+        // the spirit of SS_ANSI as we are not calling any Win32 functions here.
+
+    #ifdef SS_WIN32
+
+      #include <TCHAR.H>
+      #include <WTYPES.H>
+      #ifndef STRICT
+        #define STRICT
+      #endif
+
+        // ... but on non-Win32 platforms, we must #define the types we need.
+
+    #else
+
+      typedef const char*    PCSTR;
+      typedef char*      PSTR;
+      typedef const wchar_t*  PCWSTR;
+      typedef wchar_t*    PWSTR;
+      #ifdef UNICODE
+        typedef wchar_t    TCHAR;
+      #else
+        typedef char    TCHAR;
+      #endif
+      typedef wchar_t      OLECHAR;
+
+    #endif  // #ifndef _WIN32
+
+
+    // Make sure ASSERT and verify are defined using only ANSI stuff
+
+    #ifndef ASSERT
+      #include <assert.h>
+      #define ASSERT(f) assert((f))
+    #endif
+    #ifndef VERIFY
+      #ifdef _DEBUG
+        #define VERIFY(x) ASSERT((x))
+      #else
+        #define VERIFY(x) x
+      #endif
+    #endif
+
+  #else // ...else SS_ANSI is NOT defined
+
+    #include <TCHAR.H>
+    #include <WTYPES.H>
+    #ifndef STRICT
+      #define STRICT
+    #endif
+
+    // Make sure ASSERT and verify are defined
+
+    #ifndef ASSERT
+      #include <crtdbg.h>
+      #define ASSERT(f) _ASSERTE((f))
+    #endif
+    #ifndef VERIFY
+      #ifdef _DEBUG
+        #define VERIFY(x) ASSERT((x))
+      #else
+        #define VERIFY(x) x
+      #endif
+    #endif
+
+  #endif // #ifdef SS_ANSI
+
+  #ifndef UNUSED
+    #define UNUSED(x) x
+  #endif
+
+#endif // #ifndef W32BASE_H
+
+// Standard headers needed
+
+#include <string>      // basic_string
+#include <algorithm>    // for_each, etc.
+#include <functional>    // for StdStringLessNoCase, et al
+#ifndef SS_NO_LOCALE
+  #include <locale>      // for various facets
+#endif
+
+// If this is a recent enough version of VC include comdef.h, so we can write
+// member functions to deal with COM types & compiler support classes e.g.
+// _bstr_t
+
+#if defined (_MSC_VER) && (_MSC_VER >= 1100)
+ #include <comdef.h>
+ #define SS_INC_COMDEF  // signal that we #included MS comdef.h file
+ #define STDSTRING_INC_COMDEF
+ #define SS_NOTHROW __declspec(nothrow)
+#else
+  #define SS_NOTHROW
+#endif
+
+#ifndef TRACE
+  #define TRACE_DEFINED_HERE
+  #define TRACE
+#endif
+
+// Microsoft defines PCSTR, PCWSTR, etc, but no PCTSTR.  I hate to use the
+// versions with the "L" in front of them because that's a leftover from Win 16
+// days, even though it evaluates to the same thing.  Therefore, Define a PCSTR
+// as an LPCTSTR.
+
+#if !defined(PCTSTR) && !defined(PCTSTR_DEFINED)
+  typedef const TCHAR*      PCTSTR;
+  #define PCTSTR_DEFINED
+#endif
+
+#if !defined(PCOLESTR) && !defined(PCOLESTR_DEFINED)
+  typedef const OLECHAR*      PCOLESTR;
+  #define PCOLESTR_DEFINED
+#endif
+
+#if !defined(POLESTR) && !defined(POLESTR_DEFINED)
+  typedef OLECHAR*        POLESTR;
+  #define POLESTR_DEFINED
+#endif
+
+#if !defined(PCUSTR) && !defined(PCUSTR_DEFINED)
+  typedef const unsigned char*  PCUSTR;
+  typedef unsigned char*      PUSTR;
+  #define PCUSTR_DEFINED
+#endif
+
+
+// SGI compiler 7.3 doesnt know these  types - oh and btw, remember to use
+// -LANG:std in the CXX Flags
+#if defined(__sgi)
+    typedef unsigned long           DWORD;
+    typedef void *                  LPCVOID;
+#endif
+
+
+// SS_USE_FACET macro and why we need it:
+//
+// Since I'm a good little Standard C++ programmer, I use locales.  Thus, I
+// need to make use of the use_facet<> template function here.   Unfortunately,
+// this need is complicated by the fact the MS' implementation of the Standard
+// C++ Library has a non-standard version of use_facet that takes more
+// arguments than the standard dictates.  Since I'm trying to write CStdString
+// to work with any version of the Standard library, this presents a problem.
+//
+// The upshot of this is that I can't do 'use_facet' directly.  The MS' docs
+// tell me that I have to use a macro, _USE() instead.  Since _USE obviously
+// won't be available in other implementations, this means that I have to write
+// my OWN macro -- SS_USE_FACET -- that evaluates either to _USE or to the
+// standard, use_facet.
+//
+// If you are having trouble with the SS_USE_FACET macro, in your implementation
+// of the Standard C++ Library, you can define your own version of SS_USE_FACET.
+
+#ifndef schMSG
+  #define schSTR(x)     #x
+  #define schSTR2(x)  schSTR(x)
+  #define schMSG(desc) message(__FILE__ "(" schSTR2(__LINE__) "):" #desc)
+#endif
+
+#ifndef SS_USE_FACET
+
+  // STLPort #defines a macro (__STL_NO_EXPLICIT_FUNCTION_TMPL_ARGS) for
+  // all MSVC builds, erroneously in my opinion.  It causes problems for
+  // my SS_ANSI builds.  In my code, I always comment out that line.  You'll
+  // find it in   \stlport\config\stl_msvc.h
+
+  #if defined(__SGI_STL_PORT) && (__SGI_STL_PORT >= 0x400 )
+
+    #if defined(__STL_NO_EXPLICIT_FUNCTION_TMPL_ARGS) && defined(_MSC_VER)
+      #ifdef SS_ANSI
+        #pragma schMSG(__STL_NO_EXPLICIT_FUNCTION_TMPL_ARGS defined!!)
+      #endif
+    #endif
+    #define SS_USE_FACET(loc, fac) std::use_facet<fac >(loc)
+
+  #elif defined(_MSC_VER )
+
+    #define SS_USE_FACET(loc, fac) std::_USE(loc, fac)
+
+  // ...and
+  #elif defined(_RWSTD_NO_TEMPLATE_ON_RETURN_TYPE)
+
+        #define SS_USE_FACET(loc, fac) std::use_facet(loc, (fac*)0)
+
+  #else
+
+    #define SS_USE_FACET(loc, fac) std::use_facet<fac >(loc)
+
+  #endif
+
+#endif
+
+// =============================================================================
+// UNICODE/MBCS conversion macros.  Made to work just like the MFC/ATL ones.
+// =============================================================================
+
+#include <wchar.h>      // Added to Std Library with Amendment #1.
+
+// First define the conversion helper functions.  We define these regardless of
+// any preprocessor macro settings since their names won't collide.
+
+// Not sure if we need all these headers.   I believe ANSI says we do.
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <wctype.h>
+#include <ctype.h>
+#include <stdlib.h>
+#ifndef va_start
+  #include <varargs.h>
+#endif
+
+
+#ifdef SS_NO_LOCALE
+
+  #if defined(_WIN32) || defined (_WIN32_WCE)
+
+    inline PWSTR StdCodeCvt(PWSTR pDstW, int nDst, PCSTR pSrcA, int nSrc,
+      UINT acp=CP_ACP)
+    {
+      ASSERT(0 != pSrcA);
+      ASSERT(0 != pDstW);
+      pDstW[0] = '\0';
+      MultiByteToWideChar(acp, 0, pSrcA, nSrc, pDstW, nDst);
+      return pDstW;
+    }
+    inline PWSTR StdCodeCvt(PWSTR pDstW, int nDst, PCUSTR pSrcA, int nSrc,
+      UINT acp=CP_ACP)
+    {
+      return StdCodeCvt(pDstW, nDst, (PCSTR)pSrcA, nSrc, acp);
+    }
+
+    inline PSTR StdCodeCvt(PSTR pDstA, int nDst, PCWSTR pSrcW, int nSrc,
+      UINT acp=CP_ACP)
+    {
+      ASSERT(0 != pDstA);
+      ASSERT(0 != pSrcW);
+      pDstA[0] = '\0';
+      WideCharToMultiByte(acp, 0, pSrcW, nSrc, pDstA, nDst, 0, 0);
+      return pDstA;
+    }
+    inline PUSTR StdCodeCvt(PUSTR pDstA, int nDst, PCWSTR pSrcW, int nSrc,
+      UINT acp=CP_ACP)
+    {
+      return (PUSTR)StdCodeCvt((PSTR)pDstA, nDst, pSrcW, nSrc, acp);
+    }
+  #else
+  #endif
+
+#else
+
+  // StdCodeCvt - made to look like Win32 functions WideCharToMultiByte
+  //        and MultiByteToWideChar but uses locales in SS_ANSI
+  //        builds.  There are a number of overloads.
+  //              First argument is the destination buffer.
+  //              Second argument is the source buffer
+  //#if defined (SS_ANSI) || !defined (SS_WIN32)
+
+  // 'SSCodeCvt' - shorthand name for the codecvt facet we use
+
+  typedef std::codecvt<wchar_t, char, mbstate_t> SSCodeCvt;
+
+  inline PWSTR StdCodeCvt(PWSTR pDstW, int nDst, PCSTR pSrcA, int nSrc,
+    const std::locale& loc=std::locale())
+  {
+    ASSERT(0 != pSrcA);
+    ASSERT(0 != pDstW);
+
+    pDstW[0]          = '\0';
+
+    if ( nSrc > 0 )
+    {
+      PCSTR pNextSrcA      = pSrcA;
+      PWSTR pNextDstW      = pDstW;
+      SSCodeCvt::result res  = SSCodeCvt::ok;
+      const SSCodeCvt& conv  = SS_USE_FACET(loc, SSCodeCvt);
+      SSCodeCvt::state_type st= { 0 };
+      res            = conv.in(st,
+                    pSrcA, pSrcA + nSrc, pNextSrcA,
+                    pDstW, pDstW + nDst, pNextDstW);
+#ifdef _LINUX
+#define ASSERT2(a) if (!(a)) {fprintf(stderr, "StdString: Assertion Failed on line %d\n", __LINE__);}
+#else
+#define ASSERT2 ASSERT
+#endif
+      ASSERT2(SSCodeCvt::ok == res);
+      ASSERT2(SSCodeCvt::error != res);
+      ASSERT2(pNextDstW >= pDstW);
+      ASSERT2(pNextSrcA >= pSrcA);
+#undef ASSERT2
+      // Null terminate the converted string
+
+      if ( pNextDstW - pDstW > nDst )
+        *(pDstW + nDst) = '\0';
+      else
+        *pNextDstW = '\0';
+    }
+    return pDstW;
+  }
+  inline PWSTR StdCodeCvt(PWSTR pDstW, int nDst, PCUSTR pSrcA, int nSrc,
+    const std::locale& loc=std::locale())
+  {
+    return StdCodeCvt(pDstW, nDst, (PCSTR)pSrcA, nSrc, loc);
+  }
+
+  inline PSTR StdCodeCvt(PSTR pDstA, int nDst, PCWSTR pSrcW, int nSrc,
+    const std::locale& loc=std::locale())
+  {
+    ASSERT(0 != pDstA);
+    ASSERT(0 != pSrcW);
+
+    pDstA[0]          = '\0';
+
+    if ( nSrc > 0 )
+    {
+      PSTR pNextDstA      = pDstA;
+      PCWSTR pNextSrcW    = pSrcW;
+      SSCodeCvt::result res  = SSCodeCvt::ok;
+      const SSCodeCvt& conv  = SS_USE_FACET(loc, SSCodeCvt);
+      SSCodeCvt::state_type st= { 0 };
+      res            = conv.out(st,
+                    pSrcW, pSrcW + nSrc, pNextSrcW,
+                    pDstA, pDstA + nDst, pNextDstA);
+#ifdef _LINUX
+#define ASSERT2(a) if (!(a)) {fprintf(stderr, "StdString: Assertion Failed on line %d\n", __LINE__);}
+#else
+#define ASSERT2 ASSERT
+#endif
+      ASSERT2(SSCodeCvt::error != res);
+      ASSERT2(SSCodeCvt::ok == res);  // strict, comment out for sanity
+      ASSERT2(pNextDstA >= pDstA);
+      ASSERT2(pNextSrcW >= pSrcW);
+#undef ASSERT2
+
+      // Null terminate the converted string
+
+      if ( pNextDstA - pDstA > nDst )
+        *(pDstA + nDst) = '\0';
+      else
+        *pNextDstA = '\0';
+    }
+    return pDstA;
+  }
+
+  inline PUSTR StdCodeCvt(PUSTR pDstA, int nDst, PCWSTR pSrcW, int nSrc,
+    const std::locale& loc=std::locale())
+  {
+    return (PUSTR)StdCodeCvt((PSTR)pDstA, nDst, pSrcW, nSrc, loc);
+  }
+
+#endif
+
+
+
+// Unicode/MBCS conversion macros are only available on implementations of
+// the "C" library that have the non-standard _alloca function.  As far as I
+// know that's only Microsoft's though I've heard that the function exists
+// elsewhere.
+
+#if defined(SS_ALLOCA) && !defined SS_NO_CONVERSION
+
+    #include <malloc.h>  // needed for _alloca
+
+    // Define our conversion macros to look exactly like Microsoft's to
+    // facilitate using this stuff both with and without MFC/ATL
+
+    #ifdef _CONVERSION_USES_THREAD_LOCALE
+
+      #ifndef _DEBUG
+        #define SSCVT int _cvt; _cvt; UINT _acp=GetACP(); \
+          _acp; PCWSTR _pw; _pw; PCSTR _pa; _pa
+      #else
+        #define SSCVT int _cvt = 0; _cvt; UINT _acp=GetACP();\
+           _acp; PCWSTR _pw=0; _pw; PCSTR _pa=0; _pa
+      #endif
+      #define SSA2W(pa) (\
+        ((_pa = pa) == 0) ? 0 : (\
+          _cvt = (sslen(_pa)),\
+          StdCodeCvt((PWSTR) _alloca((_cvt+1)*2), (_cvt+1)*2, \
+              _pa, _cvt, _acp)))
+      #define SSW2A(pw) (\
+        ((_pw = pw) == 0) ? 0 : (\
+          _cvt = sslen(_pw), \
+          StdCodeCvt((LPSTR) _alloca((_cvt+1)*2), (_cvt+1)*2, \
+          _pw, _cvt, _acp)))
+  #else
+
+      #ifndef _DEBUG
+        #define SSCVT int _cvt; _cvt; UINT _acp=CP_ACP; _acp;\
+           PCWSTR _pw; _pw; PCSTR _pa; _pa
+      #else
+        #define SSCVT int _cvt = 0; _cvt; UINT _acp=CP_ACP; \
+          _acp; PCWSTR _pw=0; _pw; PCSTR _pa=0; _pa
+      #endif
+      #define SSA2W(pa) (\
+        ((_pa = pa) == 0) ? 0 : (\
+          _cvt = (sslen(_pa)),\
+          StdCodeCvt((PWSTR) _alloca((_cvt+1)*2), (_cvt+1)*2, \
+          _pa, _cvt)))
+      #define SSW2A(pw) (\
+        ((_pw = pw) == 0) ? 0 : (\
+          _cvt = (sslen(_pw)),\
+          StdCodeCvt((LPSTR) _alloca((_cvt+1)*2), (_cvt+1)*2, \
+          _pw, _cvt)))
+    #endif
+
+    #define SSA2CW(pa) ((PCWSTR)SSA2W((pa)))
+    #define SSW2CA(pw) ((PCSTR)SSW2A((pw)))
+
+    #ifdef UNICODE
+      #define SST2A  SSW2A
+      #define SSA2T  SSA2W
+      #define SST2CA  SSW2CA
+      #define SSA2CT  SSA2CW
+    // (Did you get a compiler error here about not being able to convert
+    // PTSTR into PWSTR?  Then your _UNICODE and UNICODE flags are messed
+    // up.  Best bet: #define BOTH macros before including any MS headers.)
+      inline PWSTR  SST2W(PTSTR p)      { return p; }
+      inline PTSTR  SSW2T(PWSTR p)      { return p; }
+      inline PCWSTR  SST2CW(PCTSTR p)    { return p; }
+      inline PCTSTR  SSW2CT(PCWSTR p)    { return p; }
+    #else
+      #define SST2W  SSA2W
+      #define SSW2T  SSW2A
+      #define SST2CW  SSA2CW
+      #define SSW2CT  SSW2CA
+      inline PSTR    SST2A(PTSTR p)      { return p; }
+      inline PTSTR  SSA2T(PSTR p)      { return p; }
+      inline PCSTR  SST2CA(PCTSTR p)    { return p; }
+      inline PCTSTR  SSA2CT(PCSTR p)      { return p; }
+    #endif // #ifdef UNICODE
+
+    #if defined(UNICODE)
+    // in these cases the default (TCHAR) is the same as OLECHAR
+      inline PCOLESTR  SST2COLE(PCTSTR p)    { return p; }
+      inline PCTSTR  SSOLE2CT(PCOLESTR p)  { return p; }
+      inline POLESTR  SST2OLE(PTSTR p)    { return p; }
+      inline PTSTR  SSOLE2T(POLESTR p)    { return p; }
+    #elif defined(OLE2ANSI)
+    // in these cases the default (TCHAR) is the same as OLECHAR
+      inline PCOLESTR  SST2COLE(PCTSTR p)    { return p; }
+      inline PCTSTR  SSOLE2CT(PCOLESTR p)  { return p; }
+      inline POLESTR  SST2OLE(PTSTR p)    { return p; }
+      inline PTSTR  SSOLE2T(POLESTR p)    { return p; }
+    #else
+      //CharNextW doesn't work on Win95 so we use this
+      #define SST2COLE(pa)  SSA2CW((pa))
+      #define SST2OLE(pa)    SSA2W((pa))
+      #define SSOLE2CT(po)  SSW2CA((po))
+      #define SSOLE2T(po)    SSW2A((po))
+    #endif
+
+    #ifdef OLE2ANSI
+      #define SSW2OLE    SSW2A
+      #define SSOLE2W    SSA2W
+      #define SSW2COLE  SSW2CA
+      #define SSOLE2CW  SSA2CW
+      inline POLESTR    SSA2OLE(PSTR p)    { return p; }
+      inline PSTR      SSOLE2A(POLESTR p)  { return p; }
+      inline PCOLESTR    SSA2COLE(PCSTR p)  { return p; }
+      inline PCSTR    SSOLE2CA(PCOLESTR p){ return p; }
+    #else
+      #define SSA2OLE    SSA2W
+      #define SSOLE2A    SSW2A
+      #define SSA2COLE  SSA2CW
+      #define SSOLE2CA  SSW2CA
+      inline POLESTR    SSW2OLE(PWSTR p)  { return p; }
+      inline PWSTR    SSOLE2W(POLESTR p)  { return p; }
+      inline PCOLESTR    SSW2COLE(PCWSTR p)  { return p; }
+      inline PCWSTR    SSOLE2CW(PCOLESTR p){ return p; }
+    #endif
+
+    // Above we've defined macros that look like MS' but all have
+    // an 'SS' prefix.  Now we need the real macros.  We'll either
+    // get them from the macros above or from MFC/ATL.
+
+  #if defined (USES_CONVERSION)
+
+    #define _NO_STDCONVERSION  // just to be consistent
+
+  #else
+
+    #ifdef _MFC_VER
+
+      #include <afxconv.h>
+      #define _NO_STDCONVERSION // just to be consistent
+
+    #else
+
+      #define USES_CONVERSION SSCVT
+      #define A2CW      SSA2CW
+      #define W2CA      SSW2CA
+      #define T2A        SST2A
+      #define A2T        SSA2T
+      #define T2W        SST2W
+      #define W2T        SSW2T
+      #define T2CA      SST2CA
+      #define A2CT      SSA2CT
+      #define T2CW      SST2CW
+      #define W2CT      SSW2CT
+      #define ocslen      sslen
+      #define ocscpy      sscpy
+      #define T2COLE      SST2COLE
+      #define OLE2CT      SSOLE2CT
+      #define T2OLE      SST2COLE
+      #define OLE2T      SSOLE2CT
+      #define A2OLE      SSA2OLE
+      #define OLE2A      SSOLE2A
+      #define W2OLE      SSW2OLE
+      #define OLE2W      SSOLE2W
+      #define A2COLE      SSA2COLE
+      #define OLE2CA      SSOLE2CA
+      #define W2COLE      SSW2COLE
+      #define OLE2CW      SSOLE2CW
+
+    #endif // #ifdef _MFC_VER
+  #endif // #ifndef USES_CONVERSION
+#endif // #ifndef SS_NO_CONVERSION
+
+// Define ostring - generic name for std::basic_string<OLECHAR>
+
+#if !defined(ostring) && !defined(OSTRING_DEFINED)
+  typedef std::basic_string<OLECHAR> ostring;
+  #define OSTRING_DEFINED
+#endif
+
+// StdCodeCvt when there's no conversion to be done
+template <typename T>
+inline T* StdCodeCvt(T* pDst, int nDst, const T* pSrc, int nSrc)
+{
+  int nChars = SSMIN(nSrc, nDst);
+
+  if ( nChars > 0 )
+  {
+    pDst[0]        = '\0';
+    std::basic_string<T>::traits_type::copy(pDst, pSrc, nChars);
+//    std::char_traits<T>::copy(pDst, pSrc, nChars);
+    pDst[nChars]  = '\0';
+  }
+
+  return pDst;
+}
+inline PSTR StdCodeCvt(PSTR pDst, int nDst, PCUSTR pSrc, int nSrc)
+{
+  return StdCodeCvt(pDst, nDst, (PCSTR)pSrc, nSrc);
+}
+inline PUSTR StdCodeCvt(PUSTR pDst, int nDst, PCSTR pSrc, int nSrc)
+{
+  return (PUSTR)StdCodeCvt((PSTR)pDst, nDst, pSrc, nSrc);
+}
+
+// Define tstring -- generic name for std::basic_string<TCHAR>
+
+#if !defined(tstring) && !defined(TSTRING_DEFINED)
+  typedef std::basic_string<TCHAR> tstring;
+  #define TSTRING_DEFINED
+#endif
+
+// a very shorthand way of applying the fix for KB problem Q172398
+// (basic_string assignment bug)
+
+#if defined ( _MSC_VER ) && ( _MSC_VER < 1200 )
+  #define Q172398(x) (x).erase()
+#else
+  #define Q172398(x)
+#endif
+
+// =============================================================================
+// INLINE FUNCTIONS ON WHICH CSTDSTRING RELIES
+//
+// Usually for generic text mapping, we rely on preprocessor macro definitions
+// to map to string functions.  However the CStdStr<> template cannot use
+// macro-based generic text mappings because its character types do not get
+// resolved until template processing which comes AFTER macro processing.  In
+// other words, the preprocessor macro UNICODE is of little help to us in the
+// CStdStr template
+//
+// Therefore, to keep the CStdStr declaration simple, we have these inline
+// functions.  The template calls them often.  Since they are inline (and NOT
+// exported when this is built as a DLL), they will probably be resolved away
+// to nothing.
+//
+// Without these functions, the CStdStr<> template would probably have to broken
+// out into two, almost identical classes.  Either that or it would be a huge,
+// convoluted mess, with tons of "if" statements all over the place checking the
+// size of template parameter CT.
+// =============================================================================
+
+#ifdef SS_NO_LOCALE
+
+  // --------------------------------------------------------------------------
+  // Win32 GetStringTypeEx wrappers
+  // --------------------------------------------------------------------------
+  inline bool wsGetStringType(LCID lc, DWORD dwT, PCSTR pS, int nSize,
+    WORD* pWd)
+  {
+    return FALSE != GetStringTypeExA(lc, dwT, pS, nSize, pWd);
+  }
+  inline bool wsGetStringType(LCID lc, DWORD dwT, PCWSTR pS, int nSize,
+    WORD* pWd)
+  {
+    return FALSE != GetStringTypeExW(lc, dwT, pS, nSize, pWd);
+  }
+
+
+  template<typename CT>
+    inline bool ssisspace (CT t)
+  {
+    WORD toYourMother;
+    return  wsGetStringType(GetThreadLocale(), CT_CTYPE1, &t, 1, &toYourMother)
+      && 0 != (C1_BLANK & toYourMother);
+  }
+
+#endif
+
+// If they defined SS_NO_REFCOUNT, then we must convert all assignments
+
+#if defined (_MSC_VER) && (_MSC_VER < 1300)
+  #ifdef SS_NO_REFCOUNT
+    #define SSREF(x) (x).c_str()
+  #else
+    #define SSREF(x) (x)
+  #endif
+#else
+  #define SSREF(x) (x)
+#endif
+
+// -----------------------------------------------------------------------------
+// sslen: strlen/wcslen wrappers
+// -----------------------------------------------------------------------------
+template<typename CT> inline int sslen(const CT* pT)
+{
+  return 0 == pT ? 0 : (int)std::basic_string<CT>::traits_type::length(pT);
+//  return 0 == pT ? 0 : std::char_traits<CT>::length(pT);
+}
+inline SS_NOTHROW int sslen(const std::string& s)
+{
+  return static_cast<int>(s.length());
+}
+inline SS_NOTHROW int sslen(const std::wstring& s)
+{
+  return static_cast<int>(s.length());
+}
+
+// -----------------------------------------------------------------------------
+// sstolower/sstoupper -- convert characters to upper/lower case
+// -----------------------------------------------------------------------------
+
+#ifdef SS_NO_LOCALE
+  inline char sstoupper(char ch)    { return (char)::toupper(ch); }
+  inline wchar_t sstoupper(wchar_t ch){ return (wchar_t)::towupper(ch); }
+  inline char sstolower(char ch)    { return (char)::tolower(ch); }
+  inline wchar_t sstolower(wchar_t ch){ return (wchar_t)::tolower(ch); }
+#else
+  template<typename CT>
+  inline CT sstolower(const CT& t, const std::locale& loc = std::locale())
+  {
+    return std::tolower<CT>(t, loc);
+  }
+  template<typename CT>
+  inline CT sstoupper(const CT& t, const std::locale& loc = std::locale())
+  {
+    return std::toupper<CT>(t, loc);
+  }
+#endif
+
+// -----------------------------------------------------------------------------
+// ssasn: assignment functions -- assign "sSrc" to "sDst"
+// -----------------------------------------------------------------------------
+typedef std::string::size_type    SS_SIZETYPE; // just for shorthand, really
+typedef std::string::pointer    SS_PTRTYPE;
+typedef std::wstring::size_type    SW_SIZETYPE;
+typedef std::wstring::pointer    SW_PTRTYPE;
+
+
+template <typename T>
+inline void  ssasn(std::basic_string<T>& sDst, const std::basic_string<T>& sSrc)
+{
+  if ( sDst.c_str() != sSrc.c_str() )
+  {
+    sDst.erase();
+    sDst.assign(SSREF(sSrc));
+  }
+}
+template <typename T>
+inline void  ssasn(std::basic_string<T>& sDst, const T *pA)
+{
+  // Watch out for NULLs, as always.
+
+  if ( 0 == pA )
+  {
+    sDst.erase();
+  }
+
+  // If pA actually points to part of sDst, we must NOT erase(), but
+  // rather take a substring
+
+  else if ( pA >= sDst.c_str() && pA <= sDst.c_str() + sDst.size() )
+  {
+    sDst =sDst.substr(static_cast<typename std::basic_string<T>::size_type>(pA-sDst.c_str()));
+  }
+
+  // Otherwise (most cases) apply the assignment bug fix, if applicable
+  // and do the assignment
+
+  else
+  {
+    Q172398(sDst);
+    sDst.assign(pA);
+  }
+}
+inline void  ssasn(std::string& sDst, const std::wstring& sSrc)
+{
+  if ( sSrc.empty() )
+  {
+    sDst.erase();
+  }
+  else
+  {
+    int nDst  = static_cast<int>(sSrc.size());
+
+    // In MBCS builds, pad the buffer to account for the possibility of
+    // some 3 byte characters.  Not perfect but should get most cases.
+
+#ifdef SS_MBCS
+    // In MBCS builds, we don't know how long the destination string will be.
+    nDst  = static_cast<int>(static_cast<double>(nDst) * 1.3);
+    sDst.resize(nDst+1);
+    PCSTR szCvt = StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()), nDst,
+      sSrc.c_str(), static_cast<int>(sSrc.size()));
+    sDst.resize(sslen(szCvt));
+#else
+    sDst.resize(nDst+1);
+    StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()), nDst,
+      sSrc.c_str(), static_cast<int>(sSrc.size()));
+    sDst.resize(sSrc.size());
+#endif
+  }
+}
+inline void  ssasn(std::string& sDst, PCWSTR pW)
+{
+  int nSrc  = sslen(pW);
+  if ( nSrc > 0 )
+  {
+    int nSrc  = sslen(pW);
+    int nDst  = nSrc;
+
+    // In MBCS builds, pad the buffer to account for the possibility of
+    // some 3 byte characters.  Not perfect but should get most cases.
+
+#ifdef SS_MBCS
+    nDst  = static_cast<int>(static_cast<double>(nDst) * 1.3);
+    // In MBCS builds, we don't know how long the destination string will be.
+    sDst.resize(nDst + 1);
+    PCSTR szCvt = StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()), nDst,
+      pW, nSrc);
+    sDst.resize(sslen(szCvt));
+#else
+    sDst.resize(nDst + 1);
+    StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()), nDst, pW, nSrc);
+    sDst.resize(nDst);
+#endif
+  }
+  else
+  {
+    sDst.erase();
+  }
+}
+inline void ssasn(std::string& sDst, const int nNull)
+{
+  //UNUSED(nNull);
+  ASSERT(nNull==0);
+  sDst.assign("");
+}
+#undef StrSizeType
+inline void  ssasn(std::wstring& sDst, const std::string& sSrc)
+{
+  if ( sSrc.empty() )
+  {
+    sDst.erase();
+  }
+  else
+  {
+    int nSrc  = static_cast<int>(sSrc.size());
+    int nDst  = nSrc;
+
+    sDst.resize(nSrc+1);
+    PCWSTR szCvt = StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()), nDst,
+      sSrc.c_str(), nSrc);
+
+    sDst.resize(sslen(szCvt));
+  }
+}
+inline void  ssasn(std::wstring& sDst, PCSTR pA)
+{
+  int nSrc  = sslen(pA);
+
+  if ( 0 == nSrc )
+  {
+    sDst.erase();
+  }
+  else
+  {
+    int nDst  = nSrc;
+    sDst.resize(nDst+1);
+    PCWSTR szCvt = StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()), nDst, pA,
+      nSrc);
+
+    sDst.resize(sslen(szCvt));
+  }
+}
+inline void ssasn(std::wstring& sDst, const int nNull)
+{
+  //UNUSED(nNull);
+  ASSERT(nNull==0);
+  sDst.assign(L"");
+}
+
+// -----------------------------------------------------------------------------
+// ssadd: string object concatenation -- add second argument to first
+// -----------------------------------------------------------------------------
+inline void  ssadd(std::string& sDst, const std::wstring& sSrc)
+{
+  int nSrc  = static_cast<int>(sSrc.size());
+
+  if ( nSrc > 0 )
+  {
+    int nDst  = static_cast<int>(sDst.size());
+    int nAdd  = nSrc;
+
+    // In MBCS builds, pad the buffer to account for the possibility of
+    // some 3 byte characters.  Not perfect but should get most cases.
+
+#ifdef SS_MBCS
+    nAdd    = static_cast<int>(static_cast<double>(nAdd) * 1.3);
+    sDst.resize(nDst+nAdd+1);
+    PCSTR szCvt = StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()+nDst),
+      nAdd, sSrc.c_str(), nSrc);
+    sDst.resize(nDst + sslen(szCvt));
+#else
+    sDst.resize(nDst+nAdd+1);
+    StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()+nDst), nAdd, sSrc.c_str(), nSrc);
+    sDst.resize(nDst + nAdd);
+#endif
+  }
+}
+template <typename T>
+inline void  ssadd(typename std::basic_string<T>& sDst, const typename std::basic_string<T>& sSrc)
+{
+  sDst += sSrc;
+}
+inline void  ssadd(std::string& sDst, PCWSTR pW)
+{
+  int nSrc    = sslen(pW);
+  if ( nSrc > 0 )
+  {
+    int nDst  = static_cast<int>(sDst.size());
+    int nAdd  = nSrc;
+
+#ifdef SS_MBCS
+    nAdd  = static_cast<int>(static_cast<double>(nAdd) * 1.3);
+    sDst.resize(nDst + nAdd + 1);
+    PCSTR szCvt = StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()+nDst),
+      nAdd, pW, nSrc);
+    sDst.resize(nDst + sslen(szCvt));
+#else
+    sDst.resize(nDst + nAdd + 1);
+    StdCodeCvt(const_cast<SS_PTRTYPE>(sDst.data()+nDst), nAdd, pW, nSrc);
+    sDst.resize(nDst + nSrc);
+#endif
+  }
+}
+template <typename T>
+inline void  ssadd(typename std::basic_string<T>& sDst, const T *pA)
+{
+  if ( pA )
+  {
+    // If the string being added is our internal string or a part of our
+    // internal string, then we must NOT do any reallocation without
+    // first copying that string to another object (since we're using a
+    // direct pointer)
+
+    if ( pA >= sDst.c_str() && pA <= sDst.c_str()+sDst.length())
+    {
+      if ( sDst.capacity() <= sDst.size()+sslen(pA) )
+        sDst.append(std::basic_string<T>(pA));
+      else
+        sDst.append(pA);
+    }
+    else
+    {
+      sDst.append(pA);
+    }
+  }
+}
+inline void  ssadd(std::wstring& sDst, const std::string& sSrc)
+{
+  if ( !sSrc.empty() )
+  {
+    int nSrc  = static_cast<int>(sSrc.size());
+    int nDst  = static_cast<int>(sDst.size());
+
+    sDst.resize(nDst + nSrc + 1);
+#ifdef SS_MBCS
+    PCWSTR szCvt = StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()+nDst),
+      nSrc, sSrc.c_str(), nSrc+1);
+    sDst.resize(nDst + sslen(szCvt));
+#else
+    StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()+nDst), nSrc, sSrc.c_str(), nSrc+1);
+    sDst.resize(nDst + nSrc);
+#endif
+  }
+}
+inline void  ssadd(std::wstring& sDst, PCSTR pA)
+{
+  int nSrc    = sslen(pA);
+
+  if ( nSrc > 0 )
+  {
+    int nDst  = static_cast<int>(sDst.size());
+
+    sDst.resize(nDst + nSrc + 1);
+#ifdef SS_MBCS
+    PCWSTR szCvt = StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()+nDst),
+      nSrc, pA, nSrc+1);
+    sDst.resize(nDst + sslen(szCvt));
+#else
+    StdCodeCvt(const_cast<SW_PTRTYPE>(sDst.data()+nDst), nSrc, pA, nSrc+1);
+    sDst.resize(nDst + nSrc);
+#endif
+  }
+}
+
+// -----------------------------------------------------------------------------
+// sscmp: comparison (case sensitive, not affected by locale)
+// -----------------------------------------------------------------------------
+template<typename CT>
+inline int sscmp(const CT* pA1, const CT* pA2)
+{
+    CT f;
+    CT l;
+
+    do
+    {
+      f = *(pA1++);
+      l = *(pA2++);
+    } while ( (f) && (f == l) );
+
+    return (int)(f - l);
+}
+
+// -----------------------------------------------------------------------------
+// ssicmp: comparison (case INsensitive, not affected by locale)
+// -----------------------------------------------------------------------------
+template<typename CT>
+inline int ssicmp(const CT* pA1, const CT* pA2)
+{
+  // Using the "C" locale = "not affected by locale"
+
+  std::locale loc = std::locale::classic();
+    const std::ctype<CT>& ct = SS_USE_FACET(loc, std::ctype<CT>);
+    CT f;
+    CT l;
+
+    do
+    {
+      f = ct.tolower(*(pA1++));
+      l = ct.tolower(*(pA2++));
+    } while ( (f) && (f == l) );
+
+    return (int)(f - l);
+}
+
+// -----------------------------------------------------------------------------
+// ssupr/sslwr: Uppercase/Lowercase conversion functions
+// -----------------------------------------------------------------------------
+
+template<typename CT>
+inline void sslwr(CT* pT, size_t nLen, const std::locale& loc=std::locale())
+{
+  SS_USE_FACET(loc, std::ctype<CT>).tolower(pT, pT+nLen);
+}
+template<typename CT>
+inline void ssupr(CT* pT, size_t nLen, const std::locale& loc=std::locale())
+{
+  SS_USE_FACET(loc, std::ctype<CT>).toupper(pT, pT+nLen);
+}
+
+// -----------------------------------------------------------------------------
+// vsprintf/vswprintf or _vsnprintf/_vsnwprintf equivalents.  In standard
+// builds we can't use _vsnprintf/_vsnwsprintf because they're MS extensions.
+//
+// -----------------------------------------------------------------------------
+// Borland's headers put some ANSI "C" functions in the 'std' namespace.
+// Promote them to the global namespace so we can use them here.
+
+#if defined(__BORLANDC__)
+    using std::vsprintf;
+    using std::vswprintf;
+#endif
+
+  // GNU is supposed to have vsnprintf and vsnwprintf.  But only the newer
+  // distributions do.
+
+#if defined(__GNUC__)
+
+  inline int ssvsprintf(PSTR pA, size_t nCount, PCSTR pFmtA, va_list vl)
+  {
+    return vsnprintf(pA, nCount, pFmtA, vl);
+  }
+  inline int ssvsprintf(PWSTR pW, size_t nCount, PCWSTR pFmtW, va_list vl)
+  {
+    return vswprintf(pW, nCount, pFmtW, vl);
+  }
+
+  // Microsofties can use
+#elif defined(_MSC_VER) && !defined(SS_ANSI)
+
+  inline int  ssvsprintf(PSTR pA, size_t nCount, PCSTR pFmtA, va_list vl)
+  {
+    return _vsnprintf(pA, nCount, pFmtA, vl);
+  }
+  inline int  ssvsprintf(PWSTR pW, size_t nCount, PCWSTR pFmtW, va_list vl)
+  {
+    return _vsnwprintf(pW, nCount, pFmtW, vl);
+  }
+
+#elif defined (SS_DANGEROUS_FORMAT)  // ignore buffer size parameter if needed?
+
+  inline int ssvsprintf(PSTR pA, size_t /*nCount*/, PCSTR pFmtA, va_list vl)
+  {
+    return vsprintf(pA, pFmtA, vl);
+  }
+
+  inline int ssvsprintf(PWSTR pW, size_t nCount, PCWSTR pFmtW, va_list vl)
+  {
+    // JMO: Some distributions of the "C" have a version of vswprintf that
+        // takes 3 arguments (e.g. Microsoft, Borland, GNU).  Others have a
+        // version which takes 4 arguments (an extra "count" argument in the
+        // second position.  The best stab I can take at this so far is that if
+        // you are NOT running with MS, Borland, or GNU, then I'll assume you
+        // have the version that takes 4 arguments.
+        //
+        // I'm sure that these checks don't catch every platform correctly so if
+        // you get compiler errors on one of the lines immediately below, it's
+        // probably because your implemntation takes a different number of
+        // arguments.  You can comment out the offending line (and use the
+        // alternate version) or you can figure out what compiler flag to check
+        // and add that preprocessor check in.  Regardless, if you get an error
+        // on these lines, I'd sure like to hear from you about it.
+        //
+        // Thanks to Ronny Schulz for the SGI-specific checks here.
+
+//  #if !defined(__MWERKS__) && !defined(__SUNPRO_CC_COMPAT) && !defined(__SUNPRO_CC)
+    #if    !defined(_MSC_VER) \
+        && !defined (__BORLANDC__) \
+        && !defined(__GNUC__) \
+        && !defined(__sgi)
+
+        return vswprintf(pW, nCount, pFmtW, vl);
+
+    // suddenly with the current SGI 7.3 compiler there is no such function as
+    // vswprintf and the substitute needs explicit casts to compile
+
+    #elif defined(__sgi)
+
+        nCount;
+        return vsprintf( (char *)pW, (char *)pFmtW, vl);
+
+    #else
+
+        nCount;
+        return vswprintf(pW, pFmtW, vl);
+
+    #endif
+
+  }
+
+#endif
+
+  // GOT COMPILER PROBLEMS HERE?
+  // ---------------------------
+  // Does your compiler choke on one or more of the following 2 functions?  It
+  // probably means that you don't have have either vsnprintf or vsnwprintf in
+  // your version of the CRT.  This is understandable since neither is an ANSI
+  // "C" function.  However it still leaves you in a dilemma.  In order to make
+  // this code build, you're going to have to to use some non-length-checked
+  // formatting functions that every CRT has:  vsprintf and vswprintf.
+  //
+  // This is very dangerous.  With the proper erroneous (or malicious) code, it
+  // can lead to buffer overlows and crashing your PC.  Use at your own risk
+  // In order to use them, just #define SS_DANGEROUS_FORMAT at the top of
+  // this file.
+  //
+  // Even THEN you might not be all the way home due to some non-conforming
+  // distributions.  More on this in the comments below.
+
+  inline int  ssnprintf(PSTR pA, size_t nCount, PCSTR pFmtA, va_list vl)
+  {
+  #ifdef _MSC_VER
+      return _vsnprintf(pA, nCount, pFmtA, vl);
+  #else
+      return vsnprintf(pA, nCount, pFmtA, vl);
+  #endif
+  }
+  inline int  ssnprintf(PWSTR pW, size_t nCount, PCWSTR pFmtW, va_list vl)
+  {
+  #ifdef _MSC_VER
+      return _vsnwprintf(pW, nCount, pFmtW, vl);
+  #else
+      return vswprintf(pW, nCount, pFmtW, vl);
+  #endif
+  }
+
+
+
+
+// -----------------------------------------------------------------------------
+// ssload: Type safe, overloaded ::LoadString wrappers
+// There is no equivalent of these in non-Win32-specific builds.  However, I'm
+// thinking that with the message facet, there might eventually be one
+// -----------------------------------------------------------------------------
+#if defined (SS_WIN32) && !defined(SS_ANSI)
+  inline int ssload(HMODULE hInst, UINT uId, PSTR pBuf, int nMax)
+  {
+    return ::LoadStringA(hInst, uId, pBuf, nMax);
+  }
+  inline int ssload(HMODULE hInst, UINT uId, PWSTR pBuf, int nMax)
+  {
+    return ::LoadStringW(hInst, uId, pBuf, nMax);
+  }
+#if defined ( _MSC_VER ) && ( _MSC_VER >= 1500 )
+  inline int ssload(HMODULE hInst, UINT uId, uint16_t *pBuf, int nMax)
+  {
+    return 0;
+  }
+  inline int ssload(HMODULE hInst, UINT uId, uint32_t *pBuf, int nMax)
+  {
+    return 0;
+  }
+#endif
+#endif
+
+
+// -----------------------------------------------------------------------------
+// sscoll/ssicoll: Collation wrappers
+//    Note -- with MSVC I have reversed the arguments order here because the
+//    functions appear to return the opposite of what they should
+// -----------------------------------------------------------------------------
+#ifndef SS_NO_LOCALE
+template <typename CT>
+inline int sscoll(const CT* sz1, int nLen1, const CT* sz2, int nLen2)
+{
+  const std::collate<CT>& coll =
+    SS_USE_FACET(std::locale(), std::collate<CT>);
+
+  return coll.compare(sz2, sz2+nLen2, sz1, sz1+nLen1);
+}
+template <typename CT>
+inline int ssicoll(const CT* sz1, int nLen1, const CT* sz2, int nLen2)
+{
+  const std::locale loc;
+  const std::collate<CT>& coll = SS_USE_FACET(loc, std::collate<CT>);
+
+  // Some implementations seem to have trouble using the collate<>
+  // facet typedefs so we'll just default to basic_string and hope
+  // that's what the collate facet uses (which it generally should)
+
+//  std::collate<CT>::string_type s1(sz1);
+//  std::collate<CT>::string_type s2(sz2);
+  const std::basic_string<CT> sEmpty;
+    std::basic_string<CT> s1(sz1 ? sz1 : sEmpty.c_str());
+    std::basic_string<CT> s2(sz2 ? sz2 : sEmpty.c_str());
+
+  sslwr(const_cast<CT*>(s1.c_str()), nLen1, loc);
+  sslwr(const_cast<CT*>(s2.c_str()), nLen2, loc);
+  return coll.compare(s2.c_str(), s2.c_str()+nLen2,
+            s1.c_str(), s1.c_str()+nLen1);
+}
+#endif
+
+
+// -----------------------------------------------------------------------------
+// ssfmtmsg: FormatMessage equivalents.  Needed because I added a CString facade
+// Again -- no equivalent of these on non-Win32 builds but their might one day
+// be one if the message facet gets implemented
+// -----------------------------------------------------------------------------
+#if defined (SS_WIN32) && !defined(SS_ANSI)
+  inline DWORD ssfmtmsg(DWORD dwFlags, LPCVOID pSrc, DWORD dwMsgId,
+              DWORD dwLangId, PSTR pBuf, DWORD nSize,
+              va_list* vlArgs)
+  {
+    return FormatMessageA(dwFlags, pSrc, dwMsgId, dwLangId,
+                pBuf, nSize,vlArgs);
+  }
+  inline DWORD ssfmtmsg(DWORD dwFlags, LPCVOID pSrc, DWORD dwMsgId,
+              DWORD dwLangId, PWSTR pBuf, DWORD nSize,
+              va_list* vlArgs)
+  {
+    return FormatMessageW(dwFlags, pSrc, dwMsgId, dwLangId,
+                pBuf, nSize,vlArgs);
+  }
+#else
+#endif
+
+
+
+// FUNCTION: sscpy.  Copies up to 'nMax' characters from pSrc to pDst.
+// -----------------------------------------------------------------------------
+// FUNCTION:  sscpy
+//    inline int sscpy(PSTR pDst, PCSTR pSrc, int nMax=-1);
+//    inline int sscpy(PUSTR pDst,  PCSTR pSrc, int nMax=-1)
+//    inline int sscpy(PSTR pDst, PCWSTR pSrc, int nMax=-1);
+//    inline int sscpy(PWSTR pDst, PCWSTR pSrc, int nMax=-1);
+//    inline int sscpy(PWSTR pDst, PCSTR pSrc, int nMax=-1);
+//
+// DESCRIPTION:
+//    This function is very much (but not exactly) like strcpy.  These
+//    overloads simplify copying one C-style string into another by allowing
+//    the caller to specify two different types of strings if necessary.
+//
+//    The strings must NOT overlap
+//
+//    "Character" is expressed in terms of the destination string, not
+//    the source.  If no 'nMax' argument is supplied, then the number of
+//    characters copied will be sslen(pSrc).  A NULL terminator will
+//    also be added so pDst must actually be big enough to hold nMax+1
+//    characters.  The return value is the number of characters copied,
+//    not including the NULL terminator.
+//
+// PARAMETERS:
+//    pSrc - the string to be copied FROM.  May be a char based string, an
+//         MBCS string (in Win32 builds) or a wide string (wchar_t).
+//    pSrc - the string to be copied TO.  Also may be either MBCS or wide
+//    nMax - the maximum number of characters to be copied into szDest.  Note
+//         that this is expressed in whatever a "character" means to pDst.
+//         If pDst is a wchar_t type string than this will be the maximum
+//         number of wchar_ts that my be copied.  The pDst string must be
+//         large enough to hold least nMaxChars+1 characters.
+//         If the caller supplies no argument for nMax this is a signal to
+//         the routine to copy all the characters in pSrc, regardless of
+//         how long it is.
+//
+// RETURN VALUE: none
+// -----------------------------------------------------------------------------
+
+template<typename CT1, typename CT2>
+inline int sscpycvt(CT1* pDst, const CT2* pSrc, int nMax)
+{
+  // Note -- we assume pDst is big enough to hold pSrc.  If not, we're in
+  // big trouble.  No bounds checking.  Caveat emptor.
+
+  int nSrc = sslen(pSrc);
+
+  const CT1* szCvt = StdCodeCvt(pDst, nMax, pSrc, nSrc);
+
+  // If we're copying the same size characters, then all the "code convert"
+  // just did was basically memcpy so the #of characters copied is the same
+  // as the number requested.  I should probably specialize this function
+  // template to achieve this purpose as it is silly to do a runtime check
+  // of a fact known at compile time.  I'll get around to it.
+
+  return sslen(szCvt);
+}
+
+template<typename T>
+inline int sscpycvt(T* pDst, const T* pSrc, int nMax)
+{
+  int nCount = nMax;
+  for (; nCount > 0 && *pSrc; ++pSrc, ++pDst, --nCount)
+    std::basic_string<T>::traits_type::assign(*pDst, *pSrc);
+
+  *pDst = 0;
+  return nMax - nCount;
+}
+
+inline int sscpycvt(PWSTR pDst, PCSTR pSrc, int nMax)
+{
+  // Note -- we assume pDst is big enough to hold pSrc.  If not, we're in
+  // big trouble.  No bounds checking.  Caveat emptor.
+
+  const PWSTR szCvt = StdCodeCvt(pDst, nMax, pSrc, nMax);
+  return sslen(szCvt);
+}
+
+template<typename CT1, typename CT2>
+inline int sscpy(CT1* pDst, const CT2* pSrc, int nMax, int nLen)
+{
+  return sscpycvt(pDst, pSrc, SSMIN(nMax, nLen));
+}
+template<typename CT1, typename CT2>
+inline int sscpy(CT1* pDst, const CT2* pSrc, int nMax)
+{
+  return sscpycvt(pDst, pSrc, SSMIN(nMax, sslen(pSrc)));
+}
+template<typename CT1, typename CT2>
+inline int sscpy(CT1* pDst, const CT2* pSrc)
+{
+  return sscpycvt(pDst, pSrc, sslen(pSrc));
+}
+template<typename CT1, typename CT2>
+inline int sscpy(CT1* pDst, const std::basic_string<CT2>& sSrc, int nMax)
+{
+  return sscpycvt(pDst, sSrc.c_str(), SSMIN(nMax, (int)sSrc.length()));
+}
+template<typename CT1, typename CT2>
+inline int sscpy(CT1* pDst, const std::basic_string<CT2>& sSrc)
+{
+  return sscpycvt(pDst, sSrc.c_str(), (int)sSrc.length());
+}
+
+#ifdef SS_INC_COMDEF
+  template<typename CT1>
+  inline int sscpy(CT1* pDst, const _bstr_t& bs, int nMax)
+  {
+    return sscpycvt(pDst, static_cast<PCOLESTR>(bs),
+            SSMIN(nMax, static_cast<int>(bs.length())));
+  }
+  template<typename CT1>
+  inline int sscpy(CT1* pDst, const _bstr_t& bs)
+  {
+    return sscpy(pDst, bs, static_cast<int>(bs.length()));
+  }
+#endif
+
+
+// -----------------------------------------------------------------------------
+// Functional objects for changing case.  They also let you pass locales
+// -----------------------------------------------------------------------------
+
+#ifdef SS_NO_LOCALE
+  template<typename CT>
+  struct SSToUpper : public std::unary_function<CT, CT>
+  {
+    inline CT operator()(const CT& t) const
+    {
+      return sstoupper(t);
+    }
+  };
+  template<typename CT>
+  struct SSToLower : public std::unary_function<CT, CT>
+  {
+    inline CT operator()(const CT& t) const
+    {
+      return sstolower(t);
+    }
+  };
+#else
+  template<typename CT>
+  struct SSToUpper : public std::binary_function<CT, std::locale, CT>
+  {
+    inline CT operator()(const CT& t, const std::locale& loc) const
+    {
+      return sstoupper<CT>(t, loc);
+    }
+  };
+  template<typename CT>
+  struct SSToLower : public std::binary_function<CT, std::locale, CT>
+  {
+    inline CT operator()(const CT& t, const std::locale& loc) const
+    {
+      return sstolower<CT>(t, loc);
+    }
+  };
+#endif
+
+// This struct is used for TrimRight() and TrimLeft() function implementations.
+//template<typename CT>
+//struct NotSpace : public std::unary_function<CT, bool>
+//{
+//  const std::locale& loc;
+//  inline NotSpace(const std::locale& locArg) : loc(locArg) {}
+//  inline bool operator() (CT t) { return !std::isspace(t, loc); }
+//};
+template<typename CT>
+struct NotSpace : public std::unary_function<CT, bool>
+{
+  // DINKUMWARE BUG:
+  // Note -- using std::isspace in a COM DLL gives us access violations
+  // because it causes the dynamic addition of a function to be called
+  // when the library shuts down.  Unfortunately the list is maintained
+  // in DLL memory but the function is in static memory.  So the COM DLL
+  // goes away along with the function that was supposed to be called,
+  // and then later when the DLL CRT shuts down it unloads the list and
+  // tries to call the long-gone function.
+  // This is DinkumWare's implementation problem.  If you encounter this
+  // problem, you may replace the calls here with good old isspace() and
+  // iswspace() from the CRT unless they specify SS_ANSI
+
+#ifdef SS_NO_LOCALE
+
+  bool operator() (CT t) const { return !ssisspace(t); }
+
+#else
+  const std::locale loc;
+  NotSpace(const std::locale& locArg=std::locale()) : loc(locArg) {}
+  bool operator() (CT t) const { return !std::isspace(t, loc); }
+#endif
+};
+
+
+
+
+//      Now we can define the template (finally!)
+// =============================================================================
+// TEMPLATE: CStdStr
+//    template<typename CT> class CStdStr : public std::basic_string<CT>
+//
+// REMARKS:
+//    This template derives from basic_string<CT> and adds some MFC CString-
+//    like functionality
+//
+//    Basically, this is my attempt to make Standard C++ library strings as
+//    easy to use as the MFC CString class.
+//
+//    Note that although this is a template, it makes the assumption that the
+//    template argument (CT, the character type) is either char or wchar_t.
+// =============================================================================
+
+//#define CStdStr _SS  // avoid compiler warning 4786
+
+//    template<typename ARG> ARG& FmtArg(ARG& arg)  { return arg; }
+//    PCSTR  FmtArg(const std::string& arg)  { return arg.c_str(); }
+//    PCWSTR FmtArg(const std::wstring& arg) { return arg.c_str(); }
+
+template<typename ARG>
+struct FmtArg
+{
+    explicit FmtArg(const ARG& arg) : a_(arg) {}
+    const ARG& operator()() const { return a_; }
+    const ARG& a_;
+private:
+    FmtArg& operator=(const FmtArg&) { return *this; }
+};
+
+template<typename CT>
+class CStdStr : public std::basic_string<CT>
+{
+  // Typedefs for shorter names.  Using these names also appears to help
+  // us avoid some ambiguities that otherwise arise on some platforms
+
+  #define MYBASE std::basic_string<CT>         // my base class
+  //typedef typename std::basic_string<CT>    MYBASE;   // my base class
+  typedef CStdStr<CT>              MYTYPE;   // myself
+  typedef typename MYBASE::const_pointer    PCMYSTR; // PCSTR or PCWSTR
+  typedef typename MYBASE::pointer      PMYSTR;   // PSTR or PWSTR
+  typedef typename MYBASE::iterator      MYITER;  // my iterator type
+  typedef typename MYBASE::const_iterator    MYCITER; // you get the idea...
+  typedef typename MYBASE::reverse_iterator  MYRITER;
+  typedef typename MYBASE::size_type      MYSIZE;
+  typedef typename MYBASE::value_type      MYVAL;
+  typedef typename MYBASE::allocator_type    MYALLOC;
+
+public:
+  // shorthand conversion from PCTSTR to string resource ID
+  #define SSRES(pctstr)  LOWORD(reinterpret_cast<unsigned long>(pctstr))
+
+  bool TryLoad(const void* pT)
+  {
+    bool bLoaded = false;
+
+#if defined(SS_WIN32) && !defined(SS_ANSI)
+    if ( ( pT != NULL ) && SS_IS_INTRESOURCE(pT) )
+    {
+      UINT nId = LOWORD(reinterpret_cast<unsigned long>(pT));
+      if ( !LoadString(nId) )
+      {
+        TRACE(_T("Can't load string %u\n"), SSRES(pT));
+      }
+      bLoaded = true;
+    }
+#endif
+
+    return bLoaded;
+  }
+
+
+  // CStdStr inline constructors
+  CStdStr()
+  {
+  }
+
+  CStdStr(const MYTYPE& str) : MYBASE(SSREF(str))
+  {
+  }
+
+  CStdStr(const std::string& str)
+  {
+    ssasn(*this, SSREF(str));
+  }
+
+  CStdStr(const std::wstring& str)
+  {
+    ssasn(*this, SSREF(str));
+  }
+
+  CStdStr(PCMYSTR pT, MYSIZE n) : MYBASE(pT, n)
+  {
+  }
+
+#ifdef SS_UNSIGNED
+  CStdStr(PCUSTR pU)
+  {
+    *this = reinterpret_cast<PCSTR>(pU);
+  }
+#endif
+
+  CStdStr(PCSTR pA)
+  {
+  #ifdef SS_ANSI
+    *this = pA;
+  #else
+    if ( !TryLoad(pA) )
+      *this = pA;
+  #endif
+  }
+
+  CStdStr(PCWSTR pW)
+  {
+  #ifdef SS_ANSI
+    *this = pW;
+  #else
+    if ( !TryLoad(pW) )
+      *this = pW;
+  #endif
+  }
+
+  CStdStr(uint16_t* pW)
+  {
+  #ifdef SS_ANSI
+    *this = pW;
+  #else
+    if ( !TryLoad(pW) )
+      *this = pW;
+  #endif
+  }
+
+  CStdStr(uint32_t* pW)
+  {
+  #ifdef SS_ANSI
+    *this = pW;
+  #else
+    if ( !TryLoad(pW) )
+      *this = pW;
+  #endif
+  }
+
+  CStdStr(MYCITER first, MYCITER last)
+    : MYBASE(first, last)
+  {
+  }
+
+  CStdStr(MYSIZE nSize, MYVAL ch, const MYALLOC& al=MYALLOC())
+    : MYBASE(nSize, ch, al)
+  {
+  }
+
+  #ifdef SS_INC_COMDEF
+    CStdStr(const _bstr_t& bstr)
+    {
+      if ( bstr.length() > 0 )
+        this->append(static_cast<PCMYSTR>(bstr), bstr.length());
+    }
+  #endif
+
+  // CStdStr inline assignment operators -- the ssasn function now takes care
+  // of fixing  the MSVC assignment bug (see knowledge base article Q172398).
+  MYTYPE& operator=(const MYTYPE& str)
+  {
+    ssasn(*this, str);
+    return *this;
+  }
+
+  MYTYPE& operator=(const std::string& str)
+  {
+    ssasn(*this, str);
+    return *this;
+  }
+
+  MYTYPE& operator=(const std::wstring& str)
+  {
+    ssasn(*this, str);
+    return *this;
+  }
+
+  MYTYPE& operator=(PCSTR pA)
+  {
+    ssasn(*this, pA);
+    return *this;
+  }
+
+  MYTYPE& operator=(PCWSTR pW)
+  {
+    ssasn(*this, pW);
+    return *this;
+  }
+
+#ifdef SS_UNSIGNED
+  MYTYPE& operator=(PCUSTR pU)
+  {
+    ssasn(*this, reinterpret_cast<PCSTR>(pU));
+    return *this;
+  }
+#endif
+
+  MYTYPE& operator=(uint16_t* pA)
+  {
+    ssasn(*this, pA);
+    return *this;
+  }
+
+  MYTYPE& operator=(uint32_t* pA)
+  {
+    ssasn(*this, pA);
+    return *this;
+  }
+
+  MYTYPE& operator=(CT t)
+  {
+    Q172398(*this);
+    this->assign(1, t);
+    return *this;
+  }
+
+  #ifdef SS_INC_COMDEF
+    MYTYPE& operator=(const _bstr_t& bstr)
+    {
+      if ( bstr.length() > 0 )
+      {
+        this->assign(static_cast<PCMYSTR>(bstr), bstr.length());
+        return *this;
+      }
+      else
+      {
+        this->erase();
+        return *this;
+      }
+    }
+  #endif
+
+
+  // Overloads  also needed to fix the MSVC assignment bug (KB: Q172398)
+  //  *** Thanks to Pete The Plumber for catching this one ***
+  // They also are compiled if you have explicitly turned off refcounting
+  #if ( defined(_MSC_VER) && ( _MSC_VER < 1200 ) ) || defined(SS_NO_REFCOUNT)
+
+    MYTYPE& assign(const MYTYPE& str)
+    {
+      Q172398(*this);
+      sscpy(GetBuffer(str.size()+1), SSREF(str));
+      this->ReleaseBuffer(str.size());
+      return *this;
+    }
+
+    MYTYPE& assign(const MYTYPE& str, MYSIZE nStart, MYSIZE nChars)
+    {
+      // This overload of basic_string::assign is supposed to assign up to
+      // <nChars> or the NULL terminator, whichever comes first.  Since we
+      // are about to call a less forgiving overload (in which <nChars>
+      // must be a valid length), we must adjust the length here to a safe
+      // value.  Thanks to Ullrich Poll�hne for catching this bug
+
+      nChars    = SSMIN(nChars, str.length() - nStart);
+      MYTYPE strTemp(str.c_str()+nStart, nChars);
+      Q172398(*this);
+      this->assign(strTemp);
+      return *this;
+    }
+
+    MYTYPE& assign(const MYBASE& str)
+    {
+      ssasn(*this, str);
+      return *this;
+    }
+
+    MYTYPE& assign(const MYBASE& str, MYSIZE nStart, MYSIZE nChars)
+    {
+      // This overload of basic_string::assign is supposed to assign up to
+      // <nChars> or the NULL terminator, whichever comes first.  Since we
+      // are about to call a less forgiving overload (in which <nChars>
+      // must be a valid length), we must adjust the length here to a safe
+      // value. Thanks to Ullrich Poll�hne for catching this bug
+
+      nChars    = SSMIN(nChars, str.length() - nStart);
+
+      // Watch out for assignment to self
+
+      if ( this == &str )
+      {
+        MYTYPE strTemp(str.c_str() + nStart, nChars);
+        static_cast<MYBASE*>(this)->assign(strTemp);
+      }
+      else
+      {
+        Q172398(*this);
+        static_cast<MYBASE*>(this)->assign(str.c_str()+nStart, nChars);
+      }
+      return *this;
+    }
+
+    MYTYPE& assign(const CT* pC, MYSIZE nChars)
+    {
+      // Q172398 only fix -- erase before assigning, but not if we're
+      // assigning from our own buffer
+
+  #if defined ( _MSC_VER ) && ( _MSC_VER < 1200 )
+      if ( !this->empty() &&
+        ( pC < this->data() || pC > this->data() + this->capacity() ) )
+      {
+        this->erase();
+      }
+  #endif
+      Q172398(*this);
+      static_cast<MYBASE*>(this)->assign(pC, nChars);
+      return *this;
+    }
+
+    MYTYPE& assign(MYSIZE nChars, MYVAL val)
+    {
+      Q172398(*this);
+      static_cast<MYBASE*>(this)->assign(nChars, val);
+      return *this;
+    }
+
+    MYTYPE& assign(const CT* pT)
+    {
+      return this->assign(pT, MYBASE::traits_type::length(pT));
+    }
+
+    MYTYPE& assign(MYCITER iterFirst, MYCITER iterLast)
+    {
+  #if defined ( _MSC_VER ) && ( _MSC_VER < 1200 )
+      // Q172398 fix.  don't call erase() if we're assigning from ourself
+      if ( iterFirst < this->begin() ||
+                 iterFirst > this->begin() + this->size() )
+            {
+        this->erase()
+            }
+  #endif
+      this->replace(this->begin(), this->end(), iterFirst, iterLast);
+      return *this;
+    }
+  #endif
+
+
+  // -------------------------------------------------------------------------
+  // CStdStr inline concatenation.
+  // -------------------------------------------------------------------------
+  MYTYPE& operator+=(const MYTYPE& str)
+  {
+    ssadd(*this, str);
+    return *this;
+  }
+
+  MYTYPE& operator+=(const std::string& str)
+  {
+    ssadd(*this, str);
+    return *this;
+  }
+
+  MYTYPE& operator+=(const std::wstring& str)
+  {
+    ssadd(*this, str);
+    return *this;
+  }
+
+  MYTYPE& operator+=(PCSTR pA)
+  {
+    ssadd(*this, pA);
+    return *this;
+  }
+
+  MYTYPE& operator+=(PCWSTR pW)
+  {
+    ssadd(*this, pW);
+    return *this;
+  }
+
+  MYTYPE& operator+=(uint16_t* pW)
+  {
+    ssadd(*this, pW);
+    return *this;
+  }
+
+  MYTYPE& operator+=(uint32_t* pW)
+  {
+    ssadd(*this, pW);
+    return *this;
+  }
+
+  MYTYPE& operator+=(CT t)
+  {
+    this->append(1, t);
+    return *this;
+  }
+  #ifdef SS_INC_COMDEF  // if we have _bstr_t, define a += for it too.
+    MYTYPE& operator+=(const _bstr_t& bstr)
+    {
+      return this->operator+=(static_cast<PCMYSTR>(bstr));
+    }
+  #endif
+
+
+  // -------------------------------------------------------------------------
+  // Case changing functions
+  // -------------------------------------------------------------------------
+
+    MYTYPE& ToUpper(const std::locale& loc=std::locale())
+  {
+    // Note -- if there are any MBCS character sets in which the lowercase
+    // form a character takes up a different number of bytes than the
+    // uppercase form, this would probably not work...
+
+    std::transform(this->begin(),
+             this->end(),
+             this->begin(),
+#ifdef SS_NO_LOCALE
+             SSToUpper<CT>());
+#else
+             std::bind2nd(SSToUpper<CT>(), loc));
+#endif
+
+    // ...but if it were, this would probably work better.  Also, this way
+    // seems to be a bit faster when anything other then the "C" locale is
+    // used...
+
+//    if ( !empty() )
+//    {
+//      ssupr(this->GetBuf(), this->size(), loc);
+//      this->RelBuf();
+//    }
+
+    return *this;
+  }
+
+  MYTYPE& ToLower(const std::locale& loc=std::locale())
+  {
+    // Note -- if there are any MBCS character sets in which the lowercase
+    // form a character takes up a different number of bytes than the
+    // uppercase form, this would probably not work...
+
+    std::transform(this->begin(),
+             this->end(),
+             this->begin(),
+#ifdef SS_NO_LOCALE
+             SSToLower<CT>());
+#else
+             std::bind2nd(SSToLower<CT>(), loc));
+#endif
+
+    // ...but if it were, this would probably work better.  Also, this way
+    // seems to be a bit faster when anything other then the "C" locale is
+    // used...
+
+//    if ( !empty() )
+//    {
+//      sslwr(this->GetBuf(), this->size(), loc);
+//      this->RelBuf();
+//    }
+    return *this;
+  }
+
+
+  MYTYPE& Normalize()
+  {
+    return Trim().ToLower();
+  }
+
+
+  // -------------------------------------------------------------------------
+  // CStdStr -- Direct access to character buffer.  In the MS' implementation,
+  // the at() function that we use here also calls _Freeze() providing us some
+  // protection from multithreading problems associated with ref-counting.
+    // In VC 7 and later, of course, the ref-counting stuff is gone.
+  // -------------------------------------------------------------------------
+
+  CT* GetBuf(int nMinLen=-1)
+  {
+    if ( static_cast<int>(this->size()) < nMinLen )
+      this->resize(static_cast<MYSIZE>(nMinLen));
+
+    return this->empty() ? const_cast<CT*>(this->data()) : &(this->at(0));
+  }
+
+  CT* SetBuf(int nLen)
+  {
+    nLen = ( nLen > 0 ? nLen : 0 );
+    if ( this->capacity() < 1 && nLen == 0 )
+      this->resize(1);
+
+    this->resize(static_cast<MYSIZE>(nLen));
+    return const_cast<CT*>(this->data());
+  }
+  void RelBuf(int nNewLen=-1)
+  {
+    this->resize(static_cast<MYSIZE>(nNewLen > -1 ? nNewLen :
+                                                        sslen(this->c_str())));
+  }
+
+  void BufferRel()     { RelBuf(); }      // backwards compatability
+  CT*  Buffer()       { return GetBuf(); }  // backwards compatability
+  CT*  BufferSet(int nLen) { return SetBuf(nLen);}// backwards compatability
+
+  bool Equals(const CT* pT, bool bUseCase=false) const
+  {
+    return  0 == (bUseCase ? this->compare(pT) : ssicmp(this->c_str(), pT));
+  }
+
+  // -------------------------------------------------------------------------
+  // FUNCTION:  CStdStr::Load
+  // REMARKS:
+  //    Loads string from resource specified by nID
+  //
+  // PARAMETERS:
+  //    nID - resource Identifier.  Purely a Win32 thing in this case
+  //
+  // RETURN VALUE:
+  //    true if successful, false otherwise
+  // -------------------------------------------------------------------------
+
+#ifndef SS_ANSI
+
+  bool Load(UINT nId, HMODULE hModule=NULL)
+  {
+    bool bLoaded    = false;  // set to true of we succeed.
+
+  #ifdef _MFC_VER    // When in Rome (or MFC land)...
+
+    // If they gave a resource handle, use it.  Note - this is archaic
+    // and not really what I would recommend.  But then again, in MFC
+    // land, you ought to be using CString for resources anyway since
+    // it walks the resource chain for you.
+
+    HMODULE hModuleOld = NULL;
+
+    if ( NULL != hModule )
+    {
+      hModuleOld = AfxGetResourceHandle();
+      AfxSetResourceHandle(hModule);
+    }
+
+    // ...load the string
+
+    CString strRes;
+    bLoaded        = FALSE != strRes.LoadString(nId);
+
+    // ...and if we set the resource handle, restore it.
+
+    if ( NULL != hModuleOld )
+      AfxSetResourceHandle(hModule);
+
+    if ( bLoaded )
+      *this      = strRes;
+
+  #else // otherwise make our own hackneyed version of CString's Load
+
+    // Get the resource name and module handle
+
+    if ( NULL == hModule )
+      hModule      = GetResourceHandle();
+
+    PCTSTR szName    = MAKEINTRESOURCE((nId>>4)+1); // lifted
+    DWORD dwSize    = 0;
+
+    // No sense continuing if we can't find the resource
+
+    HRSRC hrsrc      = ::FindResource(hModule, szName, RT_STRING);
+
+    if ( NULL == hrsrc )
+    {
+      TRACE(_T("Cannot find resource %d: 0x%X"), nId, ::GetLastError());
+    }
+    else if ( 0 == (dwSize = ::SizeofResource(hModule, hrsrc) / sizeof(CT)))
+    {
+      TRACE(_T("Cant get size of resource %d 0x%X\n"),nId,GetLastError());
+    }
+    else
+    {
+      bLoaded      = 0 != ssload(hModule, nId, GetBuf(dwSize), dwSize);
+      ReleaseBuffer();
+    }
+
+  #endif  // #ifdef _MFC_VER
+
+    if ( !bLoaded )
+      TRACE(_T("String not loaded 0x%X\n"), ::GetLastError());
+
+    return bLoaded;
+  }
+
+#endif  // #ifdef SS_ANSI
+
+  // -------------------------------------------------------------------------
+  // FUNCTION:  CStdStr::Format
+  //    void _cdecl Formst(CStdStringA& PCSTR szFormat, ...)
+  //    void _cdecl Format(PCSTR szFormat);
+  //
+  // DESCRIPTION:
+  //    This function does sprintf/wsprintf style formatting on CStdStringA
+  //    objects.  It looks a lot like MFC's CString::Format.  Some people
+  //    might even call this identical.  Fortunately, these people are now
+  //    dead... heh heh.
+  //
+  // PARAMETERS:
+  //    nId - ID of string resource holding the format string
+  //    szFormat - a PCSTR holding the format specifiers
+  //    argList - a va_list holding the arguments for the format specifiers.
+  //
+  // RETURN VALUE:  None.
+  // -------------------------------------------------------------------------
+  // formatting (using wsprintf style formatting)
+
+    // If they want a Format() function that safely handles string objects
+    // without casting
+
+#ifdef SS_SAFE_FORMAT
+
+    // Question:  Joe, you wacky coder you, why do you have so many overloads
+    //      of the Format() function
+    // Answer:  One reason only - CString compatability.  In short, by making
+    //      the Format() function a template this way, I can do strong typing
+    //      and allow people to pass CStdString arguments as fillers for
+    //      "%s" format specifiers without crashing their program!  The downside
+    //      is that I need to overload on the number of arguments.   If you are
+    //      passing more arguments than I have listed below in any of my
+    //      overloads, just add another one.
+    //
+    //      Yes, yes, this is really ugly.  In essence what I am doing here is
+    //      protecting people from a bad (and incorrect) programming practice
+    //      that they should not be doing anyway.  I am protecting them from
+    //      themselves.  Why am I doing this?  Well, if you had any idea the
+    //      number of times I've been emailed by people about this
+    //      "incompatability" in my code, you wouldn't ask.
+
+  void Fmt(const CT* szFmt, ...)
+  {
+    va_list argList;
+    va_start(argList, szFmt);
+    FormatV(szFmt, argList);
+    va_end(argList);
+  }
+
+#ifndef SS_ANSI
+
+    void Format(UINT nId)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+            this->swap(strFmt);
+    }
+    template<class A1>
+    void Format(UINT nId, const A1& v)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+            Fmt(strFmt, FmtArg<A1>(v)());
+    }
+    template<class A1, class A2>
+    void Format(UINT nId, const A1& v1, const A2& v2)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+           Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)());
+    }
+    template<class A1, class A2, class A3>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(),FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(),FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+           Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)(), FmtArg<A10>(v10)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)(),FmtArg<A10>(v10)(),FmtArg<A11>(v11)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+                FmtArg<A12>(v12)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+                FmtArg<A12>(v12)(), FmtArg<A13>(v13)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13, class A14>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13, const A14& v14)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+                FmtArg<A12>(v12)(), FmtArg<A13>(v13)(),FmtArg<A14>(v14)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13, class A14, class A15>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13, const A14& v14, const A15& v15)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+                FmtArg<A12>(v12)(),FmtArg<A13>(v13)(),FmtArg<A14>(v14)(),
+                FmtArg<A15>(v15)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13, class A14, class A15, class A16>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13, const A14& v14, const A15& v15,
+                const A16& v16)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+                FmtArg<A12>(v12)(),FmtArg<A13>(v13)(),FmtArg<A14>(v14)(),
+                FmtArg<A15>(v15)(), FmtArg<A16>(v16)());
+        }
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13, class A14, class A15, class A16, class A17>
+    void Format(UINT nId, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13, const A14& v14, const A15& v15,
+                const A16& v16, const A17& v17)
+    {
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+        {
+            Fmt(strFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+                FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+                FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+                FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+                FmtArg<A12>(v12)(),FmtArg<A13>(v13)(),FmtArg<A14>(v14)(),
+                FmtArg<A15>(v15)(),FmtArg<A16>(v16)(),FmtArg<A17>(v17)());
+        }
+    }
+
+#endif // #ifndef SS_ANSI
+
+    // ...now the other overload of Format: the one that takes a string literal
+
+    void Format(const CT* szFmt)
+    {
+        *this = szFmt;
+    }
+    template<class A1>
+    void Format(const CT* szFmt, const A1& v)
+    {
+        Fmt(szFmt, FmtArg<A1>(v)());
+    }
+    template<class A1, class A2>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)());
+    }
+    template<class A1, class A2, class A3>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)());
+    }
+    template<class A1, class A2, class A3, class A4>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)(), FmtArg<A10>(v10)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)(),FmtArg<A10>(v10)(),FmtArg<A11>(v11)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+            FmtArg<A12>(v12)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+            FmtArg<A12>(v12)(), FmtArg<A13>(v13)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13, class A14>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13, const A14& v14)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+            FmtArg<A12>(v12)(), FmtArg<A13>(v13)(),FmtArg<A14>(v14)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13, class A14, class A15>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13, const A14& v14, const A15& v15)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+            FmtArg<A12>(v12)(),FmtArg<A13>(v13)(),FmtArg<A14>(v14)(),
+            FmtArg<A15>(v15)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13, class A14, class A15, class A16>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13, const A14& v14, const A15& v15,
+                const A16& v16)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+            FmtArg<A12>(v12)(),FmtArg<A13>(v13)(),FmtArg<A14>(v14)(),
+            FmtArg<A15>(v15)(), FmtArg<A16>(v16)());
+    }
+    template<class A1, class A2, class A3, class A4, class A5, class A6,
+        class A7, class A8, class A9, class A10, class A11, class A12,
+        class A13, class A14, class A15, class A16, class A17>
+    void Format(const CT* szFmt, const A1& v1, const A2& v2, const A3& v3,
+                const A4& v4, const A5& v5, const A6& v6, const A7& v7,
+                const A8& v8, const A9& v9, const A10& v10, const A11& v11,
+                const A12& v12, const A13& v13, const A14& v14, const A15& v15,
+                const A16& v16, const A17& v17)
+    {
+        Fmt(szFmt, FmtArg<A1>(v1)(), FmtArg<A2>(v2)(),
+            FmtArg<A3>(v3)(), FmtArg<A4>(v4)(), FmtArg<A5>(v5)(),
+            FmtArg<A6>(v6)(), FmtArg<A7>(v7)(), FmtArg<A8>(v8)(),
+            FmtArg<A9>(v9)(), FmtArg<A10>(v10)(),FmtArg<A11>(v11)(),
+            FmtArg<A12>(v12)(),FmtArg<A13>(v13)(),FmtArg<A14>(v14)(),
+            FmtArg<A15>(v15)(),FmtArg<A16>(v16)(),FmtArg<A17>(v17)());
+    }
+
+#else  // #ifdef SS_SAFE_FORMAT
+
+
+#ifndef SS_ANSI
+
+  void Format(UINT nId, ...)
+  {
+    va_list argList;
+    va_start(argList, nId);
+
+    MYTYPE strFmt;
+    if ( strFmt.Load(nId) )
+      FormatV(strFmt, argList);
+
+    va_end(argList);
+  }
+
+#endif  // #ifdef SS_ANSI
+
+  void Format(const CT* szFmt, ...)
+  {
+    va_list argList;
+    va_start(argList, szFmt);
+    FormatV(szFmt, argList);
+    va_end(argList);
+  }
+
+#endif // #ifdef SS_SAFE_FORMAT
+
+  void AppendFormat(const CT* szFmt, ...)
+  {
+    va_list argList;
+    va_start(argList, szFmt);
+    AppendFormatV(szFmt, argList);
+    va_end(argList);
+  }
+
+  #define MAX_FMT_TRIES    5   // #of times we try
+  #define FMT_BLOCK_SIZE    2048 // # of bytes to increment per try
+  #define BUFSIZE_1ST  256
+  #define BUFSIZE_2ND 512
+  #define STD_BUF_SIZE    1024
+
+  // an efficient way to add formatted characters to the string.  You may only
+  // add up to STD_BUF_SIZE characters at a time, though
+  void AppendFormatV(const CT* szFmt, va_list argList)
+  {
+    CT szBuf[STD_BUF_SIZE];
+    int nLen = ssnprintf(szBuf, STD_BUF_SIZE-1, szFmt, argList);
+
+    if ( 0 < nLen )
+      this->append(szBuf, nLen);
+  }
+
+  // -------------------------------------------------------------------------
+  // FUNCTION:  FormatV
+  //    void FormatV(PCSTR szFormat, va_list, argList);
+  //
+  // DESCRIPTION:
+  //    This function formats the string with sprintf style format-specs.
+  //    It makes a general guess at required buffer size and then tries
+  //    successively larger buffers until it finds one big enough or a
+  //    threshold (MAX_FMT_TRIES) is exceeded.
+  //
+  // PARAMETERS:
+  //    szFormat - a PCSTR holding the format of the output
+  //    argList - a Microsoft specific va_list for variable argument lists
+  //
+  // RETURN VALUE:
+  // -------------------------------------------------------------------------
+
+  // NOTE: Changed by JM to actually function under non-win32,
+  //       and to remove the upper limit on size.
+  void FormatV(const CT* szFormat, va_list argList)
+  {
+    // try and grab a sufficient buffersize
+    int nChars = FMT_BLOCK_SIZE;
+    va_list argCopy;
+
+    CT *p = reinterpret_cast<CT*>(malloc(sizeof(CT)*nChars));
+    if (!p) return;
+
+    while (1)
+    {
+      va_copy(argCopy, argList);
+
+      int nActual = ssvsprintf(p, nChars, szFormat, argCopy);
+      /* If that worked, return the string. */
+      if (nActual > -1 && nActual < nChars)
+      { /* make sure it's NULL terminated */
+        p[nActual] = '\0';
+        this->assign(p, nActual);
+        free(p);
+        va_end(argCopy);
+        return;
+      }
+      /* Else try again with more space. */
+      if (nActual > -1)        /* glibc 2.1 */
+        nChars = nActual + 1;  /* precisely what is needed */
+      else                     /* glibc 2.0 */
+        nChars *= 2;           /* twice the old size */
+
+      CT *np = reinterpret_cast<CT*>(realloc(p, sizeof(CT)*nChars));
+      if (np == NULL)
+      {
+        free(p);
+        va_end(argCopy);
+        return;   // failed :(
+      }
+      p = np;
+      va_end(argCopy);
+    }
+  }
+
+  // -------------------------------------------------------------------------
+  // CString Facade Functions:
+  //
+  // The following methods are intended to allow you to use this class as a
+  // near drop-in replacement for CString.
+  // -------------------------------------------------------------------------
+  #ifdef SS_WIN32
+    BSTR AllocSysString() const
+    {
+      ostring os;
+      ssasn(os, *this);
+      return ::SysAllocString(os.c_str());
+    }
+  #endif
+
+#ifndef SS_NO_LOCALE
+  int Collate(PCMYSTR szThat) const
+  {
+    return sscoll(this->c_str(), this->length(), szThat, sslen(szThat));
+  }
+
+  int CollateNoCase(PCMYSTR szThat) const
+  {
+    return ssicoll(this->c_str(), this->length(), szThat, sslen(szThat));
+  }
+#endif
+  int Compare(PCMYSTR szThat) const
+  {
+    return this->compare(szThat);
+  }
+
+  int CompareNoCase(PCMYSTR szThat)  const
+  {
+    return ssicmp(this->c_str(), szThat);
+  }
+
+  int Delete(int nIdx, int nCount=1)
+  {
+        if ( nIdx < 0 )
+      nIdx = 0;
+
+    if ( nIdx < this->GetLength() )
+      this->erase(static_cast<MYSIZE>(nIdx), static_cast<MYSIZE>(nCount));
+
+    return GetLength();
+  }
+
+  void Empty()
+  {
+    this->erase();
+  }
+
+  int Find(CT ch) const
+  {
+    MYSIZE nIdx  = this->find_first_of(ch);
+    return static_cast<int>(MYBASE::npos == nIdx  ? -1 : nIdx);
+  }
+
+  int Find(PCMYSTR szSub) const
+  {
+    MYSIZE nIdx  = this->find(szSub);
+    return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);
+  }
+
+  int Find(CT ch, int nStart) const
+  {
+    // CString::Find docs say add 1 to nStart when it's not zero
+    // CString::Find code doesn't do that however.  We'll stick
+    // with what the code does
+
+    MYSIZE nIdx  = this->find_first_of(ch, static_cast<MYSIZE>(nStart));
+    return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);
+  }
+
+  int Find(PCMYSTR szSub, int nStart) const
+  {
+    // CString::Find docs say add 1 to nStart when it's not zero
+    // CString::Find code doesn't do that however.  We'll stick
+    // with what the code does
+
+    MYSIZE nIdx  = this->find(szSub, static_cast<MYSIZE>(nStart));
+    return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);
+  }
+
+  int FindOneOf(PCMYSTR szCharSet) const
+  {
+    MYSIZE nIdx = this->find_first_of(szCharSet);
+    return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);
+  }
+
+#ifndef SS_ANSI
+  void FormatMessage(PCMYSTR szFormat, ...) throw(std::exception)
+  {
+    va_list argList;
+    va_start(argList, szFormat);
+    PMYSTR szTemp;
+    if ( ssfmtmsg(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER,
+             szFormat, 0, 0,
+             reinterpret_cast<PMYSTR>(&szTemp), 0, &argList) == 0 ||
+       szTemp == 0 )
+    {
+      throw std::runtime_error("out of memory");
+    }
+    *this = szTemp;
+    LocalFree(szTemp);
+    va_end(argList);
+  }
+
+  void FormatMessage(UINT nFormatId, ...) throw(std::exception)
+  {
+    MYTYPE sFormat;
+    VERIFY(sFormat.LoadString(nFormatId));
+    va_list argList;
+    va_start(argList, nFormatId);
+    PMYSTR szTemp;
+    if ( ssfmtmsg(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER,
+             sFormat, 0, 0,
+             reinterpret_cast<PMYSTR>(&szTemp), 0, &argList) == 0 ||
+      szTemp == 0)
+    {
+      throw std::runtime_error("out of memory");
+    }
+    *this = szTemp;
+    LocalFree(szTemp);
+    va_end(argList);
+  }
+#endif
+
+  // GetAllocLength -- an MSVC7 function but it costs us nothing to add it.
+
+  int GetAllocLength()
+  {
+    return static_cast<int>(this->capacity());
+  }
+
+  // -------------------------------------------------------------------------
+  // GetXXXX -- Direct access to character buffer
+  // -------------------------------------------------------------------------
+  CT GetAt(int nIdx) const
+  {
+    return this->at(static_cast<MYSIZE>(nIdx));
+  }
+
+  CT* GetBuffer(int nMinLen=-1)
+  {
+    return GetBuf(nMinLen);
+  }
+
+  CT* GetBufferSetLength(int nLen)
+  {
+    return BufferSet(nLen);
+  }
+
+  // GetLength() -- MFC docs say this is the # of BYTES but
+  // in truth it is the number of CHARACTERs (chars or wchar_ts)
+  int GetLength() const
+  {
+    return static_cast<int>(this->length());
+  }
+
+  int Insert(int nIdx, CT ch)
+  {
+    if ( static_cast<MYSIZE>(nIdx) > this->size()-1 )
+      this->append(1, ch);
+    else
+      this->insert(static_cast<MYSIZE>(nIdx), 1, ch);
+
+    return GetLength();
+  }
+  int Insert(int nIdx, PCMYSTR sz)
+  {
+    if ( static_cast<MYSIZE>(nIdx) >= this->size() )
+      this->append(sz, static_cast<MYSIZE>(sslen(sz)));
+    else
+      this->insert(static_cast<MYSIZE>(nIdx), sz);
+
+    return GetLength();
+  }
+
+  bool IsEmpty() const
+  {
+    return this->empty();
+  }
+
+  MYTYPE Left(int nCount) const
+  {
+        // Range check the count.
+
+    nCount = SSMAX(0, SSMIN(nCount, static_cast<int>(this->size())));
+    return this->substr(0, static_cast<MYSIZE>(nCount));
+  }
+
+#ifndef SS_ANSI
+  bool LoadString(UINT nId)
+  {
+    return this->Load(nId);
+  }
+#endif
+
+  void MakeLower()
+  {
+    ToLower();
+  }
+
+  void MakeReverse()
+  {
+    std::reverse(this->begin(), this->end());
+  }
+
+  void MakeUpper()
+  {
+    ToUpper();
+  }
+
+  MYTYPE Mid(int nFirst) const
+  {
+    return Mid(nFirst, this->GetLength()-nFirst);
+  }
+
+  MYTYPE Mid(int nFirst, int nCount) const
+  {
+    // CString does range checking here.  Since we're trying to emulate it,
+    // we must check too.
+
+    if ( nFirst < 0 )
+      nFirst = 0;
+    if ( nCount < 0 )
+      nCount = 0;
+
+    int nSize = static_cast<int>(this->size());
+
+    if ( nFirst + nCount > nSize )
+      nCount = nSize - nFirst;
+
+    if ( nFirst > nSize )
+      return MYTYPE();
+
+    ASSERT(nFirst >= 0);
+    ASSERT(nFirst + nCount <= nSize);
+
+    return this->substr(static_cast<MYSIZE>(nFirst),
+              static_cast<MYSIZE>(nCount));
+  }
+
+  void ReleaseBuffer(int nNewLen=-1)
+  {
+    RelBuf(nNewLen);
+  }
+
+  int Remove(CT ch)
+  {
+    MYSIZE nIdx    = 0;
+    int nRemoved  = 0;
+    while ( (nIdx=this->find_first_of(ch)) != MYBASE::npos )
+    {
+      this->erase(nIdx, 1);
+      nRemoved++;
+    }
+    return nRemoved;
+  }
+
+  int Replace(CT chOld, CT chNew)
+  {
+    int nReplaced  = 0;
+
+    for ( MYITER iter=this->begin(); iter != this->end(); iter++ )
+    {
+      if ( *iter == chOld )
+      {
+        *iter = chNew;
+        nReplaced++;
+      }
+    }
+
+    return nReplaced;
+  }
+
+  int Replace(PCMYSTR szOld, PCMYSTR szNew)
+  {
+    int nReplaced    = 0;
+    MYSIZE nIdx      = 0;
+    MYSIZE nOldLen    = sslen(szOld);
+
+    if ( 0 != nOldLen )
+    {
+      // If the replacement string is longer than the one it replaces, this
+      // string is going to have to grow in size,  Figure out how much
+      // and grow it all the way now, rather than incrementally
+
+      MYSIZE nNewLen    = sslen(szNew);
+      if ( nNewLen > nOldLen )
+      {
+        int nFound      = 0;
+        while ( nIdx < this->length() &&
+          (nIdx=this->find(szOld, nIdx)) != MYBASE::npos )
+        {
+          nFound++;
+          nIdx += nOldLen;
+        }
+        this->reserve(this->size() + nFound * (nNewLen - nOldLen));
+      }
+
+
+      static const CT ch  = CT(0);
+      PCMYSTR szRealNew  = szNew == 0 ? &ch : szNew;
+      nIdx        = 0;
+
+      while ( nIdx < this->length() &&
+        (nIdx=this->find(szOld, nIdx)) != MYBASE::npos )
+      {
+        this->replace(this->begin()+nIdx, this->begin()+nIdx+nOldLen,
+          szRealNew);
+
+        nReplaced++;
+        nIdx += nNewLen;
+      }
+    }
+
+    return nReplaced;
+  }
+
+  int ReverseFind(CT ch) const
+  {
+    MYSIZE nIdx  = this->find_last_of(ch);
+    return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);
+  }
+
+  // ReverseFind overload that's not in CString but might be useful
+  int ReverseFind(PCMYSTR szFind, MYSIZE pos=MYBASE::npos) const
+  {
+    //yuvalt - this does not compile with g++ since MYTTYPE() is different type
+    //MYSIZE nIdx  = this->rfind(0 == szFind ? MYTYPE() : szFind, pos);
+    MYSIZE nIdx  = this->rfind(0 == szFind ? "" : szFind, pos);
+    return static_cast<int>(MYBASE::npos == nIdx ? -1 : nIdx);
+  }
+
+  MYTYPE Right(int nCount) const
+  {
+        // Range check the count.
+
+    nCount = SSMAX(0, SSMIN(nCount, static_cast<int>(this->size())));
+    return this->substr(this->size()-static_cast<MYSIZE>(nCount));
+  }
+
+  void SetAt(int nIndex, CT ch)
+  {
+    ASSERT(this->size() > static_cast<MYSIZE>(nIndex));
+    this->at(static_cast<MYSIZE>(nIndex))    = ch;
+  }
+
+#ifndef SS_ANSI
+  BSTR SetSysString(BSTR* pbstr) const
+  {
+    ostring os;
+    ssasn(os, *this);
+    if ( !::SysReAllocStringLen(pbstr, os.c_str(), os.length()) )
+      throw std::runtime_error("out of memory");
+
+    ASSERT(*pbstr != 0);
+    return *pbstr;
+  }
+#endif
+
+  MYTYPE SpanExcluding(PCMYSTR szCharSet) const
+  {
+        MYSIZE pos = this->find_first_of(szCharSet);
+        return pos == MYBASE::npos ? *this : Left(pos);
+  }
+
+  MYTYPE SpanIncluding(PCMYSTR szCharSet) const
+  {
+        MYSIZE pos = this->find_first_not_of(szCharSet);
+        return pos == MYBASE::npos ? *this : Left(pos);
+  }
+
+#if defined SS_WIN32 && !defined(UNICODE) && !defined(SS_ANSI)
+
+  // CString's OemToAnsi and AnsiToOem functions are available only in
+  // Unicode builds.  However since we're a template we also need a
+  // runtime check of CT and a reinterpret_cast to account for the fact
+  // that CStdStringW gets instantiated even in non-Unicode builds.
+
+  void AnsiToOem()
+  {
+    if ( sizeof(CT) == sizeof(char) && !empty() )
+    {
+      ::CharToOem(reinterpret_cast<PCSTR>(this->c_str()),
+            reinterpret_cast<PSTR>(GetBuf()));
+    }
+    else
+    {
+      ASSERT(false);
+    }
+  }
+
+  void OemToAnsi()
+  {
+    if ( sizeof(CT) == sizeof(char) && !empty() )
+    {
+      ::OemToChar(reinterpret_cast<PCSTR>(this->c_str()),
+            reinterpret_cast<PSTR>(GetBuf()));
+    }
+    else
+    {
+      ASSERT(false);
+    }
+  }
+
+#endif
+
+
+  // -------------------------------------------------------------------------
+  // Trim and its variants
+  // -------------------------------------------------------------------------
+  MYTYPE& Trim()
+  {
+    return TrimLeft().TrimRight();
+  }
+
+  MYTYPE& TrimLeft()
+  {
+    this->erase(this->begin(),
+      std::find_if(this->begin(), this->end(), NotSpace<CT>()));
+
+    return *this;
+  }
+
+  MYTYPE&  TrimLeft(CT tTrim)
+  {
+    this->erase(0, this->find_first_not_of(tTrim));
+    return *this;
+  }
+
+  MYTYPE&  TrimLeft(PCMYSTR szTrimChars)
+  {
+    this->erase(0, this->find_first_not_of(szTrimChars));
+    return *this;
+  }
+
+  MYTYPE& TrimRight()
+  {
+    // NOTE:  When comparing reverse_iterators here (MYRITER), I avoid using
+    // operator!=.  This is because namespace rel_ops also has a template
+    // operator!= which conflicts with the global operator!= already defined
+    // for reverse_iterator in the header <utility>.
+    // Thanks to John James for alerting me to this.
+
+    MYRITER it = std::find_if(this->rbegin(), this->rend(), NotSpace<CT>());
+    if ( !(this->rend() == it) )
+      this->erase(this->rend() - it);
+
+    this->erase(!(it == this->rend()) ? this->find_last_of(*it) + 1 : 0);
+    return *this;
+  }
+
+  MYTYPE&  TrimRight(CT tTrim)
+  {
+    MYSIZE nIdx  = this->find_last_not_of(tTrim);
+    this->erase(MYBASE::npos == nIdx ? 0 : ++nIdx);
+    return *this;
+  }
+
+  MYTYPE&  TrimRight(PCMYSTR szTrimChars)
+  {
+    MYSIZE nIdx  = this->find_last_not_of(szTrimChars);
+    this->erase(MYBASE::npos == nIdx ? 0 : ++nIdx);
+    return *this;
+  }
+
+  void      FreeExtra()
+  {
+    MYTYPE mt;
+    this->swap(mt);
+    if ( !mt.empty() )
+      this->assign(mt.c_str(), mt.size());
+  }
+
+  // I have intentionally not implemented the following CString
+  // functions.   You cannot make them work without taking advantage
+  // of implementation specific behavior.  However if you absolutely
+  // MUST have them, uncomment out these lines for "sort-of-like"
+  // their behavior.  You're on your own.
+
+//  CT*        LockBuffer()  { return GetBuf(); }// won't really lock
+//  void      UnlockBuffer(); { }  // why have UnlockBuffer w/o LockBuffer?
+
+  // Array-indexing operators.  Required because we defined an implicit cast
+  // to operator const CT* (Thanks to Julian Selman for pointing this out)
+
+  CT& operator[](int nIdx)
+  {
+    return static_cast<MYBASE*>(this)->operator[](static_cast<MYSIZE>(nIdx));
+  }
+
+  const CT& operator[](int nIdx) const
+  {
+    return static_cast<const MYBASE*>(this)->operator[](static_cast<MYSIZE>(nIdx));
+  }
+
+  CT& operator[](unsigned int nIdx)
+  {
+    return static_cast<MYBASE*>(this)->operator[](static_cast<MYSIZE>(nIdx));
+  }
+
+  const CT& operator[](unsigned int nIdx) const
+  {
+    return static_cast<const MYBASE*>(this)->operator[](static_cast<MYSIZE>(nIdx));
+  }
+
+  CT& operator[](unsigned long nIdx)
+  {
+    return static_cast<MYBASE*>(this)->operator[](static_cast<MYSIZE>(nIdx));
+  }
+
+  const CT& operator[](unsigned long nIdx) const
+  {
+    return static_cast<const MYBASE*>(this)->operator[](static_cast<MYSIZE>(nIdx));
+  }
+
+#ifndef SS_NO_IMPLICIT_CAST
+  operator const CT*() const
+  {
+    return this->c_str();
+  }
+#endif
+
+  // IStream related functions.  Useful in IPersistStream implementations
+
+#ifdef SS_INC_COMDEF
+
+  // struct SSSHDR - useful for non Std C++ persistence schemes.
+  typedef struct SSSHDR
+  {
+    BYTE  byCtrl;
+    ULONG  nChars;
+  } SSSHDR;  // as in "Standard String Stream Header"
+
+  #define SSSO_UNICODE  0x01  // the string is a wide string
+  #define SSSO_COMPRESS  0x02  // the string is compressed
+
+  // -------------------------------------------------------------------------
+  // FUNCTION: StreamSize
+  // REMARKS:
+  //    Returns how many bytes it will take to StreamSave() this CStdString
+  //    object to an IStream.
+  // -------------------------------------------------------------------------
+  ULONG StreamSize() const
+  {
+    // Control header plus string
+    ASSERT(this->size()*sizeof(CT) < 0xffffffffUL - sizeof(SSSHDR));
+    return (this->size() * sizeof(CT)) + sizeof(SSSHDR);
+  }
+
+  // -------------------------------------------------------------------------
+  // FUNCTION: StreamSave
+  // REMARKS:
+  //    Saves this CStdString object to a COM IStream.
+  // -------------------------------------------------------------------------
+  HRESULT StreamSave(IStream* pStream) const
+  {
+    ASSERT(this->size()*sizeof(CT) < 0xffffffffUL - sizeof(SSSHDR));
+    HRESULT hr    = E_FAIL;
+    ASSERT(pStream != 0);
+    SSSHDR hdr;
+    hdr.byCtrl    = sizeof(CT) == 2 ? SSSO_UNICODE : 0;
+    hdr.nChars    = this->size();
+
+
+    if ( FAILED(hr=pStream->Write(&hdr, sizeof(SSSHDR), 0)) )
+    {
+      TRACE(_T("StreamSave: Cannot write control header, ERR=0x%X\n"),hr);
+    }
+    else if ( empty() )
+    {
+      ;    // nothing to write
+    }
+    else if ( FAILED(hr=pStream->Write(this->c_str(),
+      this->size()*sizeof(CT), 0)) )
+    {
+      TRACE(_T("StreamSave: Cannot write string to stream 0x%X\n"), hr);
+    }
+
+    return hr;
+  }
+
+
+  // -------------------------------------------------------------------------
+  // FUNCTION: StreamLoad
+  // REMARKS:
+  //    This method loads the object from an IStream.
+  // -------------------------------------------------------------------------
+  HRESULT StreamLoad(IStream* pStream)
+  {
+    ASSERT(pStream != 0);
+    SSSHDR hdr;
+    HRESULT hr      = E_FAIL;
+
+    if ( FAILED(hr=pStream->Read(&hdr, sizeof(SSSHDR), 0)) )
+    {
+      TRACE(_T("StreamLoad: Cant read control header, ERR=0x%X\n"), hr);
+    }
+    else if ( hdr.nChars > 0 )
+    {
+      ULONG nRead    = 0;
+      PMYSTR pMyBuf  = BufferSet(hdr.nChars);
+
+      // If our character size matches the character size of the string
+      // we're trying to read, then we can read it directly into our
+      // buffer. Otherwise, we have to read into an intermediate buffer
+      // and convert.
+
+      if ( (hdr.byCtrl & SSSO_UNICODE) != 0 )
+      {
+        ULONG nBytes  = hdr.nChars * sizeof(wchar_t);
+        if ( sizeof(CT) == sizeof(wchar_t) )
+        {
+          if ( FAILED(hr=pStream->Read(pMyBuf, nBytes, &nRead)) )
+            TRACE(_T("StreamLoad: Cannot read string: 0x%X\n"), hr);
+        }
+        else
+        {
+          PWSTR pBufW = reinterpret_cast<PWSTR>(_alloca((nBytes)+1));
+          if ( FAILED(hr=pStream->Read(pBufW, nBytes, &nRead)) )
+            TRACE(_T("StreamLoad: Cannot read string: 0x%X\n"), hr);
+          else
+            sscpy(pMyBuf, pBufW, hdr.nChars);
+        }
+      }
+      else
+      {
+        ULONG nBytes  = hdr.nChars * sizeof(char);
+        if ( sizeof(CT) == sizeof(char) )
+        {
+          if ( FAILED(hr=pStream->Read(pMyBuf, nBytes, &nRead)) )
+            TRACE(_T("StreamLoad: Cannot read string: 0x%X\n"), hr);
+        }
+        else
+        {
+          PSTR pBufA = reinterpret_cast<PSTR>(_alloca(nBytes));
+          if ( FAILED(hr=pStream->Read(pBufA, hdr.nChars, &nRead)) )
+            TRACE(_T("StreamLoad: Cannot read string: 0x%X\n"), hr);
+          else
+            sscpy(pMyBuf, pBufA, hdr.nChars);
+        }
+      }
+    }
+    else
+    {
+      this->erase();
+    }
+    return hr;
+  }
+#endif // #ifdef SS_INC_COMDEF
+
+#ifndef SS_ANSI
+
+  // SetResourceHandle/GetResourceHandle.  In MFC builds, these map directly
+  // to AfxSetResourceHandle and AfxGetResourceHandle.  In non-MFC builds they
+  // point to a single static HINST so that those who call the member
+  // functions that take resource IDs can provide an alternate HINST of a DLL
+  // to search.  This is not exactly the list of HMODULES that MFC provides
+  // but it's better than nothing.
+
+  #ifdef _MFC_VER
+    static void SetResourceHandle(HMODULE hNew)
+    {
+      AfxSetResourceHandle(hNew);
+    }
+    static HMODULE GetResourceHandle()
+    {
+      return AfxGetResourceHandle();
+    }
+  #else
+    static void SetResourceHandle(HMODULE hNew)
+    {
+      SSResourceHandle() = hNew;
+    }
+    static HMODULE GetResourceHandle()
+    {
+      return SSResourceHandle();
+    }
+  #endif
+
+#endif
+};
+
+// -----------------------------------------------------------------------------
+// MSVC USERS: HOW TO EXPORT CSTDSTRING FROM A DLL
+//
+// If you are using MS Visual C++ and you want to export CStdStringA and
+// CStdStringW from a DLL, then all you need to
+//
+//    1.  make sure that all components link to the same DLL version
+//      of the CRT (not the static one).
+//    2.  Uncomment the 3 lines of code below
+//    3.  #define 2 macros per the instructions in MS KnowledgeBase
+//      article Q168958.  The macros are:
+//
+//    MACRO    DEFINTION WHEN EXPORTING    DEFINITION WHEN IMPORTING
+//    -----    ------------------------    -------------------------
+//    SSDLLEXP  (nothing, just #define it)    extern
+//    SSDLLSPEC  __declspec(dllexport)      __declspec(dllimport)
+//
+//    Note that these macros must be available to ALL clients who want to
+//    link to the DLL and use the class.  If they
+//
+// A word of advice: Don't bother.
+//
+// Really, it is not necessary to export CStdString functions from a DLL.  I
+// never do.  In my projects, I do generally link to the DLL version of the
+// Standard C++ Library, but I do NOT attempt to export CStdString functions.
+// I simply include the header where it is needed and allow for the code
+// redundancy.
+//
+// That redundancy is a lot less than you think.  This class does most of its
+// work via the Standard C++ Library, particularly the base_class basic_string<>
+// member functions.  Most of the functions here are small enough to be inlined
+// anyway.  Besides, you'll find that in actual practice you use less than 1/2
+// of the code here, even in big projects and different modules will use as
+// little as 10% of it.  That means a lot less functions actually get linked
+// your binaries.  If you export this code from a DLL, it ALL gets linked in.
+//
+// I've compared the size of the binaries from exporting vs NOT exporting.  Take
+// my word for it -- exporting this code is not worth the hassle.
+//
+// -----------------------------------------------------------------------------
+//#pragma warning(disable:4231) // non-standard extension ("extern template")
+//  SSDLLEXP template class SSDLLSPEC CStdStr<char>;
+//  SSDLLEXP template class SSDLLSPEC CStdStr<wchar_t>;
+
+
+// =============================================================================
+//            END OF CStdStr INLINE FUNCTION DEFINITIONS
+// =============================================================================
+
+//  Now typedef our class names based upon this humongous template
+
+typedef CStdStr<char>    CStdStringA;  // a better std::string
+typedef CStdStr<wchar_t>  CStdStringW;  // a better std::wstring
+typedef CStdStr<uint16_t>  CStdString16;  // a 16bit char string
+typedef CStdStr<uint32_t>  CStdString32;  // a 32bit char string
+typedef CStdStr<OLECHAR>  CStdStringO;  // almost always CStdStringW
+
+// -----------------------------------------------------------------------------
+// CStdStr addition functions defined as inline
+// -----------------------------------------------------------------------------
+
+
+inline CStdStringA operator+(const CStdStringA& s1, const CStdStringA& s2)
+{
+  CStdStringA sRet(SSREF(s1));
+  sRet.append(s2);
+  return sRet;
+}
+inline CStdStringA operator+(const CStdStringA& s1, CStdStringA::value_type t)
+{
+  CStdStringA sRet(SSREF(s1));
+  sRet.append(1, t);
+  return sRet;
+}
+inline CStdStringA operator+(const CStdStringA& s1, PCSTR pA)
+{
+  CStdStringA sRet(SSREF(s1));
+  sRet.append(pA);
+  return sRet;
+}
+inline CStdStringA operator+(PCSTR pA, const CStdStringA& sA)
+{
+  CStdStringA sRet;
+  CStdStringA::size_type nObjSize = sA.size();
+  CStdStringA::size_type nLitSize =
+    static_cast<CStdStringA::size_type>(sslen(pA));
+
+  sRet.reserve(nLitSize + nObjSize);
+  sRet.assign(pA);
+  sRet.append(sA);
+  return sRet;
+}
+
+
+inline CStdStringA operator+(const CStdStringA& s1, const CStdStringW& s2)
+{
+  return s1 + CStdStringA(s2);
+}
+inline CStdStringW operator+(const CStdStringW& s1, const CStdStringW& s2)
+{
+  CStdStringW sRet(SSREF(s1));
+  sRet.append(s2);
+  return sRet;
+}
+inline CStdStringA operator+(const CStdStringA& s1, PCWSTR pW)
+{
+  return s1 + CStdStringA(pW);
+}
+
+#ifdef UNICODE
+  inline CStdStringW operator+(PCWSTR pW, const CStdStringA& sA)
+  {
+    return CStdStringW(pW) + CStdStringW(SSREF(sA));
+  }
+  inline CStdStringW operator+(PCSTR pA, const CStdStringW& sW)
+  {
+    return CStdStringW(pA) + sW;
+  }
+#else
+  inline CStdStringA operator+(PCWSTR pW, const CStdStringA& sA)
+  {
+    return CStdStringA(pW) + sA;
+  }
+  inline CStdStringA operator+(PCSTR pA, const CStdStringW& sW)
+  {
+    return pA + CStdStringA(sW);
+  }
+#endif
+
+// ...Now the wide string versions.
+inline CStdStringW operator+(const CStdStringW& s1, CStdStringW::value_type t)
+{
+  CStdStringW sRet(SSREF(s1));
+  sRet.append(1, t);
+  return sRet;
+}
+inline CStdStringW operator+(const CStdStringW& s1, PCWSTR pW)
+{
+  CStdStringW sRet(SSREF(s1));
+  sRet.append(pW);
+  return sRet;
+}
+inline CStdStringW operator+(PCWSTR pW, const CStdStringW& sW)
+{
+  CStdStringW sRet;
+  CStdStringW::size_type nObjSize = sW.size();
+  CStdStringA::size_type nLitSize =
+    static_cast<CStdStringW::size_type>(sslen(pW));
+
+  sRet.reserve(nLitSize + nObjSize);
+  sRet.assign(pW);
+  sRet.append(sW);
+  return sRet;
+}
+
+inline CStdStringW operator+(const CStdStringW& s1, const CStdStringA& s2)
+{
+  return s1 + CStdStringW(s2);
+}
+inline CStdStringW operator+(const CStdStringW& s1, PCSTR pA)
+{
+  return s1 + CStdStringW(pA);
+}
+
+
+// New-style format function is a template
+
+#ifdef SS_SAFE_FORMAT
+
+template<>
+struct FmtArg<CStdStringA>
+{
+    explicit FmtArg(const CStdStringA& arg) : a_(arg) {}
+    PCSTR operator()() const { return a_.c_str(); }
+    const CStdStringA& a_;
+private:
+    FmtArg<CStdStringA>& operator=(const FmtArg<CStdStringA>&) { return *this; }
+};
+template<>
+struct FmtArg<CStdStringW>
+{
+    explicit FmtArg(const CStdStringW& arg) : a_(arg) {}
+    PCWSTR operator()() const { return a_.c_str(); }
+    const CStdStringW& a_;
+private:
+    FmtArg<CStdStringW>& operator=(const FmtArg<CStdStringW>&) { return *this; }
+};
+
+template<>
+struct FmtArg<std::string>
+{
+    explicit FmtArg(const std::string& arg) : a_(arg) {}
+    PCSTR operator()() const { return a_.c_str(); }
+    const std::string& a_;
+private:
+    FmtArg<std::string>& operator=(const FmtArg<std::string>&) { return *this; }
+};
+template<>
+struct FmtArg<std::wstring>
+{
+    explicit FmtArg(const std::wstring& arg) : a_(arg) {}
+    PCWSTR operator()() const { return a_.c_str(); }
+    const std::wstring& a_;
+private:
+    FmtArg<std::wstring>& operator=(const FmtArg<std::wstring>&) {return *this;}
+};
+#endif // #ifdef SS_SAFEFORMAT
+
+#ifndef SS_ANSI
+  // SSResourceHandle: our MFC-like resource handle
+  inline HMODULE& SSResourceHandle()
+  {
+    static HMODULE hModuleSS  = GetModuleHandle(0);
+    return hModuleSS;
+  }
+#endif
+
+
+// In MFC builds, define some global serialization operators
+// Special operators that allow us to serialize CStdStrings to CArchives.
+// Note that we use an intermediate CString object in order to ensure that
+// we use the exact same format.
+
+#ifdef _MFC_VER
+  inline CArchive& AFXAPI operator<<(CArchive& ar, const CStdStringA& strA)
+  {
+    CString strTemp  = strA;
+    return ar << strTemp;
+  }
+  inline CArchive& AFXAPI operator<<(CArchive& ar, const CStdStringW& strW)
+  {
+    CString strTemp  = strW;
+    return ar << strTemp;
+  }
+
+  inline CArchive& AFXAPI operator>>(CArchive& ar, CStdStringA& strA)
+  {
+    CString strTemp;
+    ar >> strTemp;
+    strA = strTemp;
+    return ar;
+  }
+  inline CArchive& AFXAPI operator>>(CArchive& ar, CStdStringW& strW)
+  {
+    CString strTemp;
+    ar >> strTemp;
+    strW = strTemp;
+    return ar;
+  }
+#endif  // #ifdef _MFC_VER -- (i.e. is this MFC?)
+
+
+
+// -----------------------------------------------------------------------------
+// GLOBAL FUNCTION:  WUFormat
+//    CStdStringA WUFormat(UINT nId, ...);
+//    CStdStringA WUFormat(PCSTR szFormat, ...);
+//
+// REMARKS:
+//    This function allows the caller for format and return a CStdStringA
+//    object with a single line of code.
+// -----------------------------------------------------------------------------
+#ifdef SS_ANSI
+#else
+  inline CStdStringA WUFormatA(UINT nId, ...)
+  {
+    va_list argList;
+    va_start(argList, nId);
+
+    CStdStringA strFmt;
+    CStdStringA strOut;
+    if ( strFmt.Load(nId) )
+      strOut.FormatV(strFmt, argList);
+
+    va_end(argList);
+    return strOut;
+  }
+  inline CStdStringA WUFormatA(PCSTR szFormat, ...)
+  {
+    va_list argList;
+    va_start(argList, szFormat);
+    CStdStringA strOut;
+    strOut.FormatV(szFormat, argList);
+    va_end(argList);
+    return strOut;
+  }
+  inline CStdStringW WUFormatW(UINT nId, ...)
+  {
+    va_list argList;
+    va_start(argList, nId);
+
+    CStdStringW strFmt;
+    CStdStringW strOut;
+    if ( strFmt.Load(nId) )
+      strOut.FormatV(strFmt, argList);
+
+    va_end(argList);
+    return strOut;
+  }
+  inline CStdStringW WUFormatW(PCWSTR szwFormat, ...)
+  {
+    va_list argList;
+    va_start(argList, szwFormat);
+    CStdStringW strOut;
+    strOut.FormatV(szwFormat, argList);
+    va_end(argList);
+    return strOut;
+  }
+#endif // #ifdef SS_ANSI
+
+
+
+#if defined(SS_WIN32) && !defined (SS_ANSI)
+  // -------------------------------------------------------------------------
+  // FUNCTION: WUSysMessage
+  //   CStdStringA WUSysMessageA(DWORD dwError, DWORD dwLangId=SS_DEFLANGID);
+  //   CStdStringW WUSysMessageW(DWORD dwError, DWORD dwLangId=SS_DEFLANGID);
+  //
+  // DESCRIPTION:
+  //   This function simplifies the process of obtaining a string equivalent
+  //   of a system error code returned from GetLastError().  You simply
+  //   supply the value returned by GetLastError() to this function and the
+  //   corresponding system string is returned in the form of a CStdStringA.
+  //
+  // PARAMETERS:
+  //   dwError - a DWORD value representing the error code to be translated
+  //   dwLangId - the language id to use.  defaults to english.
+  //
+  // RETURN VALUE:
+  //   a CStdStringA equivalent of the error code.  Currently, this function
+  //   only returns either English of the system default language strings.
+  // -------------------------------------------------------------------------
+  #define SS_DEFLANGID MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT)
+  inline CStdStringA WUSysMessageA(DWORD dwError, DWORD dwLangId=SS_DEFLANGID)
+  {
+    CHAR szBuf[512];
+
+    if ( 0 != ::FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwError,
+                   dwLangId, szBuf, 511, NULL) )
+      return WUFormatA("%s (0x%X)", szBuf, dwError);
+    else
+       return WUFormatA("Unknown error (0x%X)", dwError);
+  }
+  inline CStdStringW WUSysMessageW(DWORD dwError, DWORD dwLangId=SS_DEFLANGID)
+  {
+    WCHAR szBuf[512];
+
+    if ( 0 != ::FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwError,
+                   dwLangId, szBuf, 511, NULL) )
+      return WUFormatW(L"%s (0x%X)", szBuf, dwError);
+    else
+       return WUFormatW(L"Unknown error (0x%X)", dwError);
+  }
+#endif
+
+// Define TCHAR based friendly names for some of these functions
+
+#ifdef UNICODE
+  //#define CStdString        CStdStringW
+  typedef CStdStringW        CStdString;
+  #define WUSysMessage      WUSysMessageW
+  #define WUFormat        WUFormatW
+#else
+  //#define CStdString        CStdStringA
+  typedef CStdStringA        CStdString;
+  #define WUSysMessage      WUSysMessageA
+  #define WUFormat        WUFormatA
+#endif
+
+// ...and some shorter names for the space-efficient
+
+#define WUSysMsg          WUSysMessage
+#define WUSysMsgA          WUSysMessageA
+#define WUSysMsgW          WUSysMessageW
+#define WUFmtA            WUFormatA
+#define  WUFmtW            WUFormatW
+#define WUFmt            WUFormat
+#define WULastErrMsg()        WUSysMessage(::GetLastError())
+#define WULastErrMsgA()        WUSysMessageA(::GetLastError())
+#define WULastErrMsgW()        WUSysMessageW(::GetLastError())
+
+
+// -----------------------------------------------------------------------------
+// FUNCTIONAL COMPARATORS:
+// REMARKS:
+//    These structs are derived from the std::binary_function template.  They
+//    give us functional classes (which may be used in Standard C++ Library
+//    collections and algorithms) that perform case-insensitive comparisons of
+//    CStdString objects.  This is useful for maps in which the key may be the
+//     proper string but in the wrong case.
+// -----------------------------------------------------------------------------
+#define StdStringLessNoCaseW    SSLNCW  // avoid VC compiler warning 4786
+#define StdStringEqualsNoCaseW    SSENCW
+#define StdStringLessNoCaseA    SSLNCA
+#define StdStringEqualsNoCaseA    SSENCA
+
+#ifdef UNICODE
+  #define StdStringLessNoCase    SSLNCW
+  #define StdStringEqualsNoCase  SSENCW
+#else
+  #define StdStringLessNoCase    SSLNCA
+  #define StdStringEqualsNoCase  SSENCA
+#endif
+
+struct StdStringLessNoCaseW
+  : std::binary_function<CStdStringW, CStdStringW, bool>
+{
+  inline
+  bool operator()(const CStdStringW& sLeft, const CStdStringW& sRight) const
+  { return ssicmp(sLeft.c_str(), sRight.c_str()) < 0; }
+};
+struct StdStringEqualsNoCaseW
+  : std::binary_function<CStdStringW, CStdStringW, bool>
+{
+  inline
+  bool operator()(const CStdStringW& sLeft, const CStdStringW& sRight) const
+  { return ssicmp(sLeft.c_str(), sRight.c_str()) == 0; }
+};
+struct StdStringLessNoCaseA
+  : std::binary_function<CStdStringA, CStdStringA, bool>
+{
+  inline
+  bool operator()(const CStdStringA& sLeft, const CStdStringA& sRight) const
+  { return ssicmp(sLeft.c_str(), sRight.c_str()) < 0; }
+};
+struct StdStringEqualsNoCaseA
+  : std::binary_function<CStdStringA, CStdStringA, bool>
+{
+  inline
+  bool operator()(const CStdStringA& sLeft, const CStdStringA& sRight) const
+  { return ssicmp(sLeft.c_str(), sRight.c_str()) == 0; }
+};
+
+// If we had to define our own version of TRACE above, get rid of it now
+
+#ifdef TRACE_DEFINED_HERE
+  #undef TRACE
+  #undef TRACE_DEFINED_HERE
+#endif
+
+
+// These std::swap specializations come courtesy of Mike Crusader.
+
+//namespace std
+//{
+//  inline void swap(CStdStringA& s1, CStdStringA& s2) throw()
+//  {
+//    s1.swap(s2);
+//  }
+//  template<>
+//  inline void swap(CStdStringW& s1, CStdStringW& s2) throw()
+//  {
+//    s1.swap(s2);
+//  }
+//}
+
+// Turn back on any Borland warnings we turned off.
+
+#ifdef __BORLANDC__
+    #pragma option pop  // Turn back on inline function warnings
+//  #pragma warn +inl   // Turn back on inline function warnings
+#endif
+
+#endif  // #ifndef STDSTRING_H
diff --git a/xbmc/pvrclients/vdr-vnsi/VNSIChannelScan.cpp b/xbmc/pvrclients/vdr-vnsi/VNSIChannelScan.cpp
new file mode 100644 (file)
index 0000000..303620e
--- /dev/null
@@ -0,0 +1,711 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "VNSIChannelScan.h"
+#include <limits.h>
+#include "tools.h"
+#include "responsepacket.h"
+#include "requestpacket.h"
+#include "vdrcommand.h"
+
+#define BUTTON_START                    5
+#define BUTTON_BACK                     6
+#define BUTTON_CANCEL                   7
+#define HEADER_LABEL                    8
+
+#define SPIN_CONTROL_SOURCE_TYPE        10
+#define CONTROL_RADIO_BUTTON_TV         11
+#define CONTROL_RADIO_BUTTON_RADIO      12
+#define CONTROL_RADIO_BUTTON_FTA        13
+#define CONTROL_RADIO_BUTTON_SCRAMBLED  14
+#define CONTROL_RADIO_BUTTON_HD         15
+#define CONTROL_SPIN_COUNTRIES          16
+#define CONTROL_SPIN_SATELLITES         17
+#define CONTROL_SPIN_DVBC_INVERSION     18
+#define CONTROL_SPIN_DVBC_SYMBOLRATE    29
+#define CONTROL_SPIN_DVBC_QAM           20
+#define CONTROL_SPIN_DVBT_INVERSION     21
+#define CONTROL_SPIN_ATSC_TYPE          22
+
+#define LABEL_TYPE                      30
+#define LABEL_DEVICE                    31
+#define PROGRESS_DONE                   32
+#define LABEL_TRANSPONDER               33
+#define LABEL_SIGNAL                    34
+#define PROGRESS_SIGNAL                 35
+#define LABEL_STATUS                    36
+
+#define CMD_LOCK cMutexLock CmdLock((cMutex*)&m_Mutex)
+
+cVNSIChannelScan::cVNSIChannelScan()
+{
+}
+
+cVNSIChannelScan::~cVNSIChannelScan()
+{
+}
+
+bool cVNSIChannelScan::Open()
+{
+  m_running         = false;
+  m_Canceled        = false;
+  m_stopped         = true;
+  m_progressDone    = NULL;
+  m_progressSignal  = NULL;
+
+  if(!m_session.Open(g_szHostname, g_iPort, g_iConnectTimeout, "XBMC channel scanner"))
+    return false;
+
+  SetDescription("VNSI channel scan listener");
+  Start();
+
+  /* Load the Window as Dialog */
+  m_window = GUI->Window_create("ChannelScan.xml", "Confluence", false, true);
+  m_window->m_cbhdl   = this;
+  m_window->CBOnInit  = OnInitCB;
+  m_window->CBOnFocus = OnFocusCB;
+  m_window->CBOnClick = OnClickCB;
+  m_window->CBOnAction= OnActionCB;
+  m_window->DoModal();
+
+  GUI->Window_destroy(m_window);
+  Cancel(1);
+  m_session.Close();
+
+  return true;
+}
+
+void cVNSIChannelScan::StartScan()
+{
+  m_header = XBMC->GetLocalizedString(30025);
+  m_Signal = XBMC->GetLocalizedString(30029);
+  SetProgress(0);
+  SetSignal(0, false);
+
+  int source = m_spinSourceType->GetValue();
+  switch (source)
+  {
+    case DVB_TERR:
+      m_window->SetControlLabel(LABEL_TYPE, "DVB-T");
+      break;
+    case DVB_CABLE:
+      m_window->SetControlLabel(LABEL_TYPE, "DVB-C");
+      break;
+    case DVB_SAT:
+      m_window->SetControlLabel(LABEL_TYPE, "DVB-S/S2");
+      break;
+    case PVRINPUT:
+      m_window->SetControlLabel(LABEL_TYPE, XBMC->GetLocalizedString(30032));
+      break;
+    case PVRINPUT_FM:
+      m_window->SetControlLabel(LABEL_TYPE, XBMC->GetLocalizedString(30033));
+      break;
+    case DVB_ATSC:
+      m_window->SetControlLabel(LABEL_TYPE, "ATSC");
+      break;
+  }
+
+  cRequestPacket vrp;
+  cResponsePacket* vresp = NULL;
+  uint32_t retCode = VDR_RET_ERROR;
+  if (!vrp.init(VDR_SCAN_START))                          goto SCANError;
+  if (!vrp.add_U32(source))                               goto SCANError;
+  if (!vrp.add_U8(m_radioButtonTV->IsSelected()))         goto SCANError;
+  if (!vrp.add_U8(m_radioButtonRadio->IsSelected()))      goto SCANError;
+  if (!vrp.add_U8(m_radioButtonFTA->IsSelected()))        goto SCANError;
+  if (!vrp.add_U8(m_radioButtonScrambled->IsSelected()))  goto SCANError;
+  if (!vrp.add_U8(m_radioButtonHD->IsSelected()))         goto SCANError;
+  if (!vrp.add_U32(m_spinCountries->GetValue()))          goto SCANError;
+  if (!vrp.add_U32(m_spinDVBCInversion->GetValue()))      goto SCANError;
+  if (!vrp.add_U32(m_spinDVBCSymbolrates->GetValue()))    goto SCANError;
+  if (!vrp.add_U32(m_spinDVBCqam->GetValue()))            goto SCANError;
+  if (!vrp.add_U32(m_spinDVBTInversion->GetValue()))      goto SCANError;
+  if (!vrp.add_U32(m_spinSatellites->GetValue()))         goto SCANError;
+  if (!vrp.add_U32(m_spinATSCType->GetValue()))           goto SCANError;
+
+  vresp = ReadResult(&vrp);
+  if (!vresp)
+    goto SCANError;
+
+  retCode = vresp->extract_U32();
+  if (retCode != VDR_RET_OK)
+    goto SCANError;
+
+  return;
+
+SCANError:
+  XBMC->Log(LOG_ERROR, "cVNSIChannelScan::StartScan() - Return error after start (%i)", retCode);
+  m_window->SetControlLabel(LABEL_STATUS, XBMC->GetLocalizedString(24071));
+  m_window->SetControlLabel(BUTTON_START, XBMC->GetLocalizedString(30024));
+  m_window->SetControlLabel(HEADER_LABEL, XBMC->GetLocalizedString(30043));
+  m_stopped = true;
+}
+
+void cVNSIChannelScan::StopScan()
+{
+  cRequestPacket vrp;
+  if (!vrp.init(VDR_SCAN_STOP))
+    return;
+
+  cResponsePacket* vresp = ReadResult(&vrp);
+  if (!vresp)
+    return;
+
+  uint32_t retCode = vresp->extract_U32();
+  if (retCode != VDR_RET_OK)
+  {
+    XBMC->Log(LOG_ERROR, "cVNSIChannelScan::StopScan() - Return error after stop (%i)", retCode);
+    m_window->SetControlLabel(LABEL_STATUS, XBMC->GetLocalizedString(24071));
+    m_window->SetControlLabel(BUTTON_START, XBMC->GetLocalizedString(30024));
+    m_window->SetControlLabel(HEADER_LABEL, XBMC->GetLocalizedString(30043));
+    m_stopped = true;
+  }
+  return;
+}
+
+void cVNSIChannelScan::ReturnFromProcessView()
+{
+  if (m_running)
+  {
+    m_running = false;
+    m_window->ClearProperties();
+    m_window->SetControlLabel(BUTTON_START, XBMC->GetLocalizedString(30010));
+    m_window->SetControlLabel(HEADER_LABEL, XBMC->GetLocalizedString(30009));
+
+    if (m_progressDone)
+    {
+      GUI->Control_releaseProgress(m_progressDone);
+      m_progressDone = NULL;
+    }
+    if (m_progressSignal)
+    {
+      GUI->Control_releaseProgress(m_progressSignal);
+      m_progressSignal = NULL;
+    }
+  }
+}
+
+void cVNSIChannelScan::SetProgress(int procent)
+{
+  if (!m_progressDone)
+    m_progressDone = GUI->Control_getProgress(m_window, PROGRESS_DONE);
+
+  CStdString header;
+  header.Format(m_header, procent);
+  m_window->SetControlLabel(HEADER_LABEL, header.c_str());
+  m_progressDone->SetPercentage((float)procent);
+}
+
+void cVNSIChannelScan::SetSignal(int procent, bool locked)
+{
+  if (!m_progressSignal)
+    m_progressSignal = GUI->Control_getProgress(m_window, PROGRESS_SIGNAL);
+
+  CStdString signal;
+  signal.Format(m_Signal, procent);
+  m_window->SetControlLabel(LABEL_SIGNAL, signal.c_str());
+  m_progressSignal->SetPercentage((float)procent);
+
+  if (locked)
+    m_window->SetProperty("Locked", "true");
+  else
+    m_window->SetProperty("Locked", "");
+}
+
+bool cVNSIChannelScan::OnClick(int controlId)
+{
+  if (controlId == SPIN_CONTROL_SOURCE_TYPE)
+  {
+    int value = m_spinSourceType->GetValue();
+    SetControlsVisible((scantype_t)value);
+  }
+  else if (controlId == BUTTON_BACK)
+  {
+    m_window->Close();
+    GUI->Control_releaseSpin(m_spinSourceType);
+    GUI->Control_releaseSpin(m_spinCountries);
+    GUI->Control_releaseSpin(m_spinSatellites);
+    GUI->Control_releaseSpin(m_spinDVBCInversion);
+    GUI->Control_releaseSpin(m_spinDVBCSymbolrates);
+    GUI->Control_releaseSpin(m_spinDVBCqam);
+    GUI->Control_releaseSpin(m_spinDVBTInversion);
+    GUI->Control_releaseSpin(m_spinATSCType);
+    GUI->Control_releaseRadioButton(m_radioButtonTV);
+    GUI->Control_releaseRadioButton(m_radioButtonRadio);
+    GUI->Control_releaseRadioButton(m_radioButtonFTA);
+    GUI->Control_releaseRadioButton(m_radioButtonScrambled);
+    GUI->Control_releaseRadioButton(m_radioButtonHD);
+    if (m_progressDone)
+    {
+      GUI->Control_releaseProgress(m_progressDone);
+      m_progressDone = NULL;
+    }
+    if (m_progressSignal)
+    {
+      GUI->Control_releaseProgress(m_progressSignal);
+      m_progressSignal = NULL;
+    }
+  }
+  else if (controlId == BUTTON_START)
+  {
+    if (!m_running)
+    {
+      m_running = true;
+      m_stopped = false;
+      m_Canceled = false;
+      m_window->SetProperty("Scanning", "running");
+      m_window->SetControlLabel(BUTTON_START, XBMC->GetLocalizedString(222));
+      StartScan();
+    }
+    else if (!m_stopped)
+    {
+      m_stopped = true;
+      m_Canceled = true;
+      StopScan();
+    }
+    else
+      ReturnFromProcessView();
+  }
+  return true;
+}
+
+bool cVNSIChannelScan::OnFocus(int controlId)
+{
+  return true;
+}
+
+bool cVNSIChannelScan::OnInit()
+{
+  m_spinSourceType = GUI->Control_getSpin(m_window, SPIN_CONTROL_SOURCE_TYPE);
+  m_spinSourceType->Clear();
+  m_spinSourceType->AddLabel("DVB-T", DVB_TERR);
+  m_spinSourceType->AddLabel("DVB-C", DVB_CABLE);
+  m_spinSourceType->AddLabel("DVB-S/S2", DVB_SAT);
+  m_spinSourceType->AddLabel("Analog TV", PVRINPUT);
+  m_spinSourceType->AddLabel("Analog Radio", PVRINPUT_FM);
+  m_spinSourceType->AddLabel("ATSC", DVB_ATSC);
+
+  m_spinDVBCInversion = GUI->Control_getSpin(m_window, CONTROL_SPIN_DVBC_INVERSION);
+  m_spinDVBCInversion->Clear();
+  m_spinDVBCInversion->AddLabel("Auto", 0);
+  m_spinDVBCInversion->AddLabel("On", 1);
+  m_spinDVBCInversion->AddLabel("Off", 2);
+
+  m_spinDVBCSymbolrates = GUI->Control_getSpin(m_window, CONTROL_SPIN_DVBC_SYMBOLRATE);
+  m_spinDVBCSymbolrates->Clear();
+  m_spinDVBCSymbolrates->AddLabel("AUTO", 0);
+  m_spinDVBCSymbolrates->AddLabel("6900", 1);
+  m_spinDVBCSymbolrates->AddLabel("6875", 2);
+  m_spinDVBCSymbolrates->AddLabel("6111", 3);
+  m_spinDVBCSymbolrates->AddLabel("6250", 4);
+  m_spinDVBCSymbolrates->AddLabel("6790", 5);
+  m_spinDVBCSymbolrates->AddLabel("6811", 6);
+  m_spinDVBCSymbolrates->AddLabel("5900", 7);
+  m_spinDVBCSymbolrates->AddLabel("5000", 8);
+  m_spinDVBCSymbolrates->AddLabel("3450", 9);
+  m_spinDVBCSymbolrates->AddLabel("4000", 10);
+  m_spinDVBCSymbolrates->AddLabel("6950", 11);
+  m_spinDVBCSymbolrates->AddLabel("7000", 12);
+  m_spinDVBCSymbolrates->AddLabel("6952", 13);
+  m_spinDVBCSymbolrates->AddLabel("5156", 14);
+  m_spinDVBCSymbolrates->AddLabel("4583", 15);
+  m_spinDVBCSymbolrates->AddLabel("ALL (slow)", 16);
+
+  m_spinDVBCqam = GUI->Control_getSpin(m_window, CONTROL_SPIN_DVBC_QAM);
+  m_spinDVBCqam->Clear();
+  m_spinDVBCqam->AddLabel("AUTO", 0);
+  m_spinDVBCqam->AddLabel("64", 1);
+  m_spinDVBCqam->AddLabel("128", 2);
+  m_spinDVBCqam->AddLabel("256", 3);
+  m_spinDVBCqam->AddLabel("ALL (slow)", 4);
+
+  m_spinDVBTInversion = GUI->Control_getSpin(m_window, CONTROL_SPIN_DVBT_INVERSION);
+  m_spinDVBTInversion->Clear();
+  m_spinDVBTInversion->AddLabel("Auto", 0);
+  m_spinDVBTInversion->AddLabel("On", 1);
+  m_spinDVBTInversion->AddLabel("Off", 2);
+
+  m_spinATSCType = GUI->Control_getSpin(m_window, CONTROL_SPIN_ATSC_TYPE);
+  m_spinATSCType->Clear();
+  m_spinATSCType->AddLabel("VSB (aerial)", 0);
+  m_spinATSCType->AddLabel("QAM (cable)", 1);
+  m_spinATSCType->AddLabel("VSB + QAM (aerial + cable)", 2);
+
+  m_radioButtonTV = GUI->Control_getRadioButton(m_window, CONTROL_RADIO_BUTTON_TV);
+  m_radioButtonTV->SetSelected(true);
+
+  m_radioButtonRadio = GUI->Control_getRadioButton(m_window, CONTROL_RADIO_BUTTON_RADIO);
+  m_radioButtonRadio->SetSelected(true);
+
+  m_radioButtonFTA = GUI->Control_getRadioButton(m_window, CONTROL_RADIO_BUTTON_FTA);
+  m_radioButtonFTA->SetSelected(true);
+
+  m_radioButtonScrambled = GUI->Control_getRadioButton(m_window, CONTROL_RADIO_BUTTON_SCRAMBLED);
+  m_radioButtonScrambled->SetSelected(true);
+
+  m_radioButtonHD = GUI->Control_getRadioButton(m_window, CONTROL_RADIO_BUTTON_HD);
+  m_radioButtonHD->SetSelected(true);
+
+  if (!ReadCountries())
+    return false;
+
+  if (!ReadSatellites())
+    return false;
+
+  SetControlsVisible(DVB_TERR);
+  return true;
+}
+
+bool cVNSIChannelScan::OnAction(int actionId)
+{
+  if (actionId == ADDON_ACTION_CLOSE_DIALOG || actionId == ADDON_ACTION_PREVIOUS_MENU)
+    OnClick(BUTTON_BACK);
+
+  return true;
+}
+
+bool cVNSIChannelScan::OnInitCB(GUIHANDLE cbhdl)
+{
+  cVNSIChannelScan* scanner = static_cast<cVNSIChannelScan*>(cbhdl);
+  return scanner->OnInit();
+}
+
+bool cVNSIChannelScan::OnClickCB(GUIHANDLE cbhdl, int controlId)
+{
+  cVNSIChannelScan* scanner = static_cast<cVNSIChannelScan*>(cbhdl);
+  return scanner->OnClick(controlId);
+}
+
+bool cVNSIChannelScan::OnFocusCB(GUIHANDLE cbhdl, int controlId)
+{
+  cVNSIChannelScan* scanner = static_cast<cVNSIChannelScan*>(cbhdl);
+  return scanner->OnFocus(controlId);
+}
+
+bool cVNSIChannelScan::OnActionCB(GUIHANDLE cbhdl, int actionId)
+{
+  cVNSIChannelScan* scanner = static_cast<cVNSIChannelScan*>(cbhdl);
+  return scanner->OnAction(actionId);
+}
+
+bool cVNSIChannelScan::ReadCountries()
+{
+  m_spinCountries = GUI->Control_getSpin(m_window, CONTROL_SPIN_COUNTRIES);
+  m_spinCountries->Clear();
+
+  CStdString dvdlang = XBMC->GetDVDMenuLanguage();
+  dvdlang = dvdlang.ToUpper();
+
+  cRequestPacket vrp;
+  if (!vrp.init(VDR_SCAN_GETCOUNTRIES))
+    return false;
+
+  cResponsePacket* vresp = ReadResult(&vrp);
+  if (!vresp)
+    return false;
+
+  int startIndex = -1;
+  uint32_t retCode = vresp->extract_U32();
+  if (retCode == VDR_RET_OK)
+  {
+    while (!vresp->end())
+    {
+      uint32_t    index     = vresp->extract_U32();
+      const char *isoName   = vresp->extract_String();
+      const char *longName  = vresp->extract_String();
+      m_spinCountries->AddLabel(longName, index);
+      if (dvdlang == isoName)
+        startIndex = index;
+
+      delete[] longName;
+      delete[] isoName;
+    }
+    if (startIndex >= 0)
+      m_spinCountries->SetValue(startIndex);
+  }
+  else
+  {
+    XBMC->Log(LOG_ERROR, "cVNSIChannelScan::ReadCountries() - Return error after reading countries (%i)", retCode);
+  }
+  delete vresp;
+  return retCode == VDR_RET_OK;
+}
+
+bool cVNSIChannelScan::ReadSatellites()
+{
+  m_spinSatellites = GUI->Control_getSpin(m_window, CONTROL_SPIN_SATELLITES);
+  m_spinSatellites->Clear();
+
+  cRequestPacket vrp;
+  if (!vrp.init(VDR_SCAN_GETSATELLITES))
+    return false;
+
+  cResponsePacket* vresp = ReadResult(&vrp);
+  if (!vresp)
+    return false;
+
+  uint32_t retCode = vresp->extract_U32();
+  if (retCode == VDR_RET_OK)
+  {
+    while (!vresp->end())
+    {
+      uint32_t    index     = vresp->extract_U32();
+      const char *shortName = vresp->extract_String();
+      const char *longName  = vresp->extract_String();
+      m_spinSatellites->AddLabel(longName, index);
+      delete[] longName;
+      delete[] shortName;
+    }
+    m_spinSatellites->SetValue(6);      /* default to Astra 19.2         */
+  }
+  else
+  {
+    XBMC->Log(LOG_ERROR, "cVNSIChannelScan::ReadSatellites() - Return error after reading satellites (%i)", retCode);
+  }
+  delete vresp;
+  return retCode == VDR_RET_OK;
+}
+
+void cVNSIChannelScan::SetControlsVisible(scantype_t type)
+{
+  m_spinCountries->SetVisible(type == DVB_TERR || type == DVB_CABLE || type == PVRINPUT);
+  m_spinSatellites->SetVisible(type == DVB_SAT || type == DVB_ATSC);
+  m_spinDVBCInversion->SetVisible(type == DVB_CABLE);
+  m_spinDVBCSymbolrates->SetVisible(type == DVB_CABLE);
+  m_spinDVBCqam->SetVisible(type == DVB_CABLE);
+  m_spinDVBTInversion->SetVisible(type == DVB_TERR);
+  m_spinATSCType->SetVisible(type == DVB_ATSC);
+  m_radioButtonTV->SetVisible(type == DVB_TERR || type == DVB_CABLE || type == DVB_SAT || type == DVB_ATSC);
+  m_radioButtonRadio->SetVisible(type == DVB_TERR || type == DVB_CABLE || type == DVB_SAT || type == DVB_ATSC);
+  m_radioButtonFTA->SetVisible(type == DVB_TERR || type == DVB_CABLE || type == DVB_SAT || type == DVB_ATSC);
+  m_radioButtonScrambled->SetVisible(type == DVB_TERR || type == DVB_CABLE || type == DVB_SAT || type == DVB_ATSC);
+  m_radioButtonHD->SetVisible(type == DVB_TERR || type == DVB_CABLE || type == DVB_SAT || type == DVB_ATSC);
+}
+
+void cVNSIChannelScan::Action()
+{
+  uint32_t channelID;
+  uint32_t requestID;
+  uint32_t userDataLength;
+  uint8_t* userData;
+
+  bool readSuccess;
+
+  cResponsePacket* vresp;
+
+  while (Running())
+  {
+    readSuccess = readData((uint8_t*)&channelID, sizeof(uint32_t));  // 2s timeout atm
+    if (!readSuccess && !IsClientConnected())
+      return; // return to stop this thread
+
+    if (!readSuccess) continue; // no data was read but the connection is ok.
+
+    // Data was read
+    channelID = ntohl(channelID);
+    if (channelID == CHANNEL_REQUEST_RESPONSE)
+    {
+      if (!readData((uint8_t*)&requestID, sizeof(uint32_t))) break;
+      requestID = ntohl(requestID);
+      if (!readData((uint8_t*)&userDataLength, sizeof(uint32_t))) break;
+      userDataLength = ntohl(userDataLength);
+      if (userDataLength > 5000000) break; // how big can these packets get?
+      userData = NULL;
+      if (userDataLength > 0)
+      {
+        userData = (uint8_t*)malloc(userDataLength);
+        if (!userData) break;
+        if (!readData(userData, userDataLength)) break;
+      }
+
+      vresp = new cResponsePacket();
+      vresp->setResponse(requestID, userData, userDataLength);
+
+      CMD_LOCK;
+      SMessages::iterator it = m_queue.find(requestID);
+      if (it != m_queue.end())
+      {
+        it->second.pkt = vresp;
+        it->second.event->Signal();
+      }
+      else
+      {
+        delete vresp;
+      }
+    }
+    else if (channelID == CHANNEL_SCAN)
+    {
+      if (!readData((uint8_t*)&requestID, sizeof(uint32_t))) break;
+      requestID = ntohl(requestID);
+      if (!readData((uint8_t*)&userDataLength, sizeof(uint32_t))) break;
+      userDataLength = ntohl(userDataLength);
+      if (userDataLength > 5000000) break; // how big can these packets get?
+      userData = NULL;
+      if (userDataLength > 0)
+      {
+        userData = (uint8_t*)malloc(userDataLength);
+        if (!userData) break;
+        if (!readData(userData, userDataLength)) break;
+      }
+
+      if (requestID == VDR_SCANNER_PERCENTAGE)
+      {
+        uint32_t percent = ntohl(*(uint32_t*)&userData[0]);
+        if (percent >= 0 && percent <= 100)
+          SetProgress(percent);
+      }
+      else if (requestID == VDR_SCANNER_SIGNAL)
+      {
+        uint32_t strength = ntohl(*(uint32_t*)&userData[0]);
+        uint32_t locked   = ntohl(*(uint32_t*)&userData[4]);
+        SetSignal(strength, locked);
+      }
+      else if (requestID == VDR_SCANNER_DEVICE)
+      {
+        int length = strlen((char*)&userData[0]);
+        char* str = new char[length + 1];
+        strcpy(str, (char*)&userData[0]);
+        m_window->SetControlLabel(LABEL_DEVICE, str);
+        delete[] str;
+      }
+      else if (requestID == VDR_SCANNER_TRANSPONDER)
+      {
+        int length = strlen((char*)&userData[0]);
+        char* str = new char[length + 1];
+        strcpy(str, (char*)&userData[0]);
+        m_window->SetControlLabel(LABEL_TRANSPONDER, str);
+        delete[] str;
+      }
+      else if (requestID == VDR_SCANNER_NEWCHANNEL)
+      {
+        uint32_t isRadio      = ntohl(*(uint32_t*)&userData[0]);
+        uint32_t isEncrypted  = ntohl(*(uint32_t*)&userData[4]);
+        uint32_t isHD         = ntohl(*(uint32_t*)&userData[8]);
+        int length = strlen((char*)&userData[12]);
+        char* str = new char[length + 1];
+        strcpy(str, (char*)&userData[12]);
+
+        cListItem* item = GUI->ListItem_create(str, NULL, NULL, NULL, NULL);
+        if (isEncrypted)
+          item->SetProperty("IsEncrypted", "yes");
+        if (isRadio)
+          item->SetProperty("IsRadio", "yes");
+        if (isHD)
+          item->SetProperty("IsHD", "yes");
+        m_window->AddItem(item, 0);
+        GUI->ListItem_destroy(item);
+
+        delete[] str;
+      }
+      else if (requestID == VDR_SCANNER_FINISHED)
+      {
+        if (!m_Canceled)
+        {
+          m_window->SetControlLabel(HEADER_LABEL, XBMC->GetLocalizedString(30036));
+          m_window->SetControlLabel(BUTTON_START, XBMC->GetLocalizedString(30024));
+          m_window->SetControlLabel(LABEL_STATUS, XBMC->GetLocalizedString(30041));
+        }
+        else
+        {
+          m_window->SetControlLabel(HEADER_LABEL, XBMC->GetLocalizedString(30042));
+        }
+      }
+      else if (requestID == VDR_SCANNER_STATUS)
+      {
+        uint32_t status = ntohl(*(uint32_t*)&userData[0]);
+        if (status == 0)
+        {
+          if (m_Canceled)
+            m_window->SetControlLabel(LABEL_STATUS, XBMC->GetLocalizedString(16200));
+          else
+            m_window->SetControlLabel(LABEL_STATUS, XBMC->GetLocalizedString(30040));
+          m_window->SetControlLabel(BUTTON_START, XBMC->GetLocalizedString(30024));
+          m_stopped = true;
+        }
+        else if (status == 1)
+        {
+          m_window->SetControlLabel(LABEL_STATUS, XBMC->GetLocalizedString(30039));
+        }
+        else if (status == 2)
+        {
+          m_window->SetControlLabel(LABEL_STATUS, XBMC->GetLocalizedString(30037));
+          m_window->SetControlLabel(BUTTON_START, XBMC->GetLocalizedString(30024));
+          m_window->SetControlLabel(HEADER_LABEL, XBMC->GetLocalizedString(30043));
+          m_stopped = true;
+        }
+        else if (status == 3)
+        {
+          m_window->SetControlLabel(LABEL_STATUS, XBMC->GetLocalizedString(30038));
+        }
+      }
+
+      if (userData)
+        free(userData);
+    }
+    else
+    {
+      XBMC->Log(LOG_ERROR, "cVNSIChannelScan::Action() - Rxd a wrong response packet on channel %lu !!", channelID);
+      break;
+    }
+  }
+}
+
+cResponsePacket* cVNSIChannelScan::ReadResult(cRequestPacket* vrp)
+{
+  m_Mutex.Lock();
+
+  SMessage &message(m_queue[vrp->getSerial()]);
+  message.event = new cCondWait();
+  message.pkt   = NULL;
+
+  m_Mutex.Unlock();
+
+  if(!m_session.SendMessage(vrp))
+  {
+    m_queue.erase(vrp->getSerial());
+    return NULL;
+  }
+
+  message.event->Wait(2000);
+
+  m_Mutex.Lock();
+
+  cResponsePacket* vresp = message.pkt;
+  delete message.event;
+
+  m_queue.erase(vrp->getSerial());
+
+  m_Mutex.Unlock();
+
+  return vresp;
+}
+
+bool cVNSIChannelScan::readData(uint8_t* buffer, int totalBytes, int TimeOut)
+{
+  int ret = m_session.readData(buffer, totalBytes, TimeOut);
+  if (ret == 1)
+    return true;
+  else if (ret == 0)
+    return false;
+
+  SetClientConnected(false);
+  return false;
+}
diff --git a/xbmc/pvrclients/vdr-vnsi/VNSIChannelScan.h b/xbmc/pvrclients/vdr-vnsi/VNSIChannelScan.h
new file mode 100644 (file)
index 0000000..4cdb9b6
--- /dev/null
@@ -0,0 +1,104 @@
+#pragma once
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "client.h"
+#include "VNSISession.h"
+#include "thread.h"
+
+typedef enum scantype
+{
+  DVB_TERR    = 0,
+  DVB_CABLE   = 1,
+  DVB_SAT     = 2,
+  PVRINPUT    = 3,
+  PVRINPUT_FM = 4,
+  DVB_ATSC    = 5,
+} scantype_t;
+
+
+class cVNSIChannelScan : public cThread
+{
+public:
+  cVNSIChannelScan();
+  ~cVNSIChannelScan();
+
+  bool Open();
+
+  bool OnClick(int controlId);
+  bool OnFocus(int controlId);
+  bool OnInit();
+  bool OnAction(int actionId);
+
+  static bool OnClickCB(GUIHANDLE cbhdl, int controlId);
+  static bool OnFocusCB(GUIHANDLE cbhdl, int controlId);
+  static bool OnInitCB(GUIHANDLE cbhdl);
+  static bool OnActionCB(GUIHANDLE cbhdl, int actionId);
+
+protected:
+  virtual void Action(void);
+
+private:
+  bool ReadCountries();
+  bool ReadSatellites();
+  void SetControlsVisible(scantype_t type);
+  void StartScan();
+  void StopScan();
+  void ReturnFromProcessView();
+  void SetProgress(int procent);
+  void SetSignal(int procent, bool locked);
+
+  cResponsePacket* ReadResult(cRequestPacket* vrp);
+  bool readData(uint8_t* buffer, int totalBytes, int TimeOut = 2);
+
+  struct SMessage
+  {
+    cCondWait       *event;
+    cResponsePacket *pkt;
+  };
+  typedef std::map<int, SMessage> SMessages;
+  SMessages       m_queue;
+  cMutex          m_Mutex;
+  cVNSISession    m_session;
+  CStdString      m_header;
+  CStdString      m_Signal;
+  bool            m_running;
+  bool            m_stopped;
+  bool            m_Canceled;
+
+  cGUIWindow      *m_window;
+  cGUISpinControl *m_spinSourceType;
+  cGUISpinControl *m_spinCountries;
+  cGUISpinControl *m_spinSatellites;
+  cGUISpinControl *m_spinDVBCInversion;
+  cGUISpinControl *m_spinDVBCSymbolrates;
+  cGUISpinControl *m_spinDVBCqam;
+  cGUISpinControl *m_spinDVBTInversion;
+  cGUISpinControl *m_spinATSCType;
+  cGUIRadioButton *m_radioButtonTV;
+  cGUIRadioButton *m_radioButtonRadio;
+  cGUIRadioButton *m_radioButtonFTA;
+  cGUIRadioButton *m_radioButtonScrambled;
+  cGUIRadioButton *m_radioButtonHD;
+  cGUIProgressControl *m_progressDone;
+  cGUIProgressControl *m_progressSignal;
+
+};
diff --git a/xbmc/pvrclients/vdr-vnsi/VNSIData.cpp b/xbmc/pvrclients/vdr-vnsi/VNSIData.cpp
new file mode 100644 (file)
index 0000000..8388be7
--- /dev/null
@@ -0,0 +1,1050 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "VNSIData.h"
+#include "responsepacket.h"
+#include "requestpacket.h"
+#include "vdrcommand.h"
+#include "recordings.h"
+#include "tools.h"
+
+#define CMD_LOCK cMutexLock CmdLock((cMutex*)&m_Mutex)
+
+cVNSIData::cVNSIData()
+{
+}
+
+cVNSIData::~cVNSIData()
+{
+  Close();
+}
+
+bool cVNSIData::Open(CStdString hostname, int port, long timeout)
+{
+  if(!m_session.Open(hostname, port, timeout))
+    return false;
+
+  SetDescription("VNSI Data Listener");
+  Start();
+  SetClientConnected(true);
+  return true;
+}
+
+void cVNSIData::Close()
+{
+  Cancel(1);
+  m_session.Abort();
+  m_session.Close();
+}
+
+bool cVNSIData::CheckConnection()
+{
+  return true;
+}
+
+cResponsePacket* cVNSIData::ReadResult(cRequestPacket* vrp)
+{
+  m_Mutex.Lock();
+
+  SMessage &message(m_queue[vrp->getSerial()]);
+  message.event = new cCondWait();
+  message.pkt   = NULL;
+
+  m_Mutex.Unlock();
+
+  if(!m_session.SendMessage(vrp))
+  {
+    m_queue.erase(vrp->getSerial());
+    return NULL;
+  }
+
+  message.event->Wait(2000);
+
+  m_Mutex.Lock();
+
+  cResponsePacket* vresp = message.pkt;
+  delete message.event;
+
+  m_queue.erase(vrp->getSerial());
+
+  m_Mutex.Unlock();
+
+  return vresp;
+}
+
+bool cVNSIData::GetTime(time_t *localTime, int *gmtOffset)
+{
+  cRequestPacket vrp;
+  if (!vrp.init(VDR_GETTIME))
+  {
+    XBMC->Log(LOG_ERROR, "cVNSIData::GetTime - Can't init cRequestPacket");
+    return false;
+  }
+
+  cResponsePacket* vresp = ReadResult(&vrp);
+  if (!vresp)
+  {
+    XBMC->Log(LOG_ERROR, "cVNSIData::GetTime - Can't get response packed");
+    return false;
+  }
+
+  uint32_t vdrTime       = vresp->extract_U32();
+  int32_t  vdrTimeOffset = vresp->extract_S32();
+
+  *localTime = vdrTime;
+  *gmtOffset = vdrTimeOffset;
+
+  delete vresp;
+  return true;
+}
+
+bool cVNSIData::GetDriveSpace(long long *total, long long *used)
+{
+  cRequestPacket vrp;
+  if (!vrp.init(VDR_RECORDINGS_DISKSIZE))
+  {
+    XBMC->Log(LOG_ERROR, "cVNSIData::GetDriveSpace - Can't init cRequestPacket");
+    return false;
+  }
+
+  cResponsePacket* vresp = ReadResult(&vrp);
+  if (!vresp)
+  {
+    XBMC->Log(LOG_ERROR, "cVNSIData::GetDriveSpace - Can't get response packed");
+    return false;
+  }
+
+  uint32_t totalspace    = vresp->extract_U32();
+  uint32_t freespace     = vresp->extract_U32();
+  /* vresp->extract_U32(); percent not used */
+
+  *total = totalspace;
+  *used  = (totalspace - freespace);
+
+  /* Convert from kBytes to Bytes */
+  *total *= 1024;
+  *used  *= 1024;
+
+  delete vresp;
+  return true;
+}
+
+bool cVNSIData::SupportChannelScan()
+{
+  cRequestPacket vrp;
+  if (!vrp.init(VDR_SCAN_SUPPORTED))
+  {
+    XBMC->Log(LOG_ERROR, "cVNSIData::SupportChannelScan - Can't init cRequestPacket");
+    return false;
+  }
+
+  cResponsePacket* vresp = ReadResult(&vrp);
+  if (!vresp)
+  {
+    XBMC->Log(LOG_ERROR, "cVNSIData::SupportChannelScan - Can't get response packed");
+    return false;
+  }
+
+  uint32_t ret = vresp->extract_U32();
+  delete vresp;
+  return ret == VDR_RET_OK ? true : false;
+}
+
+bool cVNSIData::EnableStatusInterface(bool onOff)
+{
+  cRequestPacket vrp;
+  if (!vrp.init(VDR_ENABLESTATUSINTERFACE)) return false;
+  if (!vrp.add_U8(onOff)) return false;
+
+  cResponsePacket* vresp = ReadResult(&vrp);
+  if (!vresp)
+  {
+    XBMC->Log(LOG_ERROR, "cVNSIData::EnableStatusInterface - Can't get response packed");
+    return false;
+  }
+
+  uint32_t ret = vresp->extract_U32();
+  delete vresp;
+  return ret == VDR_RET_OK ? true : false;
+}
+
+bool cVNSIData::EnableOSDInterface(bool onOff)
+{
+  cRequestPacket vrp;
+  if (!vrp.init(VDR_ENABLEOSDINTERFACE)) return false;
+  if (!vrp.add_U8(onOff)) return false;
+
+  cResponsePacket* vresp = ReadResult(&vrp);
+  if (!vresp)
+  {
+    XBMC->Log(LOG_ERROR, "cVNSIData::EnableStatusInterface - Can't get response packed");
+    return false;
+  }
+
+  uint32_t ret = vresp->extract_U32();
+  delete vresp;
+  return ret == VDR_RET_OK ? true : false;
+}
+
+int cVNSIData::GetGroupsCount()
+{
+  cRequestPacket vrp;
+  if (!vrp.init(VDR_CHANNELS_GROUPSCOUNT))
+  {
+    XBMC->Log(LOG_ERROR, "cVNSIData::GetGroupsCount - Can't init cRequestPacket");
+    return -1;
+  }
+
+  cResponsePacket* vresp = ReadResult(&vrp);
+  if (!vresp)
+  {
+    XBMC->Log(LOG_ERROR, "cVNSIData::GetGroupsCount - Can't get response packed");
+    return -1;
+  }
+
+  uint32_t count = vresp->extract_U32();
+
+  delete vresp;
+  return count;
+}
+
+int cVNSIData::GetChannelsCount()
+{
+  cRequestPacket vrp;
+  if (!vrp.init(VDR_CHANNELS_GETCOUNT))
+  {
+    XBMC->Log(LOG_ERROR, "cVNSIData::GetChannelsCount - Can't init cRequestPacket");
+    return -1;
+  }
+
+  cResponsePacket* vresp = ReadResult(&vrp);
+  if (!vresp)
+  {
+    XBMC->Log(LOG_ERROR, "cVNSIData::GetChannelsCount - Can't get response packed");
+    return -1;
+  }
+
+  uint32_t count = vresp->extract_U32();
+
+  delete vresp;
+  return count;
+}
+
+bool cVNSIData::GetGroupsList(PVRHANDLE handle, bool radio)
+{
+  cRequestPacket vrp;
+  if (!vrp.init(VDR_CHANNELS_GETGROUPS))
+  {
+    XBMC->Log(LOG_ERROR, "cVNSIData::GetGroupsList - Can't init cRequestPacket");
+    return false;
+  }
+  if (!vrp.add_U32(radio))
+  {
+    XBMC->Log(LOG_ERROR, "cVNSIData::GetGroupsList - Can't add parameter to cRequestPacket");
+    return false;
+  }
+
+  cResponsePacket* vresp = ReadResult(&vrp);
+  if (!vresp)
+  {
+    XBMC->Log(LOG_ERROR, "cVNSIData::GetGroupsList - Can't get response packed");
+    return false;
+  }
+
+  while (!vresp->end())
+  {
+    uint32_t    index = vresp->extract_U32();
+    uint32_t    count = vresp->extract_U32();
+    const char *name  = vresp->extract_String();
+
+//    LOGDBG("Have added a group to list. %lu %lu %s", index, count, name);
+    delete name;
+  }
+
+  delete vresp;
+  return true;
+}
+
+bool cVNSIData::GetChannelsList(PVRHANDLE handle, bool radio)
+{
+  cRequestPacket vrp;
+  if (!vrp.init(VDR_CHANNELS_GETCHANNELS))
+  {
+    XBMC->Log(LOG_ERROR, "cVNSIData::GetChannelsList - Can't init cRequestPacket");
+    return false;
+  }
+  if (!vrp.add_U32(radio))
+  {
+    XBMC->Log(LOG_ERROR, "cVNSIData::GetChannelsList - Can't add parameter to cRequestPacket");
+    return false;
+  }
+
+  cResponsePacket* vresp = ReadResult(&vrp);
+  if (!vresp)
+  {
+    XBMC->Log(LOG_ERROR, "cVNSIData::GetChannelsList - Can't get response packed");
+    return false;
+  }
+
+  while (!vresp->end())
+  {
+    PVR_CHANNEL tag;
+    memset(&tag, 0 , sizeof(tag));
+
+    tag.number        = vresp->extract_U32();
+    tag.name          = vresp->extract_String();
+    tag.callsign      = tag.name;
+    tag.uid           = vresp->extract_U32();
+    tag.bouquet       = vresp->extract_U32();
+    tag.encryption    = vresp->extract_U32();
+    uint32_t vtype    = vresp->extract_U32();
+    tag.radio         = radio;
+    tag.input_format  = "";
+    tag.stream_url    = "";
+
+    PVR->TransferChannelEntry(handle, &tag);
+    delete tag.name;
+  }
+
+  delete vresp;
+  return true;
+}
+
+bool cVNSIData::GetEPGForChannel(PVRHANDLE handle, const PVR_CHANNEL &channel, time_t start, time_t end)
+{
+  cRequestPacket vrp;
+  if (!vrp.init(VDR_EPG_GETFORCHANNEL))
+  {
+    XBMC->Log(LOG_ERROR, "cVNSIData::GetEPGForChannel - Can't init cRequestPacket");
+    return false;
+  }
+  if (!vrp.add_U32(channel.number) || !vrp.add_U32(start) || !vrp.add_U32(end - start))
+  {
+    XBMC->Log(LOG_ERROR, "cVNSIData::GetEPGForChannel - Can't add parameter to cRequestPacket");
+    return false;
+  }
+
+  cResponsePacket* vresp = ReadResult(&vrp);
+  if (!vresp)
+  {
+    XBMC->Log(LOG_ERROR, "cVNSIData::GetEPGForChannel - Can't get response packed");
+    return false;
+  }
+
+  while (!vresp->end())
+  {
+    PVR_PROGINFO tag;
+    memset(&tag, 0 , sizeof(tag));
+
+    tag.channum         = channel.number;
+    tag.uid             = vresp->extract_U32();
+    tag.starttime       = vresp->extract_U32();
+    tag.endtime         = tag.starttime + vresp->extract_U32();
+    uint32_t content    = vresp->extract_U32();
+    tag.genre_type      = content & 0xF0;
+    tag.genre_sub_type  = content & 0x0F;
+    tag.parental_rating = vresp->extract_U32();
+    tag.title           = vresp->extract_String();
+    tag.subtitle        = vresp->extract_String();
+    tag.description     = vresp->extract_String();
+
+    PVR->TransferEpgEntry(handle, &tag);
+    delete tag.title;
+    delete tag.subtitle;
+    delete tag.description;
+  }
+
+  delete vresp;
+  return true;
+}
+
+
+/** OPCODE's 60 - 69: VNSI network functions for timer access */
+
+int cVNSIData::GetTimersCount()
+{
+  cRequestPacket vrp;
+  if (!vrp.init(VDR_TIMER_GETCOUNT))
+  {
+    XBMC->Log(LOG_ERROR, "cVNSIData::GetTimersCount - Can't init cRequestPacket");
+    return -1;
+  }
+
+  cResponsePacket* vresp = ReadResult(&vrp);
+  if (!vresp)
+  {
+    XBMC->Log(LOG_ERROR, "cVNSIData::GetTimersCount - Can't get response packed");
+    return -1;
+  }
+
+  uint32_t count = vresp->extract_U32();
+
+  delete vresp;
+  return count;
+}
+
+PVR_ERROR cVNSIData::GetTimerInfo(unsigned int timernumber, PVR_TIMERINFO &tag)
+{
+  cRequestPacket vrp;
+  if (!vrp.init(VDR_TIMER_GET))           return PVR_ERROR_UNKOWN;
+  if (!vrp.add_U32(timernumber))          return PVR_ERROR_UNKOWN;
+
+  cResponsePacket* vresp = ReadResult(&vrp);
+  if (!vresp)
+  {
+    delete vresp;
+    return PVR_ERROR_UNKOWN;
+  }
+
+  uint32_t returnCode = vresp->extract_U32();
+  if (returnCode != VDR_RET_OK)
+  {
+    delete vresp;
+    if (returnCode == VDR_RET_DATAUNKNOWN)
+      return PVR_ERROR_NOT_POSSIBLE;
+    else if (returnCode == VDR_RET_ERROR)
+      return PVR_ERROR_SERVER_ERROR;
+  }
+
+  tag.index       = vresp->extract_U32();
+  tag.active      = vresp->extract_U32();
+  uint32_t recording      = vresp->extract_U32();
+  uint32_t pending        = vresp->extract_U32();
+  tag.priority    = vresp->extract_U32();
+  tag.lifetime    = vresp->extract_U32();
+  tag.channelNum  = vresp->extract_U32();
+  tag.starttime   = vresp->extract_U32();
+  tag.endtime     = vresp->extract_U32();
+  tag.firstday    = vresp->extract_U32();
+  tag.repeatflags = vresp->extract_U32();
+  tag.repeat      = tag.repeatflags == 0 ? false : true;
+  tag.title       = vresp->extract_String();
+  tag.directory   = "";
+
+  delete tag.title;
+  delete vresp;
+  return PVR_ERROR_NO_ERROR;
+}
+
+bool cVNSIData::GetTimersList(PVRHANDLE handle)
+{
+  cRequestPacket vrp;
+  if (!vrp.init(VDR_TIMER_GETLIST))
+  {
+    XBMC->Log(LOG_ERROR, "cVNSIData::GetTimersList - Can't init cRequestPacket");
+    return false;
+  }
+
+  cResponsePacket* vresp = ReadResult(&vrp);
+  if (!vresp)
+  {
+    delete vresp;
+    XBMC->Log(LOG_ERROR, "cVNSIData::GetTimersList - Can't get response packed");
+    return false;
+  }
+
+  uint32_t numTimers = vresp->extract_U32();
+  if (numTimers > 0)
+  {
+    while (!vresp->end())
+    {
+      PVR_TIMERINFO tag;
+      tag.index       = vresp->extract_U32();
+      tag.active      = vresp->extract_U32();
+      uint32_t recording      = vresp->extract_U32();
+      uint32_t pending        = vresp->extract_U32();
+      tag.priority    = vresp->extract_U32();
+      tag.lifetime    = vresp->extract_U32();
+      tag.channelNum  = vresp->extract_U32();
+      tag.starttime   = vresp->extract_U32();
+      tag.endtime     = vresp->extract_U32();
+      tag.firstday    = vresp->extract_U32();
+      tag.repeatflags = vresp->extract_U32();
+      tag.repeat      = tag.repeatflags == 0 ? false : true;
+      tag.title       = vresp->extract_String();
+      tag.directory   = "";
+
+      PVR->TransferTimerEntry(handle, &tag);
+      delete tag.title;
+    }
+  }
+  delete vresp;
+  return true;
+}
+
+PVR_ERROR cVNSIData::AddTimer(const PVR_TIMERINFO &timerinfo)
+{
+  cRequestPacket vrp;
+  if (!vrp.init(VDR_TIMER_ADD))
+  {
+    XBMC->Log(LOG_ERROR, "cVNSIData::AddTimer - Can't init cRequestPacket");
+    return PVR_ERROR_UNKOWN;
+  }
+  if (!vrp.add_U32(timerinfo.active))     return PVR_ERROR_UNKOWN;
+  if (!vrp.add_U32(timerinfo.priority))   return PVR_ERROR_UNKOWN;
+  if (!vrp.add_U32(timerinfo.lifetime))   return PVR_ERROR_UNKOWN;
+  if (!vrp.add_U32(timerinfo.channelNum)) return PVR_ERROR_UNKOWN;
+  if (!vrp.add_U32(timerinfo.starttime))  return PVR_ERROR_UNKOWN;
+  if (!vrp.add_U32(timerinfo.endtime))    return PVR_ERROR_UNKOWN;
+  if (!vrp.add_U32(timerinfo.repeat ? timerinfo.firstday : 0))   return PVR_ERROR_UNKOWN;
+  if (!vrp.add_U32(timerinfo.repeatflags))return PVR_ERROR_UNKOWN;
+  if (!vrp.add_String(timerinfo.title))   return PVR_ERROR_UNKOWN;
+  if (!vrp.add_String(""))                return PVR_ERROR_UNKOWN;
+
+  cResponsePacket* vresp = ReadResult(&vrp);
+  if (vresp->noResponse())
+  {
+    delete vresp;
+    XBMC->Log(LOG_ERROR, "cVNSIData::AddTimer - Can't get response packed");
+    return PVR_ERROR_UNKOWN;
+  }
+  uint32_t returnCode = vresp->extract_U32();
+  delete vresp;
+  if (returnCode == VDR_RET_DATALOCKED)
+    return PVR_ERROR_ALREADY_PRESENT;
+  else if (returnCode == VDR_RET_DATAINVALID)
+    return PVR_ERROR_NOT_SAVED;
+  else if (returnCode == VDR_RET_ERROR)
+    return PVR_ERROR_SERVER_ERROR;
+
+  return PVR_ERROR_NO_ERROR;
+}
+
+PVR_ERROR cVNSIData::DeleteTimer(const PVR_TIMERINFO &timerinfo, bool force)
+{
+  cRequestPacket vrp;
+  if (!vrp.init(VDR_TIMER_DELETE))
+    return PVR_ERROR_UNKOWN;
+
+  if (!vrp.add_U32(timerinfo.index))
+    return PVR_ERROR_UNKOWN;
+
+  if (!vrp.add_U32(force))
+    return PVR_ERROR_UNKOWN;
+
+  cResponsePacket* vresp = ReadResult(&vrp);
+  if (vresp->noResponse())
+  {
+    delete vresp;
+    return PVR_ERROR_UNKOWN;
+  }
+
+  uint32_t returnCode = vresp->extract_U32();
+  delete vresp;
+
+  if (returnCode == VDR_RET_DATALOCKED)
+    return PVR_ERROR_NOT_DELETED;
+  if (returnCode == VDR_RET_RECRUNNING)
+    return PVR_ERROR_RECORDING_RUNNING;
+  else if (returnCode == VDR_RET_DATAINVALID)
+    return PVR_ERROR_NOT_POSSIBLE;
+  else if (returnCode == VDR_RET_ERROR)
+    return PVR_ERROR_SERVER_ERROR;
+
+  return PVR_ERROR_NO_ERROR;
+}
+
+PVR_ERROR cVNSIData::RenameTimer(const PVR_TIMERINFO &timerinfo, const char *newname)
+{
+  PVR_TIMERINFO timerinfo1;
+  PVR_ERROR ret = GetTimerInfo(timerinfo.index, timerinfo1);
+  if (ret != PVR_ERROR_NO_ERROR)
+    return ret;
+
+  timerinfo1.title = newname;
+  return UpdateTimer(timerinfo1);
+}
+
+PVR_ERROR cVNSIData::UpdateTimer(const PVR_TIMERINFO &timerinfo)
+{
+  cRequestPacket vrp;
+  if (!vrp.init(VDR_TIMER_UPDATE))        return PVR_ERROR_UNKOWN;
+  if (!vrp.add_U32(timerinfo.index))      return PVR_ERROR_UNKOWN;
+  if (!vrp.add_U32(timerinfo.active))     return PVR_ERROR_UNKOWN;
+  if (!vrp.add_U32(timerinfo.priority))   return PVR_ERROR_UNKOWN;
+  if (!vrp.add_U32(timerinfo.lifetime))   return PVR_ERROR_UNKOWN;
+  if (!vrp.add_U32(timerinfo.channelNum)) return PVR_ERROR_UNKOWN;
+  if (!vrp.add_U32(timerinfo.starttime))  return PVR_ERROR_UNKOWN;
+  if (!vrp.add_U32(timerinfo.endtime))    return PVR_ERROR_UNKOWN;
+  if (!vrp.add_U32(timerinfo.repeat ? timerinfo.firstday : 0))   return PVR_ERROR_UNKOWN;
+  if (!vrp.add_U32(timerinfo.repeatflags))return PVR_ERROR_UNKOWN;
+  if (!vrp.add_String(timerinfo.title))   return PVR_ERROR_UNKOWN;
+  if (!vrp.add_String(""))                return PVR_ERROR_UNKOWN;
+
+  cResponsePacket* vresp = ReadResult(&vrp);
+  if (vresp->noResponse())
+  {
+    delete vresp;
+    return PVR_ERROR_UNKOWN;
+  }
+  uint32_t returnCode = vresp->extract_U32();
+  delete vresp;
+  if (returnCode == VDR_RET_DATAUNKNOWN)
+    return PVR_ERROR_NOT_POSSIBLE;
+  else if (returnCode == VDR_RET_DATAINVALID)
+    return PVR_ERROR_NOT_SAVED;
+  else if (returnCode == VDR_RET_ERROR)
+    return PVR_ERROR_SERVER_ERROR;
+
+  return PVR_ERROR_NO_ERROR;
+}
+
+int cVNSIData::GetRecordingsCount()
+{
+  cRequestPacket vrp;
+  if (!vrp.init(VDR_RECORDINGS_GETCOUNT))
+  {
+    XBMC->Log(LOG_ERROR, "cVNSIData::GetRecordingsCount - Can't init cRequestPacket");
+    return -1;
+  }
+
+  cResponsePacket* vresp = ReadResult(&vrp);
+  if (!vresp)
+  {
+    XBMC->Log(LOG_ERROR, "cVNSIData::GetRecordingsCount - Can't get response packed");
+    return -1;
+  }
+
+  uint32_t count = vresp->extract_U32();
+
+  delete vresp;
+  return count;
+}
+
+PVR_ERROR cVNSIData::GetRecordingsList(PVRHANDLE handle)
+{
+  m_recIndex = 1;
+  if (g_bUseRecordingsDir && g_szRecordingsDir != "")
+  {
+    ScanVideoDir(handle, g_szRecordingsDir.c_str(), g_szRecordingsDir.c_str());
+  }
+  else
+  {
+    bool haveCheckedLocalAccess = true /*false*/;
+    m_RecordsPaths.clear();
+
+    cRequestPacket vrp;
+    if (!vrp.init(VDR_RECORDINGS_GETLIST))
+    {
+      XBMC->Log(LOG_ERROR, "cVNSIData::GetRecordingsList - Can't init cRequestPacket");
+      return PVR_ERROR_UNKOWN;
+    }
+
+    cResponsePacket* vresp = ReadResult(&vrp);
+    if (!vresp)
+    {
+      XBMC->Log(LOG_ERROR, "cVNSIData::GetRecordingsList - Can't get response packed");
+      return PVR_ERROR_UNKOWN;
+    }
+
+    char* videodir = vresp->extract_String();
+    m_videodir = videodir;
+    delete[] videodir;
+
+    while (!vresp->end())
+    {
+      CStdString title;
+
+      PVR_RECORDINGINFO tag;
+      tag.index           = m_recIndex++;
+      tag.recording_time  = vresp->extract_U32();
+      tag.duration        = vresp->extract_U32();
+      tag.priority        = vresp->extract_U32();
+      tag.lifetime        = vresp->extract_U32();
+      tag.channel_name    = vresp->extract_String();
+      char* name = vresp->extract_String();
+      title = name;
+      tag.subtitle        = vresp->extract_String();
+      tag.description     = vresp->extract_String();
+
+      char* fileName  = vresp->extract_String();
+
+      /* Save the given path name for later to translate the
+         index numbers to the path name. */
+      m_RecordsPaths.push_back(fileName);
+
+      /* Cleanup now the path name and remove VDR's 2 top
+         directories and the strip the base path */
+      CStdString path = fileName+m_videodir.size()+1;
+      path = path.substr(0, path.find_last_of("/\\"));
+
+      size_t found = path.find_last_of("/\\");
+      if (found != CStdString::npos)
+      {
+        /* If no title is present use recording dir name
+           as title */
+        if (title.IsEmpty())
+          title = path.substr(found+1);
+        path = path.substr(0, found);
+      }
+      else
+      {
+        /* If no title is present use recording dir name
+           as title */
+        if (title.IsEmpty())
+          title = path;
+        path = "";
+      }
+
+      tag.title           = title.c_str();
+      tag.directory       = path.c_str();
+      tag.stream_url      = "";
+
+      /* Check if we can open the first given recording dir on
+         local filesystem, if yes, scan the files itself and
+         ignore VNSI server for access them */
+      if (!haveCheckedLocalAccess)
+      {
+        XBMC->Log(LOG_NOTICE, "Trying to open '%s'", fileName);
+        DIR *vdrrecdir = opendir(fileName);
+        if (vdrrecdir)
+        {
+          closedir(vdrrecdir);
+          delete[] tag.channel_name;
+          delete[] tag.subtitle;
+          delete[] tag.description;
+          delete[] name;
+          delete[] fileName;
+          delete vresp;
+
+          XBMC->Log(LOG_NOTICE, "Found recordings on local disk, ignoring VNSI Server and scanning directories byself");
+
+          ScanVideoDir(handle, m_videodir.c_str(), m_videodir.c_str());
+          return PVR_ERROR_NO_ERROR;
+        }
+
+        haveCheckedLocalAccess = true;
+      }
+
+      PVR->TransferRecordingEntry(handle, &tag);
+
+      delete[] tag.channel_name;
+      delete[] tag.subtitle;
+      delete[] tag.description;
+      delete[] name;
+      delete[] fileName;
+    }
+
+    delete vresp;
+  }
+
+  return PVR_ERROR_NO_ERROR;
+}
+
+PVR_ERROR cVNSIData::DeleteRecording(CStdString path)
+{
+  cRequestPacket vrp;
+  if (!vrp.init(VDR_RECORDINGS_DELETE))
+    return PVR_ERROR_UNKOWN;
+
+  if (!vrp.add_String(path))
+    return PVR_ERROR_UNKOWN;
+
+  cResponsePacket* vresp = ReadResult(&vrp);
+  if (vresp->noResponse())
+  {
+    delete vresp;
+    return PVR_ERROR_UNKOWN;
+  }
+
+  uint32_t returnCode = vresp->extract_U32();
+  delete vresp;
+
+  if (returnCode == VDR_RET_DATALOCKED)
+    return PVR_ERROR_NOT_DELETED;
+  if (returnCode == VDR_RET_RECRUNNING)
+    return PVR_ERROR_RECORDING_RUNNING;
+  else if (returnCode == VDR_RET_DATAINVALID)
+    return PVR_ERROR_NOT_POSSIBLE;
+  else if (returnCode == VDR_RET_ERROR)
+    return PVR_ERROR_SERVER_ERROR;
+
+  return PVR_ERROR_NO_ERROR;
+}
+
+void cVNSIData::Action()
+{
+  uint32_t channelID;
+  uint32_t requestID;
+  uint32_t userDataLength;
+  uint8_t* userData;
+
+  uint32_t timeNow;
+  uint32_t lastKAsent = 0;
+  uint32_t lastKArecv = time(NULL);
+  bool readSuccess;
+
+  cResponsePacket* vresp;
+
+  while (Running())
+  {
+    timeNow = time(NULL);
+
+    readSuccess = readData((uint8_t*)&channelID, sizeof(uint32_t));  // 2s timeout atm
+    if (!readSuccess && !IsClientConnected())
+      return; // return to stop this thread
+
+    // Error or timeout.
+    if (!lastKAsent) // have not sent a KA
+    {
+      if (lastKArecv < (timeNow - 5))
+      {
+        DEVDBG("cVNSIData::Action() - Sending KA packet");
+        if (!sendKA(timeNow))
+        {
+          XBMC->Log(LOG_ERROR, "cVNSIData::Action() - Could not send KA, calling connectionDied");
+          SetClientConnected(false);
+          return;
+        }
+        lastKAsent = timeNow;
+      }
+    }
+    else
+    {
+      if (lastKAsent <= (timeNow - 10))
+      {
+        XBMC->Log(LOG_ERROR, "cVNSIData::Action() - lastKA over 10s ago, calling connectionDied");
+        SetClientConnected(false);
+        return;
+      }
+    }
+
+    if (!readSuccess) continue; // no data was read but the connection is ok.
+
+    // Data was read
+    channelID = ntohl(channelID);
+    if (channelID == CHANNEL_REQUEST_RESPONSE)
+    {
+      if (!readData((uint8_t*)&requestID, sizeof(uint32_t))) break;
+      requestID = ntohl(requestID);
+      if (!readData((uint8_t*)&userDataLength, sizeof(uint32_t))) break;
+      userDataLength = ntohl(userDataLength);
+      if (userDataLength > 5000000) break; // how big can these packets get?
+      userData = NULL;
+      if (userDataLength > 0)
+      {
+        userData = (uint8_t*)malloc(userDataLength);
+        if (!userData) break;
+        if (!readData(userData, userDataLength)) break;
+      }
+
+      vresp = new cResponsePacket();
+      vresp->setResponse(requestID, userData, userDataLength);
+      DEVDBG("cVNSIData::Action() - Rxd a response packet, requestID=%lu, len=%lu", requestID, userDataLength);
+
+      CMD_LOCK;
+      SMessages::iterator it = m_queue.find(requestID);
+      if (it != m_queue.end())
+      {
+        it->second.pkt = vresp;
+        it->second.event->Signal();
+      }
+      else
+      {
+        delete vresp;
+      }
+    }
+    else if (channelID == CHANNEL_STATUS)
+    {
+      if (!readData((uint8_t*)&requestID, sizeof(uint32_t))) break;
+      requestID = ntohl(requestID);
+      if (!readData((uint8_t*)&userDataLength, sizeof(uint32_t))) break;
+      userDataLength = ntohl(userDataLength);
+      if (userDataLength > 5000000) break; // how big can these packets get?
+      userData = NULL;
+      if (userDataLength > 0)
+      {
+        userData = (uint8_t*)malloc(userDataLength);
+        if (!userData) break;
+        if (!readData(userData, userDataLength)) break;
+      }
+
+      if (requestID == VDR_STATUS_MESSAGE)
+      {
+        uint32_t type = ntohl(*(uint32_t*)&userData[0]);
+        int length = strlen((char*)&userData[4]);
+        char* str = new char[length + 1];
+        strcpy(str, (char*)&userData[4]);
+
+        CStdString text = str;
+        if (g_bCharsetConv)
+          XBMC->UnknownToUTF8(text);
+
+        if (type == 2)
+          XBMC->QueueNotification(QUEUE_ERROR, text.c_str());
+        if (type == 1)
+          XBMC->QueueNotification(QUEUE_WARNING, text.c_str());
+        else
+          XBMC->QueueNotification(QUEUE_INFO, text.c_str());
+
+        delete[] str;
+      }
+      else if (requestID == VDR_STATUS_RECORDING)
+      {
+        uint32_t device = ntohl(*(uint32_t*)&userData[0]);
+        uint32_t on     = ntohl(*(uint32_t*)&userData[4]);
+
+        int length = strlen((char*)&userData[8]);
+        char* str = NULL;
+        if (length > 1)
+        {
+          str = new char[length + 1];
+          strcpy(str, (char*)&userData[8]);
+        }
+
+        int length2 = strlen((char*)&userData[8+length]);
+        char* str2 = NULL;
+        if (length2 > 1)
+        {
+          str2 = new char[length2 + 1];
+          strcpy(str2, (char*)&userData[8+length]);
+        }
+
+        PVR->Recording(str, str2, on);
+        PVR->TriggerTimerUpdate();
+
+        delete[] str;
+        delete[] str2;
+      }
+      else if (requestID == VDR_STATUS_TIMERCHANGE)
+      {
+        uint32_t status = ntohl(*(uint32_t*)&userData[0]);
+        int length = strlen((char*)&userData[4]);
+        char* str = new char[length + 1];
+        strcpy(str, (char*)&userData[4]);
+        delete[] str;
+        PVR->TriggerTimerUpdate();
+      }
+
+      if (userData)
+        free(userData);
+    }
+    else if (channelID == CHANNEL_KEEPALIVE)
+    {
+      uint32_t KAreply = 0;
+      if (!readData((uint8_t*)&KAreply, sizeof(uint32_t))) break;
+      KAreply = (uint32_t)ntohl(KAreply);
+      if (KAreply == lastKAsent) // successful KA response
+      {
+        lastKAsent = 0;
+        lastKArecv = KAreply;
+        DEVDBG("cVNSIData::Action() - Rxd correct KA reply");
+      }
+    }
+    else
+    {
+      XBMC->Log(LOG_ERROR, "cVNSIData::Action() - Rxd a response packet on channel %lu !!", channelID);
+      break;
+    }
+  }
+}
+
+bool cVNSIData::sendKA(uint32_t timeStamp)
+{
+  char buffer[8];
+  *(uint32_t*)&buffer[0] = htonl(CHANNEL_KEEPALIVE);
+  *(uint32_t*)&buffer[4] = htonl(timeStamp);
+  if ((uint32_t)m_session.sendData(buffer, 8) != 8) return false;
+  return true;
+}
+
+bool cVNSIData::readData(uint8_t* buffer, int totalBytes, int TimeOut)
+{
+  int ret = m_session.readData(buffer, totalBytes, TimeOut);
+  if (ret == 1)
+    return true;
+  else if (ret == 0)
+    return false;
+
+  SetClientConnected(false);
+  return false;
+}
+
+CStdString cVNSIData::GetRecordingPath(int index)
+{
+  if (index <= 0 || index > m_RecordsPaths.size())
+    return "";
+
+  return m_RecordsPaths[index-1];
+}
+
+#define MAX_LINK_LEVEL  6
+void cVNSIData::ScanVideoDir(PVRHANDLE handle, const char *DirName, const char *DirBase, bool Deleted, int LinkLevel)
+{
+  cReadDir d(DirName);
+  struct dirent *e;
+  while ((e = d.Next()) != NULL)
+  {
+    if (strcmp(e->d_name, ".") && strcmp(e->d_name, ".."))
+    {
+      char *buffer = strdup(AddDirectory(DirName, e->d_name));
+      struct stat st;
+      if (stat(buffer, &st) == 0)
+      {
+        int Link = 0;
+        if (S_ISLNK(st.st_mode))
+        {
+          if (LinkLevel > MAX_LINK_LEVEL)
+          {
+            XBMC->Log(LOG_ERROR, "max link level exceeded - not scanning %s", buffer);
+            continue;
+          }
+          Link = 1;
+          char *old = buffer;
+          buffer = ReadLink(old);
+          free(old);
+          if (!buffer)
+            continue;
+          if (stat(buffer, &st) != 0)
+          {
+            free(buffer);
+            continue;
+          }
+        }
+        if (S_ISDIR(st.st_mode))
+        {
+          if (endswith(buffer, Deleted ? DELEXT : RECEXT))
+          {
+            cRecording recording(buffer, DirBase);
+
+            PVR_RECORDINGINFO tag;
+            tag.index           = m_recIndex++;
+            tag.channel_name    = recording.ChannelName();
+            tag.lifetime        = recording.Lifetime();
+            tag.priority        = recording.Priority();
+            tag.recording_time  = recording.StartTime();
+            tag.duration        = recording.Duration();
+            tag.subtitle        = recording.ShortText();
+            tag.description     = recording.Description();
+            tag.title           = recording.Title();
+            tag.directory       = recording.Directory();
+            tag.stream_url      = recording.StreamURL();
+
+            PVR->TransferRecordingEntry(handle, &tag);
+          }
+          else
+            ScanVideoDir(handle, buffer, DirBase, Deleted, LinkLevel + Link);
+        }
+      }
+      free(buffer);
+    }
+  }
+}
diff --git a/xbmc/pvrclients/vdr-vnsi/VNSIData.h b/xbmc/pvrclients/vdr-vnsi/VNSIData.h
new file mode 100644 (file)
index 0000000..eac7877
--- /dev/null
@@ -0,0 +1,91 @@
+#pragma once
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "client.h"
+#include "thread.h"
+#include "VNSISession.h"
+
+class cResponsePacket;
+class cRequestPacket;
+
+class cVNSIData : public cThread
+{
+public:
+  cVNSIData();
+  ~cVNSIData();
+
+  bool Open(CStdString hostname, int port, long timeout);
+  void Close();
+  bool CheckConnection();
+
+  cResponsePacket*  ReadResult(cRequestPacket* vrp);
+  int         GetProtocol()   { return m_session.GetProtocol(); }
+  CStdString  GetServerName() { return m_session.GetServerName(); }
+  CStdString  GetVersion()    { return m_session.GetVersion(); }
+  bool        SupportChannelScan();
+  bool        EnableStatusInterface(bool onOff);
+  bool        EnableOSDInterface(bool onOff);
+  bool        GetTime(time_t *localTime, int *gmtOffset);
+  bool        GetDriveSpace(long long *total, long long *used);
+  int         GetGroupsCount();
+  int         GetChannelsCount();
+  bool        GetGroupsList(PVRHANDLE handle, bool radio = false);
+  bool        GetChannelsList(PVRHANDLE handle, bool radio = false);
+  bool        GetEPGForChannel(PVRHANDLE handle, const PVR_CHANNEL &channel, time_t start, time_t end);
+  bool        GetTimersList(PVRHANDLE handle);
+
+  int         GetTimersCount();
+  PVR_ERROR   AddTimer(const PVR_TIMERINFO &timerinfo);
+  PVR_ERROR   GetTimerInfo(unsigned int timernumber, PVR_TIMERINFO &tag);
+  PVR_ERROR   DeleteTimer(const PVR_TIMERINFO &timerinfo, bool force = false);
+  PVR_ERROR   RenameTimer(const PVR_TIMERINFO &timerinfo, const char *newname);
+  PVR_ERROR   UpdateTimer(const PVR_TIMERINFO &timerinfo);
+
+  int         GetRecordingsCount();
+  PVR_ERROR   GetRecordingsList(PVRHANDLE handle);
+  CStdString  GetRecordingPath(int index);
+  PVR_ERROR   DeleteRecording(CStdString path);
+
+
+protected:
+  virtual void Action(void);
+
+private:
+  bool readData(uint8_t* buffer, int totalBytes, int TimeOut = 2);
+  bool sendKA(uint32_t timeStamp);
+  void ScanVideoDir(PVRHANDLE handle, const char *DirName, const char *DirBase, bool Deleted = false, int LinkLevel = 0);
+
+  struct SMessage
+  {
+    cCondWait       *event;
+    cResponsePacket *pkt;
+  };
+  typedef std::map<int, SMessage> SMessages;
+  typedef std::vector<CStdString> RecordPaths;
+
+  cVNSISession    m_session;
+  cMutex          m_Mutex;
+  SMessages       m_queue;
+  RecordPaths     m_RecordsPaths;
+  CStdString      m_videodir;
+       int             m_recIndex;
+};
diff --git a/xbmc/pvrclients/vdr-vnsi/VNSIDemux.cpp b/xbmc/pvrclients/vdr-vnsi/VNSIDemux.cpp
new file mode 100644 (file)
index 0000000..23a2397
--- /dev/null
@@ -0,0 +1,481 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#define __STDC_CONSTANT_MACROS
+#include <stdint.h>
+#include <limits.h>
+#include <libavcodec/avcodec.h> // For codec id's
+#include "VNSIDemux.h"
+#include "tools.h"
+#include "responsepacket.h"
+#include "requestpacket.h"
+#include "vdrcommand.h"
+
+cVNSIDemux::cVNSIDemux()
+  : m_startup(false)
+  , m_channel(0)
+  , m_StatusCount(0)
+{
+  m_Streams.nstreams = 0;
+}
+
+cVNSIDemux::~cVNSIDemux()
+{
+  Close();
+}
+
+bool cVNSIDemux::Open(const PVR_CHANNEL &channelinfo)
+{
+  m_channel = channelinfo.number;
+
+  if(!m_session.Open(g_szHostname, g_iPort, g_iConnectTimeout, "XBMC Live stream receiver"))
+    return false;
+
+  cRequestPacket vrp;
+  if (!vrp.init(VDR_CHANNELSTREAM_OPEN) ||
+      !vrp.add_U32(m_channel) ||
+      !m_session.ReadSuccess(&vrp))
+  {
+    XBMC->Log(LOG_ERROR, "cVNSIDemux::Open - Can't open channel %i - %s", m_channel, channelinfo.name);
+    return false;
+  }
+
+  m_StatusCount = 0;
+  m_startup = true;
+
+  while (m_Streams.nstreams == 0 && m_StatusCount == 0)
+  {
+    DemuxPacket* pkg = Read();
+    if(!pkg)
+    {
+      Close();
+      return false;
+    }
+    PVR->FreeDemuxPacket(pkg);
+  }
+
+  return true;
+}
+
+void cVNSIDemux::Close()
+{
+  cRequestPacket vrp;
+  m_session.Close();
+}
+
+bool cVNSIDemux::GetStreamProperties(PVR_STREAMPROPS* props)
+{
+  props->nstreams = m_Streams.nstreams;
+  for (int i = 0; i < m_Streams.nstreams; i++)
+  {
+    props->stream[i].id           = m_Streams.stream[i].id;
+    props->stream[i].physid       = m_Streams.stream[i].physid;
+    props->stream[i].codec_type   = m_Streams.stream[i].codec_type;
+    props->stream[i].codec_id     = m_Streams.stream[i].codec_id;
+    props->stream[i].height       = m_Streams.stream[i].height;
+    props->stream[i].width        = m_Streams.stream[i].width;
+    props->stream[i].language[0]  = m_Streams.stream[i].language[0];
+    props->stream[i].language[1]  = m_Streams.stream[i].language[1];
+    props->stream[i].language[2]  = m_Streams.stream[i].language[2];
+    props->stream[i].language[3]  = m_Streams.stream[i].language[3];
+    props->stream[i].identifier   = m_Streams.stream[i].identifier;
+  }
+  return (props->nstreams > 0);
+}
+
+void cVNSIDemux::Abort()
+{
+  m_Streams.nstreams = 0;
+  m_session.Abort();
+}
+
+DemuxPacket* cVNSIDemux::Read()
+{
+  cResponsePacket *resp;
+  while ((resp = m_session.ReadMessage(3)))
+  {
+    if (resp->getChannelID() == CHANNEL_STREAM)
+    {
+      if (resp->getOpCodeID() == VDR_STREAM_CHANGE)
+      {
+        StreamChange(resp);
+        if (!m_startup)
+        {
+          DemuxPacket* pkt  = PVR->AllocateDemuxPacket(0);
+          pkt->iStreamId    = DMX_SPECIALID_STREAMCHANGE;
+          delete resp;
+          return pkt;
+        }
+        else
+          m_startup = false;
+      }
+      else if (resp->getOpCodeID() == VDR_STREAM_STATUS)
+        StreamStatus(resp);
+      else if (resp->getOpCodeID() == VDR_STREAM_SIGNALINFO)
+        StreamSignalInfo(resp);
+      else if (resp->getOpCodeID() == VDR_STREAM_CONTENTINFO)
+      {
+        StreamContentInfo(resp);
+        DemuxPacket* pkt = PVR->AllocateDemuxPacket(sizeof(PVR_STREAMPROPS));
+        memcpy(pkt->pData, &m_Streams, sizeof(PVR_STREAMPROPS));
+        pkt->iStreamId  = DMX_SPECIALID_STREAMINFO;
+        pkt->iSize      = sizeof(PVR_STREAMPROPS);
+        delete resp;
+        return pkt;
+      }
+      else if (resp->getOpCodeID() == VDR_STREAM_MUXPKT)
+      {
+        void   *bin       = resp->getUserData();
+        size_t  binlen    = resp->getUserDataLength();
+
+        DemuxPacket* pkt = PVR->AllocateDemuxPacket(binlen);
+
+        memcpy(pkt->pData, bin, binlen);
+
+        pkt->iSize      = binlen;
+        pkt->duration   = (double)resp->getDuration() * DVD_TIME_BASE / 1000000;
+        pkt->dts        = (double)resp->getDTS() * DVD_TIME_BASE / 1000000;
+        pkt->pts        = (double)resp->getPTS() * DVD_TIME_BASE / 1000000;
+        pkt->iStreamId  = -1;
+        for(int i = 0; i < m_Streams.nstreams; i++)
+        {
+          if(m_Streams.stream[i].physid == (int)resp->getStreamID())
+          {
+            pkt->iStreamId = i;
+            break;
+          }
+        }
+
+        free(bin);
+        delete resp;
+        return pkt;
+      }
+    }
+
+    break;
+  }
+
+  if (resp)
+  {
+    delete resp;
+    return PVR->AllocateDemuxPacket(0);
+  }
+  return NULL;
+}
+
+bool cVNSIDemux::SwitchChannel(const PVR_CHANNEL &channelinfo)
+{
+  XBMC->Log(LOG_DEBUG, "changing to channel %d", channelinfo.number);
+
+  cRequestPacket vrp;
+  if (!vrp.init(VDR_CHANNELSTREAM_OPEN) || !vrp.add_U32(channelinfo.number) || !m_session.ReadSuccess(&vrp))
+  {
+    XBMC->Log(LOG_ERROR, "cVNSIDemux::SetChannel - failed to set channel");
+  }
+  else
+  {
+    m_channel           = channelinfo.number;
+    m_Streams.nstreams  = 0;
+    m_startup           = true;
+    while (m_Streams.nstreams == 0 && m_StatusCount == 0)
+    {
+      DemuxPacket* pkg = Read();
+      if(!pkg)
+        return false;
+      PVR->FreeDemuxPacket(pkg);
+    }
+    return true;
+  }
+  return false;
+}
+
+bool cVNSIDemux::GetSignalStatus(PVR_SIGNALQUALITY &qualityinfo)
+{
+  if (m_Quality.fe_name.IsEmpty())
+    return false;
+
+  strncpy(qualityinfo.frontend_name, m_Quality.fe_name.c_str(), sizeof(qualityinfo.frontend_name));
+  strncpy(qualityinfo.frontend_status, m_Quality.fe_status.c_str(), sizeof(qualityinfo.frontend_status));
+  qualityinfo.signal = (uint16_t)m_Quality.fe_signal;
+  qualityinfo.snr = (uint16_t)m_Quality.fe_snr;
+  qualityinfo.ber = (uint32_t)m_Quality.fe_ber;
+  qualityinfo.unc = (uint32_t)m_Quality.fe_unc;
+  qualityinfo.video_bitrate = 0;
+  qualityinfo.audio_bitrate = 0;
+  qualityinfo.dolby_bitrate = 0;
+
+  return true;
+}
+
+void cVNSIDemux::StreamChange(cResponsePacket *resp)
+{
+  m_Streams.nstreams = 0;
+
+  while (!resp->end())
+  {
+    uint32_t    index = resp->extract_U32();
+    const char* type  = resp->extract_String();
+
+    DEVDBG("cVNSIDemux::StreamChange - id: %d, type: %s", index, type);
+
+    m_Streams.stream[m_Streams.nstreams].fpsscale         = 0;
+    m_Streams.stream[m_Streams.nstreams].fpsrate          = 0;
+    m_Streams.stream[m_Streams.nstreams].height           = 0;
+    m_Streams.stream[m_Streams.nstreams].width            = 0;
+    m_Streams.stream[m_Streams.nstreams].aspect           = 0.0;
+
+    m_Streams.stream[m_Streams.nstreams].channels         = 0;
+    m_Streams.stream[m_Streams.nstreams].samplerate       = 0;
+    m_Streams.stream[m_Streams.nstreams].blockalign       = 0;
+    m_Streams.stream[m_Streams.nstreams].bitrate          = 0;
+    m_Streams.stream[m_Streams.nstreams].bits_per_sample  = 0;
+
+    if(!strcmp(type, "AC3"))
+    {
+      const char *language = resp->extract_String();
+
+      m_Streams.stream[m_Streams.nstreams].id         = m_Streams.nstreams;
+      m_Streams.stream[m_Streams.nstreams].physid     = index;
+      m_Streams.stream[m_Streams.nstreams].codec_type = CODEC_TYPE_AUDIO;
+      m_Streams.stream[m_Streams.nstreams].codec_id   = CODEC_ID_AC3;
+      m_Streams.stream[m_Streams.nstreams].language[0]= language[0];
+      m_Streams.stream[m_Streams.nstreams].language[1]= language[1];
+      m_Streams.stream[m_Streams.nstreams].language[2]= language[2];
+      m_Streams.stream[m_Streams.nstreams].language[3]= 0;
+      m_Streams.stream[m_Streams.nstreams].identifier = -1;
+      m_Streams.nstreams++;
+    }
+    else if(!strcmp(type, "MPEG2AUDIO"))
+    {
+      const char *language = resp->extract_String();
+
+      m_Streams.stream[m_Streams.nstreams].id         = m_Streams.nstreams;
+      m_Streams.stream[m_Streams.nstreams].physid     = index;
+      m_Streams.stream[m_Streams.nstreams].codec_type = CODEC_TYPE_AUDIO;
+      m_Streams.stream[m_Streams.nstreams].codec_id   = CODEC_ID_MP2;
+      m_Streams.stream[m_Streams.nstreams].language[0]= language[0];
+      m_Streams.stream[m_Streams.nstreams].language[1]= language[1];
+      m_Streams.stream[m_Streams.nstreams].language[2]= language[2];
+      m_Streams.stream[m_Streams.nstreams].language[3]= 0;
+      m_Streams.stream[m_Streams.nstreams].identifier = -1;
+      m_Streams.nstreams++;
+    }
+    else if(!strcmp(type, "AAC"))
+    {
+      const char *language = resp->extract_String();
+
+      m_Streams.stream[m_Streams.nstreams].id         = m_Streams.nstreams;
+      m_Streams.stream[m_Streams.nstreams].physid     = index;
+      m_Streams.stream[m_Streams.nstreams].codec_type = CODEC_TYPE_AUDIO;
+      m_Streams.stream[m_Streams.nstreams].codec_id   = CODEC_ID_AAC;
+      m_Streams.stream[m_Streams.nstreams].language[0]= language[0];
+      m_Streams.stream[m_Streams.nstreams].language[1]= language[1];
+      m_Streams.stream[m_Streams.nstreams].language[2]= language[2];
+      m_Streams.stream[m_Streams.nstreams].language[3]= 0;
+      m_Streams.stream[m_Streams.nstreams].identifier = -1;
+      m_Streams.nstreams++;
+    }
+    else if(!strcmp(type, "DTS"))
+    {
+      const char *language = resp->extract_String();
+
+      m_Streams.stream[m_Streams.nstreams].id         = m_Streams.nstreams;
+      m_Streams.stream[m_Streams.nstreams].physid     = index;
+      m_Streams.stream[m_Streams.nstreams].codec_type = CODEC_TYPE_AUDIO;
+      m_Streams.stream[m_Streams.nstreams].codec_id   = CODEC_ID_DTS;
+      m_Streams.stream[m_Streams.nstreams].language[0]= language[0];
+      m_Streams.stream[m_Streams.nstreams].language[1]= language[1];
+      m_Streams.stream[m_Streams.nstreams].language[2]= language[2];
+      m_Streams.stream[m_Streams.nstreams].language[3]= 0;
+      m_Streams.stream[m_Streams.nstreams].identifier = -1;
+      m_Streams.nstreams++;
+    }
+    else if(!strcmp(type, "EAC3"))
+    {
+      const char *language = resp->extract_String();
+
+      m_Streams.stream[m_Streams.nstreams].id         = m_Streams.nstreams;
+      m_Streams.stream[m_Streams.nstreams].physid     = index;
+      m_Streams.stream[m_Streams.nstreams].codec_type = CODEC_TYPE_AUDIO;
+      m_Streams.stream[m_Streams.nstreams].codec_id   = CODEC_ID_EAC3;
+      m_Streams.stream[m_Streams.nstreams].language[0]= language[0];
+      m_Streams.stream[m_Streams.nstreams].language[1]= language[1];
+      m_Streams.stream[m_Streams.nstreams].language[2]= language[2];
+      m_Streams.stream[m_Streams.nstreams].language[3]= 0;
+      m_Streams.stream[m_Streams.nstreams].identifier = -1;
+      m_Streams.nstreams++;
+    }
+    else if(!strcmp(type, "MPEG2VIDEO"))
+    {
+      m_Streams.stream[m_Streams.nstreams].id         = m_Streams.nstreams;
+      m_Streams.stream[m_Streams.nstreams].physid     = index;
+      m_Streams.stream[m_Streams.nstreams].codec_type = CODEC_TYPE_VIDEO;
+      m_Streams.stream[m_Streams.nstreams].codec_id   = CODEC_ID_MPEG2VIDEO;
+      m_Streams.stream[m_Streams.nstreams].fpsscale   = resp->extract_U32();
+      m_Streams.stream[m_Streams.nstreams].fpsrate    = resp->extract_U32();
+      m_Streams.stream[m_Streams.nstreams].height     = resp->extract_U32();
+      m_Streams.stream[m_Streams.nstreams].width      = resp->extract_U32();
+      m_Streams.stream[m_Streams.nstreams].aspect     = resp->extract_Double();
+      m_Streams.stream[m_Streams.nstreams].language[0]= 0;
+      m_Streams.stream[m_Streams.nstreams].language[1]= 0;
+      m_Streams.stream[m_Streams.nstreams].language[2]= 0;
+      m_Streams.stream[m_Streams.nstreams].language[3]= 0;
+      m_Streams.stream[m_Streams.nstreams].identifier = -1;
+      m_Streams.nstreams++;
+    }
+    else if(!strcmp(type, "H264"))
+    {
+      m_Streams.stream[m_Streams.nstreams].id         = m_Streams.nstreams;
+      m_Streams.stream[m_Streams.nstreams].physid     = index;
+      m_Streams.stream[m_Streams.nstreams].codec_type = CODEC_TYPE_VIDEO;
+      m_Streams.stream[m_Streams.nstreams].codec_id   = CODEC_ID_H264;
+      m_Streams.stream[m_Streams.nstreams].fpsscale   = resp->extract_U32();
+      m_Streams.stream[m_Streams.nstreams].fpsrate    = resp->extract_U32();
+      m_Streams.stream[m_Streams.nstreams].height     = resp->extract_U32();
+      m_Streams.stream[m_Streams.nstreams].width      = resp->extract_U32();
+      m_Streams.stream[m_Streams.nstreams].aspect     = resp->extract_Double();
+      m_Streams.stream[m_Streams.nstreams].language[0]= 0;
+      m_Streams.stream[m_Streams.nstreams].language[1]= 0;
+      m_Streams.stream[m_Streams.nstreams].language[2]= 0;
+      m_Streams.stream[m_Streams.nstreams].language[3]= 0;
+      m_Streams.stream[m_Streams.nstreams].identifier = -1;
+      m_Streams.nstreams++;
+    }
+    else if(!strcmp(type, "DVBSUB"))
+    {
+      const char *language    = resp->extract_String();
+      uint32_t composition_id = resp->extract_U32();
+      uint32_t ancillary_id   = resp->extract_U32();
+
+      m_Streams.stream[m_Streams.nstreams].id         = m_Streams.nstreams;
+      m_Streams.stream[m_Streams.nstreams].physid     = index;
+      m_Streams.stream[m_Streams.nstreams].codec_type = CODEC_TYPE_SUBTITLE;
+      m_Streams.stream[m_Streams.nstreams].codec_id   = CODEC_ID_DVB_SUBTITLE;
+      m_Streams.stream[m_Streams.nstreams].language[0]= language[0];
+      m_Streams.stream[m_Streams.nstreams].language[1]= language[1];
+      m_Streams.stream[m_Streams.nstreams].language[2]= language[2];
+      m_Streams.stream[m_Streams.nstreams].language[3]= 0;
+      m_Streams.stream[m_Streams.nstreams].identifier = (composition_id & 0xffff) | ((ancillary_id & 0xffff) << 16);
+      m_Streams.nstreams++;
+    }
+    else if(!strcmp(type, "TEXTSUB"))
+    {
+      const char *language = resp->extract_String();
+
+      m_Streams.stream[m_Streams.nstreams].id         = m_Streams.nstreams;
+      m_Streams.stream[m_Streams.nstreams].physid     = index;
+      m_Streams.stream[m_Streams.nstreams].codec_type = CODEC_TYPE_SUBTITLE;
+      m_Streams.stream[m_Streams.nstreams].codec_id   = CODEC_ID_TEXT;
+      m_Streams.stream[m_Streams.nstreams].language[0]= language[0];
+      m_Streams.stream[m_Streams.nstreams].language[1]= language[1];
+      m_Streams.stream[m_Streams.nstreams].language[2]= language[2];
+      m_Streams.stream[m_Streams.nstreams].language[3]= 0;
+      m_Streams.stream[m_Streams.nstreams].identifier = -1;
+      m_Streams.nstreams++;
+    }
+    else if(!strcmp(type, "TELETEXT"))
+    {
+      m_Streams.stream[m_Streams.nstreams].id         = m_Streams.nstreams;
+      m_Streams.stream[m_Streams.nstreams].physid     = index;
+      m_Streams.stream[m_Streams.nstreams].codec_type = CODEC_TYPE_SUBTITLE;
+      m_Streams.stream[m_Streams.nstreams].codec_id   = CODEC_ID_DVB_TELETEXT;
+      m_Streams.stream[m_Streams.nstreams].language[0]= 0;
+      m_Streams.stream[m_Streams.nstreams].language[1]= 0;
+      m_Streams.stream[m_Streams.nstreams].language[2]= 0;
+      m_Streams.stream[m_Streams.nstreams].language[3]= 0;
+      m_Streams.stream[m_Streams.nstreams].identifier = -1;
+      m_Streams.nstreams++;
+    }
+
+    if (m_Streams.nstreams >= PVR_STREAM_MAX_STREAMS)
+    {
+      XBMC->Log(LOG_ERROR, "cVNSIDemux::StreamChange - max amount of streams reached");
+      break;
+    }
+  }
+}
+
+void cVNSIDemux::StreamStatus(cResponsePacket *resp)
+{
+  const char* status = resp->extract_String();
+  if(status == NULL)
+    m_Status = "";
+  else
+  {
+    m_StatusCount++;
+    m_Status = status;
+    XBMC->Log(LOG_DEBUG, "cVNSIDemux::StreamStatus - %s", status);
+    XBMC->QueueNotification(QUEUE_INFO, status);
+  }
+}
+
+void cVNSIDemux::StreamSignalInfo(cResponsePacket *resp)
+{
+  m_Quality.fe_name   = resp->extract_String();
+  m_Quality.fe_status = resp->extract_String();
+  m_Quality.fe_snr    = resp->extract_U32();
+  m_Quality.fe_signal = resp->extract_U32();
+  m_Quality.fe_ber    = resp->extract_U32();
+  m_Quality.fe_unc    = resp->extract_U32();
+}
+
+void cVNSIDemux::StreamContentInfo(cResponsePacket *resp)
+{
+  for (int i = 0; i < m_Streams.nstreams && !resp->end(); i++)
+  {
+    uint32_t index = resp->extract_U32();
+    if (index == m_Streams.stream[i].physid)
+    {
+      if (m_Streams.stream[i].codec_type == CODEC_TYPE_AUDIO)
+      {
+        const char *language = resp->extract_String();
+        m_Streams.stream[i].channels          = resp->extract_U32();
+        m_Streams.stream[i].samplerate        = resp->extract_U32();
+        m_Streams.stream[i].blockalign        = resp->extract_U32();
+        m_Streams.stream[i].bitrate           = resp->extract_U32();
+        m_Streams.stream[i].bits_per_sample   = resp->extract_U32();
+        m_Streams.stream[i].language[0]       = language[0];
+        m_Streams.stream[i].language[1]       = language[1];
+        m_Streams.stream[i].language[2]       = language[2];
+        m_Streams.stream[i].language[3]       = 0;
+      }
+      else if (m_Streams.stream[i].codec_type == CODEC_TYPE_VIDEO)
+      {
+        m_Streams.stream[i].fpsscale         = resp->extract_U32();
+        m_Streams.stream[i].fpsrate          = resp->extract_U32();
+        m_Streams.stream[i].height           = resp->extract_U32();
+        m_Streams.stream[i].width            = resp->extract_U32();
+        m_Streams.stream[i].aspect           = resp->extract_Double();
+      }
+      else if (m_Streams.stream[i].codec_type == CODEC_TYPE_SUBTITLE)
+      {
+        const char *language    = resp->extract_String();
+        uint32_t composition_id = resp->extract_U32();
+        uint32_t ancillary_id   = resp->extract_U32();
+        m_Streams.stream[i].identifier = (composition_id & 0xffff) | ((ancillary_id & 0xffff) << 16);
+        m_Streams.stream[i].language[0]= language[0];
+        m_Streams.stream[i].language[1]= language[1];
+        m_Streams.stream[i].language[2]= language[2];
+        m_Streams.stream[i].language[3]= 0;
+      }
+    }
+  }
+}
diff --git a/xbmc/pvrclients/vdr-vnsi/VNSIDemux.h b/xbmc/pvrclients/vdr-vnsi/VNSIDemux.h
new file mode 100644 (file)
index 0000000..dc563ef
--- /dev/null
@@ -0,0 +1,68 @@
+#pragma once
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "client.h"
+#include "VNSISession.h"
+#include "thread.h"
+
+class cResponsePacket;
+
+struct SQuality
+{
+  CStdString  fe_name;
+  CStdString  fe_status;
+  uint32_t    fe_snr;
+  uint32_t    fe_signal;
+  uint32_t    fe_ber;
+  uint32_t    fe_unc;
+};
+
+class cVNSIDemux
+{
+public:
+  cVNSIDemux();
+  ~cVNSIDemux();
+
+  bool Open(const PVR_CHANNEL &channelinfo);
+  void Close();
+  bool GetStreamProperties(PVR_STREAMPROPS* props);
+  void Abort();
+  DemuxPacket* Read();
+  bool SwitchChannel(const PVR_CHANNEL &channelinfo);
+  int CurrentChannel() { return m_channel; }
+  bool GetSignalStatus(PVR_SIGNALQUALITY &qualityinfo);
+
+protected:
+  void StreamChange(cResponsePacket *resp);
+  void StreamStatus(cResponsePacket *resp);
+  void StreamSignalInfo(cResponsePacket *resp);
+  void StreamContentInfo(cResponsePacket *resp);
+
+private:
+  bool            m_startup;
+  cVNSISession    m_session;
+  int             m_channel;
+  int             m_StatusCount;
+  CStdString      m_Status;
+  PVR_STREAMPROPS m_Streams;
+  SQuality        m_Quality;
+};
diff --git a/xbmc/pvrclients/vdr-vnsi/VNSIRecording.cpp b/xbmc/pvrclients/vdr-vnsi/VNSIRecording.cpp
new file mode 100644 (file)
index 0000000..265b603
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "VNSIRecording.h"
+#include <limits.h>
+#include "tools.h"
+#include "responsepacket.h"
+#include "requestpacket.h"
+#include "vdrcommand.h"
+
+#define SEEK_POSSIBLE 0x10 // flag used to check if protocol allows seeks
+
+cVNSIRecording::cVNSIRecording()
+{
+}
+
+cVNSIRecording::~cVNSIRecording()
+{
+  Close();
+}
+
+bool cVNSIRecording::Open(CStdString path)
+{
+  bool ret = false;
+
+  if(!m_session.Open(g_szHostname, g_iPort, g_iConnectTimeout, "XBMC Recording stream receiver"))
+    return ret;
+
+  cRequestPacket vrp;
+  if (!vrp.init(VDR_RECSTREAM_OPEN) ||
+      !vrp.add_String(path))
+  {
+    return ret;
+  }
+
+  cResponsePacket* vresp = m_session.ReadResult(&vrp);
+  if (!vresp)
+    return ret;
+
+  uint32_t returnCode = vresp->extract_U32();
+  if (returnCode == VDR_RET_OK)
+  {
+    m_currentPlayingRecordFrames    = vresp->extract_U32();
+    m_currentPlayingRecordBytes     = vresp->extract_U64();
+    m_currentPlayingRecordPosition  = 0;
+
+    ret = true;
+  }
+  else
+  {
+    XBMC->Log(LOG_ERROR, "cVNSIDemux::Open - Can't open recording %s", path.c_str());
+    ret = false;
+  }
+
+  delete vresp;
+  return ret;
+}
+
+void cVNSIRecording::Close()
+{
+  cRequestPacket vrp;
+  vrp.init(VDR_RECSTREAM_CLOSE);
+  m_session.ReadSuccess(&vrp);
+  m_session.Close();
+}
+
+int cVNSIRecording::Read(unsigned char* buf, int buf_size)
+{
+  if (m_currentPlayingRecordPosition >= m_currentPlayingRecordBytes)
+    return 0;
+
+  cRequestPacket vrp;
+  if (!vrp.init(VDR_RECSTREAM_GETBLOCK) ||
+      !vrp.add_U64(m_currentPlayingRecordPosition) ||
+      !vrp.add_U32(buf_size))
+  {
+    return 0;
+  }
+
+  if (!IsClientConnected())
+    return -1;
+
+  cResponsePacket* vresp = m_session.ReadResult(&vrp);
+  if (!vresp)
+    return 0;
+
+  uint32_t length = vresp->getUserDataLength();
+  uint8_t *data   = vresp->getUserData();
+  if (length > buf_size)
+  {
+    XBMC->Log(LOG_ERROR, "cVNSIRecording::Read: PANIC - Received more bytes as requested");
+    free(data);
+    delete vresp;
+    return 0;
+  }
+
+  memcpy(buf, data, length);
+  m_currentPlayingRecordPosition += length;
+  free(data);
+  delete vresp;
+  return length;
+}
+
+long long cVNSIRecording::Seek(long long pos, int whence)
+{
+  int64_t nextPos = m_currentPlayingRecordPosition;
+
+  switch (whence)
+  {
+    case SEEK_SET:
+      nextPos = pos;
+      break;
+
+    case SEEK_CUR:
+      nextPos += pos;
+      break;
+
+    case SEEK_END:
+      if (m_currentPlayingRecordBytes)
+        nextPos = m_currentPlayingRecordBytes - pos;
+      else
+        return -1;
+
+      break;
+
+    case SEEK_POSSIBLE:
+      return 1;
+
+    default:
+      return -1;
+  }
+
+  if (nextPos >= m_currentPlayingRecordBytes)
+  {
+    return 0;
+  }
+
+  m_currentPlayingRecordPosition = nextPos;
+
+  return m_currentPlayingRecordPosition;
+}
+
+long long cVNSIRecording::Position(void)
+{
+  return m_currentPlayingRecordPosition;
+}
+
+long long cVNSIRecording::Length(void)
+{
+  return m_currentPlayingRecordBytes;
+}
+
diff --git a/xbmc/pvrclients/vdr-vnsi/VNSIRecording.h b/xbmc/pvrclients/vdr-vnsi/VNSIRecording.h
new file mode 100644 (file)
index 0000000..21fdb05
--- /dev/null
@@ -0,0 +1,49 @@
+#pragma once
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "client.h"
+#include "VNSISession.h"
+#include "thread.h"
+
+class cResponsePacket;
+
+class cVNSIRecording
+{
+public:
+  cVNSIRecording();
+  ~cVNSIRecording();
+
+  bool Open(CStdString path);
+  void Close();
+
+  int Read(unsigned char* buf, int buf_size);
+  long long Seek(long long pos, int whence);
+  long long Position(void);
+  long long Length(void);
+
+private:
+  cVNSISession    m_session;
+  uint64_t        m_currentPlayingRecordBytes;
+  uint32_t        m_currentPlayingRecordFrames;
+  uint64_t        m_currentPlayingRecordPosition;
+
+};
diff --git a/xbmc/pvrclients/vdr-vnsi/VNSISession.cpp b/xbmc/pvrclients/vdr-vnsi/VNSISession.cpp
new file mode 100644 (file)
index 0000000..7ecc10d
--- /dev/null
@@ -0,0 +1,482 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "VNSISession.h"
+#include "client.h"
+#ifdef _MSC_VER
+#include <winsock2.h>
+#define SHUT_RDWR SD_BOTH
+#define ETIMEDOUT WSAETIMEDOUT
+#else
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#endif
+
+#include "responsepacket.h"
+#include "requestpacket.h"
+#include "vdrcommand.h"
+#include "tools.h"
+
+/* Needed on Mac OS/X */
+#ifndef SOL_TCP
+#define SOL_TCP IPPROTO_TCP
+#endif
+using namespace std;
+
+cVNSISession::cVNSISession()
+  : m_fd(INVALID_SOCKET)
+  , m_protocol(0)
+  , m_queue_size(1000)
+{
+}
+
+cVNSISession::~cVNSISession()
+{
+  Close();
+}
+
+void cVNSISession::Abort()
+{
+  shutdown(m_fd, SHUT_RDWR);
+}
+
+void cVNSISession::Close()
+{
+  if(m_fd != INVALID_SOCKET)
+  {
+    closesocket(m_fd);
+    m_fd = INVALID_SOCKET;
+  }
+}
+
+bool cVNSISession::Open(CStdString hostname, int port, long timeout, const char *name)
+{
+  struct hostent hostbuf, *hp;
+  int herr, fd, r, res, err;
+  struct sockaddr_in6 in6;
+  struct sockaddr_in in;
+  socklen_t errlen  = sizeof(int);
+  size_t hstbuflen  = 1024;
+  char *tmphstbuf   = (char*)malloc(hstbuflen);
+
+  if (port == 0)
+    port = 34890;
+
+  while((res = gethostbyname_r(hostname.c_str(), &hostbuf, tmphstbuf, hstbuflen, &hp, &herr)) == ERANGE)
+  {
+    hstbuflen *= 2;
+    tmphstbuf = (char*)realloc(tmphstbuf, hstbuflen);
+  }
+
+  if(res != 0)
+  {
+    XBMC->Log(LOG_ERROR, "cVNSISession::Open - Resolver internal error");
+    free(tmphstbuf);
+    return false;
+  }
+  else if(herr != 0)
+  {
+    switch(herr)
+    {
+      case HOST_NOT_FOUND:
+        XBMC->Log(LOG_ERROR, "cVNSISession::Open - The specified host is unknown");
+        break;
+      case NO_ADDRESS:
+        XBMC->Log(LOG_ERROR, "cVNSISession::Open - The requested name is valid but does not have an IP address");
+        break;
+      case NO_RECOVERY:
+        XBMC->Log(LOG_ERROR, "cVNSISession::Open - A non-recoverable name server error occurred");
+        break;
+      case TRY_AGAIN:
+        XBMC->Log(LOG_ERROR, "cVNSISession::Open - A temporary error occurred on an authoritative name server");
+        break;
+      default:
+        XBMC->Log(LOG_ERROR, "cVNSISession::Open - Unknown error");
+        break;
+    }
+
+    free(tmphstbuf);
+    return false;
+  }
+  else if(hp == NULL)
+  {
+    XBMC->Log(LOG_ERROR, "cVNSISession::Open - Resolver internal error");
+    free(tmphstbuf);
+    return false;
+  }
+
+  fd = socket(hp->h_addrtype, SOCK_STREAM, 0);
+  if (fd == -1)
+  {
+    XBMC->Log(LOG_ERROR, "cVNSISession::Open - Unable to create socket: %s", strerror(errno));
+    free(tmphstbuf);
+    return false;
+  }
+
+  /**
+   * Switch to nonblocking
+   */
+  fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
+
+  switch(hp->h_addrtype)
+  {
+    case AF_INET:
+      memset(&in, 0, sizeof(in));
+      in.sin_family = AF_INET;
+      in.sin_port = htons(port);
+      memcpy(&in.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
+      r = connect(fd, (struct sockaddr *)&in, sizeof(struct sockaddr_in));
+      break;
+
+    case AF_INET6:
+      memset(&in6, 0, sizeof(in6));
+      in6.sin6_family = AF_INET6;
+      in6.sin6_port = htons(port);
+      memcpy(&in6.sin6_addr, hp->h_addr_list[0], sizeof(struct in6_addr));
+      r = connect(fd, (struct sockaddr *)&in, sizeof(struct sockaddr_in6));
+      break;
+
+    default:
+      XBMC->Log(LOG_ERROR, "cVNSISession::Open - Invalid protocol family");
+      free(tmphstbuf);
+      return false;
+  }
+
+  free(tmphstbuf);
+
+  if (r == -1)
+  {
+    if (errno == EINPROGRESS)
+    {
+      struct pollfd pfd;
+
+      pfd.fd = fd;
+      pfd.events = POLLOUT;
+      pfd.revents = 0;
+
+      r = poll(&pfd, 1, timeout*1000);
+      if (r == 0) /* Timeout */
+        XBMC->Log(LOG_ERROR, "Connection attempt timed out %i", timeout);
+
+      if (r == -1)
+      {
+        XBMC->Log(LOG_ERROR, "poll() error: %s", strerror(errno));
+        return false;
+      }
+
+      getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&err, &errlen);
+    }
+    else
+    {
+      err = errno;
+    }
+  }
+  else
+  {
+    err = 0;
+  }
+
+  if (err != 0)
+  {
+    XBMC->Log(LOG_ERROR, "%s", strerror(err));
+    return false;
+  }
+
+  fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_NONBLOCK);
+
+  int val = 1;
+  setsockopt(fd, SOL_TCP, TCP_NODELAY, &val, sizeof(val));
+
+  try
+  {
+    m_fd = fd;
+    if (m_fd == INVALID_SOCKET)
+      throw "Can't connect to VSNI Server";
+
+    cRequestPacket vrp;
+    if (!vrp.init(VDR_LOGIN))                 throw "Can't init cRequestPacket";
+    if (!vrp.add_U32(VNSIProtocolVersion))    throw "Can't add protocol version to RequestPacket";
+    if (!vrp.add_U8(false))                   throw "Can't add netlog flag";
+    if (name && strlen(name) > 0)
+      if (!vrp.add_String(name))                throw "Can't add client name to RequestPacket";
+    else
+      if (!vrp.add_String("XBMC Media Center")) throw "Can't add client name to RequestPacket";
+
+    // read welcome
+    cResponsePacket* vresp = ReadResult(&vrp);
+    if (!vresp)
+      throw "failed to read greeting from server";
+
+    uint32_t    protocol      = vresp->extract_U32();
+    uint32_t    vdrTime       = vresp->extract_U32();
+    int32_t     vdrTimeOffset = vresp->extract_S32();
+    const char *ServerName    = vresp->extract_String();
+    const char *ServerVersion = vresp->extract_String();
+
+    m_server    = ServerName;
+    m_version   = ServerVersion;
+    m_protocol  = protocol;
+
+    if (!name || strlen(name) <= 0)
+      XBMC->Log(LOG_NOTICE, "Logged in at '%lu+%lu' to '%s' Version: '%s' with protocol version '%lu'", vdrTime, vdrTimeOffset, ServerName, ServerVersion, protocol);
+
+    delete vresp;
+  }
+  catch (const char * str)
+  {
+    XBMC->Log(LOG_ERROR, "cVNSISession::Open - %s", str);
+    close(m_fd);
+    m_fd = INVALID_SOCKET;
+    return false;
+  }
+
+  return true;
+}
+
+cResponsePacket* cVNSISession::ReadMessage(int timeout)
+{
+  uint32_t channelID;
+  uint32_t requestID;
+  uint32_t userDataLength;
+  uint8_t* userData;
+  uint32_t streamID;
+  uint32_t duration;
+  uint32_t opCodeID;
+  int64_t  dts;
+  int64_t  pts;
+
+  cResponsePacket* vresp = NULL;
+
+  bool readSuccess = readData((uint8_t*)&channelID, sizeof(uint32_t), timeout) > 0;  // 2s timeout atm
+  if (!readSuccess)
+    return NULL;
+
+  // Data was read
+
+  channelID = ntohl(channelID);
+  if (channelID == CHANNEL_REQUEST_RESPONSE)
+  {
+    if (!readData((uint8_t*)&requestID, sizeof(uint32_t))) return vresp;
+    requestID = ntohl(requestID);
+    if (!readData((uint8_t*)&userDataLength, sizeof(uint32_t))) return vresp;
+    userDataLength = ntohl(userDataLength);
+    if (userDataLength > 5000000) return vresp; // how big can these packets get?
+    userData = NULL;
+    if (userDataLength > 0)
+    {
+      userData = (uint8_t*)malloc(userDataLength);
+      if (!userData) return vresp;
+      if (!readData(userData, userDataLength)) return vresp;
+    }
+
+    vresp = new cResponsePacket();
+    vresp->setResponse(requestID, userData, userDataLength);
+    DEVDBG("cVNSISession::ReadMessage() - Rxd a response packet, requestID=%lu, len=%lu", requestID, userDataLength);
+  }
+  else if (channelID == CHANNEL_STREAM)
+  {
+    if (!readData((uint8_t*)&opCodeID, sizeof(uint32_t))) return vresp;
+    opCodeID = ntohl(opCodeID);
+
+    if (!readData((uint8_t*)&streamID, sizeof(uint32_t))) return vresp;
+    streamID = ntohl(streamID);
+
+    if (!readData((uint8_t*)&duration, sizeof(uint32_t))) return vresp;
+    duration = ntohl(duration);
+
+    if (!readData((uint8_t*)&pts, sizeof(int64_t))) return vresp;
+    pts = ntohll(pts);
+
+    if (!readData((uint8_t*)&dts, sizeof(int64_t))) return vresp;
+    dts = ntohll(dts);
+
+    if (!readData((uint8_t*)&userDataLength, sizeof(uint32_t))) return vresp;
+    userDataLength = ntohl(userDataLength);
+    userData = NULL;
+    if (userDataLength > 0)
+    {
+      userData = (uint8_t*)malloc(userDataLength);
+      if (!userData)  return vresp;
+      if (!readData(userData, userDataLength)) return vresp;
+    }
+
+    vresp = new cResponsePacket();
+    vresp->setStream(opCodeID, streamID, duration, dts, pts, userData, userDataLength);
+  }
+  else
+  {
+    XBMC->Log(LOG_ERROR, "cVNSISession::ReadMessage() - Rxd a response packet on channel %lu !!", channelID);
+  }
+
+  return vresp;
+}
+
+bool cVNSISession::SendMessage(cRequestPacket* vrp)
+{
+  if (sendData(vrp->getPtr(), vrp->getLen()) != vrp->getLen())
+    return false;
+
+  return true;
+}
+
+cResponsePacket* cVNSISession::ReadResult(cRequestPacket* vrp, bool sequence)
+{
+  cResponsePacket *pkt = NULL;
+
+  if(!SendMessage(vrp))
+    return NULL;
+
+  std::deque<cResponsePacket*> queue;
+  m_queue.swap(queue);
+
+  while((pkt = ReadMessage()))
+  {
+    if (!pkt)
+      return NULL;
+
+    /* Discard everything other as response packets until it is received */
+    if (pkt->getChannelID() != CHANNEL_REQUEST_RESPONSE)
+    {
+      delete pkt;
+      continue;
+    }
+
+    if(!sequence)
+      break;
+
+    if (pkt->getRequestID() == vrp->getSerial())
+      break;
+
+    queue.push_back(pkt);
+    if(queue.size() >= m_queue_size)
+    {
+      XBMC->Log(LOG_ERROR, "cVNSISession::ReadResult - maximum queue size (%u) reached", m_queue_size);
+      m_queue.swap(queue);
+      return NULL;
+    }
+  }
+
+  m_queue.swap(queue);
+
+  return pkt;
+}
+
+bool cVNSISession::ReadSuccess(cRequestPacket* vrp, bool sequence, std::string action)
+{
+  cResponsePacket *pkt = NULL;
+  if((pkt = ReadResult(vrp, sequence)) == NULL)
+  {
+    DEVDBG("cVNSISession::ReadSuccess - failed to %s", action.c_str());
+    return false;
+  }
+  uint32_t retCode = pkt->extract_U32();
+  delete pkt;
+
+  if(retCode != VDR_RET_OK)
+  {
+    XBMC->Log(LOG_ERROR, "cVNSISession::ReadSuccess - failed with error code '%i' to %s", retCode, action.c_str());
+    return false;
+  }
+  return true;
+}
+
+int cVNSISession::sendData(void* bufR, size_t count)
+{
+  size_t bytes_sent = 0;
+  int this_write;
+  int temp_write;
+
+  unsigned char* buf = (unsigned char*)bufR;
+
+  while (bytes_sent < count)
+  {
+#ifndef WIN32
+    do
+    {
+      temp_write = this_write = write(m_fd, buf, count - bytes_sent);
+    } while ( (this_write < 0) && (errno == EINTR) );
+#else
+    do
+    {
+      temp_write = this_write = send(m_fd,(char*) buf, count- bytes_sent,0);
+    } while ( (this_write == SOCKET_ERROR) && (WSAGetLastError() == WSAEINTR) );
+#endif
+    if (this_write <= 0)
+    {
+      return(this_write);
+    }
+    bytes_sent += this_write;
+    buf += this_write;
+  }
+
+  return(count);
+}
+
+int cVNSISession::readData(uint8_t* buffer, int totalBytes, int TimeOut)
+{
+  int bytesRead = 0;
+  int thisRead;
+  int readTries = 0;
+  int success;
+  fd_set readSet;
+  struct timeval timeout;
+
+  while(1)
+  {
+    FD_ZERO(&readSet);
+    FD_SET(m_fd, &readSet);
+    timeout.tv_sec = TimeOut;
+    timeout.tv_usec = 0;
+    success = select(m_fd + 1, &readSet, NULL, NULL, &timeout);
+    if (success < 1)
+    {
+      return 0;  // error, or timeout
+    }
+#ifndef WIN32
+    thisRead = read(m_fd, &buffer[bytesRead], totalBytes - bytesRead);
+#else
+    thisRead = recv(m_fd, (char*)&buffer[bytesRead], totalBytes - bytesRead, 0);
+#endif
+    if (!thisRead)
+    {
+      // if read returns 0 then connection is closed
+      // in non-blocking mode if read is called with no data available, it returns -1
+      // and sets errno to EGAGAIN. but we use select so it wouldn't do that anyway.
+      XBMC->Log(LOG_ERROR, "cVNSISession::readData - Detected connection closed");
+      SetClientConnected(false);
+      return -1;
+    }
+    bytesRead += thisRead;
+    if (bytesRead == totalBytes)
+    {
+      return 1;
+    }
+    else
+    {
+      if (++readTries == 100)
+      {
+        XBMC->Log(LOG_ERROR, "cVNSISession::readData - Too many reads");
+        // return 0;
+      }
+    }
+  }
+}
diff --git a/xbmc/pvrclients/vdr-vnsi/VNSISession.h b/xbmc/pvrclients/vdr-vnsi/VNSISession.h
new file mode 100644 (file)
index 0000000..77b24ad
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+#include <deque>
+#include "pvrclient-vdrVNSI_os.h"
+#include "StdString.h"
+
+#include <algorithm>
+#include <vector>
+#include <map>
+
+class cResponsePacket;
+class cRequestPacket;
+
+class cVNSISession
+{
+public:
+  cVNSISession();
+  ~cVNSISession();
+
+  bool              Open(CStdString hostname, int port, long timeout, const char *name = "");
+  void              Close();
+  void              Abort();
+
+  cResponsePacket*  ReadMessage(int timeout = 10);
+  bool              SendMessage(cRequestPacket* vrp);
+  int               sendData(void* bufR, size_t count);
+  int               readData(uint8_t* buffer, int totalBytes, int TimeOut = 2);
+
+  cResponsePacket*  ReadResult(cRequestPacket* vrp, bool sequence = true);
+  bool              ReadSuccess(cRequestPacket* m, bool sequence = true, std::string action = "");
+  int               GetProtocol()   { return m_protocol; }
+  CStdString        GetServerName() { return m_server; }
+  CStdString        GetVersion()    { return m_version; }
+
+private:
+  SOCKET      m_fd;
+  int         m_protocol;
+  CStdString  m_server;
+  CStdString  m_version;
+
+  std::deque<cResponsePacket*> m_queue;
+  const unsigned int    m_queue_size;
+};
diff --git a/xbmc/pvrclients/vdr-vnsi/client.cpp b/xbmc/pvrclients/vdr-vnsi/client.cpp
new file mode 100644 (file)
index 0000000..baa5dbf
--- /dev/null
@@ -0,0 +1,675 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, 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 "VNSIDemux.h"
+#include "VNSIRecording.h"
+#include "VNSIData.h"
+#include "VNSIChannelScan.h"
+
+using namespace std;
+
+bool m_bCreated               = false;
+bool m_connected              = false;
+int  m_retries                = 0;
+ADDON_STATUS m_CurStatus      = STATUS_UNKNOWN;
+int g_clientID                = -1;
+
+/* User adjustable settings are saved here.
+ * Default values are defined inside client.h
+ * and exported to the other source files.
+ */
+CStdString    g_szHostname              = DEFAULT_HOST;
+int           g_iPort                   = DEFAULT_PORT;
+bool          g_bCharsetConv            = DEFAULT_CHARCONV;     ///< Convert VDR's incoming strings to UTF8 character set
+bool          g_bHandleMessages         = DEFAULT_HANDLE_MSG;   ///< Send VDR's OSD status messages to XBMC OSD
+bool          g_bUseRecordingsDir       = DEFAULT_USE_REC_DIR;  ///< Use a normal directory if true for recordings
+CStdString    g_szRecordingsDir         = DEFAULT_REC_DIR;      ///< The path to the recordings directory
+int           g_iConnectTimeout         = DEFAULT_TIMEOUT;      ///< The Socket connection timeout
+int           g_iPriority               = DEFAULT_PRIORITY;     ///< The Priority this client have in response to other clients
+CStdString    g_szUserPath              = "";
+CStdString    g_szClientPath            = "";
+cHelper_libXBMC_addon *XBMC   = NULL;
+cHelper_libXBMC_gui   *GUI    = NULL;
+cHelper_libXBMC_pvr   *PVR    = NULL;
+cVNSIDemux      *VNSIDemuxer       = NULL;
+cVNSIData       *VNSIData          = NULL;
+cVNSIRecording  *VNSIRecording     = NULL;
+
+bool IsClientConnected(bool forceReconnect)
+{
+  if (forceReconnect)
+    m_retries = 0;
+
+  if (!m_connected && m_bCreated)
+  {
+    while (m_retries < 5)
+    {
+      m_retries++;
+      if (VNSIDemuxer)
+      {
+        delete VNSIDemuxer;
+        VNSIDemuxer = NULL;
+      }
+      if (VNSIRecording)
+      {
+        delete VNSIRecording;
+        VNSIRecording = NULL;
+      }
+      if (VNSIData)
+      {
+        XBMC->Log(LOG_NOTICE, "Trying to reconnect to VNSI Server (try %i)", m_retries+1);
+        sleep(2);
+        if (VNSIData->Open(g_szHostname, g_iPort, g_iConnectTimeout))
+        {
+          XBMC->Log(LOG_NOTICE, "Reconnect to VNSI Server succesfull");
+          m_CurStatus = STATUS_OK;
+          m_connected = true;
+          m_retries   = 0;
+          return true;
+        }
+      }
+    }
+  }
+  return m_connected;
+}
+
+void SetClientConnected(bool yesNo)
+{
+  if (yesNo)
+  {
+    m_connected = true;
+  }
+  else
+  {
+    XBMC->Log(LOG_ERROR, "Lost connection to VNSI Server");
+
+    if (VNSIData)
+      VNSIData->Close();
+    if (VNSIDemuxer)
+      VNSIDemuxer->Close();
+    if (VNSIRecording)
+      VNSIRecording->Close();
+
+    m_connected = false;
+    m_CurStatus = STATUS_LOST_CONNECTION;
+  }
+}
+
+extern "C" {
+
+/***********************************************************
+ * Standart AddOn related public library functions
+ ***********************************************************/
+
+ADDON_STATUS Create(void* hdl, void* props)
+{
+  if (!hdl || !props)
+    return STATUS_UNKNOWN;
+
+  PVR_PROPS* pvrprops = (PVR_PROPS*)props;
+
+  XBMC = new cHelper_libXBMC_addon;
+  if (!XBMC->RegisterMe(hdl))
+    return STATUS_UNKNOWN;
+
+  GUI = new cHelper_libXBMC_gui;
+  if (!GUI->RegisterMe(hdl))
+    return STATUS_UNKNOWN;
+
+  PVR = new cHelper_libXBMC_pvr;
+  if (!PVR->RegisterMe(hdl))
+    return STATUS_UNKNOWN;
+
+  XBMC->Log(LOG_DEBUG, "Creating VDR VNSI PVR-Client");
+
+  m_CurStatus    = STATUS_UNKNOWN;
+  g_clientID     = pvrprops->clientID;
+  g_szUserPath   = pvrprops->userpath;
+  g_szClientPath = pvrprops->clientpath;
+
+  /* Read setting "host" from settings.xml */
+  char * buffer;
+  buffer = (char*) malloc (1024);
+  buffer[0] = 0; /* Set the end of string */
+
+  if (XBMC->GetSetting("host", buffer))
+    g_szHostname = buffer;
+  else
+  {
+    /* If setting is unknown fallback to defaults */
+    XBMC->Log(LOG_ERROR, "Couldn't get 'host' setting, falling back to '%s' as default", DEFAULT_HOST);
+    g_szHostname = DEFAULT_HOST;
+  }
+  buffer[0] = 0; /* Set the end of string */
+
+  /* Read setting "port" from settings.xml */
+  if (!XBMC->GetSetting("port", &g_iPort))
+  {
+    /* If setting is unknown fallback to defaults */
+    XBMC->Log(LOG_ERROR, "Couldn't get 'port' setting, falling back to '%i' as default", DEFAULT_PORT);
+    g_iPort = DEFAULT_PORT;
+  }
+
+  /* Read setting "priority" from settings.xml */
+  if (!XBMC->GetSetting("priority", &g_iPriority))
+  {
+    /* If setting is unknown fallback to defaults */
+    XBMC->Log(LOG_ERROR, "Couldn't get 'priority' setting, falling back to %i as default", DEFAULT_PRIORITY);
+    g_iPriority = DEFAULT_PRIORITY;
+  }
+
+  /* Read setting "convertchar" from settings.xml */
+  if (!XBMC->GetSetting("convertchar", &g_bCharsetConv))
+  {
+    /* If setting is unknown fallback to defaults */
+    XBMC->Log(LOG_ERROR, "Couldn't get 'convertchar' setting, falling back to 'false' as default");
+    g_bCharsetConv = DEFAULT_CHARCONV;
+  }
+
+  /* Read setting "timeout" from settings.xml */
+  if (!XBMC->GetSetting("timeout", &g_iConnectTimeout))
+  {
+    /* If setting is unknown fallback to defaults */
+    XBMC->Log(LOG_ERROR, "Couldn't get 'timeout' setting, falling back to %i seconds as default", DEFAULT_TIMEOUT);
+    g_iConnectTimeout = DEFAULT_TIMEOUT;
+  }
+
+  /* Read setting "handlemessages" from settings.xml */
+  if (!XBMC->GetSetting("handlemessages", &g_bHandleMessages))
+  {
+    /* If setting is unknown fallback to defaults */
+    XBMC->Log(LOG_ERROR, "Couldn't get 'handlemessages' setting, falling back to 'true' as default");
+    g_bHandleMessages = DEFAULT_HANDLE_MSG;
+  }
+
+  /* Read setting "usedirectory" from settings.xml */
+  if (!XBMC->GetSetting("usedirectory", &g_bUseRecordingsDir))
+  {
+    /* If setting is unknown fallback to defaults */
+    XBMC->Log(LOG_ERROR, "Couldn't get 'usedirectory' setting, falling back to 'false' as default");
+    g_bUseRecordingsDir = DEFAULT_USE_REC_DIR;
+  }
+
+  if (g_bUseRecordingsDir)
+  {
+    /* Read setting "recordingdir" from settings.xml */
+    buffer = (char*) malloc (2048);
+    buffer[0] = 0; /* Set the end of string */
+
+    if (XBMC->GetSetting("recordingdir", buffer))
+      g_szRecordingsDir = buffer;
+    else
+    {
+      /* If setting is unknown fallback to defaults */
+      XBMC->Log(LOG_ERROR, "Couldn't get 'recordingdir' setting, directory not set");
+      g_szRecordingsDir = DEFAULT_REC_DIR;
+      g_bUseRecordingsDir = false;
+    }
+    free (buffer);
+  }
+
+  VNSIData = new cVNSIData;
+  if (!VNSIData->Open(g_szHostname, g_iPort, g_iConnectTimeout))
+  {
+    m_CurStatus = STATUS_LOST_CONNECTION;
+    return m_CurStatus;
+  }
+
+  VNSIData->EnableStatusInterface(g_bHandleMessages);
+
+  m_CurStatus = STATUS_OK;
+  m_bCreated = true;
+  return m_CurStatus;
+}
+
+ADDON_STATUS GetStatus()
+{
+  return m_CurStatus;
+}
+
+void Destroy()
+{
+  if (m_bCreated)
+  {
+    delete VNSIData;
+    VNSIData = NULL;
+  }
+  m_CurStatus = STATUS_UNKNOWN;
+}
+
+bool HasSettings()
+{
+  return true;
+}
+
+unsigned int GetSettings(StructSetting ***sSet)
+{
+  return 0;
+}
+
+ADDON_STATUS SetSetting(const char *settingName, const void *settingValue)
+{
+  string str = settingName;
+  if (str == "host")
+  {
+    string tmp_sHostname;
+    XBMC->Log(LOG_INFO, "Changed Setting 'host' from %s to %s", g_szHostname.c_str(), (const char*) settingValue);
+    tmp_sHostname = g_szHostname;
+    g_szHostname = (const char*) settingValue;
+    if (tmp_sHostname != g_szHostname)
+      return STATUS_NEED_RESTART;
+  }
+  else if (str == "port")
+  {
+    XBMC->Log(LOG_INFO, "Changed Setting 'port' from %u to %u", g_iPort, *(int*) settingValue);
+    if (g_iPort != *(int*) settingValue)
+    {
+      g_iPort = *(int*) settingValue;
+      return STATUS_NEED_RESTART;
+    }
+  }
+  else if (str == "priority")
+  {
+    XBMC->Log(LOG_INFO, "Changed Setting 'priority' from %u to %u", g_iPriority, *(int*) settingValue);
+    g_iPriority = *(int*) settingValue;
+  }
+  else if (str == "convertchar")
+  {
+    XBMC->Log(LOG_INFO, "Changed Setting 'convertchar' from %u to %u", g_bCharsetConv, *(bool*) settingValue);
+    g_bCharsetConv = *(bool*) settingValue;
+  }
+  else if (str == "timeout")
+  {
+    XBMC->Log(LOG_INFO, "Changed Setting 'timeout' from %u to %u", g_iConnectTimeout, *(int*) settingValue);
+    g_iConnectTimeout = *(int*) settingValue;
+  }
+  else if (str == "handlemessages")
+  {
+    XBMC->Log(LOG_INFO, "Changed Setting 'handlemessages' from %u to %u", g_bHandleMessages, *(bool*) settingValue);
+    g_bHandleMessages = *(bool*) settingValue;
+    if (VNSIData) VNSIData->EnableStatusInterface(g_bHandleMessages);
+  }
+  else if (str == "usedirectory")
+  {
+    XBMC->Log(LOG_INFO, "Changed Setting 'usedirectory' from %u to %u", g_bUseRecordingsDir, *(bool*) settingValue);
+    g_bUseRecordingsDir = *(bool*) settingValue;
+  }
+  else if (str == "recordingdir")
+  {
+    XBMC->Log(LOG_INFO, "Changed Setting 'recordingdir' from %s to %s", g_szRecordingsDir.c_str(), (const char*) settingValue);
+    g_bUseRecordingsDir = (const char*) settingValue;
+  }
+
+  return STATUS_OK;
+}
+
+void Stop()
+{
+}
+
+void FreeSettings()
+{
+
+}
+
+/***********************************************************
+ * PVR Client AddOn specific public library functions
+ ***********************************************************/
+
+PVR_ERROR GetProperties(PVR_SERVERPROPS* props)
+{
+  props->SupportChannelLogo        = false;
+  props->SupportTimeShift          = false;
+  props->SupportEPG                = true;
+  props->SupportRecordings         = true;
+  props->SupportTimers             = true;
+  props->SupportTV                 = true;
+  props->SupportRadio              = true;
+  props->SupportChannelSettings    = false;
+  props->SupportDirector           = false;
+  props->SupportBouquets           = false;
+  props->HandleInputStream         = true;
+  props->HandleDemuxing            = true;
+  if (VNSIData && VNSIData->SupportChannelScan())
+    props->SupportChannelScan      = true;
+  else
+    props->SupportChannelScan      = false;
+
+  return PVR_ERROR_NO_ERROR;
+}
+
+const char * GetBackendName()
+{
+  static CStdString BackendName = VNSIData ? VNSIData->GetServerName() : "unknown";
+  return BackendName.c_str();
+}
+
+const char * GetBackendVersion()
+{
+  static CStdString BackendVersion;
+  if (VNSIData)
+    BackendVersion.Format("%s (Protocol: %i)", VNSIData->GetVersion(), VNSIData->GetProtocol());
+  return BackendVersion.c_str();
+}
+
+const char * GetConnectionString()
+{
+  static CStdString ConnectionString;
+  if (VNSIData)
+    ConnectionString.Format("%s:%i%s", g_szHostname.c_str(), g_iPort, IsClientConnected() ? "" : " (Not connected!)");
+  else
+    ConnectionString.Format("%s:%i (addon error!)", g_szHostname.c_str(), g_iPort);
+  return ConnectionString.c_str();
+}
+
+PVR_ERROR GetDriveSpace(long long *total, long long *used)
+{
+  if (IsClientConnected() && VNSIData && VNSIData->GetDriveSpace(total, used))
+    return PVR_ERROR_NO_ERROR;
+
+  return PVR_ERROR_SERVER_ERROR;
+}
+
+PVR_ERROR GetBackendTime(time_t *localTime, int *gmtOffset)
+{
+  if (IsClientConnected() && VNSIData && VNSIData->GetTime(localTime, gmtOffset))
+    return PVR_ERROR_NO_ERROR;
+
+  return PVR_ERROR_SERVER_ERROR;
+}
+
+PVR_ERROR DialogChannelScan()
+{
+  cVNSIChannelScan scanner;
+  scanner.Open();
+  return PVR_ERROR_NO_ERROR;
+}
+
+/*******************************************/
+/** PVR EPG Functions                     **/
+
+PVR_ERROR RequestEPGForChannel(PVRHANDLE handle, const PVR_CHANNEL &channel, time_t start, time_t end)
+{
+  if (IsClientConnected() && VNSIData && VNSIData->GetEPGForChannel(handle, channel, start, end))
+    return PVR_ERROR_NO_ERROR;
+
+  return PVR_ERROR_SERVER_ERROR;
+}
+
+
+/*******************************************/
+/** PVR Channel Functions                 **/
+
+int GetNumChannels()
+{
+  if (!IsClientConnected(true) || !VNSIData)
+    return 0;
+
+  return VNSIData->GetChannelsCount();
+}
+
+PVR_ERROR RequestChannelList(PVRHANDLE handle, int radio)
+{
+  if (IsClientConnected(true) && VNSIData && VNSIData->GetChannelsList(handle, radio))
+    return PVR_ERROR_NO_ERROR;
+
+  return PVR_ERROR_SERVER_ERROR;
+}
+
+
+/*******************************************/
+/** PVR Timer Functions                   **/
+
+int GetNumTimers(void)
+{
+  if (!IsClientConnected() || !VNSIData)
+    return 0;
+
+  return VNSIData->GetTimersCount();
+}
+
+PVR_ERROR RequestTimerList(PVRHANDLE handle)
+{
+  if (IsClientConnected() && VNSIData && VNSIData->GetTimersList(handle))
+    return PVR_ERROR_NO_ERROR;
+
+  return PVR_ERROR_SERVER_ERROR;
+}
+
+PVR_ERROR AddTimer(const PVR_TIMERINFO &timerinfo)
+{
+  if (!IsClientConnected() || !VNSIData)
+    return PVR_ERROR_SERVER_ERROR;
+
+  return VNSIData->AddTimer(timerinfo);
+}
+
+PVR_ERROR DeleteTimer(const PVR_TIMERINFO &timerinfo, bool force)
+{
+  if (!IsClientConnected() || !VNSIData)
+    return PVR_ERROR_SERVER_ERROR;
+
+  return VNSIData->DeleteTimer(timerinfo, force);
+}
+
+PVR_ERROR RenameTimer(const PVR_TIMERINFO &timerinfo, const char *newname)
+{
+  if (!IsClientConnected() || !VNSIData)
+    return PVR_ERROR_SERVER_ERROR;
+
+  return VNSIData->RenameTimer(timerinfo, newname);
+}
+
+PVR_ERROR UpdateTimer(const PVR_TIMERINFO &timerinfo)
+{
+  if (!IsClientConnected() || !VNSIData)
+    return PVR_ERROR_SERVER_ERROR;
+
+  return VNSIData->UpdateTimer(timerinfo);
+}
+
+
+/*******************************************/
+/** PVR Recording Functions               **/
+
+int GetNumRecordings(void)
+{
+  if (!IsClientConnected() || !VNSIData)
+    return 0;
+
+  return VNSIData->GetRecordingsCount();
+}
+
+PVR_ERROR RequestRecordingsList(PVRHANDLE handle)
+{
+  if (!VNSIData || !IsClientConnected())
+    return PVR_ERROR_SERVER_ERROR;
+
+  return VNSIData->GetRecordingsList(handle);
+}
+
+PVR_ERROR DeleteRecording(const PVR_RECORDINGINFO &recinfo)
+{
+  if (IsClientConnected() && VNSIData && VNSIData->DeleteRecording(VNSIData->GetRecordingPath(recinfo.index)))
+    return PVR_ERROR_NO_ERROR;
+
+  return PVR_ERROR_SERVER_ERROR;
+}
+
+/*******************************************/
+/** PVR Live Stream Functions             **/
+
+bool OpenLiveStream(const PVR_CHANNEL &channelinfo)
+{
+  if (!IsClientConnected(true))
+    return false;
+
+  CloseLiveStream();
+
+  VNSIDemuxer = new cVNSIDemux;
+  return VNSIDemuxer->Open(channelinfo);
+}
+
+void CloseLiveStream()
+{
+  if (IsClientConnected() && VNSIDemuxer)
+  {
+    VNSIDemuxer->Close();
+    delete VNSIDemuxer;
+    VNSIDemuxer = NULL;
+  }
+}
+
+PVR_ERROR GetStreamProperties(PVR_STREAMPROPS* props)
+{
+  if (IsClientConnected() && VNSIDemuxer && VNSIDemuxer->GetStreamProperties(props))
+    return PVR_ERROR_NO_ERROR;
+
+  return PVR_ERROR_SERVER_ERROR;
+}
+
+void DemuxAbort()
+{
+  if (IsClientConnected() && VNSIDemuxer) VNSIDemuxer->Abort();
+}
+
+DemuxPacket* DemuxRead()
+{
+  if (!IsClientConnected())
+    return NULL;
+
+  return VNSIDemuxer->Read();
+}
+
+int GetCurrentClientChannel()
+{
+  if (IsClientConnected() && VNSIDemuxer)
+    return VNSIDemuxer->CurrentChannel();
+
+  return -1;
+}
+
+bool SwitchChannel(const PVR_CHANNEL &channelinfo)
+{
+  if (IsClientConnected() && VNSIDemuxer)
+    return VNSIDemuxer->SwitchChannel(channelinfo);
+
+  return false;
+}
+
+PVR_ERROR SignalQuality(PVR_SIGNALQUALITY &qualityinfo)
+{
+  if (IsClientConnected() && VNSIDemuxer && VNSIDemuxer->GetSignalStatus(qualityinfo))
+    return PVR_ERROR_NO_ERROR;
+
+  return PVR_ERROR_SERVER_ERROR;
+}
+
+
+/*******************************************/
+/** PVR Recording Stream Functions        **/
+
+bool OpenRecordedStream(const PVR_RECORDINGINFO &recinfo)
+{
+  if (!IsClientConnected(true))
+    return false;
+
+  CloseRecordedStream();
+
+  CStdString name = VNSIData->GetRecordingPath(recinfo.index);
+  VNSIRecording = new cVNSIRecording;
+  return VNSIRecording->Open(name);
+}
+
+void CloseRecordedStream(void)
+{
+  if (IsClientConnected() && VNSIRecording)
+  {
+    VNSIRecording->Close();
+    delete VNSIRecording;
+    VNSIRecording = NULL;
+  }
+}
+
+int ReadRecordedStream(unsigned char* buf, int buf_size)
+{
+  if (!IsClientConnected())
+    return -1;
+
+  return VNSIRecording->Read(buf, buf_size);
+}
+
+long long SeekRecordedStream(long long pos, int whence)
+{
+  if (IsClientConnected() && VNSIRecording)
+    return VNSIRecording->Seek(pos, whence);
+
+  return -1;
+}
+
+long long PositionRecordedStream(void)
+{
+  if (IsClientConnected() && VNSIRecording)
+    return VNSIRecording->Position();
+
+  return 0;
+}
+
+long long LengthRecordedStream(void)
+{
+  if (IsClientConnected() && VNSIRecording)
+    return VNSIRecording->Length();
+
+  return 0;
+}
+
+
+
+/** UNUSED API FUNCTIONS */
+PVR_ERROR MenuHook(const PVR_MENUHOOK &menuhook) { return PVR_ERROR_NOT_IMPLEMENTED; }
+int GetNumBouquets() { return 0; }
+PVR_ERROR RequestBouquetsList(PVRHANDLE handle, int radio) { return PVR_ERROR_NOT_IMPLEMENTED; }
+PVR_ERROR DeleteChannel(unsigned int number) { return PVR_ERROR_NOT_IMPLEMENTED; }
+PVR_ERROR RenameChannel(unsigned int number, const char *newname) { return PVR_ERROR_NOT_IMPLEMENTED; }
+PVR_ERROR MoveChannel(unsigned int number, unsigned int newnumber) { return PVR_ERROR_NOT_IMPLEMENTED; }
+PVR_ERROR DialogChannelSettings(const PVR_CHANNEL &channelinfo) { return PVR_ERROR_NOT_IMPLEMENTED; }
+PVR_ERROR DialogAddChannel(const PVR_CHANNEL &channelinfo) { return PVR_ERROR_NOT_IMPLEMENTED; }
+PVR_ERROR RenameRecording(const PVR_RECORDINGINFO &recinfo, const char *newname) { return PVR_ERROR_NOT_IMPLEMENTED; }
+bool HaveCutmarks() { return false; }
+PVR_ERROR RequestCutMarksList(PVRHANDLE handle) { return PVR_ERROR_NOT_IMPLEMENTED; }
+PVR_ERROR AddCutMark(const PVR_CUT_MARK &cutmark) { return PVR_ERROR_NOT_IMPLEMENTED; }
+PVR_ERROR DeleteCutMark(const PVR_CUT_MARK &cutmark) { return PVR_ERROR_NOT_IMPLEMENTED; }
+PVR_ERROR StartCut() { return PVR_ERROR_NOT_IMPLEMENTED; }
+bool SwapLiveTVSecondaryStream() { return false; }
+bool OpenSecondaryStream(const PVR_CHANNEL &channelinfo) { return false; }
+void CloseSecondaryStream() {}
+int ReadSecondaryStream(unsigned char* buf, int buf_size) { return 0; }
+void DemuxReset(){}
+void DemuxFlush(){}
+int ReadLiveStream(unsigned char* buf, int buf_size) { return 0; }
+long long SeekLiveStream(long long pos, int whence) { return -1; }
+long long PositionLiveStream(void) { return -1; }
+long long LengthLiveStream(void) { return -1; }
+const char * GetLiveStreamURL(const PVR_CHANNEL &channelinfo) { return ""; }
+
+}
diff --git a/xbmc/pvrclients/vdr-vnsi/client.h b/xbmc/pvrclients/vdr-vnsi/client.h
new file mode 100644 (file)
index 0000000..f5f9409
--- /dev/null
@@ -0,0 +1,59 @@
+#pragma once
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef CLIENT_H
+#define CLIENT_H
+
+#include "StdString.h"
+#include "../../../addons/library.xbmc.addon/libXBMC_addon.h"
+#include "../../../addons/library.xbmc.gui/libXBMC_gui.h"
+#include "../../../addons/library.xbmc.pvr/libXBMC_pvr.h"
+
+#define DEFAULT_HOST          "127.0.0.1"
+#define DEFAULT_PORT          34890
+#define DEFAULT_CHARCONV      false
+#define DEFAULT_HANDLE_MSG    true
+#define DEFAULT_PRIORITY      99
+#define DEFAULT_TIMEOUT       3
+#define DEFAULT_USE_REC_DIR   false
+#define DEFAULT_REC_DIR       ""
+
+extern bool IsClientConnected(bool forceReconnect = false);
+extern void SetClientConnected(bool yesNo);
+
+extern bool         m_bCreated;
+extern CStdString   g_szHostname;
+extern int          g_iPort;
+extern int          g_iConnectTimeout;
+extern int          g_clientID;
+extern CStdString   g_szUserPath;
+extern CStdString   g_szClientPath;
+extern int          g_iPriority;          ///< The Priority this client have in response to other clients
+extern bool         g_bCharsetConv;       ///< Convert VDR's incoming strings to UTF8 character set
+extern bool         g_bHandleMessages;    ///< Send VDR's OSD status messages to XBMC OSD
+extern bool         g_bUseRecordingsDir;  ///< Use a normal directory if true for recordings
+extern CStdString   g_szRecordingsDir;    ///< The path to the recordings directory
+extern cHelper_libXBMC_addon *XBMC;
+extern cHelper_libXBMC_gui   *GUI;
+extern cHelper_libXBMC_pvr   *PVR;
+
+#endif /* CLIENT_H */
diff --git a/xbmc/pvrclients/vdr-vnsi/linux/os_posix.h b/xbmc/pvrclients/vdr-vnsi/linux/os_posix.h
new file mode 100644 (file)
index 0000000..0adf70d
--- /dev/null
@@ -0,0 +1,93 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef PVRCLIENT_VDR_OS_POSIX_H
+#define PVRCLIENT_VDR_OS_POSIX_H
+
+#define _FILE_OFFSET_BITS 64
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/syscall.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/timeb.h>
+#include <sys/wait.h>
+#include <sys/signal.h>
+#include <sys/resource.h>
+#ifndef __APPLE__
+#include <sys/prctl.h> 
+#endif
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <pthread.h>
+#include <poll.h>
+
+typedef int bool_t;
+typedef int SOCKET;
+
+#ifndef closesocket
+#define closesocket(a) close(a)
+#endif
+
+#define SOCKET_ERROR   (-1)
+#define INVALID_SOCKET (-1)
+#define SD_BOTH SHUT_RDWR
+
+#define LIBTYPE
+#define sock_getlasterror errno
+#define sock_getlasterror_socktimeout (errno == EAGAIN)
+#define console_vprintf vprintf
+#define console_printf printf
+#define THREAD_FUNC_PREFIX void *
+
+#ifndef __STL_CONFIG_H
+template<class T> inline T min(T a, T b) { return a <= b ? a : b; }
+template<class T> inline T max(T a, T b) { return a >= b ? a : b; }
+template<class T> inline int sgn(T a) { return a < 0 ? -1 : a > 0 ? 1 : 0; }
+template<class T> inline void swap(T &a, T &b) { T t = a; a = b; b = t; }
+#endif
+
+#define Sleep(t) usleep(t*1000)
+
+static inline uint64_t getcurrenttime(void)
+{
+       struct timeval t;
+       gettimeofday(&t, NULL);
+       return ((uint64_t)t.tv_sec * 1000) + (t.tv_usec / 1000);
+}
+
+static inline int setsocktimeout(int s, int level, int optname, uint64_t timeout)
+{
+       struct timeval t;
+       t.tv_sec = timeout / 1000;
+       t.tv_usec = (timeout % 1000) * 1000;
+       return setsockopt(s, level, optname, (char *)&t, sizeof(t));
+}
+
+#endif
diff --git a/xbmc/pvrclients/vdr-vnsi/pvrclient-vdrVNSI_os.h b/xbmc/pvrclients/vdr-vnsi/pvrclient-vdrVNSI_os.h
new file mode 100644 (file)
index 0000000..adbf167
--- /dev/null
@@ -0,0 +1,44 @@
+#pragma once
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef PVRCLIENT_VNSI_OS_H
+#define PVRCLIENT_VNSI_OS_H
+
+#if defined(_WIN32) || defined(_WIN64)
+#define __WINDOWS__
+#endif
+
+#if defined(__WINDOWS__)
+#include "windows/os_windows.h"
+#else
+#include "linux/os_posix.h"
+#endif
+
+#if !defined(TRUE)
+#define TRUE 1
+#endif
+
+#if !defined(FALSE)
+#define FALSE 0
+#endif
+
+#endif /* PVRCLIENT_VNSI_OS_H */
diff --git a/xbmc/pvrclients/vdr-vnsi/recordings.cpp b/xbmc/pvrclients/vdr-vnsi/recordings.cpp
new file mode 100644 (file)
index 0000000..1e065ca
--- /dev/null
@@ -0,0 +1,312 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+/*
+ * This code is taken from recordings.c in the Video Disk Recorder ('VDR')
+ */
+
+#include "StdString.h"
+#include "recordings.h"
+#include "client.h"
+
+#define RESUME_NOT_INITIALIZED (-2)
+
+cRecording::cRecording()
+{
+  m_channelName     = NULL;
+  m_aux             = NULL;
+  m_title           = NULL;
+  m_shortText       = NULL;
+  m_description     = NULL;
+  m_fileName        = NULL;
+  m_directory       = NULL;
+  m_framesPerSecond = DEFAULTFRAMESPERSECOND;
+  m_priority        = MAXPRIORITY;
+  m_lifetime        = MAXLIFETIME;
+  m_EventID         = 0;
+  m_StartTime       = 0;
+  m_Duration        = 0;
+  m_TableID         = 0;
+  m_Version         = 0xFF;
+  m_vps             = 0;
+  m_Index           = -1;
+  m_isPesRecording  = false;
+  m_resume          = RESUME_NOT_INITIALIZED;
+  m_fileSizeMB      = -1; // unknown
+  m_deleted         = false;
+}
+
+cRecording::cRecording(const PVR_RECORDINGINFO *Recording)
+{
+
+}
+
+cRecording::cRecording(const char *FileName, const char *BaseDir)
+{
+  m_channelName     = NULL;
+  m_aux             = NULL;
+  m_title           = NULL;
+  m_shortText       = NULL;
+  m_description     = NULL;
+  m_fileName        = NULL;
+  m_directory       = NULL;
+  m_resume          = RESUME_NOT_INITIALIZED;
+  m_fileSizeMB      = -1; // unknown
+  m_priority        = MAXPRIORITY;
+  m_lifetime        = MAXLIFETIME;
+  m_isPesRecording  = false;
+  m_framesPerSecond = DEFAULTFRAMESPERSECOND;
+  m_deleted         = false;
+
+  FileName = m_fileName = strdup(FileName);
+  if (*(m_fileName + strlen(m_fileName) - 1) == '/')
+     *(m_fileName + strlen(m_fileName) - 1) = 0;
+  FileName += strlen(BaseDir) + 1;
+  const char *p = strrchr(FileName, '/');
+  if (p)
+  {
+    time_t now = time(NULL);
+    struct tm tm_r;
+    struct tm t = *localtime_r(&now, &tm_r); // this initializes the time zone in 't'
+    t.tm_isdst = -1; // makes sure mktime() will determine the correct DST setting
+    int channel;
+    int instanceId;
+    if (7 == sscanf(p + 1, DATAFORMATTS, &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &channel, &instanceId)
+        || 7 == sscanf(p + 1, DATAFORMATPES, &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &m_priority, &m_lifetime))
+    {
+      t.tm_year -= 1900;
+      t.tm_mon--;
+      t.tm_sec = 0;
+      m_StartTime = mktime(&t);
+      char *path = MALLOC(char, p - FileName + 1);
+      strncpy(path, FileName, p - FileName);
+      path[p - FileName] = 0;
+
+      /* Make some default title based upon directory names */
+      p = strrchr(path, '/');
+      if (p)
+        m_title = strcpyrealloc(m_title, p + 1);
+      else
+        m_title = strcpyrealloc(m_title, path);
+
+      m_directory = MALLOC(char, strlen(path) - strlen(m_title) + 1);
+      strncpy(m_directory, path, strlen(path) - strlen(m_title));
+      m_directory[strlen(path) - strlen(m_title)] = 0;
+
+      if (g_bCharsetConv)
+      {
+        CStdString str_result = m_directory;
+        XBMC->UnknownToUTF8(str_result);
+        m_directory = strcpyrealloc(m_directory, str_result.c_str());
+      }
+
+      m_isPesRecording = instanceId < 0;
+      m_stream_url.Format("%s/%s", m_fileName, m_isPesRecording ? "*.vdr" : "*.ts");
+      free(path);
+    }
+    else
+      return;
+    // read info file:
+    CStdString InfoFileName;
+    InfoFileName.Format("%s%s", m_fileName, m_isPesRecording ? INFOFILESUFFIX ".vdr" : INFOFILESUFFIX);
+    FILE *f = fopen(InfoFileName.c_str(), "r");
+    if (f)
+    {
+      if (!Read(f))
+        XBMC->Log(LOG_ERROR, "EPG data problem in file %s", InfoFileName.c_str());
+      fclose(f);
+    }
+    else if (errno != ENOENT)
+      XBMC->Log(LOG_ERROR, "ERROR (%s,%d,s): %m", __FILE__, __LINE__, InfoFileName.c_str());
+  }
+}
+
+cRecording::~cRecording()
+{
+  free(m_aux);
+  free(m_channelName);
+  free(m_title);
+  free(m_shortText);
+  free(m_description);
+  free(m_fileName);
+  free(m_directory);
+}
+
+bool cRecording::Read(FILE *f)
+{
+  cReadLine ReadLine;
+  char *s;
+  int line = 0;
+  while ((s = ReadLine.Read(f)) != NULL)
+  {
+    ++line;
+    CStdString str_result = s;
+    if (g_bCharsetConv)
+      XBMC->UnknownToUTF8(str_result);
+    if (!ParseLine(str_result.c_str()))
+      return false;
+  }
+  return true;
+}
+
+bool cRecording::ParseLine(const char *s)
+{
+  char *t = skipspace(s + 1);
+  switch (*s)
+  {
+    case 'C':
+      {
+        char *p = strchr(t, ' ');
+        if (p)
+        {
+          free(m_channelName);
+          m_channelName = strdup(compactspace(p));
+          *p = 0; // strips optional channel name
+        }
+  //      if (*t)
+  //        channelID = tChannelID::FromString(t);
+      }
+      return true;
+    case 'D':
+      strreplace(t, '|', '\n');
+      SetDescription(t);
+      return true;
+    case 'E':
+      {
+        unsigned int EventID;
+        time_t StartTime;
+        int Duration;
+        unsigned int TableID = 0;
+        unsigned int Version = 0xFF;
+        int n = sscanf(t, "%u %ld %d %X %X", &EventID, &StartTime, &Duration, &TableID, &Version);
+        if (n >= 3 && n <= 5)
+        {
+          m_EventID   = EventID;
+          m_StartTime = StartTime;
+          m_Duration  = Duration;
+          m_TableID   = TableID;
+          m_Version   = Version;
+        }
+      }
+      return true;
+    case 'F':
+      m_framesPerSecond = atof(t);
+      return true;
+    case 'L':
+      m_lifetime = atoi(t);
+      return true;
+    case 'P':
+      m_priority = atoi(t);
+      return true;
+    case 'S':
+      SetShortText(t);
+      return true;
+    case 'T':
+      SetTitle(t);
+      return true;
+    case 'V':
+      m_vps = atoi(t);
+      return true;
+    case '@':
+      free(m_aux);
+      m_aux = strdup(t);
+      return true;
+    case '#':
+      return true; // comments are ignored
+
+
+    default:
+      break;
+  }
+  return false;
+}
+
+bool cRecording::ParseEntryLine(const char *s)
+{
+  if (*s >= '0' && *s <= '9')
+  {
+    char recdate[256];
+    char rectime[256];
+    char rectext[1024];
+
+    if (sscanf(s, " %u %[^ ] %[^ ] %[^\n]", &m_Index, recdate, rectime, rectext) >= 3)
+    {
+      CStdString fileName = rectext;
+      fileName.Replace('/', '_');
+      fileName.Replace('\\', '_');
+      fileName.Replace('?', '_');
+#if defined(_WIN32) || defined(_WIN64)
+      // just filter out some illegal characters on windows
+      fileName.Replace(':', '_');
+      fileName.Replace('*', '_');
+      fileName.Replace('?', '_');
+      fileName.Replace('\"', '_');
+      fileName.Replace('<', '_');
+      fileName.Replace('>', '_');
+      fileName.Replace('|', '_');
+      fileName.TrimRight(".");
+      fileName.TrimRight(" ");
+#endif
+      size_t found = fileName.find_last_of("~");
+      if (found != CStdString::npos)
+      {
+        CStdString dir = fileName.substr(0,found);
+        dir.Replace('~','/');
+        m_directory = strcpyrealloc(m_directory, dir.c_str());
+        m_fileName = strcpyrealloc(m_fileName, fileName.substr(found+1).c_str());
+      }
+      else
+      {
+        m_fileName = strcpyrealloc(m_fileName, fileName.c_str());
+        m_directory = strcpyrealloc(m_directory, "");
+      }
+    }
+    return true;
+  }
+
+  return false;
+}
+
+//    case 'X': if (!components)
+//                 components = new cComponents;
+//              components->SetComponent(components->NumComponents(), t);
+//              break;
+
+
+void cRecording::SetFramesPerSecond(double FramesPerSecond)
+{
+  m_framesPerSecond = FramesPerSecond;
+}
+
+void cRecording::SetTitle(const char *Title)
+{
+  m_title = strcpyrealloc(m_title, Title);
+}
+
+void cRecording::SetShortText(const char *ShortText)
+{
+  m_shortText = strcpyrealloc(m_shortText, ShortText);
+}
+
+void cRecording::SetDescription(const char *Description)
+{
+  m_description = strcpyrealloc(m_description, Description);
+}
diff --git a/xbmc/pvrclients/vdr-vnsi/recordings.h b/xbmc/pvrclients/vdr-vnsi/recordings.h
new file mode 100644 (file)
index 0000000..83d15e3
--- /dev/null
@@ -0,0 +1,101 @@
+#pragma once
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef __RECORDINGS_H
+#define __RECORDINGS_H
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "tools.h"
+
+#define DEFAULTFRAMESPERSECOND 25.0
+#define MAXPRIORITY       99
+#define MAXLIFETIME       99
+#define RECEXT            ".rec"
+#define DELEXT            ".del"
+#define DATAFORMATPES     "%4d-%02d-%02d.%02d%*c%02d.%02d.%02d" RECEXT
+#define NAMEFORMATPES     "%s/%s/" "%4d-%02d-%02d.%02d.%02d.%02d.%02d" RECEXT
+#define DATAFORMATTS      "%4d-%02d-%02d.%02d.%02d.%d-%d" RECEXT
+#define NAMEFORMATTS      "%s/%s/" DATAFORMATTS
+#define RESUMEFILESUFFIX  "/resume%s%s"
+#define SUMMARYFILESUFFIX "/summary.vdr"
+#define INFOFILESUFFIX    "/info"
+#define MARKSFILESUFFIX   "/marks"
+
+class cRecording
+{
+private:
+  int m_Index;
+  int m_resume;
+  int m_fileSizeMB;
+  char *m_channelName;
+  char *m_fileName;
+  char *m_directory;
+  CStdString m_stream_url;
+  double m_framesPerSecond;
+  int m_priority;
+  int m_lifetime;
+  char *m_aux;
+  unsigned int m_EventID;
+  time_t m_StartTime;
+  int m_Duration;
+  unsigned int m_TableID;
+  unsigned int m_Version;
+  bool m_isPesRecording;
+  bool m_deleted;
+  char *m_title;             // Title of this event
+  char *m_shortText;         // Short description of this event (typically the episode name in case of a series)
+  char *m_description;       // Description of this event
+  time_t m_vps;              // Video Programming Service timestamp (VPS, aka "Programme Identification Label", PIL)
+  bool Read(FILE *f);
+
+public:
+  cRecording(const char *FileName, const char *BaseDir);
+  cRecording(const PVR_RECORDINGINFO *Recording);
+  cRecording();
+  virtual ~cRecording();
+
+  bool ParseLine(const char *s);
+  bool ParseEntryLine(const char *s);
+  const char *ChannelName(void) const { return m_channelName; }
+  const char *Aux(void) const { return m_aux; }
+  double FramesPerSecond(void) const { return m_framesPerSecond; }
+  void SetFramesPerSecond(double m_FramesPerSecond);
+  int Index(void) const { return m_Index; }
+  int Priority(void) const { return m_priority; }
+  int Lifetime(void) const { return m_lifetime; }
+  time_t StartTime(void) const { return m_StartTime; }
+  time_t Duration(void) const { return m_Duration; }
+  time_t Vps(void) const { return m_vps; }
+  const char *Title(void) const { return m_title; }
+  const char *ShortText(void) const { return m_shortText; }
+  const char *Description(void) const { return m_description; }
+  const char *FileName(void) const { return m_fileName; }
+  const char *Directory(void) const { return m_directory; }
+  const char *StreamURL(void) const { return m_stream_url.c_str(); }
+  void SetTitle(const char *Title);
+  void SetShortText(const char *ShortText);
+  void SetDescription(const char *Description);
+};
+
+#endif //__RECORDINGS_H
diff --git a/xbmc/pvrclients/vdr-vnsi/requestpacket.cpp b/xbmc/pvrclients/vdr-vnsi/requestpacket.cpp
new file mode 100644 (file)
index 0000000..c2523f5
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef WIN32
+#include <arpa/inet.h>
+#else
+
+#endif
+#include <stdlib.h>
+#include <string.h>
+
+#include "requestpacket.h"
+#include "vdrcommand.h"
+#include "tools.h"
+
+uint32_t cRequestPacket::serialNumberCounter = 1;
+
+cRequestPacket::cRequestPacket()
+{
+  buffer        = NULL;
+  bufSize       = 0;
+  bufUsed       = 0;
+  lengthSet     = false;
+  serialNumber  = 0;
+  opcode        = 0;
+}
+
+cRequestPacket::~cRequestPacket()
+{
+  free(buffer);
+}
+
+bool cRequestPacket::init(uint32_t topcode, bool stream, bool setUserDataLength, uint32_t userDataLength)
+{
+  if (buffer) return false;
+
+  if (setUserDataLength)
+  {
+    bufSize = headerLength + userDataLength;
+    lengthSet = true;
+  }
+  else
+  {
+    bufSize = 512;
+    userDataLength = 0; // so the below will write a zero
+  }
+
+  buffer = (uint8_t*)malloc(bufSize);
+  if (!buffer) return false;
+
+  if (!stream)
+    channel     = CHANNEL_REQUEST_RESPONSE;
+  else
+    channel     = CHANNEL_STREAM;
+  serialNumber  = serialNumberCounter++;
+  opcode        = topcode;
+
+  *(uint32_t*)&buffer[0] = htonl(channel);
+  *(uint32_t*)&buffer[4] = htonl(serialNumber);
+  *(uint32_t*)&buffer[8] = htonl(opcode);
+  *(uint32_t*)&buffer[userDataLenPos] = htonl(userDataLength);
+  bufUsed = headerLength;
+
+  return true;
+}
+
+bool cRequestPacket::copyin(const uint8_t* src, uint32_t len)
+{
+  if (!checkExtend(len)) return false;
+  memcpy(buffer + bufUsed, src, len);
+  bufUsed += len;
+  if (!lengthSet) *(uint32_t*)&buffer[userDataLenPos] = htonl(bufUsed - headerLength);
+  return true;
+}
+
+bool cRequestPacket::add_String(const char* string)
+{
+  uint32_t len = strlen(string) + 1;
+  if (!checkExtend(len)) return false;
+  memcpy(buffer + bufUsed, string, len);
+  bufUsed += len;
+  if (!lengthSet) *(uint32_t*)&buffer[userDataLenPos] = htonl(bufUsed - headerLength);
+  return true;
+}
+
+bool cRequestPacket::add_U8(uint8_t c)
+{
+  if (!checkExtend(sizeof(uint8_t))) return false;
+  buffer[bufUsed] = c;
+  bufUsed += sizeof(uint8_t);
+  if (!lengthSet) *(uint32_t*)&buffer[userDataLenPos] = htonl(bufUsed - headerLength);
+  return true;
+}
+
+bool cRequestPacket::add_S32(int32_t l)
+{
+  if (!checkExtend(sizeof(int32_t))) return false;
+  *(int32_t*)&buffer[bufUsed] = htonl(l);
+  bufUsed += sizeof(int32_t);
+  if (!lengthSet) *(uint32_t*)&buffer[userDataLenPos] = htonl(bufUsed - headerLength);
+  return true;
+}
+
+bool cRequestPacket::add_U32(uint32_t ul)
+{
+  if (!checkExtend(sizeof(uint32_t))) return false;
+  *(uint32_t*)&buffer[bufUsed] = htonl(ul);
+  bufUsed += sizeof(uint32_t);
+  if (!lengthSet) *(uint32_t*)&buffer[userDataLenPos] = htonl(bufUsed - headerLength);
+  return true;
+}
+
+bool cRequestPacket::add_U64(uint64_t ull)
+{
+  if (!checkExtend(sizeof(uint64_t))) return false;
+  *(uint64_t*)&buffer[bufUsed] = htonll(ull);
+  bufUsed += sizeof(uint64_t);
+  if (!lengthSet) *(uint32_t*)&buffer[userDataLenPos] = htonl(bufUsed - headerLength);
+  return true;
+}
+
+bool cRequestPacket::checkExtend(uint32_t by)
+{
+  if (lengthSet) return true;
+  if ((bufUsed + by) < bufSize) return true;
+  uint8_t* newBuf = (uint8_t*)realloc(buffer, bufSize + 512);
+  if (!newBuf) return false;
+  buffer = newBuf;
+  bufSize += 512;
+  return true;
+}
+
diff --git a/xbmc/pvrclients/vdr-vnsi/requestpacket.h b/xbmc/pvrclients/vdr-vnsi/requestpacket.h
new file mode 100644 (file)
index 0000000..3ccb4e4
--- /dev/null
@@ -0,0 +1,69 @@
+#pragma once
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <stdlib.h>
+#ifndef WIN32
+#include <arpa/inet.h>
+#else
+
+#endif
+#include <stdio.h>
+
+class cRequestPacket
+{
+  public:
+    cRequestPacket();
+    ~cRequestPacket();
+
+    bool init(uint32_t opcode, bool stream = false, bool setUserDataLength = false, uint32_t userDataLength = 0);
+    bool copyin(const uint8_t* src, uint32_t len);
+    bool add_String(const char* string);
+    bool add_U8(uint8_t c);
+    bool add_U32(uint32_t ul);
+    bool add_S32(int32_t l);
+    bool add_U64(uint64_t ull);
+
+    uint8_t* getPtr() { return buffer; }
+    uint32_t getLen() { return bufUsed; }
+    uint32_t getChannel() { return channel; }
+    uint32_t getSerial() { return serialNumber; }
+
+    uint32_t getOpcode() { return opcode; }
+
+  private:
+    static uint32_t serialNumberCounter;
+
+    uint8_t* buffer;
+    uint32_t bufSize;
+    uint32_t bufUsed;
+    bool lengthSet;
+
+    uint32_t channel;
+    uint32_t serialNumber;
+
+    uint32_t opcode;
+
+    bool checkExtend(uint32_t by);
+
+    const static uint32_t headerLength = 16;
+    const static uint32_t userDataLenPos = 12;
+};
diff --git a/xbmc/pvrclients/vdr-vnsi/responsepacket.cpp b/xbmc/pvrclients/vdr-vnsi/responsepacket.cpp
new file mode 100644 (file)
index 0000000..13eb343
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "responsepacket.h"
+#include "vdrcommand.h"
+#include "tools.h"
+
+cResponsePacket::cResponsePacket()
+{
+  userDataLength  = 0;
+  packetPos       = 0;
+  userData        = NULL;
+  ownBlock        = true;
+  channelID       = 0;
+  requestID       = 0;
+  streamID        = 0;
+}
+
+cResponsePacket::~cResponsePacket()
+{
+  if (!ownBlock) return; // don't free if it's a getblock
+
+  if (userData) free(userData);
+}
+
+void cResponsePacket::setResponse(uint32_t trequestID, uint8_t* tuserData, uint32_t tuserDataLength)
+{
+  channelID       = CHANNEL_REQUEST_RESPONSE;
+  requestID       = trequestID;
+  userData        = tuserData;
+  userDataLength  = tuserDataLength;
+}
+
+void cResponsePacket::setStream(uint32_t topcodeID, uint32_t tstreamID, uint32_t tduration, int64_t tdts, int64_t tpts, uint8_t* tuserData, uint32_t tuserDataLength)
+{
+  channelID       = CHANNEL_STREAM;
+  opcodeID        = topcodeID;
+  streamID        = tstreamID;
+  duration        = tduration;
+  dts             = tdts;
+  pts             = tpts;
+  userData        = tuserData;
+  userDataLength  = tuserDataLength;
+}
+
+bool cResponsePacket::end()
+{
+  return (packetPos >= userDataLength);
+}
+
+int cResponsePacket::serverError()
+{
+  if ((packetPos == 0) && (userDataLength == 4) && !ntohl(*(uint32_t*)userData)) return 1;
+  else return 0;
+}
+
+char* cResponsePacket::extract_String()
+{
+  if (serverError()) return NULL;
+
+  int length = strlen((char*)&userData[packetPos]);
+  if ((packetPos + length) > userDataLength) return NULL;
+  char* str = new char[length + 1];
+  strcpy(str, (char*)&userData[packetPos]);
+  packetPos += length + 1;
+  return str;
+}
+
+uint8_t cResponsePacket::extract_U8()
+{
+  if ((packetPos + sizeof(uint8_t)) > userDataLength) return 0;
+  uint8_t uc = userData[packetPos];
+  packetPos += sizeof(uint8_t);
+  return uc;
+}
+
+uint32_t cResponsePacket::extract_U32()
+{
+  if ((packetPos + sizeof(uint32_t)) > userDataLength) return 0;
+  uint32_t ul = ntohl(*(uint32_t*)&userData[packetPos]);
+  packetPos += sizeof(uint32_t);
+  return ul;
+}
+
+uint64_t cResponsePacket::extract_U64()
+{
+  if ((packetPos + sizeof(uint64_t)) > userDataLength) return 0;
+  uint64_t ull = ntohll(*(uint64_t*)&userData[packetPos]);
+  packetPos += sizeof(uint64_t);
+  return ull;
+}
+
+double cResponsePacket::extract_Double()
+{
+  if ((packetPos + sizeof(uint64_t)) > userDataLength) return 0;
+  uint64_t ull = ntohll(*(uint64_t*)&userData[packetPos]);
+  double d;
+  memcpy(&d,&ull,sizeof(double));
+  packetPos += sizeof(uint64_t);
+  return d;
+}
+
+int32_t cResponsePacket::extract_S32()
+{
+  if ((packetPos + sizeof(int32_t)) > userDataLength) return 0;
+  int32_t l = ntohl(*(int32_t*)&userData[packetPos]);
+  packetPos += sizeof(int32_t);
+  return l;
+}
+
+int64_t cResponsePacket::extract_S64()
+{
+  if ((packetPos + sizeof(int64_t)) > userDataLength) return 0;
+  int64_t ll = ntohll(*(int64_t*)&userData[packetPos]);
+  packetPos += sizeof(int64_t);
+  return ll;
+}
+
+uint8_t* cResponsePacket::getUserData()
+{
+  ownBlock = false;
+  return userData;
+}
+
diff --git a/xbmc/pvrclients/vdr-vnsi/responsepacket.h b/xbmc/pvrclients/vdr-vnsi/responsepacket.h
new file mode 100644 (file)
index 0000000..768117c
--- /dev/null
@@ -0,0 +1,83 @@
+#pragma once
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef WIN32
+#include <arpa/inet.h>
+#else
+
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+class cResponsePacket
+{
+  public:
+    cResponsePacket();
+    ~cResponsePacket();
+
+    void setResponse(uint32_t requestID, uint8_t* packet, uint32_t packetLength);
+    void setStream(uint32_t opcodeID, uint32_t streamID, uint32_t duration, int64_t dts, int64_t pts, uint8_t* packet, uint32_t packetLength);
+
+    bool noResponse() { return (userData == NULL); };
+    int  serverError();
+
+    uint32_t  getUserDataLength() { return userDataLength; }
+    uint32_t  getChannelID()      { return channelID; }
+    uint32_t  getRequestID()      { return requestID; }
+    uint32_t  getStreamID()       { return streamID; }
+    uint32_t  getOpCodeID()       { return opcodeID; }
+    uint32_t  getDuration()       { return duration; }
+    int64_t   getDTS()            { return dts; }
+    int64_t   getPTS()            { return pts; }
+
+    uint32_t  getPacketPos()      { return packetPos; }
+
+    char*     extract_String();
+    uint8_t   extract_U8();
+    uint32_t  extract_U32();
+    uint64_t  extract_U64();
+    int32_t   extract_S32();
+    int64_t   extract_S64();
+    double    extract_Double();
+
+    bool      end();
+
+    // If you call this, the memory becomes yours. Free with free()
+    uint8_t* getUserData();
+
+  private:
+    uint8_t* userData;
+    uint32_t userDataLength;
+    uint32_t packetPos;
+
+    uint32_t channelID;
+
+    uint32_t requestID;
+    uint32_t streamID;
+    uint32_t opcodeID;
+    uint32_t duration;
+    int64_t  dts;
+    int64_t  pts;
+
+    bool ownBlock;
+};
diff --git a/xbmc/pvrclients/vdr-vnsi/thread.cpp b/xbmc/pvrclients/vdr-vnsi/thread.cpp
new file mode 100644 (file)
index 0000000..069c1bd
--- /dev/null
@@ -0,0 +1,409 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+/*
+ * Most of this code is taken from thread.c in the Video Disk Recorder ('VDR')
+ */
+
+#include "tools.h"
+#include "thread.h"
+#include <errno.h>
+#ifndef __APPLE__
+#include <malloc.h>
+#endif
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include "StdString.h"
+
+static bool GetAbsTime(struct timespec *Abstime, int MillisecondsFromNow)
+{
+  struct timeval now;
+  if (gettimeofday(&now, NULL) == 0) {           // get current time
+     now.tv_sec  += MillisecondsFromNow / 1000;  // add full seconds
+     now.tv_usec += (MillisecondsFromNow % 1000) * 1000;  // add microseconds
+     if (now.tv_usec >= 1000000) {               // take care of an overflow
+        now.tv_sec++;
+        now.tv_usec -= 1000000;
+        }
+     Abstime->tv_sec = now.tv_sec;          // seconds
+     Abstime->tv_nsec = now.tv_usec * 1000; // nano seconds
+     return true;
+     }
+  return false;
+}
+
+// --- cCondWait -------------------------------------------------------------
+
+cCondWait::cCondWait(void)
+{
+  signaled = false;
+  pthread_mutex_init(&mutex, NULL);
+  pthread_cond_init(&cond, NULL);
+}
+
+cCondWait::~cCondWait()
+{
+  pthread_cond_broadcast(&cond); // wake up any sleepers
+  pthread_cond_destroy(&cond);
+  pthread_mutex_destroy(&mutex);
+}
+
+void cCondWait::SleepMs(int TimeoutMs)
+{
+  cCondWait w;
+  w.Wait(max(TimeoutMs, 3)); // making sure the time is >2ms to avoid a possible busy wait
+}
+
+bool cCondWait::Wait(int TimeoutMs)
+{
+  pthread_mutex_lock(&mutex);
+  if (!signaled) {
+     if (TimeoutMs) {
+        struct timespec abstime;
+        if (GetAbsTime(&abstime, TimeoutMs)) {
+           while (!signaled) {
+                 if (pthread_cond_timedwait(&cond, &mutex, &abstime) == ETIMEDOUT)
+                    break;
+                 }
+           }
+        }
+     else
+        pthread_cond_wait(&cond, &mutex);
+     }
+  bool r = signaled;
+  signaled = false;
+  pthread_mutex_unlock(&mutex);
+  return r;
+}
+
+void cCondWait::Signal(void)
+{
+  pthread_mutex_lock(&mutex);
+  signaled = true;
+  pthread_cond_broadcast(&cond);
+  pthread_mutex_unlock(&mutex);
+}
+
+// --- cCondVar --------------------------------------------------------------
+
+cCondVar::cCondVar(void)
+{
+  pthread_cond_init(&cond, 0);
+}
+
+cCondVar::~cCondVar()
+{
+  pthread_cond_broadcast(&cond); // wake up any sleepers
+  pthread_cond_destroy(&cond);
+}
+
+void cCondVar::Wait(cMutex &Mutex)
+{
+  if (Mutex.locked) {
+     int locked = Mutex.locked;
+     Mutex.locked = 0; // have to clear the locked count here, as pthread_cond_wait
+                       // does an implicit unlock of the mutex
+     pthread_cond_wait(&cond, &Mutex.mutex);
+     Mutex.locked = locked;
+     }
+}
+
+bool cCondVar::TimedWait(cMutex &Mutex, int TimeoutMs)
+{
+  bool r = true; // true = condition signaled, false = timeout
+
+  if (Mutex.locked) {
+     struct timespec abstime;
+     if (GetAbsTime(&abstime, TimeoutMs)) {
+        int locked = Mutex.locked;
+        Mutex.locked = 0; // have to clear the locked count here, as pthread_cond_timedwait
+                          // does an implicit unlock of the mutex.
+        if (pthread_cond_timedwait(&cond, &Mutex.mutex, &abstime) == ETIMEDOUT)
+           r = false;
+        Mutex.locked = locked;
+        }
+     }
+  return r;
+}
+
+void cCondVar::Broadcast(void)
+{
+  pthread_cond_broadcast(&cond);
+}
+
+// --- cMutex ----------------------------------------------------------------
+
+cMutex::cMutex(void)
+{
+  locked = 0;
+  pthread_mutexattr_t attr;
+  pthread_mutexattr_init(&attr);
+#ifndef __APPLE__
+  pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK_NP);
+#else
+  pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
+#endif
+  pthread_mutex_init(&mutex, &attr);
+}
+
+cMutex::~cMutex()
+{
+  pthread_mutex_destroy(&mutex);
+}
+
+void cMutex::Lock(void)
+{
+  pthread_mutex_lock(&mutex);
+  locked++;
+}
+
+void cMutex::Unlock(void)
+{
+ if (!--locked)
+    pthread_mutex_unlock(&mutex);
+}
+
+// --- cThread ---------------------------------------------------------------
+
+tThreadId cThread::mainThreadId = 0;
+
+cThread::cThread(const char *Description)
+{
+  active = running = false;
+#if !defined(__WINDOWS__)
+  childTid = 0;
+#endif
+  childThreadId = 0;
+  description = NULL;
+  if (Description)
+     SetDescription("%s", Description);
+}
+
+cThread::~cThread()
+{
+  Cancel(); // just in case the derived class didn't call it
+  free(description);
+}
+
+void cThread::SetPriority(int Priority)
+{
+#if !defined(__WINDOWS__)
+  if (setpriority(PRIO_PROCESS, 0, Priority) < 0)
+     XBMC->Log(LOG_ERROR, "ERROR (%s,%d): %m", __FILE__, __LINE__);
+#endif
+}
+
+void cThread::SetIOPriority(int Priority)
+{
+#if !defined(__WINDOWS__)
+#ifdef HAVE_LINUXIOPRIO
+  if (syscall(SYS_ioprio_set, 1, 0, (Priority & 0xff) | (2 << 13)) < 0) // best effort class
+     XBMC->Log(LOG_ERROR, "ERROR (%s,%d): %m", __FILE__, __LINE__);
+#endif
+#endif
+}
+
+void cThread::SetDescription(const char *Description, ...)
+{
+  free(description);
+  description = NULL;
+  if (Description)
+  {
+     va_list ap;
+     va_start(ap, Description);
+     CStdString desc;
+     desc.FormatV(Description, ap);
+     description = strdup(desc.c_str());
+     va_end(ap);
+  }
+}
+
+void *cThread::StartThread(cThread *Thread)
+{
+  Thread->childThreadId = ThreadId();
+  if (Thread->description) {
+     XBMC->Log(LOG_DEBUG, "%s thread started (pid=%d, tid=%d)", Thread->description, getpid(), Thread->childThreadId);
+#ifdef PR_SET_NAME
+     if (prctl(PR_SET_NAME, Thread->description, 0, 0, 0) < 0)
+        XBMC->Log(LOG_ERROR, "%s thread naming failed (pid=%d, tid=%d)", Thread->description, getpid(), Thread->childThreadId);
+#endif
+     }
+  Thread->Action();
+  if (Thread->description)
+     XBMC->Log(LOG_DEBUG, "%s thread ended (pid=%d, tid=%d)", Thread->description, getpid(), Thread->childThreadId);
+  Thread->running = false;
+  Thread->active = false;
+  return NULL;
+}
+
+#define THREAD_STOP_TIMEOUT  3000 // ms to wait for a thread to stop before newly starting it
+#define THREAD_STOP_SLEEP      30 // ms to sleep while waiting for a thread to stop
+
+bool cThread::Start(void)
+{
+  if (!running) {
+     if (active) {
+        // Wait until the previous incarnation of this thread has completely ended
+        // before starting it newly:
+        cTimeMs RestartTimeout;
+        while (!running && active && RestartTimeout.Elapsed() < THREAD_STOP_TIMEOUT)
+              cCondWait::SleepMs(THREAD_STOP_SLEEP);
+        }
+     if (!active) {
+        active = running = true;
+        if (pthread_create(&childTid, NULL, (void *(*) (void *))&StartThread, (void *)this) == 0) {
+           pthread_detach(childTid); // auto-reap
+           }
+        else {
+           XBMC->Log(LOG_ERROR, "ERROR (%s,%d): %m", __FILE__, __LINE__);
+           active = running = false;
+           return false;
+           }
+        }
+     }
+  return true;
+}
+
+bool cThread::Active(void)
+{
+  if (active) {
+     //
+     // Single UNIX Spec v2 says:
+     //
+     // The pthread_kill() function is used to request
+     // that a signal be delivered to the specified thread.
+     //
+     // As in kill(), if sig is zero, error checking is
+     // performed but no signal is actually sent.
+     //
+     int err;
+     if ((err = pthread_kill(childTid, 0)) != 0) {
+        if (err != ESRCH)
+           XBMC->Log(LOG_ERROR, "ERROR (%s,%d): %m", __FILE__, __LINE__);
+#if !defined(__WINDOWS__)
+        childTid = 0;
+#endif
+        active = running = false;
+        }
+     else
+        return true;
+     }
+  return false;
+}
+
+void cThread::Cancel(int WaitSeconds)
+{
+  running = false;
+  if (active && WaitSeconds > -1)
+  {
+    if (WaitSeconds > 0)
+    {
+      for (time_t t0 = time(NULL) + WaitSeconds; time(NULL) < t0; )
+      {
+        if (!Active())
+          return;
+        cCondWait::SleepMs(10);
+      }
+      XBMC->Log(LOG_ERROR, "ERROR: %s thread %d won't end (waited %d seconds) - canceling it...", description ? description : "", childThreadId, WaitSeconds);
+    }
+    pthread_cancel(childTid);
+#if !defined(__WINDOWS__)
+    childTid = 0;
+#endif
+    active = false;
+  }
+}
+
+tThreadId cThread::ThreadId(void)
+{
+#ifdef __APPLE__
+    return (int)pthread_self();
+#else
+#ifdef __WINDOWS__
+  return GetCurrentThreadId();
+#else
+  return syscall(__NR_gettid);
+#endif
+#endif
+}
+
+void cThread::SetMainThreadId(void)
+{
+  if (mainThreadId == 0)
+     mainThreadId = ThreadId();
+  else
+     XBMC->Log(LOG_ERROR, "ERROR: attempt to set main thread id to %d while it already is %d", ThreadId(), mainThreadId);
+}
+
+// --- cMutexLock ------------------------------------------------------------
+
+cMutexLock::cMutexLock(cMutex *Mutex)
+{
+  mutex = NULL;
+  locked = false;
+  Lock(Mutex);
+}
+
+cMutexLock::~cMutexLock()
+{
+  if (mutex && locked)
+    mutex->Unlock();
+}
+
+bool cMutexLock::Lock(cMutex *Mutex)
+{
+  if (Mutex && !mutex)
+  {
+    mutex = Mutex;
+    Mutex->Lock();
+    locked = true;
+    return true;
+  }
+  return false;
+}
+
+// --- cThreadLock -----------------------------------------------------------
+
+cThreadLock::cThreadLock(cThread *Thread)
+{
+  thread = NULL;
+  locked = false;
+  Lock(Thread);
+}
+
+cThreadLock::~cThreadLock()
+{
+  if (thread && locked)
+    thread->Unlock();
+}
+
+bool cThreadLock::Lock(cThread *Thread)
+{
+  if (Thread && !thread)
+  {
+  thread = Thread;
+  Thread->Lock();
+  locked = true;
+  return true;
+  }
+  return false;
+}
diff --git a/xbmc/pvrclients/vdr-vnsi/thread.h b/xbmc/pvrclients/vdr-vnsi/thread.h
new file mode 100644 (file)
index 0000000..0fdc6c4
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef __THREAD_H
+#define __THREAD_H
+
+#include <stdio.h>
+#include <sys/types.h>
+#include "tools.h"
+
+class cCondWait {
+private:
+  pthread_mutex_t mutex;
+  pthread_cond_t cond;
+  bool signaled;
+public:
+  cCondWait(void);
+  ~cCondWait();
+  static void SleepMs(int TimeoutMs);
+       ///< Creates a cCondWait object and uses it to sleep for TimeoutMs
+       ///< milliseconds, immediately giving up the calling thread's time
+       ///< slice and thus avoiding a "busy wait".
+       ///< In order to avoid a possible busy wait, TimeoutMs will be automatically
+       ///< limited to values >2.
+  bool Wait(int TimeoutMs = 0);
+       ///< Waits at most TimeoutMs milliseconds for a call to Signal(), or
+       ///< forever if TimeoutMs is 0.
+       ///< \return Returns true if Signal() has been called, false it the given
+       ///< timeout has expired.
+  void Signal(void);
+       ///< Signals a caller of Wait() that the condition it is waiting for is met.
+  };
+
+class cMutex;
+
+class cCondVar {
+private:
+  pthread_cond_t cond;
+public:
+  cCondVar(void);
+  ~cCondVar();
+  void Wait(cMutex &Mutex);
+  bool TimedWait(cMutex &Mutex, int TimeoutMs);
+  void Broadcast(void);
+  };
+
+class cMutex {
+  friend class cCondVar;
+private:
+  pthread_mutex_t mutex;
+  int locked;
+public:
+  cMutex(void);
+  ~cMutex();
+  void Lock(void);
+  void Unlock(void);
+  };
+
+typedef pid_t tThreadId;
+
+class cThread {
+  friend class cThreadLock;
+private:
+  bool active;
+  bool running;
+  pthread_t childTid;
+  tThreadId childThreadId;
+  cMutex mutex;
+  char *description;
+  static tThreadId mainThreadId;
+  static void *StartThread(cThread *Thread);
+protected:
+  void SetPriority(int Priority);
+  void SetIOPriority(int Priority);
+  void Lock(void) { mutex.Lock(); }
+  void Unlock(void) { mutex.Unlock(); }
+  virtual void Action(void) = 0;
+       ///< A derived cThread class must implement the code it wants to
+       ///< execute as a separate thread in this function. If this is
+       ///< a loop, it must check Running() repeatedly to see whether
+       ///< it's time to stop.
+  bool Running(void) { return running; }
+       ///< Returns false if a derived cThread object shall leave its Action()
+       ///< function.
+  void Cancel(int WaitSeconds = 0);
+       ///< Cancels the thread by first setting 'running' to false, so that
+       ///< the Action() loop can finish in an orderly fashion and then waiting
+       ///< up to WaitSeconds seconds for the thread to actually end. If the
+       ///< thread doesn't end by itself, it is killed.
+       ///< If WaitSeconds is -1, only 'running' is set to false and Cancel()
+       ///< returns immediately, without killing the thread.
+public:
+  cThread(const char *Description = NULL);
+       ///< Creates a new thread.
+       ///< If Description is present, a log file entry will be made when
+       ///< the thread starts and stops. The Start() function must be called
+       ///< to actually start the thread.
+  virtual ~cThread();
+#ifdef __WINDOWS__
+  void SetDescription(const char *Description, ...);
+#else
+  void SetDescription(const char *Description, ...) __attribute__ ((format (printf, 2, 3)));
+#endif
+  bool Start(void);
+       ///< Actually starts the thread.
+       ///< If the thread is already running, nothing happens.
+  bool Active(void);
+       ///< Checks whether the thread is still alive.
+  static tThreadId ThreadId(void);
+  static tThreadId IsMainThread(void) { return ThreadId() == mainThreadId; }
+  static void SetMainThreadId(void);
+  };
+
+// cMutexLock can be used to easily set a lock on mutex and make absolutely
+// sure that it will be unlocked when the block will be left. Several locks can
+// be stacked, so a function that makes many calls to another function which uses
+// cMutexLock may itself use a cMutexLock to make one longer lock instead of many
+// short ones.
+
+class cMutexLock {
+private:
+  cMutex *mutex;
+  bool locked;
+public:
+  cMutexLock(cMutex *Mutex = NULL);
+  ~cMutexLock();
+  bool Lock(cMutex *Mutex);
+  };
+
+// cThreadLock can be used to easily set a lock in a thread and make absolutely
+// sure that it will be unlocked when the block will be left. Several locks can
+// be stacked, so a function that makes many calls to another function which uses
+// cThreadLock may itself use a cThreadLock to make one longer lock instead of many
+// short ones.
+
+class cThreadLock {
+private:
+  cThread *thread;
+  bool locked;
+public:
+  cThreadLock(cThread *Thread = NULL);
+  ~cThreadLock();
+  bool Lock(cThread *Thread);
+  };
+
+#define LOCK_THREAD cThreadLock ThreadLock(this)
+
+#endif //__THREAD_H
diff --git a/xbmc/pvrclients/vdr-vnsi/tools.cpp b/xbmc/pvrclients/vdr-vnsi/tools.cpp
new file mode 100644 (file)
index 0000000..6ba005c
--- /dev/null
@@ -0,0 +1,740 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+/*
+ * Most of this code is taken from tools.c in the Video Disk Recorder ('VDR')
+ */
+
+#include "tools.h"
+
+uint64_t ntohll(uint64_t a)
+{
+  return htonll(a);
+}
+
+uint64_t htonll(uint64_t a)
+{
+#if BYTE_ORDER == BIG_ENDIAN
+  return a;
+#else
+  uint64_t b = 0;
+
+  b = ((a << 56) & 0xFF00000000000000ULL)
+    | ((a << 40) & 0x00FF000000000000ULL)
+    | ((a << 24) & 0x0000FF0000000000ULL)
+    | ((a <<  8) & 0x000000FF00000000ULL)
+    | ((a >>  8) & 0x00000000FF000000ULL)
+    | ((a >> 24) & 0x0000000000FF0000ULL)
+    | ((a >> 40) & 0x000000000000FF00ULL)
+    | ((a >> 56) & 0x00000000000000FFULL) ;
+
+  return b;
+#endif
+}
+
+ssize_t safe_read(int filedes, void *buffer, size_t size)
+{
+  for (;;) {
+      ssize_t p = read(filedes, buffer, size);
+      if (p < 0 && errno == EINTR) {
+         XBMC->Log(LOG_DEBUG, "EINTR while reading from file handle %d - retrying", filedes);
+         continue;
+         }
+      return p;
+      }
+}
+
+ssize_t safe_write(int filedes, const void *buffer, size_t size)
+{
+  ssize_t p = 0;
+  ssize_t written = size;
+  const unsigned char *ptr = (const unsigned char *)buffer;
+  while (size > 0) {
+        p = write(filedes, ptr, size);
+        if (p < 0) {
+           if (errno == EINTR) {
+              XBMC->Log(LOG_DEBUG, "EINTR while writing to file handle %d - retrying", filedes);
+              continue;
+              }
+           break;
+           }
+        ptr  += p;
+        size -= p;
+        }
+  return p < 0 ? p : written;
+}
+
+void writechar(int filedes, char c)
+{
+  safe_write(filedes, &c, sizeof(c));
+}
+
+int WriteAllOrNothing(int fd, const unsigned char *Data, int Length, int TimeoutMs, int RetryMs)
+{
+  int written = 0;
+  while (Length > 0) {
+        int w = write(fd, Data + written, Length);
+        if (w > 0) {
+           Length -= w;
+           written += w;
+           }
+        else if (written > 0 && !FATALERRNO) {
+           // we've started writing, so we must finish it!
+           cTimeMs t;
+           cPoller Poller(fd, true);
+           Poller.Poll(RetryMs);
+           if (TimeoutMs > 0 && (TimeoutMs -= t.Elapsed()) <= 0)
+              break;
+           }
+        else
+           // nothing written yet (or fatal error), so we can just return the error code:
+           return w;
+        }
+  return written;
+}
+
+char *strcpyrealloc(char *dest, const char *src)
+{
+  if (src) {
+     int l = max(dest ? strlen(dest) : 0, strlen(src)) + 1; // don't let the block get smaller!
+     dest = (char *)realloc(dest, l);
+     if (dest)
+        strcpy(dest, src);
+     else
+        XBMC->Log(LOG_ERROR, "ERROR: out of memory");
+     }
+  else {
+     free(dest);
+     dest = NULL;
+     }
+  return dest;
+}
+
+char *strn0cpy(char *dest, const char *src, size_t n)
+{
+  char *s = dest;
+  for ( ; --n && (*dest = *src) != 0; dest++, src++) ;
+  *dest = 0;
+  return s;
+}
+
+char *strreplace(char *s, char c1, char c2)
+{
+  if (s) {
+     char *p = s;
+     while (*p) {
+           if (*p == c1)
+              *p = c2;
+           p++;
+           }
+     }
+  return s;
+}
+
+char *strreplace(char *s, const char *s1, const char *s2)
+{
+  char *p = strstr(s, s1);
+  if (p) {
+     int of = p - s;
+     int l  = strlen(s);
+     int l1 = strlen(s1);
+     int l2 = strlen(s2);
+     if (l2 > l1)
+        s = (char *)realloc(s, l + l2 - l1 + 1);
+     char *sof = s + of;
+     if (l2 != l1)
+        memmove(sof + l2, sof + l1, l - of - l1 + 1);
+     strncpy(sof, s2, l2);
+     }
+  return s;
+}
+
+char *stripspace(char *s)
+{
+  if (s && *s) {
+     for (char *p = s + strlen(s) - 1; p >= s; p--) {
+         if (!isspace(*p))
+            break;
+         *p = 0;
+         }
+     }
+  return s;
+}
+
+char *compactspace(char *s)
+{
+  if (s && *s) {
+     char *t = stripspace(skipspace(s));
+     char *p = t;
+     while (p && *p) {
+           char *q = skipspace(p);
+           if (q - p > 1)
+              memmove(p + 1, q, strlen(q) + 1);
+           p++;
+           }
+     if (t != s)
+        memmove(s, t, strlen(t) + 1);
+     }
+  return s;
+}
+
+bool startswith(const char *s, const char *p)
+{
+  while (*p) {
+        if (*p++ != *s++)
+           return false;
+        }
+  return true;
+}
+
+bool endswith(const char *s, const char *p)
+{
+  const char *se = s + strlen(s) - 1;
+  const char *pe = p + strlen(p) - 1;
+  while (pe >= p) {
+        if (*pe-- != *se-- || (se < s && pe >= p))
+           return false;
+        }
+  return true;
+}
+
+bool isempty(const char *s)
+{
+  return !(s && *skipspace(s));
+}
+
+int numdigits(int n)
+{
+  int res = 1;
+  while (n >= 10) {
+        n /= 10;
+        res++;
+        }
+  return res;
+}
+
+bool IsNumber(const char *s)
+{
+  if (!*s)
+     return false;
+  do {
+     if (!isdigit(*s))
+        return false;
+     } while (*++s);
+  return true;
+}
+
+CStdString AddDirectory(const char *DirName, const char *FileName)
+{
+  CStdString ret;
+  ret.Format("%s/%s", DirName && *DirName ? DirName : ".", FileName);
+  return ret;
+}
+
+char *ReadLink(const char *FileName)
+{
+#if defined(__WINDOWS__)
+  return NULL;
+#else
+  if (!FileName)
+    return NULL;
+  char *TargetName = NULL;
+  char *res = realpath(FileName, TargetName);
+  if (!res)
+  {
+    if (errno == ENOENT) // file doesn't exist
+      TargetName = strdup(FileName);
+    else // some other error occurred
+      XBMC->Log(LOG_ERROR, "ERROR (%s,%d,s): %m", __FILE__, __LINE__, FileName);
+  }
+  return TargetName;
+#endif
+}
+
+
+// --- cTimeMs ---------------------------------------------------------------
+
+cTimeMs::cTimeMs(int Ms)
+{
+  Set(Ms);
+}
+
+uint64_t cTimeMs::Now(void)
+{
+#if _POSIX_TIMERS > 0 && defined(_POSIX_MONOTONIC_CLOCK)
+#define MIN_RESOLUTION 5 // ms
+  static bool initialized = false;
+  static bool monotonic = false;
+  struct timespec tp;
+  if (!initialized) {
+     // check if monotonic timer is available and provides enough accurate resolution:
+     if (clock_getres(CLOCK_MONOTONIC, &tp) == 0) {
+        long Resolution = tp.tv_nsec;
+        // require a minimum resolution:
+        if (tp.tv_sec == 0 && tp.tv_nsec <= MIN_RESOLUTION * 1000000) {
+           if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0) {
+              XBMC->Log(LOG_DEBUG, "cTimeMs: using monotonic clock (resolution is %ld ns)", Resolution);
+              monotonic = true;
+              }
+           else
+              XBMC->Log(LOG_ERROR, "cTimeMs: clock_gettime(CLOCK_MONOTONIC) failed");
+           }
+        else
+           XBMC->Log(LOG_DEBUG, "cTimeMs: not using monotonic clock - resolution is too bad (%ld s %ld ns)", tp.tv_sec, tp.tv_nsec);
+        }
+     else
+        XBMC->Log(LOG_ERROR, "cTimeMs: clock_getres(CLOCK_MONOTONIC) failed");
+     initialized = true;
+     }
+  if (monotonic) {
+     if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0)
+        return (uint64_t(tp.tv_sec)) * 1000 + tp.tv_nsec / 1000000;
+     XBMC->Log(LOG_ERROR, "cTimeMs: clock_gettime(CLOCK_MONOTONIC) failed");
+     monotonic = false;
+     // fall back to gettimeofday()
+     }
+#else
+#if !defined(__WINDOWS__)
+#  warning Posix monotonic clock not available
+#endif
+#endif
+  struct timeval t;
+  if (gettimeofday(&t, NULL) == 0)
+     return (uint64_t(t.tv_sec)) * 1000 + t.tv_usec / 1000;
+  return 0;
+}
+
+void cTimeMs::Set(int Ms)
+{
+  begin = Now() + Ms;
+}
+
+bool cTimeMs::TimedOut(void)
+{
+  return Now() >= begin;
+}
+
+uint64_t cTimeMs::Elapsed(void)
+{
+  return Now() - begin;
+}
+
+// --- cPoller ---------------------------------------------------------------
+
+cPoller::cPoller(int FileHandle, bool Out)
+{
+  numFileHandles = 0;
+  Add(FileHandle, Out);
+}
+
+bool cPoller::Add(int FileHandle, bool Out)
+{
+  if (FileHandle >= 0) {
+     for (int i = 0; i < numFileHandles; i++) {
+         if (pfd[i].fd == FileHandle && pfd[i].events == (Out ? POLLOUT : POLLIN))
+            return true;
+         }
+     if (numFileHandles < MaxPollFiles) {
+        pfd[numFileHandles].fd = FileHandle;
+        pfd[numFileHandles].events = Out ? POLLOUT : POLLIN;
+        pfd[numFileHandles].revents = 0;
+        numFileHandles++;
+        return true;
+        }
+     XBMC->Log(LOG_ERROR, "ERROR: too many file handles in cPoller");
+     }
+  return false;
+}
+
+bool cPoller::Poll(int TimeoutMs)
+{
+  if (numFileHandles) {
+     if (poll(pfd, numFileHandles, TimeoutMs) != 0)
+        return true; // returns true even in case of an error, to let the caller
+                     // access the file and thus see the error code
+     }
+  return false;
+}
+
+// --- cFile -----------------------------------------------------------------
+
+bool cFile::files[FD_SETSIZE] = { false };
+int cFile::maxFiles = 0;
+
+cFile::cFile(void)
+{
+  f = -1;
+}
+
+cFile::~cFile()
+{
+  Close();
+}
+
+bool cFile::Open(const char *FileName, int Flags, mode_t Mode)
+{
+  if (!IsOpen())
+#ifdef __WINDOWS__
+    return Open(open(FileName, Flags, 0));
+#else
+    return Open(open(FileName, Flags, Mode));
+#endif
+  XBMC->Log(LOG_ERROR, "ERROR: attempt to re-open %s", FileName);
+  return false;
+}
+
+bool cFile::Open(int FileDes)
+{
+  if (FileDes >= 0) {
+     if (!IsOpen()) {
+        f = FileDes;
+        if (f >= 0) {
+           if (f < FD_SETSIZE) {
+              if (f >= maxFiles)
+                 maxFiles = f + 1;
+              if (!files[f])
+                 files[f] = true;
+              else
+                 XBMC->Log(LOG_ERROR, "ERROR: file descriptor %d already in files[]", f);
+              return true;
+              }
+           else
+              XBMC->Log(LOG_ERROR, "ERROR: file descriptor %d is larger than FD_SETSIZE (%d)", f, FD_SETSIZE);
+           }
+        }
+     else
+        XBMC->Log(LOG_ERROR, "ERROR: attempt to re-open file descriptor %d", FileDes);
+     }
+  return false;
+}
+
+void cFile::Close(void)
+{
+  if (f >= 0) {
+     close(f);
+     files[f] = false;
+     f = -1;
+     }
+}
+
+bool cFile::Ready(bool Wait)
+{
+  return f >= 0 && AnyFileReady(f, Wait ? 1000 : 0);
+}
+
+bool cFile::AnyFileReady(int FileDes, int TimeoutMs)
+{
+  fd_set set;
+  FD_ZERO(&set);
+  for (int i = 0; i < maxFiles; i++) {
+      if (files[i])
+         FD_SET(i, &set);
+      }
+  if (0 <= FileDes && FileDes < FD_SETSIZE && !files[FileDes])
+     FD_SET(FileDes, &set); // in case we come in with an arbitrary descriptor
+  if (TimeoutMs == 0)
+     TimeoutMs = 10; // load gets too heavy with 0
+  struct timeval timeout;
+  timeout.tv_sec  = TimeoutMs / 1000;
+  timeout.tv_usec = (TimeoutMs % 1000) * 1000;
+  return select(FD_SETSIZE, &set, NULL, NULL, &timeout) > 0 && (FileDes < 0 || FD_ISSET(FileDes, &set));
+}
+
+bool cFile::FileReady(int FileDes, int TimeoutMs)
+{
+  fd_set set;
+  struct timeval timeout;
+  FD_ZERO(&set);
+  FD_SET(FileDes, &set);
+  if (TimeoutMs >= 0) {
+     if (TimeoutMs < 100)
+        TimeoutMs = 100;
+     timeout.tv_sec  = TimeoutMs / 1000;
+     timeout.tv_usec = (TimeoutMs % 1000) * 1000;
+     }
+  return select(FD_SETSIZE, &set, NULL, NULL, (TimeoutMs >= 0) ? &timeout : NULL) > 0 && FD_ISSET(FileDes, &set);
+}
+
+bool cFile::FileReadyForWriting(int FileDes, int TimeoutMs)
+{
+  fd_set set;
+  struct timeval timeout;
+  FD_ZERO(&set);
+  FD_SET(FileDes, &set);
+  if (TimeoutMs < 100)
+     TimeoutMs = 100;
+  timeout.tv_sec  = 0;
+  timeout.tv_usec = TimeoutMs * 1000;
+  return select(FD_SETSIZE, NULL, &set, NULL, &timeout) > 0 && FD_ISSET(FileDes, &set);
+}
+
+
+// --- cUnbufferedFile -------------------------------------------------------
+
+#if !defined(__WINDOWS__)
+#if !defined(__APPLE__)
+#define USE_FADVISE
+#endif
+#endif
+
+#define WRITE_BUFFER KILOBYTE(800)
+
+cUnbufferedFile::cUnbufferedFile(void)
+{
+  fd = -1;
+}
+
+cUnbufferedFile::~cUnbufferedFile()
+{
+  Close();
+}
+
+int cUnbufferedFile::Open(const char *FileName, int Flags, mode_t Mode)
+{
+  Close();
+  fd = open(FileName, Flags, Mode);
+  curpos = 0;
+#ifdef USE_FADVISE
+  begin = lastpos = ahead = 0;
+  cachedstart = 0;
+  cachedend = 0;
+  readahead = KILOBYTE(128);
+  written = 0;
+  totwritten = 0;
+  if (fd >= 0)
+     posix_fadvise(fd, 0, 0, POSIX_FADV_RANDOM); // we could use POSIX_FADV_SEQUENTIAL, but we do our own readahead, disabling the kernel one.
+#endif
+  return fd;
+}
+
+int cUnbufferedFile::Close(void)
+{
+  if (fd >= 0) {
+#ifdef USE_FADVISE
+     if (totwritten)    // if we wrote anything make sure the data has hit the disk before
+        fdatasync(fd);  // calling fadvise, as this is our last chance to un-cache it.
+     posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED);
+#endif
+     int OldFd = fd;
+     fd = -1;
+     return close(OldFd);
+     }
+  errno = EBADF;
+  return -1;
+}
+
+// When replaying and going e.g. FF->PLAY the position jumps back 2..8M
+// hence we do not want to drop recently accessed data at once.
+// We try to handle the common cases such as PLAY->FF->PLAY, small
+// jumps, moving editing marks etc.
+
+#define FADVGRAN   KILOBYTE(4) // AKA fadvise-chunk-size; PAGE_SIZE or getpagesize(2) would also work.
+#define READCHUNK  MEGABYTE(8)
+
+void cUnbufferedFile::SetReadAhead(size_t ra)
+{
+  readahead = ra;
+}
+
+int cUnbufferedFile::FadviseDrop(off_t Offset, off_t Len)
+{
+#ifdef USE_FADVISE
+  // rounding up the window to make sure that not PAGE_SIZE-aligned data gets freed.
+  return posix_fadvise(fd, Offset - (FADVGRAN - 1), Len + (FADVGRAN - 1) * 2, POSIX_FADV_DONTNEED);
+#else
+  return 0;
+#endif
+}
+
+off_t cUnbufferedFile::Seek(off_t Offset, int Whence)
+{
+  if (Whence == SEEK_SET && Offset == curpos)
+     return curpos;
+  curpos = lseek(fd, Offset, Whence);
+  return curpos;
+}
+
+ssize_t cUnbufferedFile::Read(void *Data, size_t Size)
+{
+  if (fd >= 0) {
+#ifdef USE_FADVISE
+     off_t jumped = curpos-lastpos; // nonzero means we're not at the last offset
+     if ((cachedstart < cachedend) && (curpos < cachedstart || curpos > cachedend)) {
+        // current position is outside the cached window -- invalidate it.
+        FadviseDrop(cachedstart, cachedend-cachedstart);
+        cachedstart = curpos;
+        cachedend = curpos;
+        }
+     cachedstart = min(cachedstart, curpos);
+#endif
+     ssize_t bytesRead = safe_read(fd, Data, Size);
+#ifdef USE_FADVISE
+     if (bytesRead > 0) {
+        curpos += bytesRead;
+        cachedend = max(cachedend, curpos);
+
+        // Read ahead:
+        // no jump? (allow small forward jump still inside readahead window).
+        if (jumped >= 0 && jumped <= (off_t)readahead) {
+           // Trigger the readahead IO, but only if we've used at least
+           // 1/2 of the previously requested area. This avoids calling
+           // fadvise() after every read() call.
+           if (ahead - curpos < (off_t)(readahead / 2)) {
+              posix_fadvise(fd, curpos, readahead, POSIX_FADV_WILLNEED);
+              ahead = curpos + readahead;
+              cachedend = max(cachedend, ahead);
+              }
+           if (readahead < Size * 32) { // automagically tune readahead size.
+              readahead = Size * 32;
+              }
+           }
+        else
+           ahead = curpos; // jumped -> we really don't want any readahead, otherwise e.g. fast-rewind gets in trouble.
+        }
+
+     if (cachedstart < cachedend) {
+        if (curpos - cachedstart > READCHUNK * 2) {
+           // current position has moved forward enough, shrink tail window.
+           FadviseDrop(cachedstart, curpos - READCHUNK - cachedstart);
+           cachedstart = curpos - READCHUNK;
+           }
+        else if (cachedend > ahead && cachedend - curpos > READCHUNK * 2) {
+           // current position has moved back enough, shrink head window.
+           FadviseDrop(curpos + READCHUNK, cachedend - (curpos + READCHUNK));
+           cachedend = curpos + READCHUNK;
+           }
+        }
+     lastpos = curpos;
+#endif
+     return bytesRead;
+     }
+  return -1;
+}
+
+ssize_t cUnbufferedFile::Write(const void *Data, size_t Size)
+{
+  if (fd >=0) {
+     ssize_t bytesWritten = safe_write(fd, Data, Size);
+#ifdef USE_FADVISE
+     if (bytesWritten > 0) {
+        begin = min(begin, curpos);
+        curpos += bytesWritten;
+        written += bytesWritten;
+        lastpos = max(lastpos, curpos);
+        if (written > WRITE_BUFFER) {
+           if (lastpos > begin) {
+              // Now do three things:
+              // 1) Start writeback of begin..lastpos range
+              // 2) Drop the already written range (by the previous fadvise call)
+              // 3) Handle nonpagealigned data.
+              //    This is why we double the WRITE_BUFFER; the first time around the
+              //    last (partial) page might be skipped, writeback will start only after
+              //    second call; the third call will still include this page and finally
+              //    drop it from cache.
+              off_t headdrop = min(begin, off_t(WRITE_BUFFER * 2));
+              posix_fadvise(fd, begin - headdrop, lastpos - begin + headdrop, POSIX_FADV_DONTNEED);
+              }
+           begin = lastpos = curpos;
+           totwritten += written;
+           written = 0;
+           // The above fadvise() works when writing slowly (recording), but could
+           // leave cached data around when writing at a high rate, e.g. when cutting,
+           // because by the time we try to flush the cached pages (above) the data
+           // can still be dirty - we are faster than the disk I/O.
+           // So we do another round of flushing, just like above, but at larger
+           // intervals -- this should catch any pages that couldn't be released
+           // earlier.
+           if (totwritten > MEGABYTE(32)) {
+              // It seems in some setups, fadvise() does not trigger any I/O and
+              // a fdatasync() call would be required do all the work (reiserfs with some
+              // kind of write gathering enabled), but the syncs cause (io) load..
+              // Uncomment the next line if you think you need them.
+              //fdatasync(fd);
+              off_t headdrop = min(off_t(curpos - totwritten), off_t(totwritten * 2));
+              posix_fadvise(fd, curpos - totwritten - headdrop, totwritten + headdrop, POSIX_FADV_DONTNEED);
+              totwritten = 0;
+              }
+           }
+        }
+#endif
+     return bytesWritten;
+     }
+  return -1;
+}
+
+cUnbufferedFile *cUnbufferedFile::Create(const char *FileName, int Flags, mode_t Mode)
+{
+  cUnbufferedFile *File = new cUnbufferedFile;
+  if (File->Open(FileName, Flags, Mode) < 0) {
+     delete File;
+     File = NULL;
+     }
+  return File;
+}
+
+// --- cReadDir --------------------------------------------------------------
+
+cReadDir::cReadDir(const char *Directory)
+{
+  directory = opendir(Directory);
+}
+
+cReadDir::~cReadDir()
+{
+  if (directory)
+    closedir(directory);
+}
+
+struct dirent *cReadDir::Next(void)
+{
+  return directory && readdir_r(directory, &u.d, &result) == 0 ? result : NULL;
+}
+
+// --- cReadLine -------------------------------------------------------------
+
+cReadLine::cReadLine(void)
+{
+  size = 0;
+  buffer = NULL;
+}
+
+cReadLine::~cReadLine()
+{
+  free(buffer);
+}
+
+char *cReadLine::Read(FILE *f)
+{
+  int n = getline(&buffer, &size, f);
+  if (n > 0) {
+     n--;
+     if (buffer[n] == '\n') {
+        buffer[n] = 0;
+        if (n > 0) {
+           n--;
+           if (buffer[n] == '\r')
+              buffer[n] = 0;
+           }
+        }
+     return buffer;
+     }
+  return NULL;
+}
diff --git a/xbmc/pvrclients/vdr-vnsi/tools.h b/xbmc/pvrclients/vdr-vnsi/tools.h
new file mode 100644 (file)
index 0000000..df2323f
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef __TOOLS_H
+#define __TOOLS_H
+
+#include "pvrclient-vdrVNSI_os.h"
+#include "client.h"
+#include "StdString.h"
+#include <errno.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <dirent.h>
+#if !defined(__WINDOWS__)
+#include <sys/stat.h>
+#endif
+
+#ifdef __APPLE__
+#include <osx/OSXGNUReplacements.h>
+#endif
+
+#define TS_SYNC_BYTE          0x47
+#define TS_SIZE               188
+#define TS_ERROR              0x80
+#define TS_PAYLOAD_START      0x40
+#define TS_TRANSPORT_PRIORITY 0x20
+#define TS_PID_MASK_HI        0x1F
+#define TS_SCRAMBLING_CONTROL 0xC0
+#define TS_ADAPT_FIELD_EXISTS 0x20
+#define TS_PAYLOAD_EXISTS     0x10
+#define TS_CONT_CNT_MASK      0x0F
+#define TS_ADAPT_DISCONT      0x80
+#define TS_ADAPT_RANDOM_ACC   0x40 // would be perfect for detecting independent frames, but unfortunately not used by all broadcasters
+#define TS_ADAPT_ELEM_PRIO    0x20
+#define TS_ADAPT_PCR          0x10
+#define TS_ADAPT_OPCR         0x08
+#define TS_ADAPT_SPLICING     0x04
+#define TS_ADAPT_TP_PRIVATE   0x02
+#define TS_ADAPT_EXTENSION    0x01
+
+#define ERRNUL(e) {errno=e;return 0;}
+#define ERRSYS(e) {errno=e;return -1;}
+
+#define SECSINDAY  86400
+
+#define KILOBYTE(n) ((n) * 1024)
+#define MEGABYTE(n) ((n) * 1024LL * 1024LL)
+
+#define MALLOC(type, size)  (type *)malloc(sizeof(type) * (size))
+
+#define DELETENULL(p) (delete (p), p = NULL)
+//
+#define FATALERRNO (errno && errno != EAGAIN && errno != EINTR)
+
+#define DEVDBG(x...)// do{ printf("DBG - "); printf(x); printf("\n"); } while(0)
+
+uint64_t ntohll(uint64_t a);
+uint64_t htonll(uint64_t a);
+
+ssize_t safe_read(int filedes, void *buffer, size_t size);
+ssize_t safe_write(int filedes, const void *buffer, size_t size);
+void writechar(int filedes, char c);
+int WriteAllOrNothing(int fd, const unsigned char *Data, int Length, int TimeoutMs = 0, int RetryMs = 0);
+    ///< Writes either all Data to the given file descriptor, or nothing at all.
+    ///< If TimeoutMs is greater than 0, it will only retry for that long, otherwise
+    ///< it will retry forever. RetryMs defines the time between two retries.
+char *strcpyrealloc(char *dest, const char *src);
+char *strn0cpy(char *dest, const char *src, size_t n);
+char *strreplace(char *s, char c1, char c2);
+char *strreplace(char *s, const char *s1, const char *s2); ///< re-allocates 's' and deletes the original string if necessary!
+inline char *skipspace(const char *s)
+{
+  if ((unsigned char)*s > ' ') // most strings don't have any leading space, so handle this case as fast as possible
+     return (char *)s;
+  while (*s && (unsigned char)*s <= ' ') // avoiding isspace() here, because it is much slower
+        s++;
+  return (char *)s;
+}
+char *stripspace(char *s);
+char *compactspace(char *s);
+bool startswith(const char *s, const char *p);
+bool endswith(const char *s, const char *p);
+bool isempty(const char *s);
+int numdigits(int n);
+bool IsNumber(const char *s);
+CStdString AddDirectory(const char *DirName, const char *FileName);
+char *ReadLink(const char *FileName); ///< returns a new string allocated on the heap, which the caller must delete (or NULL in case of an error)
+
+class cTimeMs
+{
+private:
+  uint64_t begin;
+public:
+  cTimeMs(int Ms = 0);
+      ///< Creates a timer with ms resolution and an initial timeout of Ms.
+  static uint64_t Now(void);
+  void Set(int Ms = 0);
+  bool TimedOut(void);
+  uint64_t Elapsed(void);
+};
+
+class cPoller
+{
+private:
+  enum { MaxPollFiles = 16 };
+  pollfd pfd[MaxPollFiles];
+  int numFileHandles;
+public:
+  cPoller(int FileHandle = -1, bool Out = false);
+  bool Add(int FileHandle, bool Out);
+  bool Poll(int TimeoutMs = 0);
+};
+
+class cFile
+{
+private:
+  static bool files[];
+  static int maxFiles;
+  int f;
+public:
+  cFile(void);
+  ~cFile();
+  operator int () { return f; }
+  bool Open(const char *FileName, int Flags, mode_t Mode = DEFFILEMODE);
+  bool Open(int FileDes);
+  void Close(void);
+  bool IsOpen(void) { return f >= 0; }
+  bool Ready(bool Wait = true);
+  static bool AnyFileReady(int FileDes = -1, int TimeoutMs = 1000);
+  static bool FileReady(int FileDes, int TimeoutMs = 1000);
+  static bool FileReadyForWriting(int FileDes, int TimeoutMs = 1000);
+};
+
+/// cUnbufferedFile is used for large files that are mainly written or read
+/// in a streaming manner, and thus should not be cached.
+
+class cUnbufferedFile
+{
+private:
+  int fd;
+  off_t curpos;
+  off_t cachedstart;
+  off_t cachedend;
+  off_t begin;
+  off_t lastpos;
+  off_t ahead;
+  size_t readahead;
+  size_t written;
+  size_t totwritten;
+  int FadviseDrop(off_t Offset, off_t Len);
+public:
+  cUnbufferedFile(void);
+  ~cUnbufferedFile();
+  int Open(const char *FileName, int Flags, mode_t Mode = DEFFILEMODE);
+  int Close(void);
+  void SetReadAhead(size_t ra);
+  off_t Seek(off_t Offset, int Whence);
+  ssize_t Read(void *Data, size_t Size);
+  ssize_t Write(const void *Data, size_t Size);
+  static cUnbufferedFile *Create(const char *FileName, int Flags, mode_t Mode = DEFFILEMODE);
+};
+
+class cReadDir
+{
+private:
+  DIR *directory;
+  struct dirent *result;
+  union // according to "The GNU C Library Reference Manual"
+  {
+    struct dirent d;
+    char b[offsetof(struct dirent, d_name) + NAME_MAX + 1];
+  } u;
+public:
+  cReadDir(const char *Directory);
+  ~cReadDir();
+  bool Ok(void) { return directory != NULL; }
+  struct dirent *Next(void);
+};
+
+class cReadLine
+{
+private:
+  size_t size;
+  char *buffer;
+public:
+  cReadLine(void);
+  ~cReadLine();
+  char *Read(FILE *f);
+};
+
+inline int CompareStrings(const void *a, const void *b)
+{
+  return strcmp(*(const char **)a, *(const char **)b);
+}
+
+
+#endif //__TOOLS_H
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/COPYING b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/COPYING
new file mode 100644 (file)
index 0000000..f90922e
--- /dev/null
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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 of the License, 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 this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/HISTORY b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/HISTORY
new file mode 100644 (file)
index 0000000..756fa44
--- /dev/null
@@ -0,0 +1,6 @@
+VDR Plugin 'vnsiserver' Revision History
+----------------------------------------
+
+2010-03-23: Version 0.0.1
+
+- Initial revision.
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/Makefile b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/Makefile
new file mode 100644 (file)
index 0000000..9d8bafa
--- /dev/null
@@ -0,0 +1,123 @@
+#
+# Makefile for a Video Disk Recorder plugin
+#
+# $Id$
+
+# Allow console debug messages
+#CONSOLEDEBUG = 1
+
+# The official name of this plugin.
+# This name will be used in the '-P...' option of VDR to load the plugin.
+# By default the main source file also carries this name.
+# IMPORTANT: the presence of this macro is important for the Make.config
+# file. So it must be defined, even if it is not used here!
+#
+PLUGIN = vnsiserver
+
+### The version number of this plugin (taken from the main source file):
+
+VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ print $$6 }' | sed -e 's/[";]//g')
+
+### The C++ compiler and options:
+
+CXX      ?= g++
+CXXFLAGS ?= -fPIC -g -O2 -Wall -Woverloaded-virtual -Wno-parentheses
+
+### The directory environment:
+
+VDRDIR = ../../..
+LIBDIR = ../../lib
+TMPDIR = /tmp
+
+### Allow user defined options to overwrite defaults:
+
+-include $(VDRDIR)/Make.config
+-include $(VDRDIR)/Make.global
+
+### The version number of VDR's plugin API (taken from VDR's "config.h"):
+
+APIVERSION = $(shell sed -ne '/define APIVERSION/s/^.*"\(.*\)".*$$/\1/p' $(VDRDIR)/config.h)
+
+### The name of the distribution archive:
+
+ARCHIVE = $(PLUGIN)-$(VERSION)
+PACKAGE = vdr-$(ARCHIVE)
+
+### Includes and Defines (add further entries here):
+
+INCLUDES += -I$(VDRDIR)/include
+
+DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
+
+DEFINES += -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -DVNSI_SERVER_VERSION='"$(VERSION)"'
+ifdef CONSOLEDEBUG
+DEFINES += -DCONSOLEDEBUG
+endif
+LIBS =
+
+### The object files (add further files here):
+
+OBJS = $(PLUGIN).o bitstream.o cmdcontrol.o connection.o config.o cxsocket.o demuxer.o demuxer_AAC.o \
+       demuxer_AC3.o demuxer_DTS.o demuxer_h264.o demuxer_MPEGAudio.o demuxer_MPEGVideo.o \
+       demuxer_Subtitle.o demuxer_Teletext.o receiver.o recplayer.o requestpacket.o responsepacket.o \
+       server.o suspend.o tools.o
+
+### The main target:
+
+all: libvdr-$(PLUGIN).so i18n
+
+### Implicit rules:
+
+%.o: %.c
+       $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $<
+
+### Dependencies:
+
+MAKEDEP = $(CXX) -MM -MG
+DEPFILE = .dependencies
+$(DEPFILE): Makefile
+       @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@
+
+-include $(DEPFILE)
+
+### Internationalization (I18N):
+
+PODIR     = po
+LOCALEDIR = $(VDRDIR)/locale
+I18Npo    = $(wildcard $(PODIR)/*.po)
+I18Nmsgs  = $(addprefix $(LOCALEDIR)/, $(addsuffix /LC_MESSAGES/vdr-$(PLUGIN).mo, $(notdir $(foreach file, $(I18Npo), $(basename $(file))))))
+I18Npot   = $(PODIR)/$(PLUGIN).pot
+
+%.mo: %.po
+       msgfmt -c -o $@ $<
+
+$(I18Npot): $(wildcard *.c)
+       xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP --msgid-bugs-address='<see README>' -o $@ $^
+
+%.po: $(I18Npot)
+       msgmerge -U --no-wrap --no-location --backup=none -q $@ $<
+       @touch $@
+
+$(I18Nmsgs): $(LOCALEDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo
+       @mkdir -p $(dir $@)
+       cp $< $@
+
+.PHONY: i18n
+i18n: $(I18Nmsgs) $(I18Npot)
+
+### Targets:
+
+libvdr-$(PLUGIN).so: $(OBJS)
+       $(CXX) $(CXXFLAGS) -shared $(LIBS) $(OBJS) -o $@
+       @cp --remove-destination $@ $(LIBDIR)/$@.$(APIVERSION)
+
+dist: clean
+       @-rm -rf $(TMPDIR)/$(ARCHIVE)
+       @mkdir $(TMPDIR)/$(ARCHIVE)
+       @cp -a * $(TMPDIR)/$(ARCHIVE)
+       @tar czf $(PACKAGE).tgz -C $(TMPDIR) $(ARCHIVE)
+       @-rm -rf $(TMPDIR)/$(ARCHIVE)
+       @echo Distribution package created as $(PACKAGE).tgz
+
+clean:
+       @-rm -f $(OBJS) $(DEPFILE) *.so *.tgz core* *~ $(PODIR)/*.mo $(PODIR)/*.pot
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/README b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/README
new file mode 100644 (file)
index 0000000..c0a93a0
--- /dev/null
@@ -0,0 +1,43 @@
+This is a "plugin" for the Video Disk Recorder (VDR).
+
+Written by:                  Your Name <email@host.dom>
+
+Project's homepage:          URL
+
+Latest version available at: URL
+
+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 of the License, or
+(at your option) any later version.
+See the file COPYING for more information.
+
+Description:
+
+
+VNSI support also dynamic PID switching of the received DVB-TS stream. Further it detect and demuxing several
+not by VDR implemented Audio Streams, this are:
+- Enhanced AC3 (not tested)
+- Advanced Audio Coding (AAC) (not tested)
+- DTS (demuxer not finished now, and does not work)
+
+
+CHANNEL SCANNING
+----------------
+For channel scan's a modified version of the wirbelscan plugin (Version dev-0.0.5-pre11e) is
+required. You can download the orginal source from here "http://wirbel.htpc-forum.de/wirbelscan/index2.html".
+
+The VNSI communicate with wirbelscan over VDR's plugin service interface, to add this feature
+you must patch wirbelscan with the file in "patches/vdr-wirbelscan-0.0.5-pre11e-AddServiceInterface.diff".
+
+Scanning can take up to 50 minutes dependet on signal type and signal quality.
+    * DVB-T ~ 5 min
+    * DVB-C ~ 30 min (Symbolrate=AUTO, QAM=AUTO)
+    * DVB-S/S2 ~ 50 min (depend on Satellite, Beam, Hardware)
+    * Analog ~ 5 min
+
+Note: Please notice the warning on the wirbelscan plugin homepage:
+      "Development Version - kein Support. Benutzung auf eigene Verantwortung."
+      "Development Version - no support. Use at your own risk."
+      This means, the time how long the scan run and problems on VDR side are dependet on wirbelscan
+      and not part of VNSI.
\ No newline at end of file
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/bitstream.c b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/bitstream.c
new file mode 100644 (file)
index 0000000..9a699bb
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <stdio.h>
+#include <inttypes.h>
+#include "bitstream.h"
+
+cBitstream::cBitstream(uint8_t *data, int bits)
+{
+  m_data   = data;
+  m_offset = 0;
+  m_len    = bits;
+}
+
+void cBitstream::setBitstream(uint8_t *data, int bits)
+{
+  m_data   = data;
+  m_offset = 0;
+  m_len    = bits;
+}
+
+void cBitstream::skipBits(int num)
+{
+  m_offset += num;
+}
+
+unsigned int cBitstream::readBits(int num)
+{
+  int r = 0;
+
+  while(num > 0)
+  {
+    if(m_offset >= m_len)
+      return 0;
+
+    num--;
+
+    if(m_data[m_offset / 8] & (1 << (7 - (m_offset & 7))))
+      r |= 1 << num;
+
+    m_offset++;
+  }
+  return r;
+}
+
+unsigned int cBitstream::showBits(int num)
+{
+  int r = 0;
+  int offs = m_offset;
+
+  while(num > 0)
+  {
+    if(offs >= m_len)
+      return 0;
+
+    num--;
+
+    if(m_data[offs / 8] & (1 << (7 - (offs & 7))))
+      r |= 1 << num;
+
+    offs++;
+  }
+  return r;
+}
+
+unsigned int cBitstream::readGolombUE()
+{
+  int lzb = -1;
+
+  for(int b = 0; !b; lzb++)
+    b = readBits1();
+
+  return (1 << lzb) - 1 + readBits(lzb);
+}
+
+signed int cBitstream::readGolombSE()
+{
+  int v, neg;
+  v = readGolombUE();
+  if(v == 0)
+    return 0;
+
+  neg = v & 1;
+  v = (v + 1) >> 1;
+  return neg ? -v : v;
+}
+
+
+unsigned int cBitstream::remainingBits()
+{
+  return m_len - m_offset;
+}
+
+
+void cBitstream::putBits(int val, int num)
+{
+  while(num > 0) {
+    if(m_offset >= m_len)
+      return;
+
+    num--;
+
+    if(val & (1 << num))
+      m_data[m_offset / 8] |= 1 << (7 - (m_offset & 7));
+    else
+      m_data[m_offset / 8] &= ~(1 << (7 - (m_offset & 7)));
+
+    m_offset++;
+  }
+}
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/bitstream.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/bitstream.h
new file mode 100644 (file)
index 0000000..e1fa361
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef BITSTREAM_H_
+#define BITSTREAM_H_
+
+class cBitstream
+{
+private:
+  uint8_t *m_data;
+  int      m_offset;
+  int      m_len;
+
+public:
+  cBitstream(uint8_t *data, int bits);
+
+  void         setBitstream(uint8_t *data, int bits);
+  void         skipBits(int num);
+  unsigned int readBits(int num);
+  unsigned int showBits(int num);
+  unsigned int readBits1() { return readBits(1); }
+  unsigned int readGolombUE();
+  signed int   readGolombSE();
+  unsigned int remainingBits();
+  void         putBits(int val, int num);
+  int          length() { return m_len; }
+};
+
+#endif /* BITSTREAM_H_ */
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/cmdcontrol.c b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/cmdcontrol.c
new file mode 100644 (file)
index 0000000..8f8f9da
--- /dev/null
@@ -0,0 +1,1675 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <stdlib.h>
+#include <vdr/recording.h>
+#include <vdr/channels.h>
+#include <vdr/videodir.h>
+#include <vdr/plugin.h>
+#include <vdr/timers.h>
+#include <vdr/menu.h>
+
+#include "config.h"
+#include "cmdcontrol.h"
+#include "connection.h"
+#include "recplayer.h"
+#include "vdrcommand.h"
+#include "wirbelscanservice.h" /// copied from modified wirbelscan plugin
+                               /// must be hold up to date with wirbelscan
+
+cCmdControl::cCmdControl()
+{
+  m_req                   = NULL;
+  m_resp                  = NULL;
+  m_processSCAN_Response  = NULL;
+  m_processSCAN_Socket    = NULL;
+
+  Start();
+}
+
+cCmdControl::~cCmdControl()
+{
+  Cancel(10);
+}
+
+bool cCmdControl::recvRequest(cRequestPacket* newRequest)
+{
+  m_mutex.Lock();
+  m_req_queue.push(newRequest);
+  m_mutex.Unlock();
+  m_Wait.Signal();
+  return true;
+}
+
+void cCmdControl::Action(void)
+{
+  while (Running())
+  {
+    LOGCONSOLE("threadMethod waiting");
+    m_Wait.Wait(500);  // unlocks, waits, relocks
+
+    m_mutex.Lock();
+    size_t s = m_req_queue.size();
+    m_mutex.Unlock();
+
+    while(s > 0)
+    {
+      m_mutex.Lock();
+      m_req = m_req_queue.front();
+      m_req_queue.pop();
+      s = m_req_queue.size();
+      m_mutex.Unlock();
+
+      if (!processPacket())
+      {
+        esyslog("VNSI-Error: Response handler failed during processPacket, exiting thread");
+        continue;
+      }
+    }
+  }
+}
+
+bool cCmdControl::processPacket()
+{
+  m_resp = new cResponsePacket();
+  if (!m_resp->init(m_req->getRequestID()))
+  {
+    esyslog("VNSI-Error: Response packet init fail");
+    delete m_resp;
+    delete m_req;
+    m_resp = NULL;
+    return false;
+  }
+
+  bool result = false;
+  switch(m_req->getOpCode())
+  {
+    /** OPCODE 1 - 19: VNSI network functions for general purpose */
+    case VDR_LOGIN:
+      result = process_Login();
+      break;
+
+    case VDR_GETTIME:
+      result = process_GetTime();
+      break;
+
+    case VDR_ENABLESTATUSINTERFACE:
+      result = process_EnableStatusInterface();
+      break;
+
+    case VDR_ENABLEOSDINTERFACE:
+      result = process_EnableOSDInterface();
+      break;
+
+
+    /** OPCODE 20 - 39: VNSI network functions for live streaming */
+    /** NOTE: Live streaming opcodes are handled by cConnection::Action(void) */
+
+
+    /** OPCODE 40 - 59: VNSI network functions for recording streaming */
+    case VDR_RECSTREAM_OPEN:
+      result = processRecStream_Open();
+      break;
+
+    case VDR_RECSTREAM_CLOSE:
+      result = processRecStream_Close();
+      break;
+
+    case VDR_RECSTREAM_GETBLOCK:
+      result = processRecStream_GetBlock();
+      break;
+
+    case VDR_RECSTREAM_POSTOFRAME:
+      result = processRecStream_PositionFromFrameNumber();
+      break;
+
+    case VDR_RECSTREAM_FRAMETOPOS:
+      result = processRecStream_FrameNumberFromPosition();
+      break;
+
+    case VDR_RECSTREAM_GETIFRAME:
+      result = processRecStream_GetIFrame();
+      break;
+
+
+    /** OPCODE 60 - 79: VNSI network functions for channel access */
+    case VDR_CHANNELS_GROUPSCOUNT:
+      result = processCHANNELS_GroupsCount();
+      break;
+
+    case VDR_CHANNELS_GETCOUNT:
+      result = processCHANNELS_ChannelsCount();
+      break;
+
+    case VDR_CHANNELS_GETGROUPS:
+      result = processCHANNELS_GroupList();
+      break;
+
+    case VDR_CHANNELS_GETCHANNELS:
+      result = processCHANNELS_GetChannels();
+      break;
+
+
+    /** OPCODE 80 - 99: VNSI network functions for timer access */
+    case VDR_TIMER_GETCOUNT:
+      result = processTIMER_GetCount();
+      break;
+
+    case VDR_TIMER_GET:
+      result = processTIMER_Get();
+      break;
+
+    case VDR_TIMER_GETLIST:
+      result = processTIMER_GetList();
+      break;
+
+    case VDR_TIMER_ADD:
+      result = processTIMER_Add();
+      break;
+
+    case VDR_TIMER_DELETE:
+      result = processTIMER_Delete();
+      break;
+
+    case VDR_TIMER_UPDATE:
+      result = processTIMER_Update();
+      break;
+
+
+    /** OPCODE 100 - 119: VNSI network functions for recording access */
+    case VDR_RECORDINGS_DISKSIZE:
+      result = processRECORDINGS_GetDiskSpace();
+      break;
+
+    case VDR_RECORDINGS_GETCOUNT:
+      result = processRECORDINGS_GetCount();
+      break;
+
+    case VDR_RECORDINGS_GETLIST:
+      result = processRECORDINGS_GetList();
+      break;
+
+    case VDR_RECORDINGS_GETINFO:
+      result = processRECORDINGS_GetInfo();
+      break;
+
+    case VDR_RECORDINGS_DELETE:
+      result = processRECORDINGS_Delete();
+      break;
+
+    case VDR_RECORDINGS_MOVE:
+      result = processRECORDINGS_Move();
+      break;
+
+
+    /** OPCODE 120 - 139: VNSI network functions for epg access and manipulating */
+    case VDR_EPG_GETFORCHANNEL:
+      result = processEPG_GetForChannel();
+      break;
+
+
+    /** OPCODE 140 - 159: VNSI network functions for channel scanning */
+    case VDR_SCAN_SUPPORTED:
+      result = processSCAN_ScanSupported();
+      break;
+
+    case VDR_SCAN_GETCOUNTRIES:
+      result = processSCAN_GetCountries();
+      break;
+
+    case VDR_SCAN_GETSATELLITES:
+      result = processSCAN_GetSatellites();
+      break;
+
+    case VDR_SCAN_START:
+      result = processSCAN_Start();
+      break;
+
+    case VDR_SCAN_STOP:
+      result = processSCAN_Stop();
+      break;
+  }
+
+  delete m_resp;
+  m_resp = NULL;
+
+  delete m_req;
+  m_req = NULL;
+
+  return result;
+}
+
+
+/** OPCODE 1 - 19: VNSI network functions for general purpose */
+
+bool cCmdControl::process_Login() /* OPCODE 1 */
+{
+  if (m_req->getDataLength() <= 4) return false;
+
+  uint32_t protocolVersion  = m_req->extract_U32();
+  bool netLog               = m_req->extract_U8();
+  const char *clientName    = m_req->extract_String();
+
+  if (protocolVersion != VNSIProtocolVersion)
+  {
+    esyslog("VNSI-Error: Client '%s' have a not allowed protocol version '%u', terminating client", clientName, protocolVersion);
+    delete[] clientName;
+    return false;
+  }
+
+  isyslog("VNSI: Welcome client '%s' with protocol version '%u'", clientName, protocolVersion);
+
+  if (netLog)
+    m_req->getClient()->EnableNetLog(true, clientName);
+
+  // Send the login reply
+  time_t timeNow        = time(NULL);
+  struct tm* timeStruct = localtime(&timeNow);
+  int timeOffset        = timeStruct->tm_gmtoff;
+
+  m_resp->add_U32(VNSIProtocolVersion);
+  m_resp->add_U32(timeNow);
+  m_resp->add_S32(timeOffset);
+  m_resp->add_String("VDR-Network-Streaming-Interface (VNSI) Server");
+  m_resp->add_String(VNSI_SERVER_VERSION);
+  m_resp->finalise();
+  m_req->getClient()->GetSocket()->write(m_resp->getPtr(), m_resp->getLen());
+
+  m_req->getClient()->SetLoggedIn(true);
+  delete[] clientName;
+
+  return true;
+}
+
+bool cCmdControl::process_GetTime() /* OPCODE 2 */
+{
+  time_t timeNow        = time(NULL);
+  struct tm* timeStruct = localtime(&timeNow);
+  int timeOffset        = timeStruct->tm_gmtoff;
+
+  m_resp->add_U32(timeNow);
+  m_resp->add_S32(timeOffset);
+  m_resp->finalise();
+  m_req->getClient()->GetSocket()->write(m_resp->getPtr(), m_resp->getLen());
+  return true;
+}
+
+bool cCmdControl::process_EnableStatusInterface()
+{
+  bool enabled = m_req->extract_U8();
+
+  m_req->getClient()->SetStatusInterface(enabled);
+
+  m_resp->add_U32(VDR_RET_OK);
+  m_resp->finalise();
+  m_req->getClient()->GetSocket()->write(m_resp->getPtr(), m_resp->getLen());
+  return true;
+}
+
+bool cCmdControl::process_EnableOSDInterface()
+{
+  bool enabled = m_req->extract_U8();
+
+  m_req->getClient()->SetOSDInterface(enabled);
+
+  m_resp->add_U32(VDR_RET_OK);
+  m_resp->finalise();
+  m_req->getClient()->GetSocket()->write(m_resp->getPtr(), m_resp->getLen());
+  return true;
+}
+
+
+/** OPCODE 20 - 39: VNSI network functions for live streaming */
+/** NOTE: Live streaming opcodes are handled by cConnection::Action(void) */
+
+
+/** OPCODE 40 - 59: VNSI network functions for recording streaming */
+
+bool cCmdControl::processRecStream_Open() /* OPCODE 40 */
+{
+  const char *fileName  = m_req->extract_String();
+  cRecording *recording = Recordings.GetByName(fileName);
+
+  LOGCONSOLE("%s: recording pointer %p", fileName, recording);
+
+  if (recording)
+  {
+    m_req->getClient()->m_RecPlayer = new cRecPlayer(recording);
+
+    m_resp->add_U32(VDR_RET_OK);
+    m_resp->add_U32(m_req->getClient()->m_RecPlayer->getLengthFrames());
+    m_resp->add_U64(m_req->getClient()->m_RecPlayer->getLengthBytes());
+
+#if VDRVERSNUM < 10703
+    m_resp->add_U8(true);//added for TS
+#else
+    m_resp->add_U8(recording->IsPesRecording());//added for TS
+#endif
+
+    LOGCONSOLE("written totalLength");
+  }
+  else
+  {
+    m_resp->add_U32(VDR_RET_DATAUNKNOWN);
+
+    LOGCONSOLE("recording '%s' not found", fileName);
+  }
+
+  m_resp->finalise();
+  m_req->getClient()->GetSocket()->write(m_resp->getPtr(), m_resp->getLen());
+  delete[] fileName;
+
+  return true;
+}
+
+bool cCmdControl::processRecStream_Close() /* OPCODE 41 */
+{
+  if (m_req->getClient()->m_RecPlayer)
+  {
+    delete m_req->getClient()->m_RecPlayer;
+    m_req->getClient()->m_RecPlayer = NULL;
+  }
+
+  m_resp->add_U32(VDR_RET_OK);
+  m_resp->finalise();
+  m_req->getClient()->GetSocket()->write(m_resp->getPtr(), m_resp->getLen());
+  return true;
+}
+
+bool cCmdControl::processRecStream_GetBlock() /* OPCODE 42 */
+{
+  if (m_req->getClient()->IsStreaming())
+  {
+    esyslog("VNSI-Error: Get block called during live streaming");
+    return false;
+  }
+
+  if (!m_req->getClient()->m_RecPlayer)
+  {
+    esyslog("VNSI-Error: Get block called when no recording open");
+    return false;
+  }
+
+  uint64_t position  = m_req->extract_U64();
+  uint32_t amount    = m_req->extract_U32();
+
+//  LOGCONSOLE("getblock pos = %llu length = %lu", position, amount);
+
+  uint8_t* p = m_resp->reserve(amount);
+  uint32_t amountReceived = m_req->getClient()->m_RecPlayer->getBlock(p, position, amount);
+
+  if(amount > amountReceived) m_resp->unreserve(amount - amountReceived);
+
+  if (!amountReceived)
+  {
+    m_resp->add_U32(0);
+    LOGCONSOLE("written 4(0) as getblock got 0");
+  }
+
+  m_resp->finalise();
+  m_req->getClient()->GetSocket()->write(m_resp->getPtr(), m_resp->getLen());
+//  LOGCONSOLE("Finished getblock, have sent %lu", m_resp->getLen());
+  return true;
+}
+
+bool cCmdControl::processRecStream_PositionFromFrameNumber() /* OPCODE 43 */
+{
+  uint64_t retval       = 0;
+  uint32_t frameNumber  = m_req->extract_U32();
+
+  if (m_req->getClient()->m_RecPlayer)
+    retval = m_req->getClient()->m_RecPlayer->positionFromFrameNumber(frameNumber);
+
+  m_resp->add_U64(retval);
+  m_resp->finalise();
+  m_req->getClient()->GetSocket()->write(m_resp->getPtr(), m_resp->getLen());
+
+  LOGCONSOLE("Wrote posFromFrameNum reply to client");
+  return true;
+}
+
+bool cCmdControl::processRecStream_FrameNumberFromPosition() /* OPCODE 44 */
+{
+  uint32_t retval   = 0;
+  uint64_t position = m_req->extract_U64();
+
+  if (m_req->getClient()->m_RecPlayer)
+    retval = m_req->getClient()->m_RecPlayer->frameNumberFromPosition(position);
+
+  m_resp->add_U32(retval);
+  m_resp->finalise();
+  m_req->getClient()->GetSocket()->write(m_resp->getPtr(), m_resp->getLen());
+
+  LOGCONSOLE("Wrote frameNumFromPos reply to client");
+  return true;
+}
+
+bool cCmdControl::processRecStream_GetIFrame() /* OPCODE 45 */
+{
+  bool success            = false;
+  uint32_t frameNumber    = m_req->extract_U32();
+  uint32_t direction      = m_req->extract_U32();
+  uint64_t rfilePosition  = 0;
+  uint32_t rframeNumber   = 0;
+  uint32_t rframeLength   = 0;
+
+  if (m_req->getClient()->m_RecPlayer)
+    success = m_req->getClient()->m_RecPlayer->getNextIFrame(frameNumber, direction, &rfilePosition, &rframeNumber, &rframeLength);
+
+  // returns file position, frame number, length
+  if (success)
+  {
+    m_resp->add_U64(rfilePosition);
+    m_resp->add_U32(rframeNumber);
+    m_resp->add_U32(rframeLength);
+  }
+  else
+  {
+    m_resp->add_U32(0);
+  }
+
+  m_resp->finalise();
+  m_req->getClient()->GetSocket()->write(m_resp->getPtr(), m_resp->getLen());
+
+  LOGCONSOLE("Wrote GNIF reply to client %llu %lu %lu", rfilePosition, rframeNumber, rframeLength);
+  return true;
+}
+
+
+/** OPCODE 60 - 79: VNSI network functions for channel access */
+
+bool cCmdControl::processCHANNELS_GroupsCount() /* OPCODE 60 */
+{
+  int count = 0;
+  for (int curr = Channels.GetNextGroup(-1); curr >= 0; curr = Channels.GetNextGroup(curr))
+    count++;
+
+  m_resp->add_U32(count);
+
+  m_resp->finalise();
+  m_req->getClient()->GetSocket()->write(m_resp->getPtr(), m_resp->getLen());
+  return true;
+}
+
+bool cCmdControl::processCHANNELS_ChannelsCount() /* OPCODE 61 */
+{
+  int count = Channels.MaxNumber();
+
+  m_resp->add_U32(count);
+
+  m_resp->finalise();
+  m_req->getClient()->GetSocket()->write(m_resp->getPtr(), m_resp->getLen());
+  return true;
+}
+
+bool cCmdControl::processCHANNELS_GroupList() /* OPCODE 62 */
+{
+  if (m_req->getDataLength() != 4) return false;
+
+  bool radio = m_req->extract_U32();
+
+  int countInGroup = 0;
+  int index        = 0;
+  const cChannel* group = NULL;
+  for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
+  {
+    if (channel->GroupSep())
+    {
+      if (countInGroup)
+      {
+        m_resp->add_U32(index);
+        m_resp->add_U32(countInGroup);
+        m_resp->add_String(m_toUTF8.Convert(group->Name()));
+      }
+      group = channel;
+      countInGroup = 0;
+      index++;
+    }
+    else if (group)
+    {
+      if (radio)
+      {
+        if (!channel->Vpid() && channel->Apid(0))
+          countInGroup++;
+      }
+      else
+      {
+        if (channel->Vpid())
+          countInGroup++;
+      }
+    }
+  }
+
+  if (countInGroup)
+  {
+    m_resp->add_U32(index);
+    m_resp->add_U32(countInGroup);
+    m_resp->add_String(group->Name());
+  }
+
+  m_resp->finalise();
+  m_req->getClient()->GetSocket()->write(m_resp->getPtr(), m_resp->getLen());
+  return true;
+}
+
+bool cCmdControl::processCHANNELS_GetChannels() /* OPCODE 63 */
+{
+  if (m_req->getDataLength() != 4) return false;
+
+  bool radio = m_req->extract_U32();
+
+  int groupIndex = 0;
+  const cChannel* group = NULL;
+  for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
+  {
+    if (channel->GroupSep())
+    {
+      group = channel;
+      groupIndex++;
+    }
+    else
+    {
+      bool isRadio = false;
+
+      if (channel->Vpid())
+        isRadio = false;
+      else if (channel->Apid(0))
+        isRadio = true;
+      else
+        continue;
+
+      if (radio != isRadio)
+        continue;
+
+      m_resp->add_U32(channel->Number());
+      m_resp->add_String(m_toUTF8.Convert(channel->Name()));
+      m_resp->add_U32(channel->Sid());
+      m_resp->add_U32(groupIndex);
+      m_resp->add_U32(channel->Ca());
+#if APIVERSNUM >= 10701
+      m_resp->add_U32(channel->Vtype());
+#else
+      m_resp->add_U32(2);
+#endif
+    }
+  }
+
+  m_resp->finalise();
+  m_req->getClient()->GetSocket()->write(m_resp->getPtr(), m_resp->getLen());
+  return true;
+}
+
+
+/** OPCODE 80 - 99: VNSI network functions for timer access */
+
+bool cCmdControl::processTIMER_GetCount() /* OPCODE 80 */
+{
+  int count = Timers.Count();
+
+  m_resp->add_U32(count);
+
+  m_resp->finalise();
+  m_req->getClient()->GetSocket()->write(m_resp->getPtr(), m_resp->getLen());
+  return true;
+}
+
+bool cCmdControl::processTIMER_Get() /* OPCODE 81 */
+{
+  uint32_t number = m_req->extract_U32();
+
+  int numTimers = Timers.Count();
+  if (numTimers > 0)
+  {
+    cTimer *timer = Timers.Get(number-1);
+    if (timer)
+    {
+      m_resp->add_U32(VDR_RET_OK);
+
+      m_resp->add_U32(timer->Index()+1);
+      m_resp->add_U32(timer->HasFlags(tfActive));
+      m_resp->add_U32(timer->Recording());
+      m_resp->add_U32(timer->Pending());
+      m_resp->add_U32(timer->Priority());
+      m_resp->add_U32(timer->Lifetime());
+      m_resp->add_U32(timer->Channel()->Number());
+      m_resp->add_U32(timer->StartTime());
+      m_resp->add_U32(timer->StopTime());
+      m_resp->add_U32(timer->Day());
+      m_resp->add_U32(timer->WeekDays());
+      m_resp->add_String(m_toUTF8.Convert(timer->File()));
+    }
+    else
+      m_resp->add_U32(VDR_RET_DATAUNKNOWN);
+  }
+  else
+    m_resp->add_U32(VDR_RET_DATAUNKNOWN);
+
+  m_resp->finalise();
+  m_req->getClient()->GetSocket()->write(m_resp->getPtr(), m_resp->getLen());
+  return true;
+}
+
+bool cCmdControl::processTIMER_GetList() /* OPCODE 82 */
+{
+  cTimer *timer;
+  int numTimers = Timers.Count();
+
+  m_resp->add_U32(numTimers);
+
+  for (int i = 0; i < numTimers; i++)
+  {
+    timer = Timers.Get(i);
+    if (!timer)
+      continue;
+
+    m_resp->add_U32(timer->Index()+1);
+    m_resp->add_U32(timer->HasFlags(tfActive));
+    m_resp->add_U32(timer->Recording());
+    m_resp->add_U32(timer->Pending());
+    m_resp->add_U32(timer->Priority());
+    m_resp->add_U32(timer->Lifetime());
+    m_resp->add_U32(timer->Channel()->Number());
+    m_resp->add_U32(timer->StartTime());
+    m_resp->add_U32(timer->StopTime());
+    m_resp->add_U32(timer->Day());
+    m_resp->add_U32(timer->WeekDays());
+    m_resp->add_String(m_toUTF8.Convert(timer->File()));
+  }
+
+  m_resp->finalise();
+  m_req->getClient()->GetSocket()->write(m_resp->getPtr(), m_resp->getLen());
+  return true;
+}
+
+bool cCmdControl::processTIMER_Add() /* OPCODE 83 */
+{
+  uint32_t flags      = m_req->extract_U32() > 0 ? tfActive : tfNone;
+  uint32_t priority   = m_req->extract_U32();
+  uint32_t lifetime   = m_req->extract_U32();
+  uint32_t number     = m_req->extract_U32();
+  time_t startTime    = m_req->extract_U32();
+  time_t stopTime     = m_req->extract_U32();
+  time_t day          = m_req->extract_U32();
+  uint32_t weekdays   = m_req->extract_U32();
+  const char *file    = m_req->extract_String();
+  const char *aux     = m_req->extract_String();
+
+  struct tm tm_r;
+  struct tm *time = localtime_r(&startTime, &tm_r);
+  if (day <= 0)
+    day = cTimer::SetTime(startTime, 0);
+  int start = time->tm_hour * 100 + time->tm_min;
+  time = localtime_r(&stopTime, &tm_r);
+  int stop = time->tm_hour * 100 + time->tm_min;
+
+  cString buffer = cString::sprintf("%u:%i:%s:%04d:%04d:%d:%d:%s:%s\n", flags, number, *cTimer::PrintDay(day, weekdays, true), start, stop, priority, lifetime, file, aux);
+
+  delete[] file;
+  delete[] aux;
+
+  cTimer *timer = new cTimer;
+  if (timer->Parse(buffer))
+  {
+    cTimer *t = Timers.GetTimer(timer);
+    if (!t)
+    {
+      Timers.Add(timer);
+      Timers.SetModified();
+      isyslog("VNSI: Timer %s added", *timer->ToDescr());
+      m_resp->add_U32(VDR_RET_OK);
+      m_resp->finalise();
+      m_req->getClient()->GetSocket()->write(m_resp->getPtr(), m_resp->getLen());
+      return true;
+    }
+    else
+    {
+      esyslog("VNSI-Error: Timer already defined: %d %s", t->Index() + 1, *t->ToText());
+      m_resp->add_U32(VDR_RET_DATALOCKED);
+    }
+  }
+  else
+  {
+    esyslog("VNSI-Error: Error in timer settings");
+    m_resp->add_U32(VDR_RET_DATAINVALID);
+  }
+
+  delete timer;
+
+  m_resp->finalise();
+  m_req->getClient()->GetSocket()->write(m_resp->getPtr(), m_resp->getLen());
+  return true;
+}
+
+bool cCmdControl::processTIMER_Delete() /* OPCODE 84 */
+{
+  uint32_t number = m_req->extract_U32();
+  bool     force  = m_req->extract_U32();
+
+  if (number <= 0 || number > (uint32_t)Timers.Count())
+  {
+    esyslog("VNSI-Error: Unable to delete timer - invalid timer identifier");
+    m_resp->add_U32(VDR_RET_DATAINVALID);
+  }
+  else
+  {
+    cTimer *timer = Timers.Get(number-1);
+    if (timer)
+    {
+      if (!Timers.BeingEdited())
+      {
+        if (timer->Recording())
+        {
+          if (force)
+          {
+            timer->Skip();
+            cRecordControls::Process(time(NULL));
+          }
+          else
+          {
+            esyslog("VNSI-Error: Timer \"%i\" is recording and can be deleted (use force=1 to stop it)", number);
+            m_resp->add_U32(VDR_RET_RECRUNNING);
+            m_resp->finalise();
+            m_req->getClient()->GetSocket()->write(m_resp->getPtr(), m_resp->getLen());
+            return true;
+          }
+        }
+        isyslog("VNSI: Deleting timer %s", *timer->ToDescr());
+        Timers.Del(timer);
+        Timers.SetModified();
+        m_resp->add_U32(VDR_RET_OK);
+      }
+      else
+      {
+        esyslog("VNSI-Error: Unable to delete timer - timers being edited at VDR");
+        m_resp->add_U32(VDR_RET_DATALOCKED);
+      }
+    }
+    else
+    {
+      esyslog("VNSI-Error: Unable to delete timer - invalid timer identifier");
+      m_resp->add_U32(VDR_RET_DATAINVALID);
+    }
+  }
+  m_resp->finalise();
+  m_req->getClient()->GetSocket()->write(m_resp->getPtr(), m_resp->getLen());
+  return true;
+}
+
+bool cCmdControl::processTIMER_Update() /* OPCODE 85 */
+{
+  int length      = m_req->getDataLength();
+  uint32_t index  = m_req->extract_U32();
+  bool active     = m_req->extract_U32();
+
+  cTimer *timer = Timers.Get(index - 1);
+  if (!timer)
+  {
+    esyslog("VNSI-Error: Timer \"%u\" not defined", index);
+    m_resp->add_U32(VDR_RET_DATAUNKNOWN);
+    m_resp->finalise();
+    m_req->getClient()->GetSocket()->write(m_resp->getPtr(), m_resp->getLen());
+    return true;
+  }
+
+  cTimer t = *timer;
+
+  if (length == 8)
+  {
+    if (active)
+      t.SetFlags(tfActive);
+    else
+      t.ClrFlags(tfActive);
+  }
+  else
+  {
+    uint32_t flags      = active ? tfActive : tfNone;
+    uint32_t priority   = m_req->extract_U32();
+    uint32_t lifetime   = m_req->extract_U32();
+    uint32_t number     = m_req->extract_U32();
+    time_t startTime    = m_req->extract_U32();
+    time_t stopTime     = m_req->extract_U32();
+    time_t day          = m_req->extract_U32();
+    uint32_t weekdays   = m_req->extract_U32();
+    const char *file    = m_req->extract_String();
+    const char *aux     = m_req->extract_String();
+
+    struct tm tm_r;
+    struct tm *time = localtime_r(&startTime, &tm_r);
+    if (day <= 0)
+      day = cTimer::SetTime(startTime, 0);
+    int start = time->tm_hour * 100 + time->tm_min;
+    time = localtime_r(&stopTime, &tm_r);
+    int stop = time->tm_hour * 100 + time->tm_min;
+
+    cString buffer = cString::sprintf("%u:%i:%s:%04d:%04d:%d:%d:%s:%s\n", flags, number, *cTimer::PrintDay(day, weekdays, true), start, stop, priority, lifetime, file, aux);
+    
+    delete[] file;
+    delete[] aux;
+
+    if (!t.Parse(buffer))
+    {
+      esyslog("VNSI-Error: Error in timer settings");
+      m_resp->add_U32(VDR_RET_DATAINVALID);
+      m_resp->finalise();
+      m_req->getClient()->GetSocket()->write(m_resp->getPtr(), m_resp->getLen());
+      return true;
+    }
+  }
+
+  *timer = t;
+  Timers.SetModified();
+
+  m_resp->add_U32(VDR_RET_OK);
+  m_resp->finalise();
+  m_req->getClient()->GetSocket()->write(m_resp->getPtr(), m_resp->getLen());
+  return true;
+}
+
+
+/** OPCODE 100 - 119: VNSI network functions for recording access */
+
+bool cCmdControl::processRECORDINGS_GetDiskSpace() /* OPCODE 100 */
+{
+  int FreeMB;
+  int Percent = VideoDiskSpace(&FreeMB);
+  int Total   = (FreeMB / (100 - Percent)) * 100;
+
+  m_resp->add_U32(Total);
+  m_resp->add_U32(FreeMB);
+  m_resp->add_U32(Percent);
+
+  m_resp->finalise();
+  m_req->getClient()->GetSocket()->write(m_resp->getPtr(), m_resp->getLen());
+  return true;
+}
+
+bool cCmdControl::processRECORDINGS_GetCount() /* OPCODE 101 */
+{
+  int count = 0;
+  bool recordings = Recordings.Load();
+  Recordings.Sort();
+  if (recordings)
+  {
+    cRecording *recording = Recordings.Last();
+    count = recording->Index() + 1;
+  }
+
+  m_resp->add_U32(count);
+
+  m_resp->finalise();
+  m_req->getClient()->GetSocket()->write(m_resp->getPtr(), m_resp->getLen());
+  return true;
+}
+
+bool cCmdControl::processRECORDINGS_GetList() /* OPCODE 102 */
+{
+  cRecordings Recordings;
+  Recordings.Load();
+
+  m_resp->add_String(VideoDirectory);
+  for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording))
+  {
+  #if APIVERSNUM >= 10705
+    const cEvent *event = recording->Info()->GetEvent();
+  #else
+    const cEvent *event = NULL;
+  #endif
+
+    time_t recordingStart    = 0;
+    int    recordingDuration = 0;
+    if (event)
+    {
+      recordingStart    = event->StartTime();
+      recordingDuration = event->Duration();
+    }
+    else
+    {
+      cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
+      if (rc)
+      {
+        recordingStart    = rc->Timer()->StartTime();
+        recordingDuration = rc->Timer()->StopTime() - recordingStart;
+      }
+      else
+      {
+        recordingStart = recording->start;
+      }
+    }
+    LOGCONSOLE("GRI: RC: recordingStart=%lu recordingDuration=%lu", recordingStart, recordingDuration);
+
+    m_resp->add_U32(recordingStart);
+    m_resp->add_U32(recordingDuration);
+    m_resp->add_U32(recording->priority);
+    m_resp->add_U32(recording->lifetime);
+    m_resp->add_String(recording->Info()->ChannelName() ? m_toUTF8.Convert(recording->Info()->ChannelName()) : "");
+    const char* fullname = recording->Name();
+    const char* recname = strrchr(fullname, '~');
+    if(recname != NULL) {
+      recname++;
+      m_resp->add_String(m_toUTF8.Convert(recname));
+    }
+    else if (!isempty(recording->Info()->Title())) {
+      m_resp->add_String(m_toUTF8.Convert(recording->Info()->Title()));
+    }
+    else
+      m_resp->add_String("");
+    if (!isempty(recording->Info()->ShortText()))
+      m_resp->add_String(m_toUTF8.Convert(recording->Info()->ShortText()));
+    else
+      m_resp->add_String("");
+    if (!isempty(recording->Info()->Description()))
+      m_resp->add_String(m_toUTF8.Convert(recording->Info()->Description()));
+    else
+      m_resp->add_String("");
+
+    m_resp->add_String(m_toUTF8.Convert(recording->FileName()));
+  }
+
+  m_resp->finalise();
+  m_req->getClient()->GetSocket()->write(m_resp->getPtr(), m_resp->getLen());
+  return true;
+}
+
+bool cCmdControl::processRECORDINGS_GetInfo() /* OPCODE 103 */
+{
+  const char *fileName  = m_req->extract_String();
+
+  cRecordings Recordings;
+  Recordings.Load(); // probably have to do this
+
+  cRecording *recording = Recordings.GetByName(fileName);
+#if APIVERSNUM >= 10705
+  const cEvent *event = recording->Info()->GetEvent();
+#else
+  const cEvent *event = NULL;
+#endif
+
+  time_t recordingStart    = 0;
+  int    recordingDuration = 0;
+  if (event)
+  {
+    recordingStart    = event->StartTime();
+    recordingDuration = event->Duration();
+  }
+  else
+  {
+    cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
+    if (rc)
+    {
+      recordingStart    = rc->Timer()->StartTime();
+      recordingDuration = rc->Timer()->StopTime() - recordingStart;
+    }
+    else
+    {
+      recordingStart = recording->start;
+    }
+  }
+  LOGCONSOLE("GRI: RC: recordingStart=%lu recordingDuration=%lu", recordingStart, recordingDuration);
+
+  m_resp->add_U32(recordingStart);
+  m_resp->add_U32(recordingDuration);
+  m_resp->add_U32(recording->priority);
+  m_resp->add_U32(recording->lifetime);
+  m_resp->add_String(recording->Info()->ChannelName() ? m_toUTF8.Convert(recording->Info()->ChannelName()) : "");
+  if (!isempty(recording->Info()->Title()))
+    m_resp->add_String(m_toUTF8.Convert(recording->Info()->Title()));
+  else
+    m_resp->add_String("");
+  if (!isempty(recording->Info()->ShortText()))
+    m_resp->add_String(m_toUTF8.Convert(recording->Info()->ShortText()));
+  else
+    m_resp->add_String("");
+  if (!isempty(recording->Info()->Description()))
+    m_resp->add_String(m_toUTF8.Convert(recording->Info()->Description()));
+  else
+    m_resp->add_String("");
+
+#if APIVERSNUM < 10703
+  m_resp->add_double((double)FRAMESPERSEC);
+#else
+  m_resp->add_double((double)recording->Info()->FramesPerSecond());
+#endif
+
+  if (event != NULL)
+  {
+    if (event->Vps())
+      m_resp->add_U32(event->Vps());
+    else
+      m_resp->add_U32(0);
+  }
+  else
+    m_resp->add_U32(0);
+
+  const cComponents* components = recording->Info()->Components();
+  if (components)
+  {
+    m_resp->add_U32(components->NumComponents());
+
+    tComponent* component;
+    for (int i = 0; i < components->NumComponents(); i++)
+    {
+      component = components->Component(i);
+
+      LOGCONSOLE("GRI: C: %i %u %u %s %s", i, component->stream, component->type, component->language, component->description);
+
+      m_resp->add_U8(component->stream);
+      m_resp->add_U8(component->type);
+
+      if (component->language)
+        m_resp->add_String(component->language);
+      else
+        m_resp->add_String("");
+
+      if (component->description)
+        m_resp->add_String(component->description);
+      else
+        m_resp->add_String("");
+    }
+  }
+  else
+    m_resp->add_U32(0);
+
+  // Done. send it
+  m_resp->finalise();
+  m_req->getClient()->GetSocket()->write(m_resp->getPtr(), m_resp->getLen());
+
+  delete[] fileName;
+  LOGCONSOLE("Written getrecinfo");
+  return true;
+}
+
+bool cCmdControl::processRECORDINGS_Delete() /* OPCODE 104 */
+{
+  const char *recName = m_req->extract_String();
+
+  cRecordings Recordings;
+  Recordings.Load(); // probably have to do this
+
+  cRecording* recording = Recordings.GetByName(recName);
+
+  LOGCONSOLE("recording pointer %p", recording);
+
+  if (recording)
+  {
+    LOGCONSOLE("deleting recording: %s", recording->Name());
+
+    cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
+    if (!rc)
+    {
+      if (recording->Delete())
+      {
+        // Copy svdrdeveldevelp's way of doing this, see if it works
+        Recordings.DelByName(recording->FileName());
+        isyslog("VNSI: Recording \"%s\" deleted", recording->FileName());
+        m_resp->add_U32(VDR_RET_OK);
+      }
+      else
+      {
+        esyslog("VNSI-Error: Error while deleting recording!");
+        m_resp->add_U32(VDR_RET_ERROR);
+      }
+    }
+    else
+    {
+      esyslog("VNSI-Error: Recording \"%s\" is in use by timer %d", recording->Name(), rc->Timer()->Index() + 1);
+      m_resp->add_U32(VDR_RET_DATALOCKED);
+    }
+  }
+  else
+  {
+    esyslog("VNSI-Error: Error in recording name \"%s\"", recName);
+    m_resp->add_U32(VDR_RET_DATAUNKNOWN);
+  }
+
+  m_resp->finalise();
+  m_req->getClient()->GetSocket()->write(m_resp->getPtr(), m_resp->getLen());
+  
+  delete[] recName;
+  return true;
+}
+
+bool cCmdControl::processRECORDINGS_Move() /* OPCODE 105 */
+{
+  LOGCONSOLE("Process move recording");
+  const char *fileName  = m_req->extract_String();
+  const char *newPath   = m_req->extract_String();
+
+  cRecordings Recordings;
+  Recordings.Load(); // probably have to do this
+
+  cRecording* recording = Recordings.GetByName(fileName);
+
+  LOGCONSOLE("recording pointer %p", recording);
+
+  if (recording)
+  {
+    cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
+    if (!rc)
+    {
+      LOGCONSOLE("moving recording: %s", recording->Name());
+      LOGCONSOLE("moving recording: %s", recording->FileName());
+      LOGCONSOLE("to: %s", newPath);
+
+      const char* t = recording->FileName();
+
+      char* dateDirName = NULL;   int k;
+      char* titleDirName = NULL;  int j;
+
+      // Find the datedirname
+      for(k = strlen(t) - 1; k >= 0; k--)
+      {
+        if (t[k] == '/')
+        {
+          LOGCONSOLE("l1: %i", strlen(&t[k+1]) + 1);
+          dateDirName = new char[strlen(&t[k+1]) + 1];
+          strcpy(dateDirName, &t[k+1]);
+          break;
+        }
+      }
+
+      // Find the titledirname
+      for(j = k-1; j >= 0; j--)
+      {
+        if (t[j] == '/')
+        {
+          LOGCONSOLE("l2: %i", (k - j - 1) + 1);
+          titleDirName = new char[(k - j - 1) + 1];
+          memcpy(titleDirName, &t[j+1], k - j - 1);
+          titleDirName[k - j - 1] = '\0';
+          break;
+        }
+      }
+
+      LOGCONSOLE("datedirname: %s", dateDirName);
+      LOGCONSOLE("titledirname: %s", titleDirName);
+      LOGCONSOLE("viddir: %s", VideoDirectory);
+
+      char* newPathConv = new char[strlen(newPath)+1];
+      strcpy(newPathConv, newPath);
+      ExchangeChars(newPathConv, true);
+      LOGCONSOLE("EC: %s", newPathConv);
+
+      char* newContainer = new char[strlen(VideoDirectory) + strlen(newPathConv) + strlen(titleDirName) + 1];
+      LOGCONSOLE("l10: %i", strlen(VideoDirectory) + strlen(newPathConv) + strlen(titleDirName) + 1);
+      sprintf(newContainer, "%s%s%s", VideoDirectory, newPathConv, titleDirName);
+      delete[] newPathConv;
+
+      LOGCONSOLE("%s", newContainer);
+
+      struct stat dstat;
+      int statret = stat(newContainer, &dstat);
+      if ((statret == -1) && (errno == ENOENT)) // Dir does not exist
+      {
+        LOGCONSOLE("new dir does not exist");
+        int mkdirret = mkdir(newContainer, 0755);
+        if (mkdirret != 0)
+        {
+          delete[] dateDirName;
+          delete[] titleDirName;
+          delete[] newContainer;
+
+          m_resp->add_U32(VDR_RET_ERROR);
+          m_resp->finalise();
+          m_req->getClient()->GetSocket()->write(m_resp->getPtr(), m_resp->getLen());
+          return true;
+        }
+      }
+      else if ((statret == 0) && (! (dstat.st_mode && S_IFDIR))) // Something exists but it's not a dir
+      {
+        delete[] dateDirName;
+        delete[] titleDirName;
+        delete[] newContainer;
+
+        m_resp->add_U32(VDR_RET_ERROR);
+        m_resp->finalise();
+        m_req->getClient()->GetSocket()->write(m_resp->getPtr(), m_resp->getLen());
+        return true;
+      }
+
+      // Ok, the directory container has been made, or it pre-existed.
+
+      char* newDir = new char[strlen(newContainer) + 1 + strlen(dateDirName) + 1];
+      sprintf(newDir, "%s/%s", newContainer, dateDirName);
+
+      LOGCONSOLE("doing rename '%s' '%s'", t, newDir);
+      int renameret = rename(t, newDir);
+      if (renameret == 0)
+      {
+        // Success. Test for remove old dir containter
+        char* oldTitleDir = new char[k+1];
+        memcpy(oldTitleDir, t, k);
+        oldTitleDir[k] = '\0';
+        LOGCONSOLE("len: %i, cp: %i, strlen: %i, oldtitledir: %s", k+1, k, strlen(oldTitleDir), oldTitleDir);
+        rmdir(oldTitleDir); // can't do anything about a fail result at this point.
+        delete[] oldTitleDir;
+      }
+      if (renameret == 0)
+      {
+        // Tell VDR
+        Recordings.Update();
+        // Success. Send a different packet from just a ulong
+        m_resp->add_U32(VDR_RET_OK); // success
+        m_resp->add_String(newDir);
+      }
+      else
+      {
+        m_resp->add_U32(VDR_RET_ERROR);
+      }
+
+      m_resp->finalise();
+      m_req->getClient()->GetSocket()->write(m_resp->getPtr(), m_resp->getLen());
+
+      delete[] dateDirName;
+      delete[] titleDirName;
+      delete[] newContainer;
+      delete[] newDir;
+    }
+    else
+    {
+      m_resp->add_U32(VDR_RET_DATALOCKED);
+      m_resp->finalise();
+      m_req->getClient()->GetSocket()->write(m_resp->getPtr(), m_resp->getLen());
+    }
+  }
+  else
+  {
+    m_resp->add_U32(VDR_RET_DATAUNKNOWN);
+    m_resp->finalise();
+    m_req->getClient()->GetSocket()->write(m_resp->getPtr(), m_resp->getLen());
+  }
+
+  delete[] fileName;
+  delete[] newPath;
+
+  return true;
+}
+
+
+/** OPCODE 120 - 139: VNSI network functions for epg access and manipulating */
+
+bool cCmdControl::processEPG_GetForChannel() /* OPCODE 120 */
+{
+  uint32_t channelNumber  = m_req->extract_U32();
+  uint32_t startTime      = m_req->extract_U32();
+  uint32_t duration       = m_req->extract_U32();
+
+  LOGCONSOLE("get schedule called for channel %lu", channelNumber);
+
+  cChannel* channel = Channels.GetByNumber(channelNumber);
+  if (!channel)
+  {
+    m_resp->add_U32(0);
+    m_resp->finalise();
+    m_req->getClient()->GetSocket()->write(m_resp->getPtr(), m_resp->getLen());
+
+    LOGCONSOLE("written 0 because channel = NULL");
+    return true;
+  }
+
+  cSchedulesLock MutexLock;
+  const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
+  if (!Schedules)
+  {
+    m_resp->add_U32(0);
+    m_resp->finalise();
+    m_req->getClient()->GetSocket()->write(m_resp->getPtr(), m_resp->getLen());
+
+    LOGCONSOLE("written 0 because Schedule!s! = NULL");
+    return true;
+  }
+
+  const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
+  if (!Schedule)
+  {
+    m_resp->add_U32(0);
+    m_resp->finalise();
+    m_req->getClient()->GetSocket()->write(m_resp->getPtr(), m_resp->getLen());
+
+    LOGCONSOLE("written 0 because Schedule = NULL");
+    return true;
+  }
+
+  const char* empty = "";
+  bool atLeastOneEvent = false;
+
+  uint32_t thisEventID;
+  uint32_t thisEventTime;
+  uint32_t thisEventDuration;
+  uint32_t thisEventContent;
+  uint32_t thisEventRating;
+  const char* thisEventTitle;
+  const char* thisEventSubTitle;
+  const char* thisEventDescription;
+
+  for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
+  {
+    thisEventID           = event->EventID();
+    thisEventTitle        = event->Title();
+    thisEventSubTitle     = event->ShortText();
+    thisEventDescription  = event->Description();
+    thisEventTime         = event->StartTime();
+    thisEventDuration     = event->Duration();
+#if defined(USE_PARENTALRATING) || defined(PARENTALRATINGCONTENTVERSNUM)
+    thisEventContent      = event->Contents();
+    thisEventRating       = 0;
+#elif APIVERSNUM >= 10711
+    thisEventContent      = event->Contents();
+    thisEventRating       = event->ParentalRating();
+#else
+    thisEventContent      = 0;
+    thisEventRating       = 0;
+#endif
+
+    //in the past filter
+    if ((thisEventTime + thisEventDuration) < (uint32_t)time(NULL)) continue;
+
+    //start time filter
+    if ((thisEventTime + thisEventDuration) <= startTime) continue;
+
+    //duration filter
+    if (duration != 0 && thisEventTime >= (startTime + duration)) continue;
+
+    if (!thisEventTitle)        thisEventTitle        = empty;
+    if (!thisEventSubTitle)     thisEventSubTitle     = empty;
+    if (!thisEventDescription)  thisEventDescription  = empty;
+
+    m_resp->add_U32(thisEventID);
+    m_resp->add_U32(thisEventTime);
+    m_resp->add_U32(thisEventDuration);
+    m_resp->add_U32(thisEventContent);
+    m_resp->add_U32(thisEventRating);
+
+    m_resp->add_String(m_toUTF8.Convert(thisEventTitle));
+    m_resp->add_String(m_toUTF8.Convert(thisEventSubTitle));
+    m_resp->add_String(m_toUTF8.Convert(thisEventDescription));
+
+    atLeastOneEvent = true;
+  }
+
+  LOGCONSOLE("Got all event data");
+
+  if (!atLeastOneEvent)
+  {
+    m_resp->add_U32(0);
+    LOGCONSOLE("Written 0 because no data");
+  }
+
+  m_resp->finalise();
+  m_req->getClient()->GetSocket()->write(m_resp->getPtr(), m_resp->getLen());
+
+  LOGCONSOLE("written schedules packet");
+
+  return true;
+}
+
+
+/** OPCODE 140 - 169: VNSI network functions for channel scanning */
+
+bool cCmdControl::processSCAN_ScanSupported() /* OPCODE 140 */
+{
+  /** Note: Using "WirbelScanService-StopScan-v1.0" to detect
+            a present service interface in wirbelscan plugin,
+            it returns true if supported */
+  cPlugin *p = cPluginManager::GetPlugin("wirbelscan");
+  if (p && p->Service("WirbelScanService-StopScan-v1.0", NULL))
+    m_resp->add_U32(VDR_RET_OK);
+  else
+    m_resp->add_U32(VDR_RET_NOTSUPPORTED);
+
+  m_resp->finalise();
+  m_req->getClient()->GetSocket()->write(m_resp->getPtr(), m_resp->getLen());
+  return true;
+}
+
+bool cCmdControl::processSCAN_GetCountries() /* OPCODE 141 */
+{
+  if (!m_processSCAN_Response)
+  {
+    m_processSCAN_Response = m_resp;
+    cPlugin *p = cPluginManager::GetPlugin("wirbelscan");
+    if (p)
+    {
+      m_resp->add_U32(VDR_RET_OK);
+      p->Service("WirbelScanService-GetCountries-v1.0", (void*) processSCAN_AddCountry);
+    }
+    else
+    {
+      m_resp->add_U32(VDR_RET_NOTSUPPORTED);
+    }
+    m_processSCAN_Response = NULL;
+  }
+  else
+  {
+    m_resp->add_U32(VDR_RET_DATALOCKED);
+  }
+
+  m_resp->finalise();
+  m_req->getClient()->GetSocket()->write(m_resp->getPtr(), m_resp->getLen());
+  return true;
+}
+
+bool cCmdControl::processSCAN_GetSatellites() /* OPCODE 142 */
+{
+  if (!m_processSCAN_Response)
+  {
+    m_processSCAN_Response = m_resp;
+    cPlugin *p = cPluginManager::GetPlugin("wirbelscan");
+    if (p)
+    {
+      m_resp->add_U32(VDR_RET_OK);
+      p->Service("WirbelScanService-GetSatellites-v1.0", (void*) processSCAN_AddSatellite);
+    }
+    else
+    {
+      m_resp->add_U32(VDR_RET_NOTSUPPORTED);
+    }
+    m_processSCAN_Response = NULL;
+  }
+  else
+  {
+    m_resp->add_U32(VDR_RET_DATALOCKED);
+  }
+
+  m_resp->finalise();
+  m_req->getClient()->GetSocket()->write(m_resp->getPtr(), m_resp->getLen());
+  return true;
+}
+
+bool cCmdControl::processSCAN_Start() /* OPCODE 143 */
+{
+  WirbelScanService_DoScan_v1_0 svc;
+  svc.type              = (scantype_t)m_req->extract_U32();
+  svc.scan_tv           = (bool)m_req->extract_U8();
+  svc.scan_radio        = (bool)m_req->extract_U8();
+  svc.scan_fta          = (bool)m_req->extract_U8();
+  svc.scan_scrambled    = (bool)m_req->extract_U8();
+  svc.scan_hd           = (bool)m_req->extract_U8();
+  svc.CountryIndex      = (int)m_req->extract_U32();
+  svc.DVBC_Inversion    = (int)m_req->extract_U32();
+  svc.DVBC_Symbolrate   = (int)m_req->extract_U32();
+  svc.DVBC_QAM          = (int)m_req->extract_U32();
+  svc.DVBT_Inversion    = (int)m_req->extract_U32();
+  svc.SatIndex          = (int)m_req->extract_U32();
+  svc.ATSC_Type         = (int)m_req->extract_U32();
+  svc.SetPercentage     = processSCAN_SetPercentage;
+  svc.SetSignalStrength = processSCAN_SetSignalStrength;
+  svc.SetDeviceInfo     = processSCAN_SetDeviceInfo;
+  svc.SetTransponder    = processSCAN_SetTransponder;
+  svc.NewChannel        = processSCAN_NewChannel;
+  svc.IsFinished        = processSCAN_IsFinished;
+  svc.SetStatus         = processSCAN_SetStatus;
+  m_processSCAN_Socket  = m_req->getClient()->GetSocket();
+
+  cPlugin *p = cPluginManager::GetPlugin("wirbelscan");
+  if (p)
+  {
+    if (p->Service("WirbelScanService-DoScan-v1.0", (void*) &svc))
+      m_resp->add_U32(VDR_RET_OK);
+    else
+      m_resp->add_U32(VDR_RET_ERROR);
+  }
+  else
+  {
+    m_resp->add_U32(VDR_RET_NOTSUPPORTED);
+  }
+
+  m_resp->finalise();
+  m_req->getClient()->GetSocket()->write(m_resp->getPtr(), m_resp->getLen());
+  return true;
+}
+
+bool cCmdControl::processSCAN_Stop() /* OPCODE 144 */
+{
+  cPlugin *p = cPluginManager::GetPlugin("wirbelscan");
+  if (p)
+  {
+    p->Service("WirbelScanService-StopScan-v1.0", NULL);
+    m_resp->add_U32(VDR_RET_OK);
+  }
+  else
+  {
+    m_resp->add_U32(VDR_RET_NOTSUPPORTED);
+  }
+  m_resp->finalise();
+  m_req->getClient()->GetSocket()->write(m_resp->getPtr(), m_resp->getLen());
+  return true;
+}
+
+cResponsePacket *cCmdControl::m_processSCAN_Response = NULL;
+cxSocket *cCmdControl::m_processSCAN_Socket = NULL;
+
+void cCmdControl::processSCAN_AddCountry(int index, const char *isoName, const char *longName)
+{
+  m_processSCAN_Response->add_U32(index);
+  m_processSCAN_Response->add_String(isoName);
+  m_processSCAN_Response->add_String(longName);
+}
+
+void cCmdControl::processSCAN_AddSatellite(int index, const char *shortName, const char *longName)
+{
+  m_processSCAN_Response->add_U32(index);
+  m_processSCAN_Response->add_String(shortName);
+  m_processSCAN_Response->add_String(longName);
+}
+
+void cCmdControl::processSCAN_SetPercentage(int percent)
+{
+  cResponsePacket *resp = new cResponsePacket();
+  if (!resp->initScan(VDR_SCANNER_PERCENTAGE))
+  {
+    delete resp;
+    return;
+  }
+  resp->add_U32(percent);
+  resp->finalise();
+  m_processSCAN_Socket->write(resp->getPtr(), resp->getLen());
+  delete resp;
+}
+
+void cCmdControl::processSCAN_SetSignalStrength(int strength, bool locked)
+{
+  cResponsePacket *resp = new cResponsePacket();
+  if (!resp->initScan(VDR_SCANNER_SIGNAL))
+  {
+    delete resp;
+    return;
+  }
+  strength *= 100;
+  strength /= 0xFFFF;
+  resp->add_U32(strength);
+  resp->add_U32(locked);
+  resp->finalise();
+  m_processSCAN_Socket->write(resp->getPtr(), resp->getLen());
+  delete resp;
+}
+
+void cCmdControl::processSCAN_SetDeviceInfo(const char *Info)
+{
+  cResponsePacket *resp = new cResponsePacket();
+  if (!resp->initScan(VDR_SCANNER_DEVICE))
+  {
+    delete resp;
+    return;
+  }
+  resp->add_String(Info);
+  resp->finalise();
+  m_processSCAN_Socket->write(resp->getPtr(), resp->getLen());
+  delete resp;
+}
+
+void cCmdControl::processSCAN_SetTransponder(const char *Info)
+{
+  cResponsePacket *resp = new cResponsePacket();
+  if (!resp->initScan(VDR_SCANNER_TRANSPONDER))
+  {
+    delete resp;
+    return;
+  }
+  resp->add_String(Info);
+  resp->finalise();
+  m_processSCAN_Socket->write(resp->getPtr(), resp->getLen());
+  delete resp;
+}
+
+void cCmdControl::processSCAN_NewChannel(const char *Name, bool isRadio, bool isEncrypted, bool isHD)
+{
+  cResponsePacket *resp = new cResponsePacket();
+  if (!resp->initScan(VDR_SCANNER_NEWCHANNEL))
+  {
+    delete resp;
+    return;
+  }
+  resp->add_U32(isRadio);
+  resp->add_U32(isEncrypted);
+  resp->add_U32(isHD);
+  resp->add_String(Name);
+  resp->finalise();
+  m_processSCAN_Socket->write(resp->getPtr(), resp->getLen());
+  delete resp;
+}
+
+void cCmdControl::processSCAN_IsFinished()
+{
+  cResponsePacket *resp = new cResponsePacket();
+  if (!resp->initScan(VDR_SCANNER_FINISHED))
+  {
+    delete resp;
+    return;
+  }
+  resp->finalise();
+  m_processSCAN_Socket->write(resp->getPtr(), resp->getLen());
+  m_processSCAN_Socket = NULL;
+  delete resp;
+}
+
+void cCmdControl::processSCAN_SetStatus(int status)
+{
+  cResponsePacket *resp = new cResponsePacket();
+  if (!resp->initScan(VDR_SCANNER_STATUS))
+  {
+    delete resp;
+    return;
+  }
+  resp->add_U32(status);
+  resp->finalise();
+  m_processSCAN_Socket->write(resp->getPtr(), resp->getLen());
+  delete resp;
+}
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/cmdcontrol.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/cmdcontrol.h
new file mode 100644 (file)
index 0000000..96556ce
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef CMD_CONTROL_H
+#define CMD_CONTROL_H
+
+#include <queue>
+#include <vdr/thread.h>
+
+#include "responsepacket.h"
+#include "requestpacket.h"
+
+typedef std::queue<cRequestPacket*> RequestPacketQueue;
+
+class cxSocket;
+
+class cCmdControl : public cThread
+{
+public:
+  cCmdControl();
+  ~cCmdControl();
+
+  bool init();
+  bool recvRequest(cRequestPacket*);
+
+private:
+  bool processPacket();
+
+  bool process_Login();
+  bool process_GetTime();
+  bool process_EnableStatusInterface();
+  bool process_EnableOSDInterface();
+
+  bool processRecStream_Open();
+  bool processRecStream_Close();
+  bool processRecStream_GetBlock();
+  bool processRecStream_PositionFromFrameNumber();
+  bool processRecStream_FrameNumberFromPosition();
+  bool processRecStream_GetIFrame();
+
+  bool processCHANNELS_GroupsCount();
+  bool processCHANNELS_ChannelsCount();
+  bool processCHANNELS_GroupList();
+  bool processCHANNELS_GetChannels();
+
+  bool processTIMER_GetCount();
+  bool processTIMER_Get();
+  bool processTIMER_GetList();
+  bool processTIMER_Add();
+  bool processTIMER_Delete();
+  bool processTIMER_Update();
+
+  bool processRECORDINGS_GetDiskSpace();
+  bool processRECORDINGS_GetCount();
+  bool processRECORDINGS_GetList();
+  bool processRECORDINGS_GetInfo();
+  bool processRECORDINGS_Delete();
+  bool processRECORDINGS_Move();
+
+  bool processEPG_GetForChannel();
+
+  bool processSCAN_ScanSupported();
+  bool processSCAN_GetCountries();
+  bool processSCAN_GetSatellites();
+  bool processSCAN_Start();
+  bool processSCAN_Stop();
+
+  /** Static callback functions to interact with wirbelscan plugin over
+      the plugin service interface */
+  static void processSCAN_AddCountry(int index, const char *isoName, const char *longName);
+  static void processSCAN_AddSatellite(int index, const char *shortName, const char *longName);
+  static void processSCAN_SetPercentage(int percent);
+  static void processSCAN_SetSignalStrength(int strength, bool locked);
+  static void processSCAN_SetDeviceInfo(const char *Info);
+  static void processSCAN_SetTransponder(const char *Info);
+  static void processSCAN_NewChannel(const char *Name, bool isRadio, bool isEncrypted, bool isHD);
+  static void processSCAN_IsFinished();
+  static void processSCAN_SetStatus(int status);
+  static cResponsePacket *m_processSCAN_Response;
+  static cxSocket *m_processSCAN_Socket;
+
+  virtual void Action(void);
+
+  cRequestPacket     *m_req;
+  RequestPacketQueue  m_req_queue;
+  cResponsePacket    *m_resp;
+  cCondWait           m_Wait;
+  cCharSetConv        m_toUTF8;
+  cMutex              m_mutex;
+};
+
+
+#endif /* CMD_CONTROL_H */
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/config.c b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/config.c
new file mode 100644 (file)
index 0000000..40d36e0
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <vdr/plugin.h>
+
+#include "config.h"
+
+cVNSIServerConfig::cVNSIServerConfig()
+{
+  memset(this, 0, sizeof(cVNSIServerConfig));
+
+  listen_port         = LISTEN_PORT;
+  SuspendMode         = smAlways;
+  ConfigDirectory     = NULL;
+}
+
+void cVNSIServerConfig::readNoSignalStream()
+{
+  m_noSignalStreamSize = 0;
+
+  cString noSignalFileName = cString::sprintf("%s/"NO_SIGNAL_FILE, *ConfigDirectory);
+
+  FILE *const f = fopen(*noSignalFileName, "rb");
+  if (f)
+  {
+    m_noSignalStreamSize = fread(&m_noSignalStreamData[0] + 9, 1, sizeof (m_noSignalStreamData) - 9 - 9 - 4, f);
+    if (m_noSignalStreamSize == sizeof (m_noSignalStreamData) - 9 - 9 - 4)
+    {
+      esyslog("VNSI-Error: '%s' exeeds limit of %ld bytes!", *noSignalFileName, (long)(sizeof (m_noSignalStreamData) - 9 - 9 - 4 - 1));
+    }
+    else if (m_noSignalStreamSize > 0)
+    {
+      m_noSignalStreamData[ 0 ] = 0x00;
+      m_noSignalStreamData[ 1 ] = 0x00;
+      m_noSignalStreamData[ 2 ] = 0x01;
+      m_noSignalStreamData[ 3 ] = 0xe0;
+      m_noSignalStreamData[ 4 ] = (m_noSignalStreamSize + 3) >> 8;
+      m_noSignalStreamData[ 5 ] = (m_noSignalStreamSize + 3) & 0xff;
+      m_noSignalStreamData[ 6 ] = 0x80;
+      m_noSignalStreamData[ 7 ] = 0x00;
+      m_noSignalStreamData[ 8 ] = 0x00;
+      m_noSignalStreamSize += 9;
+      m_noSignalStreamData[ m_noSignalStreamSize++ ] = 0x00;
+      m_noSignalStreamData[ m_noSignalStreamSize++ ] = 0x00;
+      m_noSignalStreamData[ m_noSignalStreamSize++ ] = 0x01;
+      m_noSignalStreamData[ m_noSignalStreamSize++ ] = 0xe0;
+      m_noSignalStreamData[ m_noSignalStreamSize++ ] = 0x00;
+      m_noSignalStreamData[ m_noSignalStreamSize++ ] = 0x07;
+      m_noSignalStreamData[ m_noSignalStreamSize++ ] = 0x80;
+      m_noSignalStreamData[ m_noSignalStreamSize++ ] = 0x00;
+      m_noSignalStreamData[ m_noSignalStreamSize++ ] = 0x00;
+      m_noSignalStreamData[ m_noSignalStreamSize++ ] = 0x00;
+      m_noSignalStreamData[ m_noSignalStreamSize++ ] = 0x00;
+      m_noSignalStreamData[ m_noSignalStreamSize++ ] = 0x01;
+      m_noSignalStreamData[ m_noSignalStreamSize++ ] = 0xb7;
+    }
+    fclose(f);
+    return;
+  }
+  else
+  {
+    esyslog("VNSI-Error: couldn't open '%s'!", *noSignalFileName);
+  }
+
+  return;
+}
+
+/* Global instance */
+cVNSIServerConfig VNSIServerConfig;
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/config.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/config.h
new file mode 100644 (file)
index 0000000..7cd3cb3
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef _STREAMERNG_CONFIG_H_
+#define _STREAMERNG_CONFIG_H_
+
+#include <string.h>
+#include <stdint.h>
+
+#include <vdr/config.h>
+
+#if CONSOLEDEBUG
+  #define LOGCONSOLE(x...) do{ fprintf(stderr, "VERBOSE %s - ", __FUNCTION__); fprintf(stderr, x); fprintf(stderr, "\n"); } while(0)
+#else
+  #define LOGCONSOLE(x...)
+#endif
+
+#define ALLOWED_HOSTS_FILE  "allowed_hosts.conf"
+#define NO_SIGNAL_FILE      "noSignal.mpg"
+#define FRONTEND_DEVICE     "/dev/dvb/adapter%d/frontend%d"
+
+#define LISTEN_PORT       34890
+#define LISTEN_PORT_S    "34890"
+#define DISCOVERY_PORT    34890
+
+enum eSuspendMode
+{
+  smOffer,
+  smAlways,
+  smNever,
+  sm_Count
+};
+
+
+class cVNSIServerConfig
+{
+public:
+  cVNSIServerConfig();
+
+  void readNoSignalStream();
+
+  // Remote server settings
+  int  listen_port;         // Port of remote server
+  int  SuspendMode;
+
+  cString ConfigDirectory;
+
+  uint8_t m_noSignalStreamData[ 6 + 0xffff ];
+  long    m_noSignalStreamSize;
+};
+
+// Global instance
+extern cVNSIServerConfig VNSIServerConfig;
+
+#endif /* _STREAMERNG_CONFIG_H_ */
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/connection.c b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/connection.c
new file mode 100644 (file)
index 0000000..5f112e5
--- /dev/null
@@ -0,0 +1,451 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/types.h>
+#include <sys/syscall.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/timeb.h>
+#include <sys/wait.h>
+#include <sys/signal.h>
+#include <sys/resource.h>
+
+#include <vdr/plugin.h>
+#include <vdr/device.h>
+#include <vdr/channels.h>
+
+#include "connection.h"
+#include "receiver.h"
+#include "server.h"
+#include "tools.h"
+#include "vdrcommand.h"
+#include "recplayer.h"
+#include "responsepacket.h"
+
+cConnection::cConnection(cServer *server, int fd, unsigned int id, const char *ClientAdr)
+{
+  m_Id                      = id;
+  m_server                  = server;
+  m_Streamer                = NULL;
+  m_isStreaming             = false;
+  m_Channel                 = NULL;
+  m_NetLogFile              = NULL;
+  m_ClientAddress           = ClientAdr;
+  m_StatusInterfaceEnabled  = false;
+  m_OSDInterfaceEnabled     = false;
+
+  m_socket.set_handle(fd);
+
+  Start();
+}
+
+cConnection::~cConnection()
+{
+  isyslog("VNSI: cConnection::~cConnection()");
+  StopChannelStreaming();
+  m_socket.close(); // force closing connection
+  isyslog("VNSI: stopping cConnection thread ...");
+  Cancel(10);
+  isyslog("VNSI: done");
+
+  if (m_NetLogFile)
+  {
+    fclose(m_NetLogFile);
+    m_NetLogFile = NULL;
+  }
+}
+
+void cConnection::Action(void)
+{
+  uint32_t kaTimeStamp;
+  uint32_t logStringLen;
+  uint32_t channelID;
+  uint32_t requestID;
+  uint32_t opcode;
+  uint32_t dataLength;
+  uint8_t* data;
+
+  while (Running())
+  {
+    if (!m_socket.read((uint8_t*)&channelID, sizeof(uint32_t))) break;
+    channelID = ntohl(channelID);
+
+    if (channelID == 1)
+    {
+      if (!m_socket.read((uint8_t*)&requestID, sizeof(uint32_t), 10000)) break;
+      requestID = ntohl(requestID);
+
+      if (!m_socket.read((uint8_t*)&opcode, sizeof(uint32_t), 10000)) break;
+      opcode = ntohl(opcode);
+
+      if (!m_socket.read((uint8_t*)&dataLength, sizeof(uint32_t), 10000)) break;
+      dataLength = ntohl(dataLength);
+      if (dataLength > 200000) // a random sanity limit
+      {
+        esyslog("VNSI-Error: dataLength > 200000!");
+        break;
+      }
+
+      if (dataLength)
+      {
+        data = (uint8_t*)malloc(dataLength);
+        if (!data)
+        {
+          esyslog("VNSI-Error: Extra data buffer malloc error");
+          break;
+        }
+
+        if (!m_socket.read(data, dataLength, 10000))
+        {
+          esyslog("VNSI-Error: Could not read data");
+          free(data);
+          break;
+        }
+      }
+      else
+      {
+        data = NULL;
+      }
+
+      //LOGCONSOLE("Received chan=%lu, ser=%lu, op=%lu, edl=%lu", channelID, requestID, opcode, dataLength);
+
+      if (!m_loggedIn && (opcode != 1))
+      {
+        esyslog("VNSI-Error: Not logged in and opcode != 1");
+        if (data) free(data);
+        break;
+      }
+
+      /* Handle channel open and close inside this thread */
+      if (opcode == VDR_CHANNELSTREAM_OPEN)
+      {
+        cResponsePacket *resp = new cResponsePacket();
+        if (!resp->init(requestID))
+        {
+          esyslog("VNSI-Error: response packet init fail");
+          delete resp;
+          continue;
+        }
+
+        uint32_t number = ntohl(*(uint32_t*)&data[0]);
+        free(data);
+
+        if (m_isStreaming)
+          StopChannelStreaming();
+
+        const cChannel *channel = Channels.GetByNumber(number);
+        if (channel != NULL)
+        {
+          if (StartChannelStreaming(channel, resp))
+          {
+            isyslog("VNSI: Started streaming of channel %i - %s", number, channel->Name());
+            continue;
+          }
+          else
+          {
+            LOGCONSOLE("Can't stream channel %i - %s", number, channel->Name());
+            resp->add_U32(VDR_RET_DATALOCKED);
+          }
+        }
+        else
+        {
+          esyslog("VNSI-Error: Can't find channel %i", number);
+          resp->add_U32(VDR_RET_DATAINVALID);
+        }
+
+        resp->finalise();
+        m_socket.write(resp->getPtr(), resp->getLen());
+      }
+      else if (opcode == VDR_CHANNELSTREAM_CLOSE)
+      {
+        if (m_isStreaming)
+          StopChannelStreaming();
+      }
+      else
+      {
+        cRequestPacket* req = new cRequestPacket(requestID, opcode, data, dataLength, this);
+       m_cmdcontrol.recvRequest(req);
+      }
+    }
+    else if (channelID == 3)
+    {
+      if (!m_socket.read((uint8_t*)&kaTimeStamp, sizeof(uint32_t), 1000)) break;
+      kaTimeStamp = ntohl(kaTimeStamp);
+
+      //LOGCONSOLE("Received chan=%lu kats=%lu", channelID, kaTimeStamp);
+
+      uint8_t buffer[8];
+      *(uint32_t*)&buffer[0] = htonl(3); // KA CHANNEL
+      *(uint32_t*)&buffer[4] = htonl(kaTimeStamp);
+      if (!m_socket.write(buffer, 8))
+      {
+        esyslog("VNSI-Error: Could not send back KA reply");
+        break;
+      }
+    }
+    else if (channelID == 4)
+    {
+      if (!m_socket.read((uint8_t*)&logStringLen, sizeof(uint32_t), 1000)) break;
+      logStringLen = ntohl(logStringLen);
+
+      LOGCONSOLE("Received chan=%lu loglen=%lu", channelID, logStringLen);
+
+      uint8_t buffer[logStringLen + 1];
+      if (!m_socket.read((uint8_t*)&buffer, logStringLen, 1000)) break;
+      buffer[logStringLen] = '\0';
+
+      LOGCONSOLE("Client said: '%s'", buffer);
+      if (m_NetLogFile)
+      {
+        if (fputs((const char*)buffer, m_NetLogFile) == EOF)
+        {
+          fclose(m_NetLogFile);
+          m_NetLogFile = NULL;
+        }
+        fflush(NULL);
+      }
+    }
+    else
+    {
+      esyslog("VNSI-Error: Incoming channel number unknown");
+      break;
+    }
+  }
+
+  /* If thread is ended due to closed connection delete a
+     possible running stream here */
+  StopChannelStreaming();
+}
+
+void cConnection::EnableNetLog(bool yesNo, const char* ClientChannelName)
+{
+  if (yesNo)
+  {
+    cString Base = cString::sprintf("%s/vnsi-server/%s-%s.log", *VNSIServerConfig.ConfigDirectory, *m_ClientAddress, ClientChannelName);
+
+    m_NetLogFile = fopen(*Base, "a");
+    if (m_NetLogFile)
+      isyslog("VNSI: Client network logging started");
+  }
+  else
+  {
+    if (m_NetLogFile)
+    {
+      fclose(m_NetLogFile);
+      m_NetLogFile = NULL;
+      isyslog("VNSI: Client network logging stopped");
+    }
+  }
+}
+
+bool cConnection::StartChannelStreaming(const cChannel *channel, cResponsePacket *resp)
+{
+  m_Channel     = channel;
+  m_Streamer    = new cLiveStreamer;
+  m_isStreaming = m_Streamer->StreamChannel(m_Channel, 50, &m_socket, resp);
+  return m_isStreaming;
+}
+
+void cConnection::StopChannelStreaming()
+{
+  m_isStreaming = false;
+  if (m_Streamer)
+  {
+    delete m_Streamer;
+    m_Streamer = NULL;
+    m_Channel  = NULL;
+  }
+}
+
+void cConnection::TimerChange(const cTimer *Timer, eTimerChange Change)
+{
+  if (m_StatusInterfaceEnabled)
+  {
+    cResponsePacket *resp = new cResponsePacket();
+    if (!resp->initStatus(VDR_STATUS_TIMERCHANGE))
+    {
+      delete resp;
+      return;
+    }
+
+    resp->add_U32((int)Change);
+    resp->add_String(Timer ? *Timer->ToText(true) : "-");
+
+    resp->finalise();
+    m_socket.write(resp->getPtr(), resp->getLen());
+    delete resp;
+  }
+}
+
+//void cConnection::ChannelSwitch(const cDevice *Device, int ChannelNumber)
+//{
+//
+//}
+
+void cConnection::Recording(const cDevice *Device, const char *Name, const char *FileName, bool On)
+{
+  if (m_StatusInterfaceEnabled)
+  {
+    cResponsePacket *resp = new cResponsePacket();
+    if (!resp->initStatus(VDR_STATUS_RECORDING))
+    {
+      delete resp;
+      return;
+    }
+
+    resp->add_U32(Device->CardIndex());
+    resp->add_U32(On);
+    if (Name)
+      resp->add_String(Name);
+    else
+      resp->add_String("");
+
+    if (FileName)
+      resp->add_String(FileName);
+    else
+      resp->add_String("");
+
+    resp->finalise();
+    m_socket.write(resp->getPtr(), resp->getLen());
+    delete resp;
+  }
+}
+
+//void cConnection::Replaying(const cControl *Control, const char *Name, const char *FileName, bool On)
+//{
+//
+//}
+//
+//void cConnection::SetVolume(int Volume, bool Absolute)
+//{
+//
+//}
+//
+//void cConnection::SetAudioTrack(int Index, const char * const *Tracks)
+//{
+//
+//}
+//
+//void cConnection::SetAudioChannel(int AudioChannel)
+//{
+//
+//}
+//
+//void cConnection::SetSubtitleTrack(int Index, const char * const *Tracks)
+//{
+//
+//}
+//
+//void cConnection::OsdClear(void)
+//{
+//
+//}
+//
+//void cConnection::OsdTitle(const char *Title)
+//{
+//
+//}
+
+void cConnection::OsdStatusMessage(const char *Message)
+{
+  if (m_StatusInterfaceEnabled && Message)
+  {
+    /* Ignore this messages */
+    if (strcasecmp(Message, trVDR("Channel not available!")) == 0) return;
+    else if (strcasecmp(Message, trVDR("Delete timer?")) == 0) return;
+    else if (strcasecmp(Message, trVDR("Delete recording?")) == 0) return;
+    else if (strcasecmp(Message, trVDR("Press any key to cancel shutdown")) == 0) return;
+    else if (strcasecmp(Message, trVDR("Press any key to cancel restart")) == 0) return;
+    else if (strcasecmp(Message, trVDR("Editing - shut down anyway?")) == 0) return;
+    else if (strcasecmp(Message, trVDR("Recording - shut down anyway?")) == 0) return;
+    else if (strcasecmp(Message, trVDR("shut down anyway?")) == 0) return;
+    else if (strcasecmp(Message, trVDR("Recording - restart anyway?")) == 0) return;
+    else if (strcasecmp(Message, trVDR("Editing - restart anyway?")) == 0) return;
+    else if (strcasecmp(Message, trVDR("Delete channel?")) == 0) return;
+    else if (strcasecmp(Message, trVDR("Timer still recording - really delete?")) == 0) return;
+    else if (strcasecmp(Message, trVDR("Delete marks information?")) == 0) return;
+    else if (strcasecmp(Message, trVDR("Delete resume information?")) == 0) return;
+    else if (strcasecmp(Message, trVDR("CAM is in use - really reset?")) == 0) return;
+    else if (strcasecmp(Message, trVDR("Really restart?")) == 0) return;
+    else if (strcasecmp(Message, trVDR("Stop recording?")) == 0) return;
+    else if (strcasecmp(Message, trVDR("Cancel editing?")) == 0) return;
+    else if (strcasecmp(Message, trVDR("Cutter already running - Add to cutting queue?")) == 0) return;
+    else if (strcasecmp(Message, trVDR("No index-file found. Creating may take minutes. Create one?")) == 0) return;
+
+    cResponsePacket *resp = new cResponsePacket();
+    if (!resp->initStatus(VDR_STATUS_MESSAGE))
+    {
+      delete resp;
+      return;
+    }
+
+//    else if (type == mtWarning)
+//      resp->add_U32(1);
+//    else if (type == mtError)
+//      resp->add_U32(2);
+//    else
+    resp->add_U32(0);
+    resp->add_String(Message);
+    resp->finalise();
+    m_socket.write(resp->getPtr(), resp->getLen());
+    delete resp;
+  }
+}
+
+//void cConnection::OsdHelpKeys(const char *Red, const char *Green, const char *Yellow, const char *Blue)
+//{
+//
+//}
+//
+//void cConnection::OsdItem(const char *Text, int Index)
+//{
+//
+//}
+//
+//void cConnection::OsdCurrentItem(const char *Text)
+//{
+//
+//}
+//
+//void cConnection::OsdTextItem(const char *Text, bool Scroll)
+//{
+//
+//}
+//
+//void cConnection::OsdChannel(const char *Text)
+//{
+//
+//}
+//
+//void cConnection::OsdProgramme(time_t PresentTime, const char *PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle)
+//{
+//
+//}
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/connection.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/connection.h
new file mode 100644 (file)
index 0000000..fd79117
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef CONNECTION_H
+#define CONNECTION_H
+
+#include <vdr/thread.h>
+#include <vdr/receiver.h>
+#include <vdr/status.h>
+
+#include "config.h"
+#include "cxsocket.h"
+#include "cmdcontrol.h"
+
+class cServer;
+class cChannel;
+class cDevice;
+class cLiveStreamer;
+class cResponsePacket;
+class cRecPlayer;
+class cCmdControl;
+
+class cConnection : public cThread
+                  , public cStatus
+{
+private:
+  friend class cCmdControl;
+
+  unsigned int    m_Id;
+  cxSocket        m_socket;
+  cServer        *m_server;
+  bool            m_loggedIn;
+  bool            m_StatusInterfaceEnabled;
+  bool            m_OSDInterfaceEnabled;
+  cLiveStreamer  *m_Streamer;
+  const cChannel *m_Channel;
+  bool            m_isStreaming;
+  FILE           *m_NetLogFile;
+  cString         m_ClientAddress;
+  cRecPlayer     *m_RecPlayer;
+  cCmdControl     m_cmdcontrol;
+
+protected:
+  virtual void Action(void);
+
+  virtual void TimerChange(const cTimer *Timer, eTimerChange Change);
+//  virtual void ChannelSwitch(const cDevice *Device, int ChannelNumber);
+  virtual void Recording(const cDevice *Device, const char *Name, const char *FileName, bool On);
+//  virtual void Replaying(const cControl *Control, const char *Name, const char *FileName, bool On);
+//  virtual void SetVolume(int Volume, bool Absolute);
+//  virtual void SetAudioTrack(int Index, const char * const *Tracks);
+//  virtual void SetAudioChannel(int AudioChannel);
+//  virtual void SetSubtitleTrack(int Index, const char * const *Tracks);
+//  virtual void OsdClear(void);
+//  virtual void OsdTitle(const char *Title);
+  virtual void OsdStatusMessage(const char *Message);
+//  virtual void OsdHelpKeys(const char *Red, const char *Green, const char *Yellow, const char *Blue);
+//  virtual void OsdItem(const char *Text, int Index);
+//  virtual void OsdCurrentItem(const char *Text);
+//  virtual void OsdTextItem(const char *Text, bool Scroll);
+//  virtual void OsdChannel(const char *Text);
+//  virtual void OsdProgramme(time_t PresentTime, const char *PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle);
+
+public:
+  cConnection(cServer *server, int fd, unsigned int id, const char *ClientAdr);
+  virtual ~cConnection();
+
+  unsigned int GetID() { return m_Id; }
+  void SetLoggedIn(bool yesNo) { m_loggedIn = yesNo; }
+  void SetStatusInterface(bool yesNo) { m_StatusInterfaceEnabled = yesNo; }
+  void SetOSDInterface(bool yesNo) { m_OSDInterfaceEnabled = yesNo; }
+  void EnableNetLog(bool yesNo, const char* ClientName = "");
+  cxSocket *GetSocket() { return &m_socket; }
+  bool StartChannelStreaming(const cChannel *channel, cResponsePacket *resp);
+  void StopChannelStreaming();
+  bool IsStreaming() { return m_isStreaming; }
+  cRecPlayer *GetRecPlayer() { return m_RecPlayer; }
+};
+
+#endif /* CONNECTION_H */
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/cxsocket.c b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/cxsocket.c
new file mode 100644 (file)
index 0000000..343a2fc
--- /dev/null
@@ -0,0 +1,505 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+/*
+ * Socket wrapper classes
+ *
+ * Code is taken from xineliboutput plugin.
+ *
+ */
+
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#ifndef __APPLE__
+# include <sys/sendfile.h>
+#endif
+#include <netinet/tcp.h>
+
+#include <vdr/config.h>
+#include <vdr/tools.h>
+
+#include "config.h"
+#include "cxsocket.h"
+
+cxSocket::~cxSocket()
+{
+  CLOSESOCKET(m_fd);
+  delete m_pollerRead;
+  delete m_pollerWrite;
+}
+
+bool cxSocket::connect(struct sockaddr *addr, socklen_t len)
+{
+  return ::connect(m_fd, addr, len) == 0;
+}
+
+bool cxSocket::connect(const char *ip, int port)
+{
+  struct sockaddr_in sin;
+  sin.sin_family = AF_INET;
+  sin.sin_port = htons(port);
+  sin.sin_addr.s_addr = inet_addr(ip);
+  return connect((struct sockaddr *)&sin, sizeof(sin));
+}
+
+bool cxSocket::set_blocking(bool state)
+{
+  int flags = fcntl (m_fd, F_GETFL);
+
+  if(flags == -1)
+  {
+    esyslog("VNSI-Error: cxSocket::SetBlocking: fcntl(F_GETFL) failed");
+    return false;
+  }
+
+  flags = state ? (flags&(~O_NONBLOCK)) : (flags|O_NONBLOCK);
+
+  if(fcntl (m_fd, F_SETFL, flags) == -1)
+  {
+    esyslog("VNSI-Error: cxSocket::SetBlocking: fcntl(F_SETFL) failed");
+    return false;
+  }
+
+  return true;
+}
+
+bool cxSocket::set_buffers(int Tx, int Rx)
+{
+  int max_buf = Tx;
+  /*while(max_buf) {*/
+    errno = 0;
+    if(setsockopt(m_fd, SOL_SOCKET, SO_SNDBUF, &max_buf, sizeof(int)))
+    {
+      esyslog("VNSI-Error: cxSocket: setsockopt(SO_SNDBUF,%d) failed", max_buf);
+      /*max_buf >>= 1;*/
+    }
+    /*else {*/
+      int tmp = 0;
+      int len = sizeof(int);
+      errno = 0;
+      if(getsockopt(m_fd, SOL_SOCKET, SO_SNDBUF, &tmp, (socklen_t*)&len))
+      {
+        esyslog("VNSI-Error: cxSocket: getsockopt(SO_SNDBUF,%d) failed", max_buf);
+        /*break;*/
+      }
+      else if(tmp != max_buf)
+      {
+        dsyslog("VNSI: cxSocket: setsockopt(SO_SNDBUF): got %d bytes", tmp);
+        /*max_buf >>= 1;*/
+        /*continue;*/
+      }
+    /*}*/
+  /*}*/
+
+  max_buf = Rx;
+  setsockopt(m_fd, SOL_SOCKET, SO_RCVBUF, &max_buf, sizeof(int));
+
+  return true;
+}
+
+bool cxSocket::set_multicast(int ttl)
+{
+  int iReuse = 1, iLoop = 1, iTtl = ttl;
+
+  errno = 0;
+
+  if(setsockopt(m_fd, SOL_SOCKET, SO_REUSEADDR, &iReuse, sizeof(int)) < 0)
+  {
+    esyslog("VNSI-Error: cxSocket: setsockopt(SO_REUSEADDR) failed");
+    return false;
+  }
+
+  if(setsockopt(m_fd, IPPROTO_IP, IP_MULTICAST_TTL, &iTtl, sizeof(int)))
+  {
+    esyslog("VNSI-Error: cxSocket: setsockopt(IP_MULTICAST_TTL, %d) failed", iTtl);
+    return false;
+  }
+
+  if(setsockopt(m_fd, IPPROTO_IP, IP_MULTICAST_LOOP, &iLoop, sizeof(int)))
+  {
+    esyslog("VNSI-Error: cxSocket: setsockopt(IP_MULTICAST_LOOP) failed");
+    return false;
+  }
+
+  return true;
+}
+
+/*ssize_t cxSocket::sendfile(int fd_file, off_t *offset, size_t count)
+{
+  int r;
+#ifndef __APPLE__
+  r = ::sendfile(m_fd, fd_file, offset, count);
+  if(r<0 && (errno == ENOSYS || errno == EINVAL))
+  {
+    // fall back to read/write
+    esyslog("VNSI-Error: sendfile failed - using simple read/write");
+#endif
+    cxPoller p(*this, true);
+    char buf[0x10000];
+    int todor = count, todow, done = 0;
+    if(offset)
+    {
+      if((r=::lseek(fd_file, *offset, SEEK_SET)) < 0)
+        return r;
+    }
+    todow = ::read(fd_file, buf, count>sizeof(buf) ? sizeof(buf) : count);
+    if(todow <= 0)
+      return todow;
+    todor -= todow;
+    while(todow > 0)
+    {
+      if(p.Poll(100))
+      {
+        r = write(buf+done, todow);
+        if(r <= 0)
+          return r;
+        todow -= r;
+        done += r;
+      }
+    }
+    return done;
+#ifndef __APPLE__
+  }
+  return r;
+#endif
+}*/
+
+bool cxSocket::set_cork(bool state)
+{
+#ifdef __APPLE__
+  return false;
+#else
+  int iCork = state ? 1 : 0;
+  if(setsockopt(m_fd, IPPROTO_TCP, TCP_CORK, &iCork, sizeof(int)))
+  {
+    esyslog("VNSI-Error: cxSocket: setsockopt(TCP_CORK) failed");
+    return false;
+  }
+  return true;
+#endif
+}
+
+bool cxSocket::set_nodelay(bool state)
+{
+  int i = state ? 1 : 0;
+  if(setsockopt(m_fd, IPPROTO_TCP, TCP_NODELAY, &i, sizeof(int)))
+  {
+    esyslog("VNSI-Error: cxSocket: setsockopt(TCP_NODELAY) failed");
+    return false;
+  }
+  return true;
+}
+
+ssize_t cxSocket::tx_buffer_size(void)
+{
+  socklen_t l = sizeof(int);
+  int wmem = -1;
+  if(getsockopt(m_fd, SOL_SOCKET, SO_SNDBUF, &wmem, &l))
+  {
+    esyslog("VNSI-Error: getsockopt(SO_SNDBUF) failed");
+    return (ssize_t)-1;
+  }
+  return (ssize_t)wmem;
+}
+
+ssize_t cxSocket::tx_buffer_free(void)
+{
+  int wmem = tx_buffer_size();
+  int size = -1;
+  if(ioctl(m_fd, TIOCOUTQ, &size))
+  {
+    esyslog("VNSI-Error: ioctl(TIOCOUTQ) failed");
+    return (ssize_t)-1;
+  }
+
+  return (ssize_t)(wmem - size);
+}
+
+int cxSocket::getsockname(struct sockaddr *name, socklen_t *namelen)
+{
+  return ::getsockname(m_fd, name, namelen);
+}
+
+int cxSocket::getpeername(struct sockaddr *name, socklen_t *namelen)
+{
+  return ::getpeername(m_fd, name, namelen);
+}
+
+ssize_t cxSocket::send(const void *buf, size_t size, int flags, const struct sockaddr *to, socklen_t tolen)
+{
+  return ::sendto(m_fd, buf, size, flags, to, tolen);
+}
+
+ssize_t cxSocket::recv(void *buf, size_t size, int flags, struct sockaddr *from, socklen_t *fromlen)
+{
+  return ::recvfrom(m_fd, buf, size, flags, from, fromlen);
+}
+
+ssize_t cxSocket::write(const void *buffer, size_t size, int timeout_ms)
+{
+  cMutexLock CmdLock((cMutex*)&m_MutexWrite);
+
+  if(m_fd == -1) return -1;
+
+  ssize_t written = (ssize_t)size;
+  const unsigned char *ptr = (const unsigned char *)buffer;
+
+  while (size > 0)
+  {
+    errno = 0;
+    if(!m_pollerWrite->Poll(timeout_ms))
+    {
+      esyslog("VNSI-Error: cxSocket::write: poll() failed");
+      return written-size;
+    }
+
+    errno = 0;
+    ssize_t p = ::write(m_fd, ptr, size);
+
+    if (p <= 0)
+    {
+      if (errno == EINTR || errno == EAGAIN)
+      {
+        dsyslog("VNSI: cxSocket::write: EINTR during write(), retrying");
+        continue;
+      }
+      esyslog("VNSI-Error: cxSocket::write: write() error");
+      return p;
+    }
+
+    ptr  += p;
+    size -= p;
+  }
+
+  return written;
+}
+
+ssize_t cxSocket::read(void *buffer, size_t size, int timeout_ms)
+{
+  cMutexLock CmdLock((cMutex*)&m_MutexRead);
+
+  if(m_fd == -1) return -1;
+
+  ssize_t missing = (ssize_t)size;
+  unsigned char *ptr = (unsigned char *)buffer;
+
+  while (missing > 0)
+  {
+    if(!m_pollerRead->Poll(timeout_ms))
+    {
+      esyslog("VNSI-Error: cxSocket::read: poll() failed at %d/%d", (int)(size-missing), (int)size);
+      return size-missing;
+    }
+
+    errno = 0;
+    ssize_t p = ::read(m_fd, ptr, missing);
+
+    if (p <= 0)
+    {
+      if (errno == EINTR || errno == EAGAIN)
+      {
+        dsyslog("VNSI: cxSocket::read: EINTR/EAGAIN during read(), retrying");
+        continue;
+      }
+      esyslog("VNSI-Error: cxSocket::read: read() error at %d/%d", (int)(size-missing), (int)size);
+      return size-missing;
+    }
+
+    ptr  += p;
+    missing -= p;
+  }
+
+  return size;
+}
+
+void cxSocket::set_handle(int h)
+{
+  if(h != m_fd)
+  {
+    close();
+    m_fd = h;
+    delete m_pollerRead;
+    delete m_pollerWrite;
+    m_pollerRead = new cPoller(m_fd);
+    m_pollerWrite = new cPoller(m_fd, true);
+  }
+}
+
+ssize_t cxSocket::printf(const char *fmt, ...)
+{
+  va_list argp;
+  char buf[1024];
+  int r;
+
+  va_start(argp, fmt);
+  r = vsnprintf(buf, sizeof(buf), fmt, argp);
+  if (r<0)
+  {
+    esyslog("VNSI-Error: cxSocket::printf: vsnprintf failed");
+  }
+  else if(r >= (int)sizeof(buf))
+  {
+    dsyslog("VNSI: cxSocket::printf: vsnprintf overflow (%20s)", buf);
+  }
+  else
+    return write(buf, r);
+
+  return (ssize_t)-1;
+}
+
+/* readline return value:
+ *   <0        : failed
+ *   >=maxsize : buffer overflow
+ *   >=0       : if errno = EAGAIN -> line is not complete (there was timeout)
+ *               if errno = 0      -> succeed
+ *               (return value 0 indicates empty line "\r\n")
+ */
+ssize_t cxSocket::readline(char *buf, int bufsize, int timeout, int bufpos)
+{
+  int n = -1, cnt = bufpos;
+
+  do
+  {
+    if(timeout>0 && !m_pollerRead->Poll(timeout))
+    {
+      errno = EAGAIN;
+      return cnt;
+    }
+
+    while((n = ::read(m_fd, buf+cnt, 1)) == 1)
+    {
+      buf[++cnt] = 0;
+
+      if( cnt > 1 && buf[cnt - 2] == '\r' && buf[cnt - 1] == '\n')
+      {
+        cnt -= 2;
+        buf[cnt] = 0;
+        errno = 0;
+        return cnt;
+      }
+
+      if( cnt >= bufsize)
+      {
+        dsyslog("VNSI: cxSocket::readline: too long control message (%d bytes): %20s", cnt, buf);
+        errno = 0;
+        return bufsize;
+      }
+    }
+
+    /* connection closed ? */
+    if (n == 0)
+    {
+      dsyslog("VNSI: cxSocket::readline: disconnected");
+      if(errno == EAGAIN)
+        errno = ENOTCONN;
+      return -1;
+    }
+
+  } while (timeout>0 && n<0 && errno == EAGAIN);
+
+  if(errno == EAGAIN)
+    return cnt;
+
+  esyslog("VNSI-Error: cxSocket::readline: read failed");
+  return n;
+}
+
+#include <sys/ioctl.h>
+#include <net/if.h>
+
+uint32_t cxSocket::get_local_address(char *ip_address)
+{
+  uint32_t local_addr = 0;
+  struct ifconf conf;
+  struct ifreq buf[3];
+  unsigned int n;
+
+  struct sockaddr_in sin;
+  socklen_t len = sizeof(sin);
+
+  if(!getsockname((struct sockaddr *)&sin, &len))
+  {
+    local_addr = sin.sin_addr.s_addr;
+  }
+  else
+  {
+    //esyslog("VNSI-Error: getsockname failed");
+
+    // scan network interfaces
+    conf.ifc_len = sizeof(buf);
+    conf.ifc_req = buf;
+    memset(buf, 0, sizeof(buf));
+
+    errno = 0;
+    if(ioctl(m_fd, SIOCGIFCONF, &conf) < 0)
+    {
+      esyslog("VNSI-Error: cxSocket: can't obtain socket local address");
+    }
+    else
+    {
+      for(n=0; n<conf.ifc_len/sizeof(struct ifreq); n++)
+      {
+        struct sockaddr_in *in = (struct sockaddr_in *) &buf[n].ifr_addr;
+# if 0
+        uint32_t tmp = ntohl(in->sin_addr.s_addr);
+        dsyslog("VNSI: Local address %6s %d.%d.%d.%d",
+                conf.ifc_req[n].ifr_name,
+                ((tmp>>24)&0xff), ((tmp>>16)&0xff),
+                ((tmp>>8)&0xff), ((tmp)&0xff));
+# endif
+        if(n==0 || local_addr == htonl(INADDR_LOOPBACK))
+          local_addr = in->sin_addr.s_addr;
+        else
+          break;
+      }
+    }
+  }
+
+  if(!local_addr)
+    esyslog("VNSI-Error: No local address found");
+
+  if(ip_address)
+    cxSocket::ip2txt(local_addr, 0, ip_address);
+
+  return local_addr;
+}
+
+char *cxSocket::ip2txt(uint32_t ip, unsigned int port, char *str)
+{
+  // inet_ntoa is not thread-safe (?)
+  if(str)
+  {
+    unsigned int iph =(unsigned int)ntohl(ip);
+    unsigned int porth =(unsigned int)ntohs(port);
+    if(!porth)
+      sprintf(str, "%d.%d.%d.%d", ((iph>>24)&0xff), ((iph>>16)&0xff), ((iph>>8)&0xff), ((iph)&0xff));
+    else
+      sprintf(str, "%u.%u.%u.%u:%u", ((iph>>24)&0xff), ((iph>>16)&0xff), ((iph>>8)&0xff), ((iph)&0xff), porth);
+  }
+  return str;
+}
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/cxsocket.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/cxsocket.h
new file mode 100644 (file)
index 0000000..c0e25c6
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+/*
+ * Socket wrapper classes
+ *
+ * Code is taken from xineliboutput plugin.
+ *
+ */
+
+#ifndef __CXSOCKET_H
+#define __CXSOCKET_H
+
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <vdr/thread.h>
+#include <vdr/tools.h>
+
+#define CLOSESOCKET(fd) do { if(fd>=0) { ::close(fd); fd=-1; } } while(0)
+
+class cxSocket {
+ private:
+  int m_fd;
+  cMutex m_MutexWrite;
+  cMutex m_MutexRead;
+
+  cPoller *m_pollerRead;
+  cPoller *m_pollerWrite;
+  
+  cxSocket(const cxSocket& s);//{ m_fd = s.m_fd>=0 ? dup(s.m_fd) : -1; }
+  cxSocket &operator=(const cxSocket &S);// { close(); m_fd = S.m_fd >= 0 ? dup(S.m_fd) : -1; return *this; };
+
+ public:
+
+  typedef enum
+  {
+    estSTREAM = SOCK_STREAM,
+    estDGRAM  = SOCK_DGRAM
+  } eSockType;
+
+  cxSocket() : m_fd(-1), m_pollerRead(NULL), m_pollerWrite(NULL) {}
+  cxSocket(eSockType type) : m_fd(::socket(PF_INET, (int)type, 0)) {}
+
+  ~cxSocket();
+
+  //operator int ()  const { return Handle(); }
+  //operator bool () const { return open(); }
+  //bool operator==(const cxSocket &s) { return m_fd == s.m_fd; }
+
+  int  handle(bool take_ownership=false)
+       { int r=m_fd; if(take_ownership) m_fd=-1; return r; }
+
+  void set_handle(int h);
+
+  bool create(eSockType type) { close(); return (m_fd=::socket(PF_INET, (int)type, 0)) >= 0; }
+  bool open(void)   const { return m_fd>0; }
+  void close(void)        { CLOSESOCKET(m_fd); }
+
+  ssize_t send(const void *buf, size_t size, int flags=0,
+              const struct sockaddr *to = NULL, socklen_t tolen = 0);
+  ssize_t recv(void *buf, size_t size, int flags = 0,
+              struct sockaddr *from = NULL, socklen_t *fromlen = NULL);
+  //ssize_t sendfile(int fd_file, off_t *offset, size_t count);
+
+  ssize_t read(void *buffer, size_t size, int timeout_ms = -1);
+  ssize_t write(const void *buffer, size_t size, int timeout_ms = -1);
+
+  ssize_t printf(const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
+  ssize_t write_str(const char *str, int timeout_ms=-1, int len=0)
+  { return write(str, len ?: strlen(str), timeout_ms); }
+  ssize_t write_cmd(const char *str, int len=0)
+  { return write(str, len ?: strlen(str), 10); }
+
+/* readline return value:
+ *   <0        : failed
+ *   >=maxsize : buffer overflow
+ *   >=0       : if errno = EAGAIN -> line is not complete (there was timeout)
+ *               if errno = 0      -> succeed
+ *               (return value 0 indicates empty line "\r\n")
+ */
+  ssize_t readline(char *buf, int bufsize, int timeout=0, int bufpos=0);
+
+  bool set_buffers(int Tx, int Rx);
+  bool set_multicast(int ttl);
+  bool set_blocking(bool state);
+  bool set_cork(bool state);
+  bool flush_cork(void) { return set_nodelay(true); };
+  bool set_nodelay(bool state);
+  ssize_t tx_buffer_size(void);
+  ssize_t tx_buffer_free(void);
+  int getsockname(struct sockaddr *name, socklen_t *namelen);
+  int getpeername(struct sockaddr *name, socklen_t *namelen);
+
+
+  bool connect(struct sockaddr *addr, socklen_t len);
+  bool connect(const char *ip, int port);
+
+  uint32_t get_local_address(char *ip_address);
+
+  static char *ip2txt(uint32_t ip, unsigned int port, char *str);
+};
+
+//
+// Set socket buffers
+//
+static inline void set_socket_buffers(int s, int txbuf, int rxbuf)
+{
+  int max_buf = txbuf;
+  /*while(max_buf) {*/
+    errno = 0;
+    if(setsockopt(s, SOL_SOCKET, SO_SNDBUF, &max_buf, sizeof(int))) {
+      esyslog("VNSI-Error: setsockopt(SO_SNDBUF,%d) failed", max_buf);
+      /*max_buf >>= 1;*/
+    }
+    /*else {*/
+      int tmp = 0;
+      int len = sizeof(int);
+      errno = 0;
+      if(getsockopt(s, SOL_SOCKET, SO_SNDBUF, &tmp, (socklen_t*)&len))
+      {
+        esyslog("VNSI-Error: getsockopt(SO_SNDBUF,%d) failed", max_buf);
+        /*break;*/
+      }
+      else if(tmp != max_buf)
+      {
+        dsyslog("VNSI: setsockopt(SO_SNDBUF): got %d bytes", tmp);
+        /*max_buf >>= 1;*/
+        /*continue;*/
+      }
+    /*}*/
+  /*}*/
+
+  max_buf = rxbuf;
+  setsockopt(s, SOL_SOCKET, SO_RCVBUF, &max_buf, sizeof(int));
+}
+
+//
+// Connect data socket to client (take address from fd_control)
+//
+static inline int sock_connect(int fd_control, int port, int type)
+{
+  struct sockaddr_in sin;
+  socklen_t len = sizeof(sin);
+  int             s, one = 1;
+
+  if(getpeername(fd_control, (struct sockaddr *)&sin, &len)) {
+    esyslog("VNSI-Error: sock_connect: getpeername failed");
+    return -1;
+  }
+
+  uint32_t tmp = ntohl(sin.sin_addr.s_addr);
+  dsyslog("VNSI: Client address: %d.%d.%d.%d",
+         ((tmp>>24)&0xff), ((tmp>>16)&0xff),
+         ((tmp>>8)&0xff), ((tmp)&0xff));
+
+#if 0
+  if ((h = gethostbyname(tmp)) == NULL) {
+    LOGDBG("sock_connect: unable to resolve host name", tmp);
+  }
+#endif
+
+  if ((s = socket(PF_INET, type, type==SOCK_DGRAM?IPPROTO_UDP:IPPROTO_TCP)) < 0) {
+    esyslog("VNSI-Error: sock_connect: failed to create socket");
+    return -1;
+  }
+
+  // Set socket buffers: large send buffer, small receive buffer
+  set_socket_buffers(s, KILOBYTE(256), 2048);
+
+  if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int)) < 0)
+    esyslog("VNSI-Error: sock_connect: setsockopt(SO_REUSEADDR) failed");
+
+  sin.sin_family = AF_INET;
+  sin.sin_port   = htons(port);
+
+  if (connect(s, (struct sockaddr *)&sin, sizeof(sin))==-1 &&
+      errno != EINPROGRESS) {
+    esyslog("VNSI-Error: connect() failed");
+    CLOSESOCKET(s);
+  }
+
+  if (fcntl (s, F_SETFL, fcntl (s, F_GETFL) | O_NONBLOCK) == -1) {
+    esyslog("VNSI-Error: can't put socket in non-blocking mode");
+    CLOSESOCKET(s);
+    return -1;
+  }
+
+  return s;
+}
+
+#endif // __CXSOCKET_H
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer.c b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer.c
new file mode 100644 (file)
index 0000000..1ce392d
--- /dev/null
@@ -0,0 +1,380 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <stdlib.h>
+#include <assert.h>
+#include <vdr/remux.h>
+#include <vdr/channels.h>
+#include "config.h"
+#include "receiver.h"
+#include "demuxer.h"
+#include "demuxer_AAC.h"
+#include "demuxer_AC3.h"
+#include "demuxer_DTS.h"
+#include "demuxer_h264.h"
+#include "demuxer_MPEGAudio.h"
+#include "demuxer_MPEGVideo.h"
+#include "demuxer_Subtitle.h"
+#include "demuxer_Teletext.h"
+
+#define PTS_MASK 0x1ffffffffLL
+//#define PTS_MASK 0x7ffffLL
+
+#ifndef INT64_MIN
+#define INT64_MIN       (-0x7fffffffffffffffLL-1)
+#endif
+
+int64_t PesGetPTS(const uint8_t *buf, int len)
+{
+  /* assume mpeg2 pes header ... */
+  if (PesIsVideoPacket(buf) || PesIsAudioPacket(buf)) {
+
+    if ((buf[6] & 0xC0) != 0x80)
+      return DVD_NOPTS_VALUE;
+    if ((buf[6] & 0x30) != 0)
+      return DVD_NOPTS_VALUE;
+
+    if ((len > 13) && (buf[7] & 0x80)) { /* pts avail */
+      int64_t pts;
+      pts  = ((int64_t)(buf[ 9] & 0x0E)) << 29 ;
+      pts |= ((int64_t) buf[10])         << 22 ;
+      pts |= ((int64_t)(buf[11] & 0xFE)) << 14 ;
+      pts |= ((int64_t) buf[12])         <<  7 ;
+      pts |= ((int64_t)(buf[13] & 0xFE)) >>  1 ;
+      return pts;
+    }
+  }
+  return DVD_NOPTS_VALUE;
+}
+
+int64_t PesGetDTS(const uint8_t *buf, int len)
+{
+  if (PesIsVideoPacket(buf) || PesIsAudioPacket(buf))
+  {
+    if ((buf[6] & 0xC0) != 0x80)
+      return DVD_NOPTS_VALUE;
+    if ((buf[6] & 0x30) != 0)
+      return DVD_NOPTS_VALUE;
+
+    if (len > 18 && (buf[7] & 0x40)) { /* dts avail */
+      int64_t dts;
+      dts  = ((int64_t)( buf[14] & 0x0E)) << 29 ;
+      dts |=  (int64_t)( buf[15]         << 22 );
+      dts |=  (int64_t)((buf[16] & 0xFE) << 14 );
+      dts |=  (int64_t)( buf[17]         <<  7 );
+      dts |=  (int64_t)((buf[18] & 0xFE) >>  1 );
+      return dts;
+    }
+  }
+  return DVD_NOPTS_VALUE;
+}
+
+int64_t cParser::m_startDTS;
+
+// --- cParser -------------------------------------------------
+
+cParser::cParser(cLiveStreamer *streamer, int streamID)
+ : m_Streamer(streamer)
+ , m_streamID(streamID)
+{
+  m_curPTS    = DVD_NOPTS_VALUE;
+  m_curDTS    = DVD_NOPTS_VALUE;
+  m_LastDTS   = DVD_NOPTS_VALUE;
+  if (streamer->IsAudioOnly())
+    m_startDTS  = 0;
+  else
+    m_startDTS  = DVD_NOPTS_VALUE;
+  m_epochDTS  = 0;
+  m_badDTS    = 0;
+}
+
+int64_t cParser::Rescale(int64_t a)
+{
+  int64_t b = DVD_TIME_BASE;
+  int64_t c = 90000;
+  int64_t r = c/2;
+
+  if (b<=INT_MAX && c<=INT_MAX){
+    if (a<=INT_MAX)
+      return (a * b + r)/c;
+    else
+      return a/c*b + (a%c*b + r)/c;
+  }
+  else
+  {
+    uint64_t a0= a&0xFFFFFFFF;
+    uint64_t a1= a>>32;
+    uint64_t b0= b&0xFFFFFFFF;
+    uint64_t b1= b>>32;
+    uint64_t t1= a0*b1 + a1*b0;
+    uint64_t t1a= t1<<32;
+
+    a0 = a0*b0 + t1a;
+    a1 = a1*b1 + (t1>>32) + (a0<t1a);
+    a0 += r;
+    a1 += a0<r;
+
+    for (int i=63; i>=0; i--)
+    {
+      a1+= a1 + ((a0>>i)&1);
+      t1+=t1;
+      if (c <= a1)
+      {
+        a1 -= c;
+        t1++;
+      }
+    }
+    return t1;
+  }
+}
+
+/*
+ * Extract DTS and PTS and update current values in stream
+ */
+int cParser::ParsePESHeader(uint8_t *buf, size_t len)
+{
+  /* parse PES header */
+  unsigned int hdr_len = PesHeaderLength(buf);
+  unsigned int pes_pid = buf[3];
+  unsigned int pes_len = (buf[4] << 8) | buf[5];
+
+  /* parse PTS */
+  int64_t pts = PesGetPTS(buf, len);
+  int64_t dts = PesGetDTS(buf, len);
+  if (dts == DVD_NOPTS_VALUE)
+    dts = pts;
+
+  m_curDTS = dts & PTS_MASK;
+  m_curPTS = pts & PTS_MASK;
+  return hdr_len;
+}
+
+void cParser::SendPacket(sStreamPacket *pkt, bool checkTimestamp)
+{
+  if (!m_Streamer->IsReady())
+    return;
+
+  assert(pkt->dts != DVD_NOPTS_VALUE);
+  assert(pkt->pts != DVD_NOPTS_VALUE);
+
+  if (m_startDTS == DVD_NOPTS_VALUE)
+    return;
+
+  int64_t dts = pkt->dts;
+  int64_t pts = pkt->pts;
+
+  /* Compute delta between PTS and DTS (and watch out for 33 bit wrap) */
+  int64_t ptsoff = (pts - dts) & PTS_MASK;
+
+  /* Subtract the transport wide start offset */
+  dts -= m_startDTS;
+
+  if (m_LastDTS == DVD_NOPTS_VALUE)
+  {
+    if (dts < 0)
+    {
+      /* Early packet with negative time stamp, drop those */
+      return;
+    }
+  }
+  else if(checkTimestamp)
+  {
+    int d = dts + m_epochDTS - m_LastDTS;
+
+    if (d < 0 || d > 90000) {
+
+      if (d < -PTS_MASK || d > -PTS_MASK + 180000)
+      {
+        m_badDTS++;
+
+        if (m_badDTS < 5)
+        {
+          dsyslog("VNSI-Error: DTS discontinuity. DTS = %llu, last = %llu", dts, m_LastDTS);
+        }
+      }
+      else
+      {
+        /* DTS wrapped, increase upper bits */
+        m_epochDTS += PTS_MASK + 1;
+        m_badDTS = 0;
+      }
+    }
+    else
+    {
+      m_badDTS = 0;
+    }
+  }
+  m_badDTS++;
+
+  dts += m_epochDTS;
+  m_LastDTS = dts;
+
+  pts = dts + ptsoff;
+
+  /* Rescale to tvheadned internal 1MHz clock */
+  pkt->dts      = Rescale(dts);
+  pkt->pts      = Rescale(pts);
+  pkt->duration = Rescale(pkt->duration);
+
+  m_Streamer->sendStreamPacket(pkt);
+}
+
+
+// --- cTSDemuxer ----------------------------------------------------
+
+cTSDemuxer::cTSDemuxer(cLiveStreamer *streamer, int id, eStreamType type, int pid)
+  : m_Streamer(streamer)
+  , m_streamID(id)
+  , m_streamType(type)
+  , m_pID(pid)
+{
+  m_pesError        = false;
+  m_pesParser       = NULL;
+  m_language[0]     = 0;
+  m_FpsScale        = 0;
+  m_FpsRate         = 0;
+  m_Height          = 0;
+  m_Width           = 0;
+  m_Aspect          = 0.0f;
+  m_Channels        = 0;
+  m_SampleRate      = 0;
+  m_BitRate         = 0;
+  m_BitsPerSample   = 0;
+  m_BlockAlign      = 0;
+
+  if (m_streamType == stMPEG2VIDEO)
+    m_pesParser = new cParserMPEG2Video(this, m_Streamer, m_streamID);
+  else if (m_streamType == stH264)
+    m_pesParser = new cParserH264(this, m_Streamer, m_streamID);
+  else if (m_streamType == stMPEG2AUDIO)
+    m_pesParser = new cParserMPEG2Audio(this, m_Streamer, m_streamID);
+  else if (m_streamType == stAAC)
+    m_pesParser = new cParserAAC(this, m_Streamer, m_streamID);
+  else if (m_streamType == stAC3)
+    m_pesParser = new cParserAC3(this, m_Streamer, m_streamID);
+  else if (m_streamType == stDTS)
+    m_pesParser = new cParserDTS(this, m_Streamer, m_streamID);
+  else if (m_streamType == stEAC3)
+    m_pesParser = new cParserAC3(this, m_Streamer, m_streamID);
+  else if (m_streamType == stTELETEXT)
+    m_pesParser = new cParserTeletext(this, m_Streamer, m_streamID);
+  else if (m_streamType == stDVBSUB)
+    m_pesParser = new cParserSubtitle(this, m_Streamer, m_streamID);
+  else
+  {
+    esyslog("VNSI-Error: Unrecognised type %i inside stream %i", m_streamType, m_streamID);
+    return;
+  }
+}
+
+cTSDemuxer::~cTSDemuxer()
+{
+  if (m_pesParser)
+  {
+    delete m_pesParser;
+    m_pesParser = NULL;
+  }
+}
+
+bool cTSDemuxer::ProcessTSPacket(unsigned char *data)
+{
+  if (!data)
+    return false;
+
+  bool pusi  = TsPayloadStart(data);
+  int  bytes = TS_SIZE - TsPayloadOffset(data);
+
+  if(bytes < 0 || bytes > TS_SIZE)
+    return false;
+
+  if (TsError(data))
+  {
+    dsyslog("VNSI-Error: transport error");
+    return false;
+  }
+
+  if (!TsHasPayload(data))
+  {
+    LOGCONSOLE("VNSI-Error: no payload, size %d", bytes);
+    return true;
+  }
+
+  /* drop broken PES packets */
+  if (m_pesError && !pusi)
+  {
+    dsyslog("VNSI-Error: dropping broken PES packet");
+    return false;
+  }
+
+  /* strip ts header */
+  data += TS_SIZE - bytes;
+
+  /* handle new payload unit */
+  if (pusi)
+  {
+    if (!PesIsHeader(data))
+    {
+      esyslog("VNSI-Error: payload not PES ?");
+      m_pesError = true;
+      return false;
+    }
+    m_pesError = false;
+  }
+
+  /* Parse the data */
+  if (m_pesParser)
+    m_pesParser->Parse(data, bytes, pusi);
+
+  return true;
+}
+
+void cTSDemuxer::SetLanguage(const char *language)
+{
+  m_language[0] = language[0];
+  m_language[1] = language[1];
+  m_language[2] = language[2];
+  m_language[3] = 0;
+}
+
+void cTSDemuxer::SetVideoInformation(int FpsScale, int FpsRate, int Height, int Width, float Aspect)
+{
+  m_FpsScale        = FpsScale;
+  m_FpsRate         = FpsRate;
+  m_Height          = Height;
+  m_Width           = Width;
+  m_Aspect          = Aspect;
+}
+
+void cTSDemuxer::SetAudioInformation(int Channels, int SampleRate, int BitRate, int BitsPerSample, int BlockAlign)
+{
+  m_Channels        = Channels;
+  m_SampleRate      = SampleRate;
+  m_BlockAlign      = BlockAlign;
+  m_BitRate         = BitRate;
+  m_BitsPerSample   = BitsPerSample;
+}
+
+void cTSDemuxer::SetSubtitlingDescriptor(unsigned char SubtitlingType, uint16_t CompositionPageId, uint16_t AncillaryPageId)
+{
+  m_subtitlingType    = SubtitlingType;
+  m_compositionPageId = CompositionPageId;
+  m_ancillaryPageId   = AncillaryPageId;
+}
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer.h
new file mode 100644 (file)
index 0000000..1564ca4
--- /dev/null
@@ -0,0 +1,293 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef VNSIDEMUXER_H
+#define VNSIDEMUXER_H
+
+#include <vdr/device.h>
+
+#define DVD_TIME_BASE 1000000
+#define DVD_NOPTS_VALUE    (-1LL<<52) // should be possible to represent in both double and __int64
+
+/* PES PIDs */
+#define PRIVATE_STREAM1   0xBD
+#define PADDING_STREAM    0xBE
+#define PRIVATE_STREAM2   0xBF
+#define AUDIO_STREAM_S    0xC0      /* 1100 0000 */
+#define AUDIO_STREAM_E    0xDF      /* 1101 1111 */
+#define VIDEO_STREAM_S    0xE0      /* 1110 0000 */
+#define VIDEO_STREAM_E    0xEF      /* 1110 1111 */
+
+#define AUDIO_STREAM_MASK 0x1F  /* 0001 1111 */
+#define VIDEO_STREAM_MASK 0x0F  /* 0000 1111 */
+#define AUDIO_STREAM      0xC0  /* 1100 0000 */
+#define VIDEO_STREAM      0xE0  /* 1110 0000 */
+
+#define ECM_STREAM        0xF0
+#define EMM_STREAM        0xF1
+#define DSM_CC_STREAM     0xF2
+#define ISO13522_STREAM   0xF3
+#define PROG_STREAM_DIR   0xFF
+
+inline bool PesIsHeader(const uchar *p)
+{
+  return !(p)[0] && !(p)[1] && (p)[2] == 1;
+}
+
+inline int PesHeaderLength(const uchar *p)
+{
+  return 8 + (p)[8] + 1;
+}
+
+inline bool PesIsVideoPacket(const uchar *p)
+{
+  return (((p)[3] & ~VIDEO_STREAM_MASK) == VIDEO_STREAM);
+}
+
+inline bool PesIsMPEGAudioPacket(const uchar *p)
+{
+  return (((p)[3] & ~AUDIO_STREAM_MASK) == AUDIO_STREAM);
+}
+
+inline bool PesIsPS1Packet(const uchar *p)
+{
+  return ((p)[3] == PRIVATE_STREAM1);
+}
+
+inline bool PesIsPaddingPacket(const uchar *p)
+{
+  return ((p)[3] == PADDING_STREAM);
+}
+
+inline bool PesIsAudioPacket(const uchar *p)
+{
+  return (PesIsMPEGAudioPacket(p) || PesIsPS1Packet(p));
+}
+
+#if APIVERSNUM < 10701
+
+#define TS_ERROR              0x80
+#define TS_PAYLOAD_START      0x40
+#define TS_TRANSPORT_PRIORITY 0x20
+#define TS_PID_MASK_HI        0x1F
+#define TS_SCRAMBLING_CONTROL 0xC0
+#define TS_ADAPT_FIELD_EXISTS 0x20
+#define TS_PAYLOAD_EXISTS     0x10
+#define TS_CONT_CNT_MASK      0x0F
+#define TS_ADAPT_DISCONT      0x80
+#define TS_ADAPT_RANDOM_ACC   0x40 // would be perfect for detecting independent frames, but unfortunately not used by all broadcasters
+#define TS_ADAPT_ELEM_PRIO    0x20
+#define TS_ADAPT_PCR          0x10
+#define TS_ADAPT_OPCR         0x08
+#define TS_ADAPT_SPLICING     0x04
+#define TS_ADAPT_TP_PRIVATE   0x02
+#define TS_ADAPT_EXTENSION    0x01
+
+inline bool TsHasPayload(const uchar *p)
+{
+  return p[3] & TS_PAYLOAD_EXISTS;
+}
+
+inline bool TsHasAdaptationField(const uchar *p)
+{
+  return p[3] & TS_ADAPT_FIELD_EXISTS;
+}
+
+inline bool TsPayloadStart(const uchar *p)
+{
+  return p[1] & TS_PAYLOAD_START;
+}
+
+inline bool TsError(const uchar *p)
+{
+  return p[1] & TS_ERROR;
+}
+
+inline int TsPid(const uchar *p)
+{
+  return (p[1] & TS_PID_MASK_HI) * 256 + p[2];
+}
+
+inline bool TsIsScrambled(const uchar *p)
+{
+  return p[3] & TS_SCRAMBLING_CONTROL;
+}
+
+inline int TsPayloadOffset(const uchar *p)
+{
+  return (p[3] & TS_ADAPT_FIELD_EXISTS) ? p[4] + 5 : 4;
+}
+
+inline int TsGetPayload(const uchar **p)
+{
+  int o = TsPayloadOffset(*p);
+  *p += o;
+  return TS_SIZE - o;
+}
+
+inline int TsContinuityCounter(const uchar *p)
+{
+  return p[3] & TS_CONT_CNT_MASK;
+}
+
+inline int TsGetAdaptationField(const uchar *p)
+{
+  return TsHasAdaptationField(p) ? p[5] : 0x00;
+}
+#endif
+
+enum eStreamContent
+{
+  scVIDEO,
+  scAUDIO,
+  scSUBTITLE,
+  scTELETEXT,
+  scPROGRAMM
+};
+
+enum eStreamType
+{
+  stNone,
+  stAC3,
+  stMPEG2AUDIO,
+  stEAC3,
+  stAAC,
+  stDTS,
+  stMPEG2VIDEO,
+  stH264,
+  stDVBSUB,
+  stTEXTSUB,
+  stTELETEXT,
+};
+
+#define PKT_I_FRAME 1
+#define PKT_P_FRAME 2
+#define PKT_B_FRAME 3
+#define PKT_NTYPES  4
+struct sStreamPacket
+{
+  int64_t   id;
+  int64_t   dts;
+  int64_t   pts;
+  int       duration;
+
+  uint8_t   commercial;
+  uint8_t   componentindex;
+  uint8_t   frametype;
+
+  uint8_t  *data;
+  int       size;
+};
+
+class cLiveStreamer;
+
+class cParser
+{
+public:
+  cParser(cLiveStreamer *streamer, int streamID);
+  virtual ~cParser() {};
+
+  virtual void Parse(unsigned char *data, int size, bool pusi) = 0;
+
+  int ParsePESHeader(uint8_t *buf, size_t len);
+  void SendPacket(sStreamPacket *pkt, bool checkTimestamp = true);
+  int64_t Rescale(int64_t a);
+
+  cLiveStreamer *m_Streamer;
+
+  static int64_t m_startDTS;
+  int64_t     m_LastDTS;
+  int64_t     m_curPTS;
+  int64_t     m_curDTS;
+  int64_t     m_epochDTS;
+
+  int         m_badDTS;
+  int         m_streamID;
+};
+
+
+class cTSDemuxer
+{
+private:
+  cLiveStreamer        *m_Streamer;
+  const int             m_streamID;
+  const int             m_pID;
+  eStreamContent        m_streamContent;
+  eStreamType           m_streamType;
+
+  bool                  m_pesError;
+  cParser              *m_pesParser;
+
+  char                  m_language[4];  // ISO 639 3-letter language code (empty string if undefined)
+
+  int                   m_FpsScale;     // scale of 1000 and a rate of 29970 will result in 29.97 fps
+  int                   m_FpsRate;
+  int                   m_Height;       // height of the stream reported by the demuxer
+  int                   m_Width;        // width of the stream reported by the demuxer
+  float                 m_Aspect;       // display aspect of stream
+
+  int                   m_Channels;
+  int                   m_SampleRate;
+  int                   m_BitRate;
+  int                   m_BitsPerSample;
+  int                   m_BlockAlign;
+
+  unsigned char         m_subtitlingType;
+  uint16_t              m_compositionPageId;
+  uint16_t              m_ancillaryPageId;
+
+public:
+  cTSDemuxer(cLiveStreamer *streamer, int id, eStreamType type, int pid);
+  virtual ~cTSDemuxer();
+
+  bool ProcessTSPacket(unsigned char *data);
+
+  void SetLanguage(const char *language);
+  const char *GetLanguage() { return m_language; }
+  const eStreamContent Content() const { return m_streamContent; }
+  const eStreamType Type() const { return m_streamType; }
+  const int GetPID() const { return m_pID; }
+  const int GetStreamID() const { return m_streamID; }
+
+  /* Video Stream Information */
+  void SetVideoInformation(int FpsScale, int FpsRate, int Height, int Width, float Aspect);
+  uint32_t GetFpsScale() const { return m_FpsScale; }
+  uint32_t GetFpsRate() const { return m_FpsRate; }
+  uint32_t GetHeight() const { return m_Height; }
+  uint32_t GetWidth() const { return m_Width; }
+  double GetAspect() const { return m_Aspect; }
+
+  /* Audio Stream Information */
+  void SetAudioInformation(int Channels, int SampleRate, int BitRate, int BitsPerSample, int BlockAlign);
+  uint32_t GetChannels() const { return m_Channels; }
+  uint32_t GetSampleRate() const { return m_SampleRate; }
+  uint32_t GetBlockAlign() const { return m_BlockAlign; }
+  uint32_t GetBitRate() const { return m_BitRate; }
+  uint32_t GetBitsPerSample() const { return m_BitsPerSample; }
+
+  /* Subtitle related stream information */
+  void SetSubtitlingDescriptor(unsigned char SubtitlingType, uint16_t CompositionPageId, uint16_t AncillaryPageId);
+  unsigned char SubtitlingType() const { return m_subtitlingType; }
+  uint16_t CompositionPageId() const { return m_compositionPageId; }
+  uint16_t AncillaryPageId() const { return m_ancillaryPageId; }
+};
+
+#endif /* VNSIDEMUXER_H */
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_AAC.c b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_AAC.c
new file mode 100644 (file)
index 0000000..e0a9677
--- /dev/null
@@ -0,0 +1,278 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <stdlib.h>
+#include <assert.h>
+#include "config.h"
+
+#include "demuxer_AAC.h"
+
+static int aac_sample_rates[16] =
+{
+  96000, 88200, 64000, 48000, 44100, 32000,
+  24000, 22050, 16000, 12000, 11025, 8000, 7350
+};
+
+
+cParserAAC::cParserAAC(cTSDemuxer *demuxer, cLiveStreamer *streamer, int streamID)
+ : cParser(streamer, streamID)
+{
+  m_demuxer                   = demuxer;
+  m_streamBuffer              = NULL;
+  m_streamBufferSize          = 0;
+  m_streamBufferPtr           = 0;
+  m_streamParserPtr           = 0;
+  m_firstPUSIseen             = false;
+
+  m_Configured                = false;
+  m_FrameLengthType           = 0;
+  m_SampleRate                = 0;
+}
+
+cParserAAC::~cParserAAC()
+{
+  if (m_streamBuffer)
+    free(m_streamBuffer);
+}
+
+void cParserAAC::Parse(unsigned char *data, int size, bool pusi)
+{
+  if (pusi)
+  {
+    /* Payload unit start */
+    m_firstPUSIseen   = true;
+    m_streamBufferPtr = 0;
+    m_streamParserPtr = 0;
+  }
+
+  if (!m_firstPUSIseen)
+    return;
+
+  if (m_streamBuffer == NULL)
+  {
+    m_streamBufferSize = 4000;
+    m_streamBuffer = (uint8_t*)malloc(m_streamBufferSize);
+  }
+
+  if(m_streamBufferPtr + size >= m_streamBufferSize)
+  {
+    m_streamBufferSize += size * 4;
+    m_streamBuffer = (uint8_t*)realloc(m_streamBuffer, m_streamBufferSize);
+  }
+
+  memcpy(m_streamBuffer + m_streamBufferPtr, data, size);
+  m_streamBufferPtr += size;
+
+  if (m_streamParserPtr == 0)
+  {
+    if (m_streamBufferPtr < 9)
+      return;
+
+    int hlen = ParsePESHeader(data, size);
+    if (hlen < 0)
+      return;
+
+    m_streamParserPtr += hlen;
+  }
+
+  int p = m_streamParserPtr;
+
+  int l;
+  while ((l = m_streamBufferPtr - p) > 3)
+  {
+    if(m_streamBuffer[p] == 0x56 && (m_streamBuffer[p + 1] & 0xe0) == 0xe0)
+    {
+      int muxlen = (m_streamBuffer[p + 1] & 0x1f) << 8 | m_streamBuffer[p + 2];
+
+      if(l < muxlen + 3)
+        break;
+
+      ParseLATMAudioMuxElement(m_streamBuffer + p + 3, muxlen);
+      p += muxlen + 3;
+    }
+    else
+    {
+      p++;
+    }
+  }
+  m_streamParserPtr = p;
+}
+
+void cParserAAC::ParseLATMAudioMuxElement(uint8_t *data, int len)
+{
+  cBitstream bs(data, len * 8);
+
+  if (!bs.readBits1())
+    ReadStreamMuxConfig(&bs);
+
+  if (!m_Configured)
+    return;
+
+  if (m_FrameLengthType != 0)
+    return;
+
+  int tmp;
+  int slotLen = 0;
+  do
+  {
+    tmp = bs.readBits(8);
+    slotLen += tmp;
+  } while (tmp == 255);
+
+  if (slotLen * 8 > bs.remainingBits())
+    return;
+
+  if (m_curDTS == DVD_NOPTS_VALUE)
+    return;
+
+  sStreamPacket pkt;
+  pkt.id       = m_streamID;
+  pkt.size     = slotLen + 7;
+  pkt.data     = (uint8_t*)malloc(pkt.size);
+  pkt.dts      = m_curDTS;
+  pkt.pts      = m_curDTS;
+  pkt.duration = m_FrameDuration;
+
+  /* 7 bytes of ADTS header */
+  cBitstream out(pkt.data, 56);
+
+  out.putBits(0xfff, 12); // Sync marker
+  out.putBits(0, 1);      // ID 0 = MPEG 4
+  out.putBits(0, 2);      // Layer
+  out.putBits(1, 1);      // Protection absent
+  out.putBits(2, 2);      // AOT
+  out.putBits(m_SampleRateIndex, 4);
+  out.putBits(1, 1);      // Private bit
+  out.putBits(m_ChannelConfig, 3);
+  out.putBits(1, 1);      // Original
+  out.putBits(1, 1);      // Copy
+  out.putBits(1, 1);      // Copyright identification bit
+  out.putBits(1, 1);      // Copyright identification start
+  out.putBits(slotLen, 13);
+  out.putBits(0, 11);     // Buffer fullness
+  out.putBits(0, 2);      // RDB in frame
+
+  assert(out.remainingBits() == 0);
+
+  /* AAC RDB */
+  uint8_t *buf = pkt.data + 7;
+  for (int i = 0; i < slotLen; i++)
+    *buf++ = bs.readBits(8);
+
+  m_curDTS += m_FrameDuration;
+
+  SendPacket(&pkt);
+  return;
+}
+
+void cParserAAC::ReadStreamMuxConfig(cBitstream *bs)
+{
+  int AudioMuxVersion = bs->readBits(1);
+  m_AudioMuxVersion_A = 0;
+  if (AudioMuxVersion)                       // audioMuxVersion
+    m_AudioMuxVersion_A = bs->readBits(1);
+
+  if(m_AudioMuxVersion_A)
+    return;
+
+  if (AudioMuxVersion)
+    LATMGetValue(bs);                  // taraFullness
+
+  bs->skipBits(1);                         // allStreamSameTimeFraming = 1
+  bs->skipBits(6);                         // numSubFrames = 0
+  bs->skipBits(4);                         // numPrograms = 0
+
+  // for each program (which there is only on in DVB)
+  bs->skipBits(3);                         // numLayer = 0
+
+  // for each layer (which there is only on in DVB)
+  if (!AudioMuxVersion)
+    ReadAudioSpecificConfig(bs);
+  else
+    return;
+
+  // these are not needed... perhaps
+  m_FrameLengthType = bs->readBits(3);
+  switch (m_FrameLengthType)
+  {
+    case 0:
+      bs->readBits(8);
+      break;
+    case 1:
+      bs->readBits(9);
+      break;
+    case 3:
+    case 4:
+    case 5:
+      bs->readBits(6);                 // celp_table_index
+      break;
+    case 6:
+    case 7:
+      bs->readBits(1);                 // hvxc_table_index
+      break;
+  }
+
+  if (bs->readBits(1))
+  {                   // other data?
+    if (AudioMuxVersion)
+    {
+      LATMGetValue(bs);              // other_data_bits
+    }
+    else
+    {
+      int esc;
+      do
+      {
+        esc = bs->readBits(1);
+        bs->skipBits(8);
+      } while (esc);
+    }
+  }
+
+  if (bs->readBits(1))                   // crc present?
+    bs->skipBits(8);                     // config_crc
+  m_Configured = true;
+}
+
+void cParserAAC::ReadAudioSpecificConfig(cBitstream *bs)
+{
+  int aot = bs->readBits(5);
+  if (aot != 2)
+    return;
+
+  m_SampleRateIndex = bs->readBits(4);
+
+  if (m_SampleRateIndex == 0xf)
+    return;
+
+  m_SampleRate    = aac_sample_rates[m_SampleRateIndex];
+  m_FrameDuration = 1024 * 90000 / m_SampleRate;
+  m_ChannelConfig = bs->readBits(4);
+
+  bs->skipBits(1);      //framelen_flag
+  if (bs->readBits1())  // depends_on_coder
+    bs->skipBits(14);
+
+  if (bs->readBits(1))  // ext_flag
+    bs->skipBits(1);    // ext3_flag
+
+  m_demuxer->SetAudioInformation(m_ChannelConfig, m_SampleRate, 0, 0, 0);
+}
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_AAC.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_AAC.h
new file mode 100644 (file)
index 0000000..392e6b6
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef VNSIDEMUXER_AAC_H
+#define VNSIDEMUXER_AAC_H
+
+#include "demuxer.h"
+#include "bitstream.h"
+
+// --- cParserAAC -------------------------------------------------
+
+class cParserAAC : public cParser
+{
+private:
+  cTSDemuxer *m_demuxer;
+  uint8_t    *m_streamBuffer;
+  int         m_streamBufferSize;
+  int         m_streamBufferPtr;
+  int         m_streamParserPtr;
+  bool        m_firstPUSIseen;
+
+  bool        m_Configured;
+  int         m_AudioMuxVersion_A;
+  int         m_FrameLengthType;
+  int         m_SampleRateIndex;
+  int         m_ChannelConfig;
+  int         m_FrameDuration;
+  int         m_SampleRate;
+
+public:
+  cParserAAC(cTSDemuxer *demuxer, cLiveStreamer *streamer, int streamID);
+  virtual ~cParserAAC();
+
+  virtual void Parse(unsigned char *data, int size, bool pusi);
+  void ParseLATMAudioMuxElement(uint8_t *data, int len);
+  void ReadStreamMuxConfig(cBitstream *bs);
+  void ReadAudioSpecificConfig(cBitstream *bs);
+  uint32_t LATMGetValue(cBitstream *bs) { return bs->readBits(bs->readBits(2) * 8); }
+};
+
+#endif /* VNSIDEMUXER_AAC_H */
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_AC3.c b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_AC3.c
new file mode 100644 (file)
index 0000000..c65625b
--- /dev/null
@@ -0,0 +1,368 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <stdlib.h>
+#include <assert.h>
+#include "config.h"
+
+#include "demuxer_AC3.h"
+#include "bitstream.h"
+
+#define AC3_HEADER_SIZE 7
+
+/** Channel mode (audio coding mode) */
+typedef enum
+{
+  AC3_CHMODE_DUALMONO = 0,
+  AC3_CHMODE_MONO,
+  AC3_CHMODE_STEREO,
+  AC3_CHMODE_3F,
+  AC3_CHMODE_2F1R,
+  AC3_CHMODE_3F1R,
+  AC3_CHMODE_2F2R,
+  AC3_CHMODE_3F2R
+} AC3ChannelMode;
+
+/* possible frequencies */
+const uint16_t AC3SampleRateTable[3] = { 48000, 44100, 32000 };
+
+/* possible bitrates */
+const uint16_t AC3BitrateTable[19] = {
+    32, 40, 48, 56, 64, 80, 96, 112, 128,
+    160, 192, 224, 256, 320, 384, 448, 512, 576, 640
+};
+
+const uint8_t AC3ChannelsTable[8] = {
+    2, 1, 2, 3, 3, 4, 4, 5
+};
+
+const uint16_t AC3FrameSizeTable[38][3] = {
+    { 64,   69,   96   },
+    { 64,   70,   96   },
+    { 80,   87,   120  },
+    { 80,   88,   120  },
+    { 96,   104,  144  },
+    { 96,   105,  144  },
+    { 112,  121,  168  },
+    { 112,  122,  168  },
+    { 128,  139,  192  },
+    { 128,  140,  192  },
+    { 160,  174,  240  },
+    { 160,  175,  240  },
+    { 192,  208,  288  },
+    { 192,  209,  288  },
+    { 224,  243,  336  },
+    { 224,  244,  336  },
+    { 256,  278,  384  },
+    { 256,  279,  384  },
+    { 320,  348,  480  },
+    { 320,  349,  480  },
+    { 384,  417,  576  },
+    { 384,  418,  576  },
+    { 448,  487,  672  },
+    { 448,  488,  672  },
+    { 512,  557,  768  },
+    { 512,  558,  768  },
+    { 640,  696,  960  },
+    { 640,  697,  960  },
+    { 768,  835,  1152 },
+    { 768,  836,  1152 },
+    { 896,  975,  1344 },
+    { 896,  976,  1344 },
+    { 1024, 1114, 1536 },
+    { 1024, 1115, 1536 },
+    { 1152, 1253, 1728 },
+    { 1152, 1254, 1728 },
+    { 1280, 1393, 1920 },
+    { 1280, 1394, 1920 },
+};
+
+const uint8_t EAC3Blocks[4] = {
+  1, 2, 3, 6
+};
+
+typedef enum {
+  EAC3_FRAME_TYPE_INDEPENDENT = 0,
+  EAC3_FRAME_TYPE_DEPENDENT,
+  EAC3_FRAME_TYPE_AC3_CONVERT,
+  EAC3_FRAME_TYPE_RESERVED
+} EAC3FrameType;
+
+cParserAC3::cParserAC3(cTSDemuxer *demuxer, cLiveStreamer *streamer, int streamID)
+ : cParser(streamer, streamID)
+{
+  m_CurrentFrameStartIndex    = 0;
+  m_Offset                    = 0;
+  m_PTS                       = 0;
+  m_DTS                       = 0;
+  m_FrameSize                 = 0;
+  m_SampleRate                = 0;
+  m_Channels                  = 0;
+  m_BitRate                   = 0;
+  m_demuxer                   = demuxer;
+  m_firstPUSIseen             = false;
+  m_PESStart                  = false;
+  m_FetchTimestamp            = true;
+  m_NextDTS                   = 0;
+  m_AC3BufferPtr              = 0;
+  m_HeaderFound               = false;
+
+  for (int i = 0; i < AV_PARSER_PTS_NB; i++)
+  {
+    m_CurrentFrameDTS[i] = DVD_NOPTS_VALUE;
+    m_CurrentFramePTS[i] = DVD_NOPTS_VALUE;
+    m_CurrentFrameEnd[i] = 0;
+    m_CurrentFrameOffset[i] = 0;
+  }
+}
+
+cParserAC3::~cParserAC3()
+{
+}
+
+void cParserAC3::Parse(unsigned char *data, int size, bool pusi)
+{
+  if (pusi)
+  {
+    /* Payload unit start */
+    m_PESStart      = true;
+    m_firstPUSIseen = true;
+  }
+
+  /* Wait for first pusi */
+  if (!m_firstPUSIseen)
+    return;
+
+  if (m_PESStart)
+  {
+    if (!PesIsPS1Packet(data))
+    {
+      esyslog("VNSI-Error: AC3 PES packet contains no valid private stream 1, ignored this packet");
+      m_firstPUSIseen = false;
+      return;
+    }
+
+    int hlen = ParsePESHeader(data, size);
+    if (hlen <= 0)
+      return;
+
+    data += hlen;
+    size -= hlen;
+
+    m_PESStart = false;
+
+    assert(size >= 0);
+    if(size == 0)
+      return;
+  }
+
+  while (size > 0)
+  {
+    uint8_t *outbuf;
+    int      outlen;
+
+    int rlen = FindHeaders(&outbuf, &outlen, data, size, m_curPTS, m_curDTS);
+    if (rlen < 0)
+      break;
+
+    m_curPTS = DVD_NOPTS_VALUE;
+    m_curDTS = DVD_NOPTS_VALUE;
+
+    if (outlen)
+    {
+      sStreamPacket pkt;
+      pkt.id       = m_streamID;
+      pkt.data     = outbuf;
+      pkt.size     = outlen;
+      pkt.duration = 90000 * 1536 / m_SampleRate;
+      pkt.dts      = m_DTS;
+      if (pkt.dts == DVD_NOPTS_VALUE)
+        pkt.dts = m_NextDTS;
+      pkt.pts      = pkt.dts;
+      m_NextDTS    = pkt.dts + pkt.duration;
+
+      m_demuxer->SetAudioInformation(m_Channels, m_SampleRate, m_BitRate, 0, 0);
+
+      SendPacket(&pkt);
+    }
+    data += rlen;
+    size -= rlen;
+  }
+}
+
+int cParserAC3::FindHeaders(uint8_t **poutbuf, int *poutbuf_size,
+                                   uint8_t *buf, int buf_size,
+                                   int64_t pts, int64_t dts)
+{
+  *poutbuf      = NULL;
+  *poutbuf_size = 0;
+
+  /* add a new packet descriptor */
+  if (pts != DVD_NOPTS_VALUE || dts != DVD_NOPTS_VALUE)
+  {
+    int i = (m_CurrentFrameStartIndex + 1) & (AV_PARSER_PTS_NB - 1);
+    m_CurrentFrameStartIndex  = i;
+    m_CurrentFrameOffset[i]   = m_CurrentOffset;
+    m_CurrentFrameEnd[i]      = m_CurrentOffset + buf_size;
+    m_CurrentFramePTS[i]      = pts;
+    m_CurrentFrameDTS[i]      = dts;
+  }
+
+  if (m_FetchTimestamp)
+  {
+    m_FetchTimestamp  = false;
+    FetchTimestamp(0, false);
+  }
+
+  uint8_t *buf_ptr = buf;
+  while (buf_size > 0)
+  {
+    if (buf_ptr[0] == 0x0b && buf_ptr[1] == 0x77 && !m_HeaderFound)
+    {
+      cBitstream bs(buf_ptr + 2, AC3_HEADER_SIZE * 8);
+
+      /* read ahead to bsid to distinguish between AC-3 and E-AC-3 */
+      int bsid = bs.showBits(29) & 0x1F;
+      if (bsid > 16)
+        return -1;
+
+      if (bsid <= 10)
+      {
+        /* Normal AC-3 */
+        bs.skipBits(16);
+        int fscod       = bs.readBits(2);
+        int frmsizecod  = bs.readBits(6);
+        bs.skipBits(5); // skip bsid, already got it
+        bs.skipBits(3); // skip bitstream mode
+        int acmod       = bs.readBits(3);
+
+        if (fscod == 3 || frmsizecod > 37)
+          return -1;
+
+        if (acmod == AC3_CHMODE_STEREO)
+        {
+          bs.skipBits(2); // skip dsurmod
+        }
+        else
+        {
+          if ((acmod & 1) && acmod != AC3_CHMODE_MONO)
+            bs.skipBits(2);
+          if (acmod & 4)
+            bs.skipBits(2);
+        }
+        int lfeon = bs.readBits(1);
+
+        int srShift   = max(bsid, 8) - 8;
+        m_SampleRate  = AC3SampleRateTable[fscod] >> srShift;
+        m_BitRate     = (AC3BitrateTable[frmsizecod>>1] * 1000) >> srShift;
+        m_Channels    = AC3ChannelsTable[acmod] + lfeon;
+        m_FrameSize   = AC3FrameSizeTable[frmsizecod][fscod] * 2;
+      }
+      else
+      {
+        /* Enhanced AC-3 */
+        int frametype = bs.readBits(2);
+        if (frametype == EAC3_FRAME_TYPE_RESERVED)
+          return -1;
+
+        int substreamid = bs.readBits(3);
+
+        int framesize = (bs.readBits(11) + 1) << 1;
+        if (framesize < AC3_HEADER_SIZE)
+          return -1;
+
+        int numBlocks = 6;
+        int sr_code = bs.readBits(2);
+        if (sr_code == 3)
+        {
+          int sr_code2 = bs.readBits(2);
+          if (sr_code2 == 3)
+            return -1;
+          m_SampleRate = AC3SampleRateTable[sr_code2] / 2;
+        }
+        else
+        {
+          numBlocks = EAC3Blocks[bs.readBits(2)];
+          m_SampleRate = AC3SampleRateTable[sr_code];
+        }
+
+        int channelMode = bs.readBits(3);
+        int lfeon = bs.readBits(1);
+
+        m_BitRate  = (uint32_t)(8.0 * framesize * m_SampleRate / (numBlocks * 256.0));
+        m_Channels = AC3ChannelsTable[channelMode] + lfeon;
+      }
+      m_HeaderFound = true;
+    }
+
+    if (m_HeaderFound)
+      m_AC3Buffer[m_AC3BufferPtr++] = buf_ptr[0];
+
+    if (m_FrameSize && m_AC3BufferPtr >= m_FrameSize)
+    {
+      *poutbuf        = m_AC3Buffer;
+      *poutbuf_size   = m_AC3BufferPtr;
+
+      m_AC3BufferPtr  = 0;
+      m_HeaderFound   = false;
+      break;
+    }
+
+    buf_ptr++;
+    buf_size--;
+  }
+  int index = buf_ptr - buf;
+
+  /* update the file pointer */
+  if (*poutbuf_size)
+  {
+    /* fill the data for the current frame */
+    m_FrameOffset     = m_NextFrameOffset;
+
+    /* offset of the next frame */
+    m_NextFrameOffset = m_CurrentOffset + index;
+    m_FetchTimestamp  = true;
+  }
+  if (index < 0)
+    index = 0;
+  m_CurrentOffset += index;
+  return index;
+}
+
+void cParserAC3::FetchTimestamp(int off, bool remove)
+{
+  m_DTS = DVD_NOPTS_VALUE;
+  m_PTS = DVD_NOPTS_VALUE;
+  m_Offset = 0;
+  for (int i = 0; i < AV_PARSER_PTS_NB; i++)
+  {
+    if (   m_NextFrameOffset + off >= m_CurrentFrameOffset[i]
+        &&(m_FrameOffset           <  m_CurrentFrameOffset[i] || !m_FrameOffset)
+        && m_CurrentFrameEnd[i])
+    {
+      m_DTS    = m_CurrentFrameDTS[i];
+      m_PTS    = m_CurrentFramePTS[i];
+      m_Offset = m_NextFrameOffset - m_CurrentFrameOffset[i];
+      if (remove)
+        m_CurrentFrameOffset[i] = -1;
+    }
+  }
+}
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_AC3.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_AC3.h
new file mode 100644 (file)
index 0000000..605b205
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef VNSIDEMUXER_AC3_H
+#define VNSIDEMUXER_AC3_H
+
+#include "demuxer.h"
+
+// --- cParserAC3 -------------------------------------------------
+
+class cParserAC3 : public cParser
+{
+private:
+  cTSDemuxer *m_demuxer;
+  bool        m_firstPUSIseen;
+  bool        m_PESStart;
+  int         m_SampleRate;
+  int         m_Channels;
+  int         m_BitRate;
+
+  bool        m_FetchTimestamp;
+  int64_t     m_FrameOffset;        /* offset of the current frame */
+  int64_t     m_CurrentOffset;      /* current offset (incremented by each av_parser_parse()) */
+  int64_t     m_NextFrameOffset;    /* offset of the next frame */
+
+#define AV_PARSER_PTS_NB 4
+  int         m_CurrentFrameStartIndex;
+  int64_t     m_CurrentFrameOffset[AV_PARSER_PTS_NB];
+  int64_t     m_CurrentFramePTS[AV_PARSER_PTS_NB];
+  int64_t     m_CurrentFrameDTS[AV_PARSER_PTS_NB];
+  int64_t     m_CurrentFrameEnd[AV_PARSER_PTS_NB];
+  int64_t     m_Offset;             /* byte offset from starting packet start */
+  int64_t     m_PTS;                /* pts of the current frame */
+  int64_t     m_DTS;                /* dts of the current frame */
+  int64_t     m_NextDTS;
+
+  int         m_FrameSize;
+  bool        m_HeaderFound;
+
+#define AC3_MAX_CODED_FRAME_SIZE 1920*2
+  uint8_t     m_AC3Buffer[AC3_MAX_CODED_FRAME_SIZE];    /* input buffer */
+  int         m_AC3BufferSize;
+  int         m_AC3BufferPtr;
+
+  int FindHeaders(uint8_t **poutbuf, int *poutbuf_size,
+                  uint8_t *buf, int buf_size,
+                  int64_t pts, int64_t dts);
+  void FetchTimestamp(int off, bool remove);
+
+public:
+  cParserAC3(cTSDemuxer *demuxer, cLiveStreamer *streamer, int streamID);
+  virtual ~cParserAC3();
+
+  virtual void Parse(unsigned char *data, int size, bool pusi);
+};
+
+
+#endif /* VNSIDEMUXER_AC3_H */
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_DTS.c b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_DTS.c
new file mode 100644 (file)
index 0000000..eb4f18f
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <stdlib.h>
+#include <assert.h>
+#include "config.h"
+
+#include "demuxer_DTS.h"
+#include "bitstream.h"
+
+cParserDTS::cParserDTS(cTSDemuxer *demuxer, cLiveStreamer *streamer, int streamID)
+ : cParser(streamer, streamID)
+{
+  m_demuxer                   = demuxer;
+}
+
+cParserDTS::~cParserDTS()
+{
+}
+
+void cParserDTS::Parse(unsigned char *data, int size, bool pusi)
+{
+
+}
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_DTS.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_DTS.h
new file mode 100644 (file)
index 0000000..68b23af
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef VNSIDEMUXER_DTS_H
+#define VNSIDEMUXER_DTS_H
+
+#include "demuxer.h"
+
+// --- cParserDTS -------------------------------------------------
+
+class cParserDTS : public cParser
+{
+private:
+  cTSDemuxer *m_demuxer;
+
+public:
+  cParserDTS(cTSDemuxer *demuxer, cLiveStreamer *streamer, int streamID);
+  virtual ~cParserDTS();
+
+  virtual void Parse(unsigned char *data, int size, bool pusi);
+};
+
+
+#endif /* VNSIDEMUXER_DTS_H */
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_MPEGAudio.c b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_MPEGAudio.c
new file mode 100644 (file)
index 0000000..b34f916
--- /dev/null
@@ -0,0 +1,393 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <stdlib.h>
+#include <assert.h>
+#include "config.h"
+
+#include "demuxer_MPEGAudio.h"
+
+cParserMPEG2Audio::cParserMPEG2Audio(cTSDemuxer *demuxer, cLiveStreamer *streamer, int streamID)
+ : cParser(streamer, streamID)
+{
+  m_FrameOffset               = 0;
+  m_CurrentOffset             = 0;
+  m_NextFrameOffset           = 0;
+  m_CurrentFrameStartIndex    = 0;
+  m_Offset                    = 0;
+  m_PTS                       = 0;
+  m_DTS                       = 0;
+  m_FrameSize                 = 0;
+  m_FrameFreeFormatFrameSize  = 0;
+  m_FrameFreeFormatNextHeader = 0;
+  m_FrameHeader               = 0;
+  m_HeaderCount               = 0;
+  m_SampleRate                = 0;
+  m_Channels                  = 0;
+  m_BitRate                   = 0;
+  m_demuxer                   = demuxer;
+  m_firstPUSIseen             = false;
+  m_PESStart                  = false;
+  m_FetchTimestamp            = true;
+  m_FrameInBufferPtr          = m_FrameInBuffer;
+  m_NextDTS                   = 0;
+
+  for (int i = 0; i < AV_PARSER_PTS_NB; i++)
+  {
+    m_CurrentFrameDTS[i] = DVD_NOPTS_VALUE;
+    m_CurrentFramePTS[i] = DVD_NOPTS_VALUE;
+    m_CurrentFrameEnd[i] = 0;
+    m_CurrentFrameOffset[i] = 0;
+  }
+
+  for (int i = 0; i < MPA_MAX_CODED_FRAME_SIZE; i++)
+  {
+    m_FrameInBuffer[i] = 0;
+  }
+}
+
+cParserMPEG2Audio::~cParserMPEG2Audio()
+{
+}
+
+void cParserMPEG2Audio::Parse(unsigned char *data, int size, bool pusi)
+{
+  if (pusi)
+  {
+    /* Payload unit start */
+    m_PESStart      = true;
+    m_firstPUSIseen = true;
+  }
+
+  /* Wait for first pusi */
+  if (!m_firstPUSIseen)
+    return;
+
+  if (m_PESStart)
+  {
+    int hlen = ParsePESHeader(data, size);
+    if (hlen <= 0)
+      return;
+
+    data += hlen;
+    size -= hlen;
+
+    m_PESStart = false;
+
+    assert(size >= 0);
+    if(size == 0)
+      return;
+  }
+
+  while (size > 0)
+  {
+    uint8_t *outbuf;
+    int      outlen;
+
+    int rlen = FindHeaders(&outbuf, &outlen, data, size, m_curPTS, m_curDTS);
+    if (rlen < 0)
+      break;
+
+    m_curPTS = DVD_NOPTS_VALUE;
+    m_curDTS = DVD_NOPTS_VALUE;
+
+    if (outlen)
+    {
+      sStreamPacket pkt;
+      pkt.id       = m_streamID;
+      pkt.data     = outbuf;
+      pkt.size     = outlen;
+      pkt.duration = 90000 * 1152 / m_SampleRate;
+      pkt.dts      = m_DTS;
+      if (pkt.dts == DVD_NOPTS_VALUE)
+        pkt.dts = m_NextDTS;
+      pkt.pts      = pkt.dts;
+      m_NextDTS    = pkt.dts + pkt.duration;
+
+      SendPacket(&pkt);
+    }
+    data += rlen;
+    size -= rlen;
+  }
+}
+
+int cParserMPEG2Audio::FindHeaders(uint8_t **poutbuf, int *poutbuf_size,
+                                   uint8_t *buf, int buf_size,
+                                   int64_t pts, int64_t dts)
+{
+  *poutbuf      = NULL;
+  *poutbuf_size = 0;
+
+  /* add a new packet descriptor */
+  if (pts != DVD_NOPTS_VALUE || dts != DVD_NOPTS_VALUE)
+  {
+    int i = (m_CurrentFrameStartIndex + 1) & (AV_PARSER_PTS_NB - 1);
+    m_CurrentFrameStartIndex  = i;
+    m_CurrentFrameOffset[i]   = m_CurrentOffset;
+    m_CurrentFrameEnd[i]      = m_CurrentOffset + buf_size;
+    m_CurrentFramePTS[i]      = pts;
+    m_CurrentFrameDTS[i]      = dts;
+  }
+
+  if (m_FetchTimestamp)
+  {
+    m_FetchTimestamp  = false;
+    FetchTimestamp(0, false);
+  }
+
+  const uint8_t *buf_ptr = buf;
+  while (buf_size > 0)
+  {
+    int len = m_FrameInBufferPtr - m_FrameInBuffer;
+    if (m_FrameSize == 0)
+    {
+      /* special case for next header for first frame in free
+         format case (XXX: find a simpler method) */
+      if (m_FrameFreeFormatNextHeader != 0)
+      {
+        m_FrameInBuffer[3] = m_FrameFreeFormatNextHeader;
+        m_FrameInBuffer[2] = m_FrameFreeFormatNextHeader>>8;
+        m_FrameInBuffer[1] = m_FrameFreeFormatNextHeader>>16;
+        m_FrameInBuffer[0] = m_FrameFreeFormatNextHeader>>24;
+        m_FrameInBufferPtr = m_FrameInBuffer + 4;
+        m_FrameFreeFormatNextHeader = 0;
+        goto got_header;
+      }
+      /* no header seen : find one. We need at least MPA_HEADER_SIZE
+         bytes to parse it */
+      len = min(MPA_HEADER_SIZE - len, buf_size);
+      if (len > 0)
+      {
+        memcpy(m_FrameInBufferPtr, buf_ptr, len);
+        buf_ptr            += len;
+        buf_size           -= len;
+        m_FrameInBufferPtr += len;
+      }
+      if ((m_FrameInBufferPtr - m_FrameInBuffer) >= MPA_HEADER_SIZE)
+      {
+      got_header:
+        MPADecodeHeader s;
+        uint32_t header = ((m_FrameInBuffer[0] << 24) | (m_FrameInBuffer[1] << 16) | (m_FrameInBuffer[2] <<  8) | m_FrameInBuffer[3]);
+        if (CheckHeader(header) && DecodeHeader(&s, header))
+        {
+          if ((header&SAME_HEADER_MASK) != (m_FrameHeader&SAME_HEADER_MASK) && m_FrameHeader)
+            m_HeaderCount = -3;
+          m_FrameHeader = header;
+          m_FrameSize   = s.frame_size;
+          m_SampleRate  = s.sample_rate;
+          m_Channels    = s.nb_channels;
+          m_BitRate     = s.bit_rate;
+          m_HeaderCount++;
+        }
+        else
+        {
+          m_HeaderCount = -2;
+          /* no sync found : move by one byte (inefficient, but simple!) */
+          memmove(m_FrameInBuffer, m_FrameInBuffer + 1, m_FrameInBufferPtr - m_FrameInBuffer - 1);
+          m_FrameInBufferPtr--;
+          //LOGDBG("skip %x", header);
+          /* reset free format frame size to give a chance
+             to get a new bitrate */
+          m_FrameFreeFormatFrameSize = 0;
+        }
+      }
+    }
+    else if (len < m_FrameSize)
+    {
+      if (m_FrameSize > MPA_MAX_CODED_FRAME_SIZE)
+        m_FrameSize = MPA_MAX_CODED_FRAME_SIZE;
+      len                 = min(m_FrameSize - len, buf_size);
+      memcpy(m_FrameInBufferPtr, buf_ptr, len);
+      buf_ptr            += len;
+      m_FrameInBufferPtr += len;
+      buf_size           -= len;
+    }
+
+    if(m_FrameSize > 0 && buf_ptr - buf == m_FrameInBufferPtr - m_FrameInBuffer && buf_size + buf_ptr - buf >= m_FrameSize)
+    {
+      if(m_HeaderCount > 0)
+      {
+        *poutbuf      = buf;
+        *poutbuf_size = m_FrameSize;
+      }
+      buf_ptr             = buf + m_FrameSize;
+      m_FrameInBufferPtr  = m_FrameInBuffer;
+      m_FrameSize         = 0;
+      break;
+    }
+
+    //    next_data:
+    if (m_FrameSize > 0 && (m_FrameInBufferPtr - m_FrameInBuffer) >= m_FrameSize)
+    {
+      if (m_HeaderCount > 0)
+      {
+        *poutbuf      = m_FrameInBuffer;
+        *poutbuf_size = m_FrameInBufferPtr - m_FrameInBuffer;
+      }
+      m_FrameInBufferPtr = m_FrameInBuffer;
+      m_FrameSize = 0;
+      break;
+    }
+  }
+  int index = buf_ptr - buf;
+
+  /* update the file pointer */
+  if (*poutbuf_size)
+  {
+    /* fill the data for the current frame */
+    m_FrameOffset     = m_NextFrameOffset;
+
+    /* offset of the next frame */
+    m_NextFrameOffset = m_CurrentOffset + index;
+    m_FetchTimestamp  = true;
+  }
+  if (index < 0)
+    index = 0;
+  m_CurrentOffset += index;
+  return index;
+}
+
+void cParserMPEG2Audio::FetchTimestamp(int off, bool remove)
+{
+  m_DTS = DVD_NOPTS_VALUE;
+  m_PTS = DVD_NOPTS_VALUE;
+  m_Offset = 0;
+  for (int i = 0; i < AV_PARSER_PTS_NB; i++)
+  {
+    if (   m_NextFrameOffset + off >= m_CurrentFrameOffset[i]
+        &&(m_FrameOffset           <  m_CurrentFrameOffset[i] || !m_FrameOffset)
+        && m_CurrentFrameEnd[i])
+    {
+      m_DTS    = m_CurrentFrameDTS[i];
+      m_PTS    = m_CurrentFramePTS[i];
+      m_Offset = m_NextFrameOffset - m_CurrentFrameOffset[i];
+      if (remove)
+        m_CurrentFrameOffset[i] = -1;
+    }
+  }
+}
+
+bool cParserMPEG2Audio::DecodeHeader(MPADecodeHeader *s, uint32_t header)
+{
+  const uint16_t FrequencyTable[3] = { 44100, 48000, 32000 };
+  const uint16_t BitrateTable[2][3][15] =
+  {
+    {
+      {0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448 },
+      {0, 32, 48, 56, 64,  80,  96,  112, 128, 160, 192, 224, 256, 320, 384 },
+      {0, 32, 40, 48, 56,  64,  80,  96,  112, 128, 160, 192, 224, 256, 320 }
+    },
+    {
+      {0, 32, 48, 56, 64,  80,  96,  112, 128, 144, 160, 176, 192, 224, 256},
+      {0, 8,  16, 24, 32,  40,  48,  56,  64,  80,  96,  112, 128, 144, 160},
+      {0, 8,  16, 24, 32,  40,  48,  56,  64,  80,  96,  112, 128, 144, 160}
+    }
+  };
+
+  int sample_rate, frame_size, mpeg25, padding;
+  int sample_rate_index, bitrate_index;
+  if (header & (1<<20))
+  {
+    s->lsf = (header & (1<<19)) ? 0 : 1;
+    mpeg25 = 0;
+  }
+  else
+  {
+    s->lsf = 1;
+    mpeg25 = 1;
+  }
+
+  s->layer              = 4 - ((header >> 17) & 3);
+  /* extract frequency */
+  sample_rate_index     = (header >> 10) & 3;
+  sample_rate           = FrequencyTable[sample_rate_index] >> (s->lsf + mpeg25);
+  sample_rate_index    += 3 * (s->lsf + mpeg25);
+  s->sample_rate_index  = sample_rate_index;
+  s->error_protection   = ((header >> 16) & 1) ^ 1;
+  s->sample_rate        = sample_rate;
+
+  bitrate_index         = (header >> 12) & 0xf;
+  padding               = (header >> 9) & 1;
+  //extension           = (header >> 8) & 1;
+  s->mode               = (header >> 6) & 3;
+  s->mode_ext           = (header >> 4) & 3;
+  //copyright           = (header >> 3) & 1;
+  //original            = (header >> 2) & 1;
+  //emphasis            = header & 3;
+
+  if (s->mode == MPA_MONO)
+    s->nb_channels = 1;
+  else
+    s->nb_channels = 2;
+
+  if (bitrate_index != 0)
+  {
+    frame_size  = BitrateTable[s->lsf][s->layer - 1][bitrate_index];
+    s->bit_rate = frame_size * 1000;
+    switch(s->layer)
+    {
+    case 1:
+      frame_size = (frame_size * 12000) / sample_rate;
+      frame_size = (frame_size + padding) * 4;
+      break;
+    case 2:
+      frame_size = (frame_size * 144000) / sample_rate;
+      frame_size += padding;
+      break;
+    default:
+    case 3:
+      frame_size = (frame_size * 144000) / (sample_rate << s->lsf);
+      frame_size += padding;
+      break;
+    }
+    s->frame_size = frame_size;
+  }
+  else
+  {
+    /* if no frame size computed, signal it */
+    return false;
+  }
+
+  m_demuxer->SetAudioInformation(s->nb_channels, s->sample_rate, s->bit_rate, 0, 0);
+
+#if defined(DEBUG)
+#define MODE_EXT_MS_STEREO 2
+#define MODE_EXT_I_STEREO  1
+  printf("layer%d, %d Hz, %d kbits/s, ", s->layer, s->sample_rate, s->bit_rate);
+  if (s->nb_channels == 2)
+  {
+    if (s->layer == 3)
+    {
+      if (s->mode_ext & MODE_EXT_MS_STEREO)
+        printf("ms-");
+      if (s->mode_ext & MODE_EXT_I_STEREO)
+        printf("i-");
+    }
+    printf("stereo");
+  }
+  else
+  {
+    printf("mono");
+  }
+  printf("\n");
+#endif
+  return true;
+}
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_MPEGAudio.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_MPEGAudio.h
new file mode 100644 (file)
index 0000000..8f5cb51
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef VNSIDEMUXER_MPEGAUDIO_H
+#define VNSIDEMUXER_MPEGAUDIO_H
+
+#include "demuxer.h"
+
+// --- cParserMPEG2Audio -------------------------------------------------
+
+class cParserMPEG2Audio : public cParser
+{
+private:
+  typedef struct MPADecodeHeader
+  {
+    int frame_size;
+    int error_protection;
+    int layer;
+    int sample_rate;
+    int sample_rate_index; /* between 0 and 8 */
+    int bit_rate;
+    int nb_channels;
+    int mode;
+    int mode_ext;
+    int lsf;
+  } MPADecodeHeader;
+
+  cTSDemuxer *m_demuxer;
+  bool        m_firstPUSIseen;
+  bool        m_PESStart;
+
+  bool        m_FetchTimestamp;
+  int64_t     m_FrameOffset;        /* offset of the current frame */
+  int64_t     m_CurrentOffset;      /* current offset (incremented by each av_parser_parse()) */
+  int64_t     m_NextFrameOffset;    /* offset of the next frame */
+
+#define AV_PARSER_PTS_NB 4
+  int         m_CurrentFrameStartIndex;
+  int64_t     m_CurrentFrameOffset[AV_PARSER_PTS_NB];
+  int64_t     m_CurrentFramePTS[AV_PARSER_PTS_NB];
+  int64_t     m_CurrentFrameDTS[AV_PARSER_PTS_NB];
+  int64_t     m_CurrentFrameEnd[AV_PARSER_PTS_NB];
+  int64_t     m_Offset;             /* byte offset from starting packet start */
+  int64_t     m_PTS;                /* pts of the current frame */
+  int64_t     m_DTS;                /* dts of the current frame */
+  int64_t     m_NextDTS;
+
+#define SAME_HEADER_MASK (0xffe00000 | (3 << 17) | (3 << 10) | (3 << 19))
+#define MPA_HEADER_SIZE 4
+#define MPA_MAX_CODED_FRAME_SIZE 1792
+#define MPA_STEREO  0
+#define MPA_JSTEREO 1
+#define MPA_DUAL    2
+#define MPA_MONO    3
+  int         m_FrameSize;
+  int         m_FrameFreeFormatFrameSize;
+  int         m_FrameFreeFormatNextHeader;
+  uint32_t    m_FrameHeader;
+  int         m_HeaderCount;
+  int         m_SampleRate;
+  int         m_Channels;
+  int         m_BitRate;
+  uint8_t     m_FrameInBuffer[MPA_MAX_CODED_FRAME_SIZE];    /* input buffer */
+  uint8_t    *m_FrameInBufferPtr;
+
+  /* fast header check for resync */
+  void FetchTimestamp(int off, bool remove);
+  int FindHeaders(uint8_t **poutbuf, int *poutbuf_size,
+                  uint8_t *buf, int buf_size,
+                  int64_t pts, int64_t dts);
+  static inline bool CheckHeader(uint32_t header)
+  {
+    /* header */
+    if ((header & 0xffe00000) != 0xffe00000)
+      return false;
+    /* layer check */
+    if ((header & (3<<17)) == 0)
+      return false;
+    /* bit rate */
+    if ((header & (0xf<<12)) == 0xf<<12)
+      return false;
+    /* frequency */
+    if ((header & (3<<10)) == 3<<10)
+      return false;
+    return true;
+  }
+  bool DecodeHeader(MPADecodeHeader *s, uint32_t header);
+
+public:
+  cParserMPEG2Audio(cTSDemuxer *demuxer, cLiveStreamer *streamer, int streamID);
+  virtual ~cParserMPEG2Audio();
+
+  virtual void Parse(unsigned char *data, int size, bool pusi);
+};
+
+
+#endif /* VNSIDEMUXER_MPEGAUDIO_H */
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_MPEGVideo.c b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_MPEGVideo.c
new file mode 100644 (file)
index 0000000..6483cd9
--- /dev/null
@@ -0,0 +1,387 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <stdlib.h>
+#include <assert.h>
+#include "config.h"
+#include "bitstream.h"
+#include "receiver.h"
+
+#include "demuxer_MPEGVideo.h"
+
+using namespace std;
+
+#define MPEG_PICTURE_START      0x00000100
+#define MPEG_SEQUENCE_START     0x000001b3
+#define MPEG_SEQUENCE_EXTENSION 0x000001b5
+#define MPEG_SLICE_S            0x00000101
+#define MPEG_SLICE_E            0x000001af
+
+/**
+ * MPEG2VIDEO frame duration table (in 90kHz clock domain)
+ */
+const unsigned int mpeg2video_framedurations[16] = {
+  0,
+  3753,
+  3750,
+  3600,
+  3003,
+  3000,
+  1800,
+  1501,
+  1500,
+};
+
+cParserMPEG2Video::cParserMPEG2Video(cTSDemuxer *demuxer, cLiveStreamer *streamer, int streamID)
+ : cParser(streamer, streamID)
+{
+  m_pictureBuffer     = NULL;
+  m_pictureBufferSize = 0;
+  m_pictureBufferPtr  = 0;
+  m_StartCond         = 0;
+  m_StartCode         = 0;
+  m_StartCodeOffset   = 0;
+  m_FrameDuration     = 0;
+  m_startDTS          = DVD_NOPTS_VALUE;
+  m_vbvDelay          = -1;
+  m_vbvSize           = 0;
+  m_Height            = 0;
+  m_Width             = 0;
+  m_StreamPacket      = NULL;
+  m_demuxer           = demuxer;
+
+  /* Set the streamer ready here to increase switch times, but if XBMC use also
+     VDPAU for SD content it must be changed */
+  streamer->SetReady();
+}
+
+cParserMPEG2Video::~cParserMPEG2Video()
+{
+  while (!m_PTSQueue.empty())
+  {
+    sStreamPacket* pkt = m_PTSQueue.front();
+    m_PTSQueue.pop_front();
+    free(pkt->data);
+    delete pkt;
+  }
+  while (!m_DurationQueue.empty())
+  {
+    sStreamPacket* pkt = m_DurationQueue.front();
+    m_DurationQueue.pop_front();
+    free(pkt->data);
+    delete pkt;
+  }
+  if (m_pictureBuffer)
+  {
+    free(m_pictureBuffer);
+    m_pictureBuffer = NULL;
+  }
+}
+
+void cParserMPEG2Video::Parse(unsigned char *data, int size, bool pusi)
+{
+  uint32_t startcode = m_StartCond;
+
+  /* Parse PES header here for MPEG PS streams like from pvrinput */
+  if (pusi && m_Streamer->IsMPEGPS())
+  {
+    int hlen;
+
+    hlen = ParsePESHeader(data, size);
+  #if 0
+    int i;
+    for(i = 0; i < 16; i++)
+      printf("%02x.", data[i]);
+    printf(" %d\n", hlen);
+  #endif
+    data += hlen;
+    size  -= hlen;
+
+    if(size < 1)
+      return;
+  }
+
+  if (m_pictureBuffer == NULL)
+  {
+    m_pictureBufferSize   = 4000;
+    m_pictureBuffer       = (uint8_t*)malloc(m_pictureBufferSize);
+  }
+
+  if (m_pictureBufferPtr + size + 4 >= m_pictureBufferSize)
+  {
+    m_pictureBufferSize  += size * 4;
+    m_pictureBuffer       = (uint8_t*)realloc(m_pictureBuffer, m_pictureBufferSize);
+  }
+
+  for (int i = 0; i < size; i++)
+  {
+    if (!m_pictureBuffer)
+      break;
+
+    m_pictureBuffer[m_pictureBufferPtr++] = data[i];
+    startcode = startcode << 8 | data[i];
+
+    if ((startcode & 0xffffff00) != 0x00000100)
+      continue;
+
+    bool reset = true;
+    if (m_pictureBufferPtr - 4 > 0 && m_StartCode != 0)
+    {
+      reset = Parse_MPEG2Video(m_pictureBufferPtr - 4, startcode, m_StartCodeOffset);
+    }
+
+    if (reset)
+    {
+      /* Reset packet parser upon length error or if parser tells us so */
+      m_pictureBufferPtr = 0;
+      m_pictureBuffer[m_pictureBufferPtr++] = startcode >> 24;
+      m_pictureBuffer[m_pictureBufferPtr++] = startcode >> 16;
+      m_pictureBuffer[m_pictureBufferPtr++] = startcode >> 8;
+      m_pictureBuffer[m_pictureBufferPtr++] = startcode >> 0;
+    }
+    m_StartCode = startcode;
+    m_StartCodeOffset = m_pictureBufferPtr - 4;
+  }
+  m_StartCond = startcode;
+}
+
+bool cParserMPEG2Video::Parse_MPEG2Video(size_t len, uint32_t next_startcode, int sc_offset)
+{
+  int frametype;
+  uint8_t *buf = m_pictureBuffer + sc_offset;
+  cBitstream bs(buf + 4, (len - 4) * 8);
+
+  switch (m_StartCode)
+  {
+    case 0x000001e0 ... 0x000001ef:
+      /* System start codes for video */
+      if (len >= 9)
+        ParsePESHeader(buf, len);
+      return true;
+
+    case 0x00000100:
+      /* Picture start code */
+      if (m_FrameDuration == 0 || m_curDTS == DVD_NOPTS_VALUE)
+        return true;
+
+      if (Parse_MPEG2Video_PicStart(&frametype, &bs))
+        return true;
+
+      if (m_StreamPacket != NULL)
+      {
+        esyslog("VNSI-Error: MPEG2 Video got a new picture start code with already openend steam packed");
+      }
+
+      m_StreamPacket = new sStreamPacket;
+      m_StreamPacket->id        = m_streamID;
+      m_StreamPacket->pts       = m_curPTS;
+      m_StreamPacket->dts       = m_curDTS;
+      m_StreamPacket->frametype = frametype;
+      m_StreamPacket->duration  = 0;
+      break;
+
+    case 0x000001b3:
+      /* Sequence start code */
+      if (Parse_MPEG2Video_SeqStart(&bs))
+        return true;
+
+      break;
+
+    case 0x000001b5:
+      if(len < 5)
+        return true;
+      switch(buf[4] >> 4) {
+      case 0x1:
+        /* sequence extension */
+        //      printf("Sequence extension, len = %d\n", len);
+        if(len < 10)
+          return true;
+        //      printf("Profile = %d\n", buf[4] & 0x7);
+        //      printf("  Level = %d\n", buf[5] >> 4);
+        break;
+      }
+      break;
+
+    case 0x00000101 ... 0x000001af:
+      /* Slices */
+
+      if (next_startcode == 0x100 || next_startcode > 0x1af)
+      {
+        /* Last picture slice (because next not a slice) */
+        if(m_StreamPacket == NULL)
+        {
+          /* no packet, may've been discarded by sanity checks here */
+          return true;
+        }
+
+        m_StreamPacket->data      = m_pictureBuffer;
+        m_StreamPacket->size      = m_pictureBufferPtr - 4;
+        m_StreamPacket->duration  = m_FrameDuration;
+
+        Parse_ComputePTS(m_StreamPacket);
+        m_StreamPacket = NULL;
+
+        m_pictureBuffer = (uint8_t*)malloc(m_pictureBufferSize);
+
+        /* If we know the frame duration, increase DTS accordingly */
+        m_curDTS += m_FrameDuration;
+
+        /* PTS cannot be extrapolated (it's not linear) */
+        m_curPTS = DVD_NOPTS_VALUE;
+        return true;
+      }
+      break;
+
+    default:
+      break;
+  }
+
+  return false;
+}
+
+bool cParserMPEG2Video::Parse_MPEG2Video_SeqStart(cBitstream *bs)
+{
+  if (bs->length() < 61)
+    return true;
+
+  m_Width         = bs->readBits(12);
+  m_Height        = bs->readBits(12);
+  bs->skipBits(4);
+  m_FrameDuration = mpeg2video_framedurations[bs->readBits(4)];
+  bs->skipBits(18);
+  bs->skipBits(1);
+
+  m_vbvSize = bs->readBits(10) * 16 * 1024 / 8;
+  m_demuxer->SetVideoInformation(0,0, m_Height, m_Width, 0);
+
+  return false;
+}
+
+bool cParserMPEG2Video::Parse_MPEG2Video_PicStart(int *frametype, cBitstream *bs)
+{
+  if (bs->length() < 29)
+    return true;
+
+  bs->skipBits(10); /* temporal reference */
+
+  int pct = bs->readBits(3);
+  if (pct < PKT_I_FRAME || pct > PKT_B_FRAME)
+    return true; /* Illegal picture_coding_type */
+
+  *frametype = pct;
+
+  /* If this is the first I-frame seen, set dts_start as a reference offset */
+  if (pct == PKT_I_FRAME && m_startDTS == DVD_NOPTS_VALUE)
+    m_startDTS = m_curDTS;
+
+  int vbvDelay = bs->readBits(16); /* vbv_delay */
+  if (vbvDelay  == 0xffff)
+    m_vbvDelay = -1;
+  else
+    m_vbvDelay = Rescale(vbvDelay);
+
+  return false;
+}
+
+void cParserMPEG2Video::Parse_ComputePTS(sStreamPacket *pkt)
+{
+  bool validpts = pkt->pts != DVD_NOPTS_VALUE && m_PTSQueue.size() == 0;
+
+  /* PTS known and no other packets in queue, deliver at once */
+  if (validpts && pkt->duration)
+  {
+    SendPacket(pkt);
+    free(pkt->data);
+    delete pkt;
+    return;
+  }
+
+  if (validpts)
+    return Parse_ComputeDuration(pkt);
+
+  m_PTSQueue.push_back(pkt);
+
+  while (!m_PTSQueue.empty())
+  {
+    pkt = m_PTSQueue.front();
+
+    switch (pkt->frametype)
+    {
+      case PKT_B_FRAME:
+        /* B-frames have same PTS as DTS, pass them on */
+        pkt->pts = pkt->dts;
+        break;
+
+      case PKT_I_FRAME:
+      case PKT_P_FRAME:
+        /* Presentation occures at DTS of next I or P frame, try to find it */
+        deque<sStreamPacket*>::iterator it;
+        it = m_PTSQueue.begin()+1;
+        while (1)
+        {
+          if (it >= m_PTSQueue.end())
+            return; /* not arrived yet, wait */
+
+          sStreamPacket* pkt2 = *it++;
+          if (pkt2->frametype <= PKT_P_FRAME)
+          {
+            pkt->pts = pkt2->dts;
+            break;
+          }
+        }
+        break;
+    }
+
+    m_PTSQueue.pop_front();
+
+    if (pkt->duration == 0)
+    {
+      Parse_ComputeDuration(pkt);
+    }
+    else
+    {
+      SendPacket(pkt);
+      free(pkt->data);
+      delete pkt;
+    }
+  }
+}
+
+void cParserMPEG2Video::Parse_ComputeDuration(sStreamPacket *pkt)
+{
+  m_DurationQueue.push_back(pkt);
+
+  pkt = m_DurationQueue.front();
+  if (m_DurationQueue.size() <= 1)
+    return;
+
+  sStreamPacket *next = m_DurationQueue[1];
+
+  int64_t duration = next->dts - pkt->dts;
+  m_DurationQueue.pop_front();
+
+  if (duration >= 10)
+  {
+    pkt->duration = duration;
+    SendPacket(pkt);
+  }
+  free(pkt->data);
+}
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_MPEGVideo.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_MPEGVideo.h
new file mode 100644 (file)
index 0000000..3be470e
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef VNSIDEMUXER_MPEGVIDEO_H
+#define VNSIDEMUXER_MPEGVIDEO_H
+
+#include <deque>
+#include "demuxer.h"
+
+class cBitstream;
+
+// --- cParserMPEG2Video -------------------------------------------------
+
+class cParserMPEG2Video : public cParser
+{
+private:
+  std::deque<sStreamPacket*> m_DurationQueue;
+  std::deque<sStreamPacket*> m_PTSQueue;
+
+  cTSDemuxer     *m_demuxer;
+  uint8_t        *m_pictureBuffer;
+  int             m_pictureBufferSize;
+  int             m_pictureBufferPtr;
+  int             m_FrameDuration;
+  uint32_t        m_StartCond;
+  uint32_t        m_StartCode;
+  int             m_StartCodeOffset;
+  sStreamPacket  *m_StreamPacket;
+  int             m_vbvDelay;       /* -1 if CBR */
+  int             m_vbvSize;        /* Video buffer size (in bytes) */
+  int             m_Height;
+  int             m_Width;
+
+  bool Parse_MPEG2Video(size_t len, uint32_t next_startcode, int sc_offset);
+  bool Parse_MPEG2Video_SeqStart(cBitstream *bs);
+  bool Parse_MPEG2Video_PicStart(int *frametype, cBitstream *bs);
+  void Parse_ComputePTS(sStreamPacket* pkt);
+  void Parse_ComputeDuration(sStreamPacket* pkt);
+
+public:
+  cParserMPEG2Video(cTSDemuxer *demuxer, cLiveStreamer *streamer, int streamID);
+  virtual ~cParserMPEG2Video();
+
+  virtual void Parse(unsigned char *data, int size, bool pusi);
+};
+
+#endif /* VNSIDEMUXER_MPEGVIDEO_H */
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_Subtitle.c b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_Subtitle.c
new file mode 100644 (file)
index 0000000..581141e
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <stdlib.h>
+#include <assert.h>
+#include "config.h"
+
+#include "demuxer_Subtitle.h"
+
+cParserSubtitle::cParserSubtitle(cTSDemuxer *demuxer, cLiveStreamer *streamer, int streamID)
+ : cParser(streamer, streamID)
+{
+  m_firstPUSIseen       = false;
+  m_PESStart            = false;
+  m_subtitleBuffer      = NULL;
+  m_subtitleBufferSize  = 0;
+  m_subtitleBufferPtr   = 0;
+  m_lastDTS             = DVD_NOPTS_VALUE;
+  m_lastPTS             = DVD_NOPTS_VALUE;
+  m_lastLength          = 0;
+  m_curLength           = 0;
+}
+
+cParserSubtitle::~cParserSubtitle()
+{
+  if (m_subtitleBuffer)
+    free(m_subtitleBuffer);
+}
+
+void cParserSubtitle::Parse(unsigned char *data, int size, bool pusi)
+{
+  if (pusi)
+  {
+    /* Payload unit start */
+    m_subtitleBufferPtr = 0;
+    m_firstPUSIseen = true;
+  }
+
+  /* Wait for first pusi */
+  if (!m_firstPUSIseen)
+    return;
+
+  if (m_subtitleBuffer == NULL)
+  {
+    m_subtitleBufferSize  = 4000;
+    m_subtitleBuffer      = (uint8_t*)malloc(m_subtitleBufferSize);
+  }
+
+  if (m_subtitleBufferPtr + size + 4 >= m_subtitleBufferSize)
+  {
+    m_subtitleBufferSize  += size * 4;
+    m_subtitleBuffer       = (uint8_t*)realloc(m_subtitleBuffer, m_subtitleBufferSize);
+  }
+
+  memcpy(m_subtitleBuffer+m_subtitleBufferPtr, data, size);
+  m_subtitleBufferPtr += size;
+
+  if (m_subtitleBufferPtr < 6)
+    return;
+
+  uint32_t startcode =
+    (m_subtitleBuffer[0] << 24) |
+    (m_subtitleBuffer[1] << 16) |
+    (m_subtitleBuffer[2] << 8) |
+    (m_subtitleBuffer[3]);
+
+  if (startcode == 0x1be)
+  {
+    m_firstPUSIseen = false;
+    return;
+  }
+
+  int psize = m_subtitleBuffer[4] << 8 | m_subtitleBuffer[5];
+
+  if (m_subtitleBufferPtr != psize + 6)
+    return;
+
+  m_firstPUSIseen = false;
+
+  int hlen = ParsePESHeader(m_subtitleBuffer, m_subtitleBufferPtr);
+  if (hlen < 0)
+    return;
+
+  if (m_lastDTS == DVD_NOPTS_VALUE)
+  {
+    m_lastDTS = m_curDTS;
+    m_lastPTS = m_curPTS;
+  }
+
+  psize -= hlen - 6;
+  uint8_t *buf = m_subtitleBuffer + hlen;
+
+  if (psize < 2 || buf[0] != 0x20 || buf[1] != 0x00)
+    return;
+
+  psize -= 2;
+  buf += 2;
+
+  if (psize >= 6)
+  {
+    // end_of_PES_data_field_marker
+    if (buf[psize - 1] == 0xff)
+    {
+      sStreamPacket pkt;
+      pkt.id       = m_streamID;
+      pkt.data     = buf;
+      pkt.size     = psize - 1;
+      pkt.duration = m_curDTS-m_lastDTS;
+      pkt.dts      = m_curPTS;
+      pkt.pts      = m_curPTS;
+      SendPacket(&pkt);
+
+      m_lastDTS = m_curDTS;
+      m_lastPTS = m_curPTS;
+    }
+  }
+
+}
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_Subtitle.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_Subtitle.h
new file mode 100644 (file)
index 0000000..2372600
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef VNSIDEMUXER_SUBTITLE_H
+#define VNSIDEMUXER_SUBTITLE_H
+
+#include "demuxer.h"
+
+// --- cParserSubtitle -------------------------------------------------
+
+class cParserSubtitle : public cParser
+{
+private:
+  bool        m_firstPUSIseen;
+  bool        m_PESStart;
+  uint8_t    *m_subtitleBuffer;
+  int         m_subtitleBufferSize;
+  int         m_subtitleBufferPtr;
+  int64_t     m_lastDTS;
+  int64_t     m_lastPTS;
+  int         m_lastLength;
+  int         m_curLength;
+
+public:
+  cParserSubtitle(cTSDemuxer *demuxer, cLiveStreamer *streamer, int streamID);
+  virtual ~cParserSubtitle();
+
+  virtual void Parse(unsigned char *data, int size, bool pusi);
+};
+
+
+#endif /* VNSIDEMUXER_SUBTITLE_H */
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_Teletext.c b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_Teletext.c
new file mode 100644 (file)
index 0000000..86a532c
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <stdlib.h>
+#include "config.h"
+
+#include "demuxer_Teletext.h"
+
+cParserTeletext::cParserTeletext(cTSDemuxer *demuxer, cLiveStreamer *streamer, int streamID)
+ : cParser(streamer, streamID)
+{
+  m_firstPUSIseen       = false;
+  m_PESStart            = false;
+  m_teletextBuffer      = NULL;
+  m_teletextBufferSize  = 0;
+  m_teletextBufferPtr   = 0;
+  m_lastDTS             = DVD_NOPTS_VALUE;
+  m_lastPTS             = DVD_NOPTS_VALUE;
+}
+
+cParserTeletext::~cParserTeletext()
+{
+  if (m_teletextBuffer)
+    free(m_teletextBuffer);
+}
+
+void cParserTeletext::Parse(unsigned char *data, int size, bool pusi)
+{
+  if (pusi)
+  {
+    /* Payload unit start */
+    m_PESStart      = true;
+    m_firstPUSIseen = true;
+  }
+
+  /* Wait for first pusi */
+  if (!m_firstPUSIseen)
+    return;
+
+  if (m_PESStart)
+  {
+    if (!PesIsPS1Packet(data))
+    {
+      esyslog("VNSI-Error: Teletext PES packet contains no valid private stream 1, ignored this packet");
+      m_firstPUSIseen = false;
+      return;
+    }
+
+    m_lastDTS = m_curDTS;
+    m_lastPTS = m_curPTS;
+    int hlen = ParsePESHeader(data, size);
+
+    m_PESStart = false;
+    data      += hlen;
+    size      -= hlen;
+
+    if (data[0] < 0x10 || data[0] > 0x1F)
+    {
+      esyslog("VNSI-Error: Teletext PES packet contains no valid identifier, ignored this packet");
+      m_firstPUSIseen = false;
+      return;
+    }
+
+    if (m_teletextBuffer && m_teletextBufferPtr > 0)
+    {
+      sStreamPacket pkt;
+      pkt.id       = m_streamID;
+      pkt.data     = m_teletextBuffer;
+      pkt.size     = m_teletextBufferPtr;
+      pkt.duration = m_curDTS-m_lastDTS;
+      pkt.dts      = m_lastDTS;
+      pkt.pts      = m_lastPTS;
+      SendPacket(&pkt);
+      m_teletextBufferPtr = 0;
+    }
+  }
+
+  if (m_teletextBuffer == NULL)
+  {
+    m_teletextBufferSize  = 4000;
+    m_teletextBuffer      = (uint8_t*)malloc(m_teletextBufferSize);
+  }
+
+  if (m_teletextBufferPtr + size + 4 >= m_teletextBufferSize)
+  {
+    m_teletextBufferSize  += size * 4;
+    m_teletextBuffer       = (uint8_t*)realloc(m_teletextBuffer, m_teletextBufferSize);
+  }
+
+  memcpy(m_teletextBuffer+m_teletextBufferPtr, data, size);
+  m_teletextBufferPtr += size;
+}
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_Teletext.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_Teletext.h
new file mode 100644 (file)
index 0000000..2cc5e7c
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef VNSIDEMUXER_TELETEXT_H
+#define VNSIDEMUXER_TELETEXT_H
+
+#include "demuxer.h"
+
+// --- cParserTeletext -------------------------------------------------
+
+class cParserTeletext : public cParser
+{
+private:
+  bool        m_firstPUSIseen;
+  bool        m_PESStart;
+  uint8_t    *m_teletextBuffer;
+  int         m_teletextBufferSize;
+  int         m_teletextBufferPtr;
+  int64_t     m_lastDTS;
+  int64_t     m_lastPTS;
+
+public:
+  cParserTeletext(cTSDemuxer *demuxer, cLiveStreamer *streamer, int streamID);
+  virtual ~cParserTeletext();
+
+  virtual void Parse(unsigned char *data, int size, bool pusi);
+};
+
+#endif /* VNSIDEMUXER_TELETEXT_H */
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_h264.c b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_h264.c
new file mode 100644 (file)
index 0000000..a5e6cfd
--- /dev/null
@@ -0,0 +1,451 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <stdlib.h>
+#include <assert.h>
+#include "config.h"
+#include "bitstream.h"
+#include "receiver.h"
+
+#include "demuxer_h264.h"
+
+static const int h264_lev2cpbsize[][2] =
+{
+  {10, 175},
+  {11, 500},
+  {12, 1000},
+  {13, 2000},
+  {20, 2000},
+  {21, 4000},
+  {22, 4000},
+  {30, 10000},
+  {31, 14000},
+  {32, 20000},
+  {40, 25000},
+  {41, 62500},
+  {42, 62500},
+  {50, 135000},
+  {51, 240000},
+  {-1, -1},
+};
+
+cParserH264::cParserH264(cTSDemuxer *demuxer, cLiveStreamer *streamer, int streamID)
+ : cParser(streamer, streamID)
+{
+  m_pictureBuffer     = NULL;
+  m_pictureBufferSize = 0;
+  m_pictureBufferPtr  = 0;
+  m_StartCond         = 0;
+  m_StartCode         = 0;
+  m_StartCodeOffset   = 0;
+  m_startDTS          = DVD_NOPTS_VALUE;
+  m_PrevDTS           = DVD_NOPTS_VALUE;
+  m_Height            = 0;
+  m_Width             = 0;
+  m_FrameDuration     = 0;
+  m_demuxer           = demuxer;
+  m_FoundFrame        = false;
+  m_vbvDelay          = -1;
+  m_vbvSize           = 0;
+  m_firstPUSIseen     = false;
+  m_PixelAspect.den   = 1;
+  m_PixelAspect.num   = 1;
+  memset(&m_streamData, 0, sizeof(m_streamData));
+}
+
+cParserH264::~cParserH264()
+{
+  if (m_pictureBuffer)
+    free(m_pictureBuffer);
+}
+
+void cParserH264::Parse(unsigned char *data, int size, bool pusi)
+{
+  uint32_t startcode = m_StartCond;
+
+  if (m_pictureBuffer == NULL)
+  {
+    m_pictureBufferSize   = 80000;
+    m_pictureBuffer       = (uint8_t*)malloc(m_pictureBufferSize);
+  }
+
+  if (m_pictureBufferPtr + size + 4 >= m_pictureBufferSize)
+  {
+    m_pictureBufferSize  += size * 4;
+    m_pictureBuffer       = (uint8_t*)realloc(m_pictureBuffer, m_pictureBufferSize);
+  }
+
+  for (int i = 0; i < size; i++)
+  {
+    m_pictureBuffer[m_pictureBufferPtr++] = data[i];
+    startcode = startcode << 8 | data[i];
+
+    if ((startcode & 0xffffff00) != 0x00000100)
+      continue;
+
+    bool reset = true;
+    if (m_pictureBufferPtr - 4 > 0 && m_StartCode != 0)
+    {
+      reset = Parse_H264(m_pictureBufferPtr - 4, startcode, m_StartCodeOffset);
+    }
+
+    if (reset)
+    {
+      /* Reset packet parser upon length error or if parser tells us so */
+      m_pictureBufferPtr = 0;
+      m_pictureBuffer[m_pictureBufferPtr++] = startcode >> 24;
+      m_pictureBuffer[m_pictureBufferPtr++] = startcode >> 16;
+      m_pictureBuffer[m_pictureBufferPtr++] = startcode >> 8;
+      m_pictureBuffer[m_pictureBufferPtr++] = startcode >> 0;
+    }
+    m_StartCode = startcode;
+    m_StartCodeOffset = m_pictureBufferPtr - 4;
+  }
+  m_StartCond = startcode;
+}
+
+bool cParserH264::Parse_H264(size_t len, uint32_t next_startcode, int sc_offset)
+{
+  uint8_t nal_data[len];
+  int pkttype;
+  uint8_t *buf = m_pictureBuffer + sc_offset;
+  uint32_t startcode = m_StartCode;
+
+  if (startcode == 0x10c)
+  {
+    /* RBSP padding, we don't want this */
+    int length = len - sc_offset;
+    memcpy(buf, buf + length, 4); /* Move down new start code */
+    m_pictureBufferPtr -= length;  /* Drop buffer */
+  }
+
+  if (startcode >= 0x000001e0 && startcode <= 0x000001ef)
+  {
+    /* System start codes for video */
+    if (len >= 9)
+      ParsePESHeader(buf, len);
+
+    if (m_PrevDTS != DVD_NOPTS_VALUE)
+    {
+      int64_t duration = (m_curDTS - m_PrevDTS) & 0x1ffffffffLL;
+
+      if (duration < 90000)
+        m_FrameDuration = duration;
+    }
+    m_PrevDTS = m_curDTS;
+    return true;
+  }
+
+  switch(startcode & 0x1f)
+  {
+  case NAL_SPS:
+  {
+    int nal_len = nalUnescape(nal_data, buf + 4, len - 4);
+    if (!Parse_SPS(nal_data, nal_len))
+      return true;
+
+    m_demuxer->SetVideoInformation(0,0, m_Height, m_Width, m_PixelAspect.num/m_PixelAspect.den);
+    break;
+  }
+
+  case NAL_PPS:
+  {
+    int nal_len = nalUnescape(nal_data, buf + 4, len - 4);
+    if (!Parse_PPS(nal_data, nal_len))
+      return true;
+
+    break;
+  }
+
+  case 5: /* IDR+SLICE */
+  case NAL_SLH:
+  {
+    if (m_FoundFrame || m_FrameDuration == 0 || m_curDTS == DVD_NOPTS_VALUE)
+      break;
+
+    if (m_startDTS == DVD_NOPTS_VALUE)
+      m_startDTS = m_curDTS;
+
+    int nal_len = nalUnescape(nal_data, buf + 4, len - 4 > 64 ? 64 : len - 3);
+    if (!Parse_SLH(nal_data, nal_len, &pkttype))
+      return true;
+
+    m_StreamPacket.id         = m_streamID;
+    m_StreamPacket.pts        = m_curPTS;
+    m_StreamPacket.dts        = m_curDTS;
+    m_StreamPacket.frametype  = pkttype;
+    m_StreamPacket.duration   = m_FrameDuration;
+    m_FoundFrame = true;
+    break;
+  }
+
+  default:
+    break;
+  }
+
+  if (next_startcode >= 0x000001e0 && next_startcode <= 0x000001ef)
+  {
+    /* Complete frame */
+    if (!m_FoundFrame)
+      return true;
+
+    /* Discard Packets until we have the picture size (XBMC can't enable VDPAU without it) */
+    if (!m_Width)
+      return true;
+    else
+      m_Streamer->SetReady();
+
+    m_FoundFrame        = false;
+    m_StreamPacket.data = m_pictureBuffer;
+    m_StreamPacket.size = m_pictureBufferPtr;
+    SendPacket(&m_StreamPacket);
+    m_firstPUSIseen = true;
+    return true;
+  }
+
+  return false;
+}
+
+int cParserH264::nalUnescape(uint8_t *dst, const uint8_t *src, int len)
+{
+  int s = 0, d = 0;
+
+  while (s < len)
+  {
+    if (!src[s] && !src[s + 1])
+    {
+      // hit 00 00 xx
+      dst[d] = dst[d + 1] = 0;
+      s += 2;
+      d += 2;
+      if (src[s] == 3)
+      {
+        s++; // 00 00 03 xx --> 00 00 xx
+        if (s >= len)
+          return d;
+      }
+    }
+    dst[d++] = src[s++];
+  }
+
+  return d;
+}
+
+bool cParserH264::Parse_PPS(uint8_t *buf, int len)
+{
+  cBitstream bs(buf, len*8);
+
+  int pps_id = bs.readGolombUE();
+  int sps_id = bs.readGolombUE();
+  m_streamData.pps[pps_id].sps = sps_id;
+  return true;
+}
+
+bool cParserH264::Parse_SLH(uint8_t *buf, int len, int *pkttype)
+{
+  cBitstream bs(buf, len*8);
+
+  bs.readGolombUE(); /* first_mb_in_slice */
+  int slice_type = bs.readGolombUE();
+
+  if (slice_type > 4)
+    slice_type -= 5;  /* Fixed slice type per frame */
+
+  switch (slice_type)
+  {
+  case 0:
+    *pkttype = PKT_P_FRAME;
+    break;
+  case 1:
+    *pkttype = PKT_B_FRAME;
+    break;
+  case 2:
+    *pkttype = PKT_I_FRAME;
+    break;
+  default:
+    return false;
+  }
+
+//  /* If this is the first I-frame seen, set dts_start as a reference offset */
+//  if (*pkttype == PKT_I_FRAME && m_startDTS == DVD_NOPTS_VALUE)
+//    m_startDTS = m_curDTS;
+
+  int pps_id = bs.readGolombUE();
+  int sps_id = m_streamData.pps[pps_id].sps;
+  if (m_streamData.sps[sps_id].cbpsize == 0)
+    return false;
+
+  m_vbvSize = m_streamData.sps[sps_id].cbpsize;
+  m_vbvDelay = -1;
+  return true;
+}
+
+bool cParserH264::Parse_SPS(uint8_t *buf, int len)
+{
+  cBitstream bs(buf, len*8);
+  unsigned int tmp, frame_mbs_only;
+  int cbpsize = -1;
+
+  int profile_idc = bs.readBits(8);
+  /* constraint_set0_flag = bs.readBits1();    */
+  /* constraint_set1_flag = bs.readBits1();    */
+  /* constraint_set2_flag = bs.readBits1();    */
+  /* constraint_set3_flag = bs.readBits1();    */
+  /* reserved             = bs.readBits(4);    */
+  bs.skipBits(8);
+  int level_idc = bs.readBits(8);
+  unsigned int seq_parameter_set_id = bs.readGolombUE();
+
+  unsigned int i = 0;
+  while (h264_lev2cpbsize[i][0] != -1)
+  {
+    if (h264_lev2cpbsize[i][0] >= level_idc)
+    {
+      cbpsize = h264_lev2cpbsize[i][1];
+      break;
+    }
+    i++;
+  }
+  if (cbpsize < 0)
+    return false;
+
+  m_streamData.sps[seq_parameter_set_id].cbpsize = cbpsize * 125; /* Convert from kbit to bytes */
+
+  if (profile_idc >= 100)       /* high profile                   */
+  {
+    if(bs.readGolombUE() == 3)  /* chroma_format_idc              */
+      bs.skipBits(1);           /* residual_colour_transform_flag */
+    bs.readGolombUE();          /* bit_depth_luma - 8             */
+    bs.readGolombUE();          /* bit_depth_chroma - 8           */
+    bs.skipBits(1);             /* transform_bypass               */
+    if (bs.readBits1())         /* seq_scaling_matrix_present     */
+    {
+      for (int i = 0; i < 8; i++)
+      {
+        if (bs.readBits1())     /* seq_scaling_list_present       */
+        {
+          int last = 8, next = 8, size = (i<6) ? 16 : 64;
+          for (int j = 0; j < size; j++)
+          {
+            if (next)
+              next = (last + bs.readGolombSE()) & 0xff;
+            last = next ?: last;
+          }
+        }
+      }
+    }
+  }
+
+  bs.readGolombUE();           /* log2_max_frame_num - 4 */
+  int pic_order_cnt_type = bs.readGolombUE();
+  if (pic_order_cnt_type == 0)
+    bs.readGolombUE();         /* log2_max_poc_lsb - 4 */
+  else if (pic_order_cnt_type == 1)
+  {
+    bs.skipBits(1);            /* delta_pic_order_always_zero     */
+    bs.readGolombSE();         /* offset_for_non_ref_pic          */
+    bs.readGolombSE();         /* offset_for_top_to_bottom_field  */
+    tmp = bs.readGolombUE();   /* num_ref_frames_in_pic_order_cnt_cycle */
+    for (unsigned int i = 0; i < tmp; i++)
+      bs.readGolombSE();       /* offset_for_ref_frame[i]         */
+  }
+  else if(pic_order_cnt_type != 2)
+  {
+    /* Illegal poc */
+    return false;
+  }
+
+  bs.readGolombUE();          /* ref_frames                      */
+  bs.skipBits(1);             /* gaps_in_frame_num_allowed       */
+  m_Width  /* mbs */ = bs.readGolombUE() + 1;
+  m_Height /* mbs */ = bs.readGolombUE() + 1;
+  frame_mbs_only     = bs.readBits1();
+  LOGCONSOLE("H.264 SPS: pic_width:  %u mbs", (unsigned) m_Width);
+  LOGCONSOLE("H.264 SPS: pic_height: %u mbs", (unsigned) m_Height);
+  LOGCONSOLE("H.264 SPS: frame only flag: %d", frame_mbs_only);
+
+  m_Width  *= 16;
+  m_Height *= 16 * (2-frame_mbs_only);
+
+  if (!frame_mbs_only)
+  {
+    if (bs.readBits1())     /* mb_adaptive_frame_field_flag */
+      LOGCONSOLE("H.264 SPS: MBAFF");
+  }
+  bs.skipBits(1);           /* direct_8x8_inference_flag    */
+  if (bs.readBits1())       /* frame_cropping_flag */
+  {
+    uint32_t crop_left   = bs.readGolombUE();
+    uint32_t crop_right  = bs.readGolombUE();
+    uint32_t crop_top    = bs.readGolombUE();
+    uint32_t crop_bottom = bs.readGolombUE();
+    LOGCONSOLE("H.264 SPS: cropping %d %d %d %d", crop_left, crop_top, crop_right, crop_bottom);
+
+    m_Width -= 2*(crop_left + crop_right);
+    if (frame_mbs_only)
+      m_Height -= 2*(crop_top + crop_bottom);
+    else
+      m_Height -= 4*(crop_top + crop_bottom);
+  }
+
+  /* VUI parameters */
+  m_PixelAspect.num = 0;
+  if (bs.readBits1())    /* vui_parameters_present flag */
+  {
+    if (bs.readBits1())  /* aspect_ratio_info_present */
+    {
+      uint32_t aspect_ratio_idc = bs.readBits(8);
+      LOGCONSOLE("H.264 SPS: aspect_ratio_idc %d", aspect_ratio_idc);
+
+      if (aspect_ratio_idc == 255 /* Extended_SAR */)
+      {
+        m_PixelAspect.num = bs.readBits(16); /* sar_width */
+        m_PixelAspect.den = bs.readBits(16); /* sar_height */
+        LOGCONSOLE("H.264 SPS: -> sar %dx%d", m_PixelAspect.num, m_PixelAspect.den);
+      }
+      else
+      {
+        static const mpeg_rational_t aspect_ratios[] =
+        { /* page 213: */
+          /* 0: unknown */
+          {0, 1},
+          /* 1...16: */
+          { 1,  1}, {12, 11}, {10, 11}, {16, 11}, { 40, 33}, {24, 11}, {20, 11}, {32, 11},
+          {80, 33}, {18, 11}, {15, 11}, {64, 33}, {160, 99}, { 4,  3}, { 3,  2}, { 2,  1}
+        };
+
+        if (aspect_ratio_idc < sizeof(aspect_ratios)/sizeof(aspect_ratios[0]))
+        {
+          memcpy(&m_PixelAspect, &aspect_ratios[aspect_ratio_idc], sizeof(mpeg_rational_t));
+          LOGCONSOLE("H.264 SPS: -> aspect ratio %d / %d", m_PixelAspect.num, m_PixelAspect.den);
+        }
+        else
+        {
+          LOGCONSOLE("H.264 SPS: aspect_ratio_idc out of range !");
+        }
+      }
+    }
+  }
+
+  LOGCONSOLE("H.264 SPS: -> video size %dx%d, aspect %d:%d", m_Width, m_Height, m_PixelAspect.num, m_PixelAspect.den);
+  return true;
+}
+
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_h264.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/demuxer_h264.h
new file mode 100644 (file)
index 0000000..3d56464
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef VNSIDEMUXER_H264_H
+#define VNSIDEMUXER_H264_H
+
+#include "demuxer.h"
+
+class cBitstream;
+
+// --- cParserH264 -------------------------------------------------
+
+class cParserH264 : public cParser
+{
+private:
+  typedef struct h264_private
+  {
+    struct
+    {
+      int frame_duration;
+      int cbpsize;
+    } sps[256];
+
+    struct
+    {
+      int sps;
+    } pps[256];
+
+  } h264_private_t;
+
+  typedef struct mpeg_rational_s {
+    int num;
+    int den;
+  } mpeg_rational_t;
+
+  enum
+  {
+    NAL_SLH     = 0x01, // Slice Header
+    NAL_SEI     = 0x06, // Supplemental Enhancement Information
+    NAL_SPS     = 0x07, // Sequence Parameter Set
+    NAL_PPS     = 0x08, // Picture Parameter Set
+    NAL_AUD     = 0x09, // Access Unit Delimiter
+    NAL_END_SEQ = 0x0A  // End of Sequence
+  };
+
+  cTSDemuxer     *m_demuxer;
+  uint8_t        *m_pictureBuffer;
+  int             m_pictureBufferSize;
+  int             m_pictureBufferPtr;
+  uint32_t        m_StartCond;
+  uint32_t        m_StartCode;
+  int             m_StartCodeOffset;
+  int             m_Height;
+  int             m_Width;
+  mpeg_rational_t m_PixelAspect;
+  int64_t         m_PrevDTS;
+  int             m_FrameDuration;
+  sStreamPacket   m_StreamPacket;
+  bool            m_FoundFrame;
+  h264_private    m_streamData;
+  int             m_vbvDelay;       /* -1 if CBR */
+  int             m_vbvSize;        /* Video buffer size (in bytes) */
+  bool            m_firstPUSIseen;
+
+  bool Parse_H264(size_t len, uint32_t next_startcode, int sc_offset);
+  bool Parse_PPS(uint8_t *buf, int len);
+  bool Parse_SLH(uint8_t *buf, int len, int *pkttype);
+  bool Parse_SPS(uint8_t *buf, int len);
+  int nalUnescape(uint8_t *dst, const uint8_t *src, int len);
+
+public:
+  cParserH264(cTSDemuxer *demuxer, cLiveStreamer *streamer, int streamID);
+  virtual ~cParserH264();
+
+  virtual void Parse(unsigned char *data, int size, bool pusi);
+};
+
+
+#endif /* VNSIDEMUXER_H264_H */
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/global.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/global.h
new file mode 100644 (file)
index 0000000..23f7a1a
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * global.h: A program for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ */
+
+#ifndef __global_h_
+#define __global_h_
+
+#include <time.h>
+
+#ifndef uchar
+typedef unsigned char uchar;
+#endif
+
+#define MA_I_TYPE 1
+#define MA_P_TYPE 2
+#define MA_B_TYPE 3
+#define MA_D_TYPE 4
+#define MA_SI_TYPE 5
+#define MA_SP_TYPE 6
+#define MA_BI_TYPE 7
+
+#define MT_COMMON        0x10
+#define MT_ASPECTCHANGE  0x20
+#define MT_CHANNELCHANGE 0x30
+#define MT_CHANNELSTART  0x30
+#define MT_CHANNELSTOP   0x31
+#define MT_LOGOSTART     0x40
+#define MT_LOGOSTOP      0x41
+#define MT_BORDERSTART   0x50
+#define MT_BORDERSTOP    0x51
+#define MT_SILENCECHANGE 0x60
+#define MT_ALL           0xFF
+
+typedef struct MarkAdMark
+{
+    int Type;
+    int Position;
+    char *Comment;
+} MarkAdMark;
+
+typedef struct MarkAdAspectRatio
+{
+    int Num;
+    int Den;
+} MarkAdAspectRatio;
+
+#define MARKAD_PIDTYPE_VIDEO_H262 0x10
+#define MARKAD_PIDTYPE_VIDEO_H264 0x11
+#define MARKAD_PIDTYPE_AUDIO_AC3  0x20
+#define MARKAD_PIDTYPE_AUDIO_MP2  0x21
+
+typedef struct MarkAdPid
+{
+    int Num;
+    int Type;
+} MarkAdPid;
+
+typedef struct MarkAdContext
+{
+    char *LogoDir; // Logo Directory, default /var/lib/markad
+
+    struct StandAlone
+    {
+        int LogoExtraction;
+        int LogoWidth;
+        int LogoHeight;
+        bool ASD;
+    } StandAlone;
+
+    struct General
+    {
+        char *ChannelID;
+        MarkAdPid VPid;
+        MarkAdPid APid;
+        MarkAdPid DPid;
+    } General;
+
+    struct Video
+    {
+        struct Options
+        {
+            bool IgnoreAspectRatio;
+        } Options;
+
+        struct Info
+        {
+            int Width;  // width of pic
+            int Height; // height of pic
+            int Pict_Type; // picture type (I,P,B,S,SI,SP,BI)
+            MarkAdAspectRatio AspectRatio;
+            double FramesPerSecond;
+            bool Interlaced;
+        } Info;
+
+        struct Data
+        {
+            bool Valid; // flag, if true data is valid
+            uchar *Plane[4];  // picture planes (YUV420)
+            int PlaneLinesize[4]; // size int bytes of each picture plane line
+        } Data;
+    } Video;
+
+    struct Audio
+    {
+        struct Options
+        {
+            bool AudioSilenceDetection;
+        } Options;
+
+        struct Info
+        {
+            int Channels; // number of audio channels
+            int SampleRate;
+        } Info;
+        struct Data
+        {
+            bool Valid;
+            short *SampleBuf;
+            int SampleBufLen;
+        } Data;
+    } Audio;
+
+} MarkAdContext;
+
+#endif
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/patches/vdr-wirbelscan-0.0.5-pre11e-AddServiceInterface.diff b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/patches/vdr-wirbelscan-0.0.5-pre11e-AddServiceInterface.diff
new file mode 100644 (file)
index 0000000..f2ce0d9
--- /dev/null
@@ -0,0 +1,426 @@
+diff -NaurwB wirbelscan-0.0.5-pre11e/common.h wirbelscan-patched/common.h
+--- wirbelscan-0.0.5-pre11e/common.h   2010-03-17 11:32:34.000000000 +0100
++++ wirbelscan-patched/common.h        2010-04-19 00:55:36.000000000 +0200
+@@ -11,16 +11,7 @@
+ #include <linux/types.h>
+ #include <sys/ioctl.h>
+-
+-typedef enum scantype {
+-  DVB_TERR    = 0,
+-  DVB_CABLE   = 1,
+-  DVB_SAT     = 2,
+-  PVRINPUT    = 3,
+-  PVRINPUT_FM = 4,
+-  DVB_ATSC    = 5,
+-} scantype_t;
+-
++#include "wirbelscanservice.h"
+ /* generic functions */
+diff -NaurwB wirbelscan-0.0.5-pre11e/dvb_wrapper.c wirbelscan-patched/dvb_wrapper.c
+--- wirbelscan-0.0.5-pre11e/dvb_wrapper.c      2010-03-18 11:06:33.000000000 +0100
++++ wirbelscan-patched/dvb_wrapper.c   2010-04-23 03:59:28.000000000 +0200
+@@ -1,5 +1,4 @@
+-#include <linux/dvb/frontend.h> //either API version 3.2 or 5.0
+ #include <linux/dvb/version.h>
+ #include <vdr/dvbdevice.h>
+ #include <vdr/channels.h>
+diff -NaurwB wirbelscan-0.0.5-pre11e/menusetup.c wirbelscan-patched/menusetup.c
+--- wirbelscan-0.0.5-pre11e/menusetup.c        2010-03-17 13:10:15.000000000 +0100
++++ wirbelscan-patched/menusetup.c     2010-04-23 03:59:41.000000000 +0200
+@@ -7,7 +7,6 @@
+  */
+-#include <linux/dvb/frontend.h>
+ #include <vdr/menuitems.h>
+ #include <vdr/device.h>
+ #include <vdr/config.h>
+@@ -39,6 +38,7 @@
+ cOsdItem      * ChanNew         = NULL;
+ cOsdItem      * ChanAll         = NULL;
+ cOsdItem      * ScanType        = NULL;
++sRemoteMenuScanning * RemoteMenuScanning = NULL;
+ #define LOGLEN 8
+ cOsdItem      * LogMsg[LOGLEN];
+diff -NaurwB wirbelscan-0.0.5-pre11e/menusetup.h wirbelscan-patched/menusetup.h
+--- wirbelscan-0.0.5-pre11e/menusetup.h        2010-03-17 11:32:34.000000000 +0100
++++ wirbelscan-patched/menusetup.h     2010-04-23 02:04:08.000000000 +0200
+@@ -49,6 +49,16 @@
+ bool DoScan (int DVB_Type);
+ void DoStop (void);
++struct sRemoteMenuScanning {
++  void (*SetPercentage)(int percent);
++  void (*SetSignalStrength)(int strenght, bool locked);
++  void (*SetDeviceInfo)(const char *Info);
++  void (*SetTransponder)(const char *Info);
++  void (*NewChannel)(const char *Name, bool isRadio, bool isEncrypted, bool isHD);
++  void (*IsFinished)();
++  void (*SetStatus)(int status);
++};
++
+ class cWirbelscan {
+  private:
+  public:
+@@ -91,5 +101,6 @@
+     void AddLogMsg(const char * Msg);
+ };
+ extern cMenuScanning * MenuScanning;
++extern sRemoteMenuScanning * RemoteMenuScanning;
+ #endif
+diff -NaurwB wirbelscan-0.0.5-pre11e/scanfilter.c wirbelscan-patched/scanfilter.c
+--- wirbelscan-0.0.5-pre11e/scanfilter.c       2010-03-17 11:32:34.000000000 +0100
++++ wirbelscan-patched/scanfilter.c    2010-04-23 01:55:30.000000000 +0200
+@@ -1000,6 +1000,8 @@
+                     dlog(4, "      SDT: old %s", *PrintChannel(channel));
+                     channel->SetName(pn, ps, pp);
+                     dlog(2, "      Upd: %s", *PrintChannel(channel));
++                    if (RemoteMenuScanning)
++                      RemoteMenuScanning->NewChannel(channel->Name(), (channel->Vpid() == 0 && (channel->Apid(0) != 0 || channel->Dpid(0) != 0)), channel->Ca() != 0, sd->getServiceType() == 0x19);
+                     }
+                   }
+                 else {
+@@ -1009,6 +1011,8 @@
+                     transponder->CopyTransponderData(Channel());
+                     dlog(3, "   SDT: Add: %s", *PrintTransponder(transponder));
+                     NewTransponders.Add(transponder);
++                    if (RemoteMenuScanning)
++                      RemoteMenuScanning->NewChannel(channel->Name(), (channel->Vpid() == 0 && (channel->Apid(0) != 0 || channel->Dpid(0) != 0)), channel->Ca() != 0, sd->getServiceType() == 0x19);
+                     }
+                   dlog(2, "   SDT: Add %s", *PrintChannel(channel));
+                   }
+diff -NaurwB wirbelscan-0.0.5-pre11e/scanner.c wirbelscan-patched/scanner.c
+--- wirbelscan-0.0.5-pre11e/scanner.c  2010-03-18 10:52:52.000000000 +0100
++++ wirbelscan-patched/scanner.c       2010-04-23 04:04:27.000000000 +0200
+@@ -289,6 +289,8 @@
+     }\r
\r
+   dlog(1, "%s", *PrintChannel(channel));\r
++  if (RemoteMenuScanning)\r
++    RemoteMenuScanning->NewChannel(channel->Name(), false, false, false);\r
+   Channels.IncBeingEdited();\r
+   Channels.Add(channel);\r
+   Channels.DecBeingEdited();\r
+@@ -358,6 +360,7 @@
+        if ((dev = GetPreferredDevice(aChannel)) == NULL) {\r
+          dlog(0, "No device available - exiting!");\r
+          if (MenuScanning) MenuScanning->SetStatus((status = 2));\r
++         if (RemoteMenuScanning) RemoteMenuScanning->SetStatus((status = 2));\r
+          DeleteAndNull(aChannel);\r
+          return;\r
+          }\r
+@@ -366,6 +369,7 @@
+        GetTerrCapabilities(dev->CardIndex(), &crAuto, &modAuto, &invAuto, &bwAuto, &hAuto, &tmAuto, &gAuto);\r
+        dlog(1, "frontend %s supports", *GetFeName(dev->CardIndex()));\r
+        if (MenuScanning) MenuScanning->SetDeviceInfo(cString::sprintf("%s", *GetFeName(dev->CardIndex())));\r
++       if (RemoteMenuScanning) RemoteMenuScanning->SetDeviceInfo(*cString::sprintf("%s", *GetFeName(dev->CardIndex())));\r
\r
+        if (invAuto) {\r
+          dlog(1, "INVERSION_AUTO");\r
+@@ -439,6 +443,7 @@
+        if ((dev = GetPreferredDevice(aChannel)) == NULL) {\r
+          dlog(0, "No device available - exiting!");\r
+          if (MenuScanning) MenuScanning->SetStatus((status = 2));\r
++         if (RemoteMenuScanning) RemoteMenuScanning->SetStatus((status = 2));\r
+          DeleteAndNull(aChannel);\r
+          return;\r
+          }\r
+@@ -447,6 +452,7 @@
+        GetCableCapabilities(dev->CardIndex(), &crAuto, &modAuto, &invAuto);\r
+        dlog(1, "frontend %s supports", *GetFeName(dev->CardIndex()));\r
+        if (MenuScanning) MenuScanning->SetDeviceInfo(cString::sprintf("%s", *GetFeName(dev->CardIndex())));\r
++       if (RemoteMenuScanning) RemoteMenuScanning->SetDeviceInfo(*cString::sprintf("%s", *GetFeName(dev->CardIndex())));\r
+        if (invAuto) {\r
+          dlog(1, "INVERSION_AUTO");\r
+          caps_inversion = INVERSION_AUTO;\r
+@@ -526,6 +532,7 @@
+        #endif\r
+          dlog(0, "No DVB-S2 device available - trying fallback to DVB-S");\r
+          if (MenuScanning) MenuScanning->SetStatus(3);\r
++         if (RemoteMenuScanning) RemoteMenuScanning->SetStatus(3);\r
+ //         SetSatTransponderDataFromVDR(aChannel, cSource::FromString(sat_list[this_channellist].source_id), 11112, eHorizontal, 27500, eCoderate56, eSatModulationQpsk, eDvbs, eRolloff35);\r
+          SetSatTransponderDataFromDVB(aChannel,\r
+                                       cSource::FromString(sat_list[this_channellist].source_id),\r
+@@ -536,6 +543,7 @@
+          if ((dev = GetPreferredDevice(aChannel)) == NULL) {\r
+            dlog(0, "No device available - exiting!");\r
+            if (MenuScanning) MenuScanning->SetStatus((status = 2));\r
++           if (RemoteMenuScanning) RemoteMenuScanning->SetStatus((status = 2));\r
+            DeleteAndNull(aChannel);\r
+            return;\r
+            }\r
+@@ -545,6 +553,7 @@
+        GetSatCapabilities(dev->CardIndex(), &crAuto, &modAuto, &roAuto, &s2Support);\r
+        dlog(1, "frontend %s supports", *GetFeName(dev->CardIndex()));\r
+        if (MenuScanning) MenuScanning->SetDeviceInfo(cString::sprintf("%s", *GetFeName(dev->CardIndex())));\r
++       if (RemoteMenuScanning) RemoteMenuScanning->SetDeviceInfo(*cString::sprintf("%s", *GetFeName(dev->CardIndex())));\r
+        caps_inversion = INVERSION_AUTO;\r
+        if (crAuto) {\r
+          dlog(1, "FEC_AUTO");\r
+@@ -578,6 +587,7 @@
+        if ((dev = GetPreferredDevice(aChannel)) == NULL) {\r
+          dlog(0, "No device available - exiting!");\r
+          if (MenuScanning) MenuScanning->SetStatus((status = 2));\r
++         if (RemoteMenuScanning) RemoteMenuScanning->SetStatus((status = 2));\r
+          DeleteAndNull(aChannel);\r
+          return;\r
+          }\r
+@@ -586,6 +596,7 @@
+        GetAtscCapabilities(dev->CardIndex(), &crAuto, &modAuto, &invAuto, &vsbSupport, &qamSupport);\r
+        dlog(1, "frontend %s supports", *GetFeName(dev->CardIndex()));\r
+        if (MenuScanning) MenuScanning->SetDeviceInfo(cString::sprintf("%s", *GetFeName(dev->CardIndex())));\r
++       if (RemoteMenuScanning)  RemoteMenuScanning->SetDeviceInfo(*cString::sprintf("%s", *GetFeName(dev->CardIndex())));\r
+        if (invAuto) {\r
+          dlog(1, "INVERSION_AUTO\n");\r
+          caps_inversion = INVERSION_AUTO;\r
+@@ -660,6 +671,7 @@
+        #endif\r
+          dlog(0, "No device available - exiting! (pvrinput not running?)");\r
+          if (MenuScanning) MenuScanning->SetStatus((status = 2));\r
++         if (RemoteMenuScanning) RemoteMenuScanning->SetStatus((status = 2));\r
+          DeleteAndNull(aChannel);\r
+          return;\r
+          }\r
+@@ -707,6 +719,8 @@
+          }\r
+        if (MenuScanning)\r
+          MenuScanning->SetDeviceInfo(cString::sprintf("%s", vcap.card));\r
++       if (RemoteMenuScanning)\r
++         RemoteMenuScanning->SetDeviceInfo(*cString::sprintf("%s", vcap.card));\r
+        dev->DetachAllReceivers();\r
+        break;\r
+        }\r
+@@ -716,6 +730,7 @@
+     } // end switch type\r
\r
+   if (MenuScanning) MenuScanning->SetStatus(1);\r
++  if (RemoteMenuScanning) RemoteMenuScanning->SetStatus(1);\r
\r
+   //count channels.\r
\r
+@@ -933,6 +948,11 @@
+                                              type, (lastChannel - thisChannel));\r
+             MenuScanning->SetTransponder(aChannel);\r
+             }\r
++          if (RemoteMenuScanning)\r
++          {\r
++            RemoteMenuScanning->SetPercentage((int) (thisChannel * 100) / lastChannel);\r
++            RemoteMenuScanning->SetTransponder(*PrintTransponder(aChannel));\r
++            }\r
+           dev->SwitchChannel(aChannel, false);\r
+           SwReceiver = new cSwReceiver::cSwReceiver(aChannel);\r
+           dev->AttachReceiver(SwReceiver);\r
+@@ -952,6 +972,7 @@
+                  lock = false;\r
\r
+                if (MenuScanning) MenuScanning->SetStr(GetFrontendStrength(dev->CardIndex()), lock);\r
++               if (RemoteMenuScanning) RemoteMenuScanning->SetSignalStrength(GetFrontendStrength(dev->CardIndex()), lock);\r
+                if (! lock) {\r
+                  continue;\r
+                  }\r
+@@ -971,6 +992,8 @@
+                  cChannel *     newChannel = new cChannel;\r
+                  if (MenuScanning)\r
+                    MenuScanning->SetStr(s, true);\r
++                 if (RemoteMenuScanning)\r
++                   RemoteMenuScanning->SetSignalStrength(s, true);\r
\r
+                  newChannel->Parse(*aChannel->ToText());\r
+                  newChannel->SetName(*channelname, *shortname, (const char *) "analog");\r
+@@ -988,6 +1011,8 @@
+                else {\r
+                  if (MenuScanning)\r
+                    MenuScanning->SetStr(0, false);\r
++                 if (RemoteMenuScanning)\r
++                   RemoteMenuScanning->SetSignalStrength(0, false);\r
+                  }\r
+                break;\r
+                }\r
+@@ -1108,6 +1133,13 @@
\r
+ stop:\r
+   if (MenuScanning) MenuScanning->SetStatus((status = 0));\r
++  if (RemoteMenuScanning)\r
++  {\r
++    RemoteMenuScanning->SetStatus((status = 0));\r
++    RemoteMenuScanning->IsFinished();\r
++    delete RemoteMenuScanning;\r
++    RemoteMenuScanning = NULL;\r
++  }\r
+   dlog(3, "leaving scanner");\r
+   Cancel(0);\r
+   }\r
+diff -NaurwB wirbelscan-0.0.5-pre11e/statemachine.c wirbelscan-patched/statemachine.c
+--- wirbelscan-0.0.5-pre11e/statemachine.c     2010-03-17 11:32:34.000000000 +0100
++++ wirbelscan-patched/statemachine.c  2010-04-22 17:07:02.000000000 +0200
+@@ -129,6 +129,11 @@
+            MenuScanning->SetTransponder(Transponder);
+            MenuScanning->SetProgress(-1, DVB_TERR, -1);
+            }
++         if (RemoteMenuScanning)
++         {
++           RemoteMenuScanning->SetPercentage(-1);
++           RemoteMenuScanning->SetTransponder(*PrintTransponder(Transponder));
++           }
+          ScannedTransponder = new cChannel(* Transponder);
+          ScannedTransponders.Add(ScannedTransponder);
+@@ -143,6 +148,8 @@
+            }
+          if (MenuScanning)
+            MenuScanning->SetStr(GetFrontendStrength(dev->CardIndex()), dev->HasLock(1));
++         if (RemoteMenuScanning)
++           RemoteMenuScanning->SetSignalStrength(GetFrontendStrength(dev->CardIndex()), dev->HasLock(1));
+          break;
+        case eNextTransponder:
+diff -NaurwB wirbelscan-0.0.5-pre11e/wirbelscan.c wirbelscan-patched/wirbelscan.c
+--- wirbelscan-0.0.5-pre11e/wirbelscan.c       2010-03-17 11:32:34.000000000 +0100
++++ wirbelscan-patched/wirbelscan.c    2010-04-23 03:17:50.000000000 +0200
+@@ -9,6 +9,8 @@
+ #include <vdr/plugin.h>
+ #include <vdr/i18n.h>
+ #include "menusetup.h"
++#include "countries.h"
++#include "satellites.h"
+ #if VDRVERSNUM < 10507
+ #include "i18n.h"
+ #endif
+@@ -141,7 +143,69 @@
+ bool cPluginWirbelscan::Service(const char *Id, void *Data)
+ {
+-  // Handle custom service requests from other plugins
++  if (strcmp(Id,"WirbelScanService-DoScan-v1.0") == 0)
++  {
++    if (Data)
++    {
++      WirbelScanService_DoScan_v1_0 *svc = (WirbelScanService_DoScan_v1_0*)Data;
++
++      Wirbelscan.scanflags        = svc->scan_tv        ? SCAN_TV         : 0;
++      Wirbelscan.scanflags       |= svc->scan_radio     ? SCAN_RADIO      : 0;
++      Wirbelscan.scanflags       |= svc->scan_scrambled ? SCAN_SCRAMBLED  : 0;
++      Wirbelscan.scanflags       |= svc->scan_fta       ? SCAN_FTA        : 0;
++      Wirbelscan.scanflags       |= svc->scan_hd        ? SCAN_HD         : 0;
++      Wirbelscan.CountryIndex     = svc->CountryIndex;
++      Wirbelscan.DVBC_Inversion   = svc->DVBC_Inversion;
++      Wirbelscan.DVBC_Symbolrate  = svc->DVBC_Symbolrate;
++      Wirbelscan.DVBC_QAM         = svc->DVBC_QAM;
++      Wirbelscan.DVBT_Inversion   = svc->DVBT_Inversion;
++      Wirbelscan.SatIndex         = svc->SatIndex;
++      Wirbelscan.ATSC_type        = svc->ATSC_Type;
++
++      RemoteMenuScanning = new sRemoteMenuScanning;
++      RemoteMenuScanning->SetPercentage = svc->SetPercentage;
++      RemoteMenuScanning->SetSignalStrength = svc->SetSignalStrength;
++      RemoteMenuScanning->SetDeviceInfo = svc->SetDeviceInfo;
++      RemoteMenuScanning->SetTransponder = svc->SetTransponder;
++      RemoteMenuScanning->NewChannel = svc->NewChannel;
++      RemoteMenuScanning->IsFinished = svc->IsFinished;
++      RemoteMenuScanning->SetStatus = svc->SetStatus;
++
++      return DoScan(svc->type);
++    }
++  }
++  else if (strcmp(Id,"WirbelScanService-StopScan-v1.0") == 0)
++  {
++    DoStop();
++    return true;
++  }
++  else if (strcmp(Id,"WirbelScanService-GetCountries-v1.0") == 0)
++  {
++    if (Data)
++    {
++      WirbelScanService_GetCountries_v1_0 SetCountry = (WirbelScanService_GetCountries_v1_0) Data;
++      for (int i=0; i < COUNTRY::country_count(); i++)
++      {
++        SetCountry(COUNTRY::country_list[i].id, COUNTRY::country_list[i].short_name, COUNTRY::country_list[i].full_name);
++      }
++      return true;
++    }
++  }
++  else if (strcmp(Id,"WirbelScanService-GetSatellites-v1.0") == 0)
++  {
++    if (Data)
++    {
++      WirbelScanService_GetSatellites_v1_0 SetSatellite = (WirbelScanService_GetSatellites_v1_0) Data;
++      for (int i=0; i < sat_count(); i++)
++      {
++        SetSatellite(sat_list[i].id, sat_list[i].short_name, sat_list[i].full_name);
++      }
++      return true;
++    }
++
++    return true;
++  }
++
+   return false;
+ }
+diff -NaurwB wirbelscan-0.0.5-pre11e/wirbelscanservice.h wirbelscan-patched/wirbelscanservice.h
+--- wirbelscan-0.0.5-pre11e/wirbelscanservice.h        1970-01-01 01:00:00.000000000 +0100
++++ wirbelscan-patched/wirbelscanservice.h     2010-04-23 02:03:56.000000000 +0200
+@@ -0,0 +1,57 @@
++/*
++ * wirbelscan.c: A plugin for the Video Disk Recorder
++ *
++ * See the README file for copyright information and how to reach the author.
++ *
++ * $Id$
++ */
++
++#ifndef __WIRBELSCAN_SERVICE_H
++#define __WIRBELSCAN_SERVICE_H
++
++typedef enum scantype
++{
++  DVB_TERR    = 0,
++  DVB_CABLE   = 1,
++  DVB_SAT     = 2,
++  PVRINPUT    = 3,
++  PVRINPUT_FM = 4,
++  DVB_ATSC    = 5,
++} scantype_t;
++
++typedef void (*WirbelScanService_GetCountries_v1_0)(int index, const char *isoName, const char *longName);
++typedef void (*WirbelScanService_GetSatellites_v1_0)(int index, const char *shortName, const char *longName);
++
++struct WirbelScanService_DoScan_v1_0
++{
++  scantype_t  type;
++
++  bool        scan_tv;
++  bool        scan_radio;
++  bool        scan_fta;
++  bool        scan_scrambled;
++  bool        scan_hd;
++
++  int         CountryIndex;
++
++  int         DVBC_Inversion;
++  int         DVBC_Symbolrate;
++  int         DVBC_QAM;
++
++  int         DVBT_Inversion;
++
++  int         SatIndex;
++
++  int         ATSC_Type;
++
++  void (*SetPercentage)(int percent);
++  void (*SetSignalStrength)(int strenght, bool locked);
++  void (*SetDeviceInfo)(const char *Info);
++  void (*SetTransponder)(const char *Info);
++  void (*NewChannel)(const char *Name, bool isRadio, bool isEncrypted, bool isHD);
++  void (*IsFinished)();
++  void (*SetStatus)(int status);
++};
++
++#endif //__WIRBELSCAN_SERVICE_H
++
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/project/VNSI Server.cbp b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/project/VNSI Server.cbp
new file mode 100644 (file)
index 0000000..bd059b1
--- /dev/null
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<CodeBlocks_project_file>
+       <FileVersion major="1" minor="6" />
+       <Project>
+               <Option title="VNSI Server" />
+               <Option pch_mode="2" />
+               <Option compiler="gcc" />
+               <Build>
+                       <Target title="Release">
+                               <Option output="bin\Release\VNSI Server" prefix_auto="1" extension_auto="1" />
+                               <Option object_output="obj\Release\" />
+                               <Option type="1" />
+                               <Option compiler="gcc" />
+                               <Compiler>
+                                       <Add option="-O2" />
+                               </Compiler>
+                               <Linker>
+                                       <Add option="-s" />
+                               </Linker>
+                       </Target>
+               </Build>
+               <Compiler>
+                       <Add option="-Wall" />
+               </Compiler>
+               <Unit filename="..\COPYING" />
+               <Unit filename="..\HISTORY" />
+               <Unit filename="..\Makefile" />
+               <Unit filename="..\README" />
+               <Unit filename="..\bitstream.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="..\bitstream.h" />
+               <Unit filename="..\cmdcontrol.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="..\cmdcontrol.h" />
+               <Unit filename="..\config.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="..\config.h" />
+               <Unit filename="..\connection.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="..\connection.h" />
+               <Unit filename="..\cxsocket.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="..\cxsocket.h" />
+               <Unit filename="..\demuxer.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="..\demuxer.h" />
+               <Unit filename="..\demuxer_AC3.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="..\demuxer_AC3.h" />
+               <Unit filename="..\demuxer_MPEGAudio.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="..\demuxer_MPEGAudio.h" />
+               <Unit filename="..\demuxer_MPEGVideo.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="..\demuxer_MPEGVideo.h" />
+               <Unit filename="..\demuxer_Subtitle.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="..\demuxer_Subtitle.h" />
+               <Unit filename="..\demuxer_Teletext.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="..\demuxer_Teletext.h" />
+               <Unit filename="..\demuxer_h264.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="..\demuxer_h264.h" />
+               <Unit filename="..\global.h" />
+               <Unit filename="..\receiver.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="..\receiver.h" />
+               <Unit filename="..\recplayer.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="..\recplayer.h" />
+               <Unit filename="..\requestpacket.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="..\requestpacket.h" />
+               <Unit filename="..\responsepacket.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="..\responsepacket.h" />
+               <Unit filename="..\server.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="..\server.h" />
+               <Unit filename="..\suspend.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="..\suspend.dat" />
+               <Unit filename="..\suspend.h" />
+               <Unit filename="..\tools.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Unit filename="..\tools.h" />
+               <Unit filename="..\vdrcommand.h" />
+               <Unit filename="..\vnsiserver.c">
+                       <Option compilerVar="CC" />
+               </Unit>
+               <Extensions>
+                       <code_completion />
+                       <envvars />
+                       <debugger />
+                       <lib_finder disable_auto="1" />
+               </Extensions>
+       </Project>
+</CodeBlocks_project_file>
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/project/VNSI Server.layout b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/project/VNSI Server.layout
new file mode 100644 (file)
index 0000000..debfd79
--- /dev/null
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<CodeBlocks_layout_file>
+       <ActiveTarget name="Release" />
+       <File name="..\bitstream.h" open="0" top="0" tabpos="0">
+               <Cursor position="877" topLine="3" />
+       </File>
+       <File name="..\cmdcontrol.c" open="1" top="0" tabpos="8">
+               <Cursor position="32866" topLine="769" />
+       </File>
+       <File name="..\cmdcontrol.h" open="1" top="0" tabpos="9">
+               <Cursor position="1762" topLine="39" />
+       </File>
+       <File name="..\config.h" open="1" top="0" tabpos="5">
+               <Cursor position="1237" topLine="0" />
+       </File>
+       <File name="..\connection.c" open="1" top="1" tabpos="7">
+               <Cursor position="5049" topLine="171" />
+       </File>
+       <File name="..\connection.h" open="1" top="0" tabpos="10">
+               <Cursor position="1909" topLine="33" />
+       </File>
+       <File name="..\demuxer.c" open="1" top="0" tabpos="1">
+               <Cursor position="5993" topLine="237" />
+       </File>
+       <File name="..\demuxer.h" open="1" top="0" tabpos="3">
+               <Cursor position="6229" topLine="180" />
+       </File>
+       <File name="..\demuxer_AC3.c" open="0" top="0" tabpos="0">
+               <Cursor position="1086" topLine="0" />
+       </File>
+       <File name="..\demuxer_AC3.h" open="0" top="0" tabpos="0">
+               <Cursor position="1228" topLine="0" />
+       </File>
+       <File name="..\demuxer_MPEGAudio.c" open="0" top="0" tabpos="6">
+               <Cursor position="1584" topLine="18" />
+       </File>
+       <File name="..\demuxer_MPEGAudio.h" open="0" top="0" tabpos="7">
+               <Cursor position="1325" topLine="26" />
+       </File>
+       <File name="..\demuxer_MPEGVideo.c" open="0" top="0" tabpos="8">
+               <Cursor position="6977" topLine="210" />
+       </File>
+       <File name="..\demuxer_MPEGVideo.h" open="0" top="0" tabpos="9">
+               <Cursor position="1190" topLine="24" />
+       </File>
+       <File name="..\demuxer_Subtitle.c" open="0" top="0" tabpos="0">
+               <Cursor position="1116" topLine="0" />
+       </File>
+       <File name="..\demuxer_Subtitle.h" open="0" top="0" tabpos="0">
+               <Cursor position="1263" topLine="0" />
+       </File>
+       <File name="..\demuxer_Teletext.c" open="0" top="0" tabpos="0">
+               <Cursor position="1116" topLine="0" />
+       </File>
+       <File name="..\demuxer_Teletext.h" open="0" top="0" tabpos="0">
+               <Cursor position="1265" topLine="0" />
+       </File>
+       <File name="..\demuxer_h264.c" open="0" top="0" tabpos="0">
+               <Cursor position="1092" topLine="0" />
+       </File>
+       <File name="..\demuxer_h264.h" open="0" top="0" tabpos="0">
+               <Cursor position="1235" topLine="0" />
+       </File>
+       <File name="..\receiver.c" open="1" top="0" tabpos="2">
+               <Cursor position="4605" topLine="178" />
+       </File>
+       <File name="..\receiver.h" open="1" top="0" tabpos="4">
+               <Cursor position="2780" topLine="42" />
+       </File>
+       <File name="..\recplayer.c" open="1" top="0" tabpos="11">
+               <Cursor position="3882" topLine="167" />
+       </File>
+       <File name="..\vdrcommand.h" open="1" top="0" tabpos="6">
+               <Cursor position="3061" topLine="66" />
+       </File>
+</CodeBlocks_layout_file>
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/receiver.c b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/receiver.c
new file mode 100644 (file)
index 0000000..28de2a4
--- /dev/null
@@ -0,0 +1,1279 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <stdlib.h>
+#include <sys/ioctl.h>
+
+#include <libsi/section.h>
+#include <libsi/descriptor.h>
+
+#include <vdr/remux.h>
+#include <vdr/channels.h>
+
+#include "config.h"
+#include "receiver.h"
+#include "cxsocket.h"
+#include "vdrcommand.h"
+#include "suspend.h"
+#include "tools.h"
+#include "responsepacket.h"
+
+#if VDRVERSNUM < 10713
+#ifndef PLUGINPARAMPATCHVERSNUM
+#error "You must apply the pluginparam patch for VDR!"
+#endif
+#endif
+
+// --- cLiveReceiver -------------------------------------------------
+
+class cLiveReceiver: public cReceiver
+{
+  friend class cLiveStreamer;
+
+private:
+  cLiveStreamer *m_Streamer;
+
+protected:
+  virtual void Activate(bool On);
+  virtual void Receive(uchar *Data, int Length);
+
+public:
+  cLiveReceiver(cLiveStreamer *Streamer, tChannelID ChannelID, int Priority, const int *Pids);
+  virtual ~cLiveReceiver();
+};
+
+cLiveReceiver::cLiveReceiver(cLiveStreamer *Streamer, tChannelID ChannelID, int Priority, const int *Pids)
+ : cReceiver(ChannelID, Priority, 0, Pids)
+ , m_Streamer(Streamer)
+{
+  LOGCONSOLE("Starting live receiver");
+}
+
+cLiveReceiver::~cLiveReceiver()
+{
+  LOGCONSOLE("Killing live receiver");
+}
+
+void cLiveReceiver::Receive(uchar *Data, int Length)
+{
+  int p = m_Streamer->Put(Data, Length);
+
+  if (p != Length)
+    m_Streamer->ReportOverflow(Length - p);
+}
+
+inline void cLiveReceiver::Activate(bool On)
+{
+  m_Streamer->Activate(On);
+}
+
+// --- cLivePatFilter ----------------------------------------------------
+
+class cLivePatFilter : public cFilter
+{
+private:
+  int             m_pmtPid;
+  int             m_pmtSid;
+  int             m_pmtVersion;
+  const cChannel *m_Channel;
+  cLiveStreamer  *m_Streamer;
+
+  int GetPid(SI::PMT::Stream& stream, eStreamType *type, char *langs, int *subtitlingType, int *compositionPageId, int *ancillaryPageId);
+  void GetLanguage(SI::PMT::Stream& stream, char *langs);
+  virtual void Process(u_short Pid, u_char Tid, const u_char *Data, int Length);
+
+public:
+  cLivePatFilter(cLiveStreamer *Streamer, const cChannel *Channel);
+};
+
+cLivePatFilter::cLivePatFilter(cLiveStreamer *Streamer, const cChannel *Channel)
+{
+  LOGCONSOLE("cStreamdevPatFilter(\"%s\")", Channel->Name());
+  m_Channel     = Channel;
+  m_Streamer    = Streamer;
+  m_pmtPid      = 0;
+  m_pmtSid      = 0;
+  m_pmtVersion  = -1;
+  Set(0x00, 0x00);  // PAT
+
+}
+
+static const char * const psStreamTypes[] = {
+        "UNKNOWN",
+        "ISO/IEC 11172 Video",
+        "ISO/IEC 13818-2 Video",
+        "ISO/IEC 11172 Audio",
+        "ISO/IEC 13818-3 Audio",
+        "ISO/IEC 13818-1 Privete sections",
+        "ISO/IEC 13818-1 Private PES data",
+        "ISO/IEC 13512 MHEG",
+        "ISO/IEC 13818-1 Annex A DSM CC",
+        "0x09",
+        "ISO/IEC 13818-6 Multiprotocol encapsulation",
+        "ISO/IEC 13818-6 DSM-CC U-N Messages",
+        "ISO/IEC 13818-6 Stream Descriptors",
+        "ISO/IEC 13818-6 Sections (any type, including private data)",
+        "ISO/IEC 13818-1 auxiliary",
+        "ISO/IEC 13818-7 Audio with ADTS transport sytax",
+        "ISO/IEC 14496-2 Visual (MPEG-4)",
+        "ISO/IEC 14496-3 Audio with LATM transport syntax",
+        "0x12", "0x13", "0x14", "0x15", "0x16", "0x17", "0x18", "0x19", "0x1a",
+        "ISO/IEC 14496-10 Video (MPEG-4 part 10/AVC, aka H.264)",
+        "",
+};
+
+void cLivePatFilter::GetLanguage(SI::PMT::Stream& stream, char *langs)
+{
+  SI::Descriptor *d;
+  for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); )
+  {
+    switch (d->getDescriptorTag())
+    {
+      case SI::ISO639LanguageDescriptorTag:
+      {
+        SI::ISO639LanguageDescriptor *ld = (SI::ISO639LanguageDescriptor *)d;
+        strn0cpy(langs, I18nNormalizeLanguageCode(ld->languageCode), MAXLANGCODE1);
+        break;
+      }
+      default: ;
+    }
+    delete d;
+  }
+}
+
+int cLivePatFilter::GetPid(SI::PMT::Stream& stream, eStreamType *type, char *langs, int *subtitlingType, int *compositionPageId, int *ancillaryPageId)
+{
+  SI::Descriptor *d;
+  *langs = 0;
+
+  if (!stream.getPid())
+    return 0;
+
+  switch (stream.getStreamType())
+  {
+    case 0x01: // ISO/IEC 11172 Video
+    case 0x02: // ISO/IEC 13818-2 Video
+      LOGCONSOLE("cStreamdevPatFilter PMT scanner adding PID %d (%s)\n", stream.getPid(), psStreamTypes[stream.getStreamType()]);
+      *type = stMPEG2VIDEO;
+      return stream.getPid();
+    case 0x03: // ISO/IEC 11172 Audio
+    case 0x04: // ISO/IEC 13818-3 Audio
+      *type   = stMPEG2AUDIO;
+      GetLanguage(stream, langs);
+      LOGCONSOLE("cStreamdevPatFilter PMT scanner adding PID %d (%s) (%s)\n", stream.getPid(), psStreamTypes[stream.getStreamType()], langs);
+      return stream.getPid();
+#if 1
+    case 0x07: // ISO/IEC 13512 MHEG
+    case 0x08: // ISO/IEC 13818-1 Annex A  DSM CC
+    case 0x0a: // ISO/IEC 13818-6 Multiprotocol encapsulation
+    case 0x0b: // ISO/IEC 13818-6 DSM-CC U-N Messages
+    case 0x0c: // ISO/IEC 13818-6 Stream Descriptors
+    case 0x0d: // ISO/IEC 13818-6 Sections (any type, including private data)
+    case 0x0e: // ISO/IEC 13818-1 auxiliary
+#endif
+    case 0x0f: // ISO/IEC 13818-7 Audio with ADTS transport syntax
+    case 0x10: // ISO/IEC 14496-2 Visual (MPEG-4)
+    case 0x11: // ISO/IEC 14496-3 Audio with LATM transport syntax
+      LOGCONSOLE("cStreamdevPatFilter PMT scanner: Not adding PID %d (%s) (skipped)\n", stream.getPid(), psStreamTypes[stream.getStreamType()]);
+      break;
+    case 0x1b: // ISO/IEC 14496-10 Video (MPEG-4 part 10/AVC, aka H.264)
+      LOGCONSOLE("cStreamdevPatFilter PMT scanner adding PID %d (%s)\n", stream.getPid(), psStreamTypes[stream.getStreamType()]);
+      *type = stH264;
+      return stream.getPid();
+    case 0x05: // ISO/IEC 13818-1 private sections
+    case 0x06: // ISO/IEC 13818-1 PES packets containing private data
+      for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); )
+      {
+        switch (d->getDescriptorTag())
+        {
+          case SI::AC3DescriptorTag:
+            LOGCONSOLE("cStreamdevPatFilter PMT scanner: adding PID %d (%s) %s (%s)\n", stream.getPid(), psStreamTypes[stream.getStreamType()], "AC3", langs);
+            *type = stAC3;
+            GetLanguage(stream, langs);
+            delete d;
+            return stream.getPid();
+          case SI::EnhancedAC3DescriptorTag:
+            LOGCONSOLE("cStreamdevPatFilter PMT scanner: adding PID %d (%s) %s (%s)\n", stream.getPid(), psStreamTypes[stream.getStreamType()], "EAC3", langs);
+            *type = stEAC3;
+            GetLanguage(stream, langs);
+            delete d;
+            return stream.getPid();
+          case SI::DTSDescriptorTag:
+            LOGCONSOLE("cStreamdevPatFilter PMT scanner: adding PID %d (%s) %s (%s)\n", stream.getPid(), psStreamTypes[stream.getStreamType()], "DTS", langs);
+            *type = stDTS;
+            GetLanguage(stream, langs);
+            delete d;
+            return stream.getPid();
+          case SI::AACDescriptorTag:
+            LOGCONSOLE("cStreamdevPatFilter PMT scanner: adding PID %d (%s) %s (%s)\n", stream.getPid(), psStreamTypes[stream.getStreamType()], "AAC", langs);
+            *type = stAAC;
+            GetLanguage(stream, langs);
+            delete d;
+            return stream.getPid();
+          case SI::TeletextDescriptorTag:
+            LOGCONSOLE("cStreamdevPatFilter PMT scanner: adding PID %d (%s) %s\n", stream.getPid(), psStreamTypes[stream.getStreamType()], "Teletext");
+            *type = stTELETEXT;
+            delete d;
+            return stream.getPid();
+          case SI::SubtitlingDescriptorTag:
+          {
+            *type               = stDVBSUB;
+            *langs              = 0;
+            *subtitlingType     = 0;
+            *compositionPageId  = 0;
+            *ancillaryPageId    = 0;
+            SI::SubtitlingDescriptor *sd = (SI::SubtitlingDescriptor *)d;
+            SI::SubtitlingDescriptor::Subtitling sub;
+            char *s = langs;
+            int n = 0;
+            for (SI::Loop::Iterator it; sd->subtitlingLoop.getNext(sub, it); )
+            {
+              if (sub.languageCode[0])
+              {
+                *subtitlingType     = sub.getSubtitlingType();
+                *compositionPageId  = sub.getCompositionPageId();
+                *ancillaryPageId    = sub.getAncillaryPageId();
+                if (n > 0)
+                  *s++ = '+';
+                strn0cpy(s, I18nNormalizeLanguageCode(sub.languageCode), MAXLANGCODE1);
+                s += strlen(s);
+                if (n++ > 1)
+                  break;
+              }
+            }
+            delete d;
+            LOGCONSOLE("cStreamdevPatFilter PMT scanner: adding PID %d (%s) %s\n", stream.getPid(), psStreamTypes[stream.getStreamType()], "DVBSUB");
+            return stream.getPid();
+          }
+          default:
+            LOGCONSOLE("cStreamdevPatFilter PMT scanner: NOT adding PID %d (%s) %s (%i)\n", stream.getPid(), psStreamTypes[stream.getStreamType()], "UNKNOWN", d->getDescriptorTag());
+            break;
+        }
+        delete d;
+      }
+      break;
+    default:
+      /* This following section handles all the cases where the audio track
+       * info is stored in PMT user info with stream id >= 0x80
+       * we check the registration format identifier to see if it
+       * holds "AC-3"
+       */
+      if (stream.getStreamType() >= 0x80)
+      {
+        bool found = false;
+        for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); )
+        {
+          switch (d->getDescriptorTag())
+          {
+            case SI::RegistrationDescriptorTag:
+            /* unfortunately libsi does not implement RegistrationDescriptor */
+            if (d->getLength() >= 4)
+            {
+              found = true;
+              SI::CharArray rawdata = d->getData();
+              if (/*rawdata[0] == 5 && rawdata[1] >= 4 && */
+                  rawdata[2] == 'A' && rawdata[3] == 'C' &&
+                  rawdata[4] == '-' && rawdata[5] == '3')
+              {
+                LOGCONSOLE("cStreamdevPatFilter PMT scanner: Adding pid %d (type 0x%x) RegDesc len %d (%c%c%c%c)\n",
+                            stream.getPid(), stream.getStreamType(), d->getLength(), rawdata[2], rawdata[3], rawdata[4], rawdata[5]);
+                *type = stAC3;
+                delete d;
+                return stream.getPid();
+              }
+            }
+            break;
+            default:
+            break;
+          }
+          delete d;
+        }
+        if (!found)
+        {
+          LOGCONSOLE("Adding pid %d (type 0x%x) RegDesc not found -> assume AC-3\n", stream.getPid(), stream.getStreamType());
+          *type = stAC3;
+          return stream.getPid();
+        }
+      }
+      LOGCONSOLE("cStreamdevPatFilter PMT scanner: NOT adding PID %d (%s) %s\n", stream.getPid(), psStreamTypes[stream.getStreamType()<0x1c?stream.getStreamType():0], "UNKNOWN");
+      break;
+  }
+  *type = stNone;
+  return 0;
+}
+
+void cLivePatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length)
+{
+  if (Pid == 0x00)
+  {
+    if (Tid == 0x00)
+    {
+      SI::PAT pat(Data, false);
+      if (!pat.CheckCRCAndParse())
+        return;
+      SI::PAT::Association assoc;
+      for (SI::Loop::Iterator it; pat.associationLoop.getNext(assoc, it); )
+      {
+        if (!assoc.isNITPid())
+        {
+          const cChannel *Channel =  Channels.GetByServiceID(Source(), Transponder(), assoc.getServiceId());
+          if (Channel && (Channel == m_Channel))
+          {
+            int prevPmtPid = m_pmtPid;
+            if (0 != (m_pmtPid = assoc.getPid()))
+            {
+              m_pmtSid = assoc.getServiceId();
+              if (m_pmtPid != prevPmtPid)
+              {
+                Add(m_pmtPid, 0x02);
+                m_pmtVersion = -1;
+              }
+              return;
+            }
+          }
+        }
+      }
+    }
+  }
+  else if (Pid == m_pmtPid && Tid == SI::TableIdPMT && Source() && Transponder())
+  {
+    SI::PMT pmt(Data, false);
+    if (!pmt.CheckCRCAndParse())
+      return;
+    if (pmt.getServiceId() != m_pmtSid)
+      return; // skip broken PMT records
+    if (m_pmtVersion != -1)
+    {
+      if (m_pmtVersion != pmt.getVersionNumber())
+      {
+//        printf("cStreamdevPatFilter: PMT version changed, detaching all pids\n");
+        cFilter::Del(m_pmtPid, 0x02);
+        m_pmtPid = 0; // this triggers PAT scan
+      }
+      return;
+    }
+    m_pmtVersion = pmt.getVersionNumber();
+
+    SI::PMT::Stream stream;
+    int         pids[MAXRECEIVEPIDS + 1];
+    eStreamType types[MAXRECEIVEPIDS + 1];
+    char        langs[MAXRECEIVEPIDS + 1][MAXLANGCODE2];
+    int         subtitlingType[MAXRECEIVEPIDS + 1];
+    int         compositionPageId[MAXRECEIVEPIDS + 1];
+    int         ancillaryPageId[MAXRECEIVEPIDS + 1];
+    int         streams = 0;
+    for (SI::Loop::Iterator it; pmt.streamLoop.getNext(stream, it); )
+    {
+      eStreamType type;
+      int pid = GetPid(stream, &type, langs[streams], &subtitlingType[streams], &compositionPageId[streams], &ancillaryPageId[streams]);
+      if (0 != pid && streams < MAXRECEIVEPIDS)
+      {
+        pids[streams]   = pid;
+        types[streams]  = type;
+        streams++;
+      }
+    }
+    pids[streams] = 0;
+
+    int newstreams = 0;
+    for (int i = 0; i < streams; i++)
+    {
+      if (m_Streamer->HaveStreamDemuxer(pids[i], types[i]) == -1)
+        newstreams++;
+    }
+
+    if (newstreams > 0)
+    {
+      if (m_Streamer->m_Receiver)
+      {
+        LOGCONSOLE("Detaching Live Receiver");
+        m_Streamer->m_Device->Detach(m_Streamer->m_Receiver);
+        DELETENULL(m_Streamer->m_Receiver);
+      }
+
+      for (int idx = 0; idx < MAXRECEIVEPIDS; ++idx)
+      {
+        if (m_Streamer->m_Streams[idx])
+        {
+          DELETENULL(m_Streamer->m_Streams[idx]);
+          m_Streamer->m_Pids[idx] = 0;
+        }
+      }
+      m_Streamer->m_NumStreams  = 0;
+      m_Streamer->m_streamReady = true;
+
+      for (int i = 0; i < streams; i++)
+      {
+        switch (types[i])
+        {
+          case stMPEG2AUDIO:
+          {
+            m_Streamer->m_Streams[m_Streamer->m_NumStreams] = new cTSDemuxer(m_Streamer, m_Streamer->m_NumStreams, stMPEG2AUDIO, pids[i]);
+            m_Streamer->m_Pids[m_Streamer->m_NumStreams] = pids[i];
+            m_Streamer->m_NumStreams++;
+            break;
+          }
+          case stMPEG2VIDEO:
+          {
+            m_Streamer->m_Streams[m_Streamer->m_NumStreams] = new cTSDemuxer(m_Streamer, m_Streamer->m_NumStreams, stMPEG2VIDEO, pids[i]);
+            m_Streamer->m_Pids[m_Streamer->m_NumStreams] = pids[i];
+            m_Streamer->m_NumStreams++;
+            break;
+          }
+          case stH264:
+          {
+            m_Streamer->m_Streams[m_Streamer->m_NumStreams] = new cTSDemuxer(m_Streamer, m_Streamer->m_NumStreams, stH264, pids[i]);
+            m_Streamer->m_Pids[m_Streamer->m_NumStreams] = pids[i];
+            m_Streamer->m_NumStreams++;
+            break;
+          }
+          case stAC3:
+          {
+            m_Streamer->m_Streams[m_Streamer->m_NumStreams] = new cTSDemuxer(m_Streamer, m_Streamer->m_NumStreams, stAC3, pids[i]);
+            m_Streamer->m_Pids[m_Streamer->m_NumStreams] = pids[i];
+            m_Streamer->m_NumStreams++;
+            break;
+          }
+          case stEAC3:
+          {
+            m_Streamer->m_Streams[m_Streamer->m_NumStreams] = new cTSDemuxer(m_Streamer, m_Streamer->m_NumStreams, stEAC3, pids[i]);
+            m_Streamer->m_Pids[m_Streamer->m_NumStreams] = pids[i];
+            m_Streamer->m_NumStreams++;
+            break;
+          }
+          case stDTS:
+          {
+            m_Streamer->m_Streams[m_Streamer->m_NumStreams] = new cTSDemuxer(m_Streamer, m_Streamer->m_NumStreams, stDTS, pids[i]);
+            m_Streamer->m_Pids[m_Streamer->m_NumStreams] = pids[i];
+            m_Streamer->m_NumStreams++;
+            break;
+          }
+          case stAAC:
+          {
+            m_Streamer->m_Streams[m_Streamer->m_NumStreams] = new cTSDemuxer(m_Streamer, m_Streamer->m_NumStreams, stAAC, pids[i]);
+            m_Streamer->m_Pids[m_Streamer->m_NumStreams] = pids[i];
+            m_Streamer->m_NumStreams++;
+            break;
+          }
+          case stDVBSUB:
+          {
+            m_Streamer->m_Streams[m_Streamer->m_NumStreams] = new cTSDemuxer(m_Streamer, m_Streamer->m_NumStreams, stDVBSUB, pids[i]);
+            m_Streamer->m_Streams[m_Streamer->m_NumStreams]->SetLanguage(langs[i]);
+            m_Streamer->m_Streams[m_Streamer->m_NumStreams]->SetSubtitlingDescriptor(subtitlingType[i], compositionPageId[i], ancillaryPageId[i]);
+            m_Streamer->m_Pids[m_Streamer->m_NumStreams] = pids[i];
+            m_Streamer->m_NumStreams++;
+            break;
+          }
+          case stTELETEXT:
+          {
+            m_Streamer->m_Streams[m_Streamer->m_NumStreams] = new cTSDemuxer(m_Streamer, m_Streamer->m_NumStreams, stTELETEXT, pids[i]);
+            m_Streamer->m_Pids[m_Streamer->m_NumStreams] = pids[i];
+            m_Streamer->m_NumStreams++;
+            break;
+          }
+        }
+      }
+
+      m_Streamer->m_Receiver  = new cLiveReceiver(m_Streamer, m_Channel->GetChannelID(), m_Streamer->m_Priority, m_Streamer->m_Pids);
+      m_Streamer->m_Device->AttachReceiver(m_Streamer->m_Receiver);
+      isyslog("VNSI: Currently unknown new streams found, receiver and demuxers reinited\n");
+    }
+  }
+}
+
+// --- cLiveStreamer -------------------------------------------------
+
+cLiveStreamer::cLiveStreamer()
+ : cThread("cLiveStreamer stream processor")
+ , cRingBufferLinear(MEGABYTE(3), TS_SIZE, true)
+{
+  m_Channel         = NULL;
+  m_Priority        = NULL;
+  m_Socket          = NULL;
+  m_Device          = NULL;
+  m_Receiver        = NULL;
+  m_PatFilter       = NULL;
+  m_Frontend        = -1;
+  m_NumStreams      = 0;
+  m_streamReady     = false;
+  m_IsAudioOnly     = false;
+  m_IsMPEGPS        = false;
+  m_streamChangeSendet = false;
+  m_lastInfoSendet  = time(NULL);
+  memset(&m_FrontendInfo, 0, sizeof(m_FrontendInfo));
+  for (int idx = 0; idx < MAXRECEIVEPIDS; ++idx)
+  {
+    m_Streams[idx] = NULL;
+    m_Pids[idx]    = 0;
+  }
+  
+  SetTimeouts(0, 500);
+}
+
+cLiveStreamer::~cLiveStreamer()
+{
+  LOGCONSOLE("Started to delete live streamer");
+
+  Cancel(-1);
+
+  if (m_Device)
+  {
+    if (m_Receiver)
+    {
+      LOGCONSOLE("Detaching Live Receiver");
+      m_Device->Detach(m_Receiver);
+    }
+    else
+    {
+      LOGCONSOLE("No live receiver present");
+    }
+
+    if (m_PatFilter)
+    {
+      LOGCONSOLE("Detaching Live Filter");
+      m_Device->Detach(m_PatFilter);
+    }
+    else
+    {
+      LOGCONSOLE("No live filter present");
+    }
+
+    for (int idx = 0; idx < MAXRECEIVEPIDS; ++idx)
+    {
+      if (m_Streams[idx])
+      {
+        LOGCONSOLE("Deleting stream demuxer %i for pid=%i and type=%i", m_Streams[idx]->GetStreamID(), m_Streams[idx]->GetPID(), m_Streams[idx]->Type());
+        DELETENULL(m_Streams[idx]);
+        m_Pids[idx] = 0;
+      }
+    }
+
+    if (m_Receiver)
+    {
+      LOGCONSOLE("Deleting Live Receiver");
+      DELETENULL(m_Receiver);
+    }
+
+    if (m_PatFilter)
+    {
+      LOGCONSOLE("Deleting Live Filter");
+      DELETENULL(m_PatFilter);
+    }
+  }
+  if (m_Frontend >= 0)
+  {
+    close(m_Frontend);
+    m_Frontend = -1;
+  }
+
+  LOGCONSOLE("Finished to delete live streamer");
+}
+
+void cLiveStreamer::Action(void)
+{
+  int signalInfoCnt     = 90;
+  time_t last_data      = time(NULL);
+  int size              = 0;
+  int used              = 0;
+  unsigned char *buf    = NULL;
+
+  while (Running())
+  {
+    size = 0;
+    used = 0;
+    buf = Get(size);
+
+    if (!m_Receiver->IsAttached())
+    {
+      isyslog("VNSI: returning from streamer thread, receiver is no more attached");
+      break;
+    }
+
+    // no data
+    if (buf == NULL)
+    {
+      if(time(NULL) - last_data > 10*1000) {
+        isyslog("VNSI: returning from streamer thread, timout on reading data");
+        break;
+      }
+      continue;
+    }
+
+    if(size <= TS_SIZE) {
+      continue;
+    }
+
+    /* Make sure we are looking at a TS packet */
+    while (size > TS_SIZE)
+    {
+      if (buf[0] == TS_SYNC_BYTE && buf[TS_SIZE] == TS_SYNC_BYTE)
+        break;
+      used++;
+      buf++;
+      size--;
+    }
+
+    while (size >= TS_SIZE)
+    {
+      if(!Running())
+      {
+        break;
+      }
+
+      unsigned int ts_pid = TsPid(buf);
+      cTSDemuxer *demuxer = FindStreamDemuxer(ts_pid);
+      if (demuxer)
+      {
+        demuxer->ProcessTSPacket(buf);
+       last_data = time(NULL);
+      }
+
+      buf += TS_SIZE;
+      size -= TS_SIZE;
+      used += TS_SIZE;
+    }
+    Del(used);
+
+    /* Additional Information and NO_SIGNAL timers */
+    signalInfoCnt++;
+
+    if (time(NULL) - m_lastInfoSendet > 10)
+    {
+      m_lastInfoSendet = time(NULL);
+      sendSignalInfo();
+    }
+    else if (signalInfoCnt >= 100)
+    {
+      /* Send stream information every 100 packets expect the first one is sendet
+         after 10 packets */
+      sendStreamInfo();
+      signalInfoCnt = 0;
+    }
+  }
+}
+
+bool cLiveStreamer::StreamChannel(const cChannel *channel, int priority, cxSocket *Socket, cResponsePacket *resp)
+{
+  if (channel == NULL)
+  {
+    esyslog("VNSI-Error: Starting streaming of channel without valid channel");
+    return false;
+  }
+
+  m_Channel   = channel;
+  m_Priority  = priority;
+  m_Socket    = Socket;
+  m_Device    = GetDevice(channel, m_Priority);
+  if (m_Device != NULL)
+  {
+    dsyslog("VNSI: Successfully found following device: %p (%d) for receiving", m_Device, m_Device ? m_Device->CardIndex() + 1 : 0);
+
+    if (m_Device->SwitchChannel(m_Channel, false))
+    {
+      if (m_Channel->Vpid())
+      {
+#if APIVERSNUM >= 10701
+        if (m_Channel->Vtype() == 0x1B)
+          m_Streams[m_NumStreams] = new cTSDemuxer(this, m_NumStreams, stH264, m_Channel->Vpid());
+        else
+#endif
+          m_Streams[m_NumStreams] = new cTSDemuxer(this, m_NumStreams, stMPEG2VIDEO, m_Channel->Vpid());
+
+        m_Pids[m_NumStreams] = m_Channel->Vpid();
+        m_NumStreams++;
+      }
+      else
+      {
+        /* m_streamReady is set by the Video demuxers, to have always valid stream informations
+         * like height and width. But if no Video PID is present like for radio channels
+         * VNSI will deadlock
+         */
+        m_streamReady = true;
+        m_IsAudioOnly = true;
+      }
+
+      const int *APids = m_Channel->Apids();
+      for ( ; *APids && m_NumStreams < MAXRECEIVEPIDS; APids++)
+      {
+        int index = 0;
+        if (!FindStreamDemuxer(*APids))
+        {
+          m_Pids[m_NumStreams]    = *APids;
+          m_Streams[m_NumStreams] = new cTSDemuxer(this, m_NumStreams, stMPEG2AUDIO, *APids);
+          m_Streams[m_NumStreams]->SetLanguage(m_Channel->Alang(index));
+          m_NumStreams++;
+        }
+        index++;
+      }
+
+      const int *DPids = m_Channel->Dpids();
+      for ( ; *DPids && m_NumStreams < MAXRECEIVEPIDS; DPids++)
+      {
+        int index = 0;
+        if (!FindStreamDemuxer(*DPids))
+        {
+          m_Pids[m_NumStreams]    = *DPids;
+          m_Streams[m_NumStreams] = new cTSDemuxer(this, m_NumStreams, stAC3, *DPids);
+          m_Streams[m_NumStreams]->SetLanguage(m_Channel->Dlang(index));
+          m_NumStreams++;
+        }
+        index++;
+      }
+
+      const int *SPids = m_Channel->Spids();
+      if (SPids)
+      {
+        int index = 0;
+        for ( ; *SPids && m_NumStreams < MAXRECEIVEPIDS; SPids++)
+        {
+          if (!FindStreamDemuxer(*SPids))
+          {
+            m_Pids[m_NumStreams]    = *SPids;
+            m_Streams[m_NumStreams] = new cTSDemuxer(this, m_NumStreams, stDVBSUB, *SPids);
+            m_Streams[m_NumStreams]->SetLanguage(m_Channel->Slang(index));
+#if APIVERSNUM >= 10709
+            m_Streams[m_NumStreams]->SetSubtitlingDescriptor(m_Channel->SubtitlingType(index),
+                                                             m_Channel->CompositionPageId(index),
+                                                             m_Channel->AncillaryPageId(index));
+#endif
+            m_NumStreams++;
+          }
+          index++;
+        }
+      }
+
+      if (m_Channel->Tpid())
+      {
+        m_Streams[m_NumStreams] = new cTSDemuxer(this, m_NumStreams, stTELETEXT, m_Channel->Tpid());
+        m_Pids[m_NumStreams]    = m_Channel->Tpid();
+        m_NumStreams++;
+      }
+
+      m_Streams[m_NumStreams] = NULL;
+      m_Pids[m_NumStreams]    = 0;
+
+      /* Send the OK response here, that it is before the Stream end message */
+      resp->add_U32(VDR_RET_OK);
+      resp->finalise();
+      m_Socket->write(resp->getPtr(), resp->getLen());
+
+#if VDRVERSNUM < 10713
+      if (m_Channel && m_Channel->IsPlug()) m_IsMPEGPS = true;
+#else
+      // TODO: rework cPvrSourceParams for use with older vdr
+      if (m_Channel && ((m_Channel->Source() >> 24) == 'V')) m_IsMPEGPS = true;
+#endif
+
+      if (m_NumStreams > 0 && m_Socket)
+      {
+        dsyslog("VNSI: Creating new live Receiver");
+        m_Receiver  = new cLiveReceiver(this, m_Channel->GetChannelID(), m_Priority, m_Pids);
+        m_PatFilter = new cLivePatFilter(this, m_Channel);
+        m_Device->AttachReceiver(m_Receiver);
+        m_Device->AttachFilter(m_PatFilter);
+      }
+
+      isyslog("VNSI: Successfully switched to channel %i - %s", m_Channel->Number(), m_Channel->Name());
+      return true;
+    }
+    else
+    {
+      dsyslog("VNSI: Can't switch to channel %i - %s", m_Channel->Number(), m_Channel->Name());
+    }
+  }
+  else
+  {
+    esyslog("VNSI-Error: Can't get device for channel %i - %s", m_Channel->Number(), m_Channel->Name());
+  }
+  return false;
+}
+
+cTSDemuxer *cLiveStreamer::FindStreamDemuxer(int Pid)
+{
+  int idx;
+  for (idx = 0; idx < m_NumStreams; ++idx)
+    if (m_Streams[idx] && m_Streams[idx]->GetPID() == Pid)
+      return m_Streams[idx];
+  return NULL;
+}
+
+int cLiveStreamer::HaveStreamDemuxer(int Pid, eStreamType streamType)
+{
+  int idx;
+  for (idx = 0; idx < m_NumStreams; ++idx)
+    if (m_Streams[idx] && (Pid == 0 || m_Streams[idx]->GetPID() == Pid) && m_Streams[idx]->Type() == streamType)
+      return idx;
+  return -1;
+}
+
+inline void cLiveStreamer::Activate(bool On)
+{
+  if (On)
+  {
+    LOGCONSOLE("VDR active, sending stream start message");
+    m_streamChangeSendet = false;
+    Start();
+  }
+  else
+  {
+    LOGCONSOLE("VDR inactive, sending stream end message");
+    Cancel(5);
+  }
+}
+
+void cLiveStreamer::Attach(void)
+{
+  LOGCONSOLE("cLiveStreamer::Attach()");
+  if (m_Device)
+  {
+    if (m_Receiver)
+    {
+      m_Device->Detach(m_Receiver);
+      m_Device->AttachReceiver(m_Receiver);
+    }
+  }
+}
+
+void cLiveStreamer::Detach(void)
+{
+  LOGCONSOLE("cLiveStreamer::Detach()");
+  if (m_Device)
+  {
+    if (m_Receiver)
+      m_Device->Detach(m_Receiver);
+  }
+}
+
+cDevice *cLiveStreamer::GetDevice(const cChannel *Channel, int Priority)
+{
+  cDevice *device = NULL;
+
+  LOGCONSOLE("+ Statistics:");
+  LOGCONSOLE("+ Current Channel: %d", cDevice::CurrentChannel());
+  LOGCONSOLE("+ Current Device: %d", cDevice::ActualDevice()->CardIndex());
+  LOGCONSOLE("+ Transfer Mode: %s", cDevice::ActualDevice() == cDevice::PrimaryDevice() ? "false" : "true");
+  LOGCONSOLE("+ Replaying: %s", cDevice::PrimaryDevice()->Replaying() ? "true" : "false");
+  LOGCONSOLE(" * GetDevice(const cChannel*, int)");
+  LOGCONSOLE(" * -------------------------------");
+
+  device = cDevice::GetDevice(Channel, Priority, false);
+
+  LOGCONSOLE(" * Found following device: %p (%d)", device, device ? device->CardIndex() + 1 : 0);
+
+  return device;
+ /*
+#if CONSOLEDEBUG
+  if (device == cDevice::ActualDevice())
+  {
+    LOGCONSOLE(" * is actual device");
+  }
+  if (!cSuspendCtl::IsActive() && VNSIServerConfig.SuspendMode != smAlways)
+  {
+    LOGCONSOLE(" * NOT suspended");
+  }
+#endif
+
+  if (!device || (device == cDevice::ActualDevice()
+                  && !cSuspendCtl::IsActive()
+                  && VNSIServerConfig.SuspendMode != smAlways))
+  {
+    // mustn't switch actual device
+    // maybe a device would be free if THIS connection did turn off its streams?
+    LOGCONSOLE(" * trying again...");
+    const cChannel *current = Channels.GetByNumber(cDevice::CurrentChannel());
+    isyslog("VNSI: Detaching current receiver");
+    Detach();
+    device = cDevice::GetDevice(Channel, Priority, false);
+    Attach();
+    LOGCONSOLE(" * Found following device: %p (%d)", device, device ? device->CardIndex() + 1 : 0);
+
+#if CONSOLEDEBUG
+    if (device == cDevice::ActualDevice())
+    {
+      LOGCONSOLE(" * is actual device");
+    }
+    if (!cSuspendCtl::IsActive() && VNSIServerConfig.SuspendMode != smAlways)
+    {
+      LOGCONSOLE(" * NOT suspended");
+    }
+    if (current && !TRANSPONDER(Channel, current))
+    {
+      LOGCONSOLE(" * NOT same transponder");
+    }
+#endif
+
+    if (device && (device == cDevice::ActualDevice()
+                    && !cSuspendCtl::IsActive()
+                    && VNSIServerConfig.SuspendMode != smAlways
+                    && current != NULL
+                    && !TRANSPONDER(Channel, current)))
+    {
+      // now we would have to switch away live tv...let's see if live tv
+      // can be handled by another device
+      cDevice *newdev = NULL;
+      for (int i = 0; i < cDevice::NumDevices(); ++i)
+      {
+        cDevice *dev = cDevice::GetDevice(i);
+        if (dev->ProvidesChannel(current, 0) && dev != device)
+        {
+          newdev = dev;
+          break;
+        }
+      }
+      LOGCONSOLE(" * Found device for live tv: %p (%d)", newdev, newdev ? newdev->CardIndex() + 1 : 0);
+      if (newdev == NULL || newdev == device)
+        // no suitable device to continue live TV, giving up...
+        device = NULL;
+      else
+        newdev->SwitchChannel(current, true);
+    }
+  }
+
+  return device;*/
+}
+
+void cLiveStreamer::sendStreamPacket(sStreamPacket *pkt)
+{
+  if (!m_streamChangeSendet)
+  {
+    sendStreamChange();
+    m_streamChangeSendet = true;
+  }
+
+  if (pkt)
+  {
+#if 0
+    LOGCONSOLE("sendet: %d %d %10lu %10lu %10d %10d", pkt->id, pkt->frametype, pkt->dts, pkt->pts, pkt->duration, pkt->size);
+#endif
+    uint32_t bufferLength = sizeof(uint32_t) * 5 + sizeof(int64_t) * 2;
+    uint8_t buffer[bufferLength];
+    *(uint32_t*)&buffer[0]  = htonl(CHANNEL_STREAM);        // stream channel
+    *(uint32_t*)&buffer[4]  = htonl(VDR_STREAM_MUXPKT);     // Stream packet operation code
+    *(uint32_t*)&buffer[8]  = htonl(pkt->id);               // Stream ID
+    *(uint32_t*)&buffer[12] = htonl(pkt->duration);         // Duration
+    *(int64_t*) &buffer[16] = htonll(pkt->pts);             // DTS
+    *(int64_t*) &buffer[24] = htonll(pkt->dts);             // PTS
+    *(uint32_t*)&buffer[32] = htonl(pkt->size);             // Data length
+    m_Socket->write(&buffer, bufferLength);
+    m_Socket->write(pkt->data, pkt->size);
+  }
+}
+
+void cLiveStreamer::sendStreamChange()
+{
+  cResponsePacket *resp = new cResponsePacket();
+  if (!resp->initStream(VDR_STREAM_CHANGE, 0, 0, 0, 0))
+  {
+    esyslog("VNSI-Error: stream response packet init fail");
+    delete resp;
+    return;
+  }
+
+  for (int idx = 0; idx < m_NumStreams; ++idx)
+  {
+    if (m_Streams[idx])
+    {
+      resp->add_U32(m_Streams[idx]->GetStreamID());
+      if (m_Streams[idx]->Type() == stMPEG2AUDIO)
+      {
+        resp->add_String("MPEG2AUDIO");
+        resp->add_String(m_Streams[idx]->GetLanguage());
+      }
+      else if (m_Streams[idx]->Type() == stMPEG2VIDEO)
+      {
+        resp->add_String("MPEG2VIDEO");
+        resp->add_U32(m_Streams[idx]->GetFpsScale());
+        resp->add_U32(m_Streams[idx]->GetFpsRate());
+        resp->add_U32(m_Streams[idx]->GetHeight());
+        resp->add_U32(m_Streams[idx]->GetWidth());
+        resp->add_double(m_Streams[idx]->GetAspect());
+      }
+      else if (m_Streams[idx]->Type() == stAC3)
+      {
+        resp->add_String("AC3");
+        resp->add_String(m_Streams[idx]->GetLanguage());
+      }
+      else if (m_Streams[idx]->Type() == stH264)
+      {
+        resp->add_String("H264");
+        resp->add_U32(m_Streams[idx]->GetFpsScale());
+        resp->add_U32(m_Streams[idx]->GetFpsRate());
+        resp->add_U32(m_Streams[idx]->GetHeight());
+        resp->add_U32(m_Streams[idx]->GetWidth());
+        resp->add_double(m_Streams[idx]->GetAspect());
+      }
+      else if (m_Streams[idx]->Type() == stDVBSUB)
+      {
+        resp->add_String("DVBSUB");
+        resp->add_String(m_Streams[idx]->GetLanguage());
+        resp->add_U32(m_Streams[idx]->CompositionPageId());
+        resp->add_U32(m_Streams[idx]->AncillaryPageId());
+      }
+      else if (m_Streams[idx]->Type() == stTELETEXT)
+        resp->add_String("TELETEXT");
+      else if (m_Streams[idx]->Type() == stAAC)
+      {
+        resp->add_String("AAC");
+        resp->add_String(m_Streams[idx]->GetLanguage());
+      }
+      else if (m_Streams[idx]->Type() == stEAC3)
+      {
+        resp->add_String("EAC3");
+        resp->add_String(m_Streams[idx]->GetLanguage());
+      }
+      else if (m_Streams[idx]->Type() == stDTS)
+      {
+        resp->add_String("DTS");
+        resp->add_String(m_Streams[idx]->GetLanguage());
+      }
+    }
+  }
+
+  resp->finaliseStream();
+  m_Socket->write(resp->getPtr(), resp->getLen());
+  delete resp;
+}
+
+#define MINSIGNALSTRENGTH       16383
+void cLiveStreamer::sendSignalInfo()
+{
+  /* If no frontend is found m_Frontend is set to -2, in this case
+     return a empty signalinfo package */
+  if (m_Frontend == -2)
+  {
+    cResponsePacket *resp = new cResponsePacket();
+    if (!resp->initStream(VDR_STREAM_SIGNALINFO, 0, 0, 0, 0))
+    {
+      esyslog("VNSI-Error: stream response packet init fail");
+      delete resp;
+      return;
+    }
+
+    resp->add_String(*cString::sprintf("Unknown"));
+    resp->add_String(*cString::sprintf("Unknown"));
+    resp->add_U32(0);
+    resp->add_U32(0);
+    resp->add_U32(0);
+    resp->add_U32(0);
+
+    resp->finaliseStream();
+    m_Socket->write(resp->getPtr(), resp->getLen());
+    delete resp;
+    return;
+  }
+
+#if VDRVERSNUM < 10713
+  if (m_Channel && m_Channel->IsPlug())
+#else
+  // TODO: rework cPvrSourceParams for use with older vdr
+  if (m_Channel && ((m_Channel->Source() >> 24) == 'V'))
+#endif
+  {
+    struct v4l2_tuner tuner;
+    if (m_Frontend < 0)
+    {
+      for (int i = 0; i < 8; i++)
+      {
+        m_DeviceString = cString::sprintf("/dev/video%d", i);
+        m_Frontend = open(m_DeviceString, O_RDONLY | O_NONBLOCK);
+        if (m_Frontend >= 0)
+        {
+          if (ioctl(m_Frontend, VIDIOC_QUERYCAP, &m_vcap) < 0)
+          {
+            esyslog("VNSI-Error: cannot read analog frontend info.");
+            close(m_Frontend);
+            m_Frontend = -1;
+            memset(&m_vcap, 0, sizeof(m_vcap));
+            continue;
+          }
+          break;
+        }
+      }
+      if (m_Frontend < 0)
+        m_Frontend = -2;
+    }
+
+    if (m_Frontend >= 0)
+    {
+      cResponsePacket *resp = new cResponsePacket();
+      if (!resp->initStream(VDR_STREAM_SIGNALINFO, 0, 0, 0, 0))
+      {
+        esyslog("VNSI-Error: stream response packet init fail");
+        delete resp;
+        return;
+      }
+/*
+      memset(&tuner, 0, sizeof(tuner));
+      tuner.index = 0;
+      tuner.type  = V4L2_TUNER_ANALOG_TV;
+
+      if (ioctl(m_Frontend, VIDIOC_G_TUNER, &tuner) == 0)
+      {
+        int timeout = 1000;
+        while (timeout > 0)
+        {
+          cCondWait::SleepMs(10);
+          timeout -= 10;
+          ioctl(m_Frontend, VIDIOC_G_TUNER, &tuner);
+          if (tuner.signal > MINSIGNALSTRENGTH)
+          {
+            break;
+          }
+        }
+      }
+*/
+      resp->add_String(*cString::sprintf("Analog #%s - %s (%s)", *m_DeviceString, (char *) m_vcap.card, m_vcap.driver));
+//      resp->add_String(*cString::sprintf("%s", (tuner.signal > MINSIGNALSTRENGTH) ? "LOCKED" : "-"));
+      resp->add_String("");
+      resp->add_U32(0);
+      resp->add_U32(0);
+//      resp->add_U32(tuner.signal);
+      resp->add_U32(0);
+      resp->add_U32(0);
+
+      resp->finaliseStream();
+      m_Socket->write(resp->getPtr(), resp->getLen());
+      delete resp;
+    }
+  }
+  else
+  {
+    if (m_Frontend < 0)
+    {
+      m_DeviceString = cString::sprintf(FRONTEND_DEVICE, m_Device->CardIndex(), 0);
+      m_Frontend = open(m_DeviceString, O_RDONLY | O_NONBLOCK);
+      if (m_Frontend >= 0)
+      {
+        if (ioctl(m_Frontend, FE_GET_INFO, &m_FrontendInfo) < 0)
+        {
+          esyslog("VNSI-Error: cannot read frontend info.");
+          close(m_Frontend);
+          m_Frontend = -2;
+          memset(&m_FrontendInfo, 0, sizeof(m_FrontendInfo));
+          return;
+        }
+      }
+    }
+
+    if (m_Frontend >= 0)
+    {
+      cResponsePacket *resp = new cResponsePacket();
+      if (!resp->initStream(VDR_STREAM_SIGNALINFO, 0, 0, 0, 0))
+      {
+        esyslog("VNSI-Error: stream response packet init fail");
+        delete resp;
+        return;
+      }
+
+      fe_status_t status;
+      uint16_t fe_snr;
+      uint16_t fe_signal;
+      uint32_t fe_ber;
+      uint32_t fe_unc;
+
+      memset(&status, 0, sizeof(status));
+      ioctl(m_Frontend, FE_READ_STATUS, &status);
+
+      if (ioctl(m_Frontend, FE_READ_SIGNAL_STRENGTH, &fe_signal) == -1)
+        fe_signal = -2;
+      if (ioctl(m_Frontend, FE_READ_SNR, &fe_snr) == -1)
+        fe_snr = -2;
+      if (ioctl(m_Frontend, FE_READ_BER, &fe_ber) == -1)
+        fe_ber = -2;
+      if (ioctl(m_Frontend, FE_READ_UNCORRECTED_BLOCKS, &fe_unc) == -1)
+        fe_unc = -2;
+
+      switch (m_Channel->Source() & cSource::st_Mask)
+      {
+        case cSource::stSat:
+          resp->add_String(*cString::sprintf("DVB-S%s #%d - %s", (m_FrontendInfo.caps & 0x10000000) ? "2" : "",  cDevice::ActualDevice()->CardIndex(), m_FrontendInfo.name));
+          break;
+        case cSource::stCable:
+          resp->add_String(*cString::sprintf("DVB-C #%d - %s", cDevice::ActualDevice()->CardIndex(), m_FrontendInfo.name));
+          break;
+        case cSource::stTerr:
+          resp->add_String(*cString::sprintf("DVB-T #%d - %s", cDevice::ActualDevice()->CardIndex(), m_FrontendInfo.name));
+          break;
+      }
+      resp->add_String(*cString::sprintf("%s:%s:%s:%s:%s", (status & FE_HAS_LOCK) ? "LOCKED" : "-", (status & FE_HAS_SIGNAL) ? "SIGNAL" : "-", (status & FE_HAS_CARRIER) ? "CARRIER" : "-", (status & FE_HAS_VITERBI) ? "VITERBI" : "-", (status & FE_HAS_SYNC) ? "SYNC" : "-"));
+      resp->add_U32(fe_snr);
+      resp->add_U32(fe_signal);
+      resp->add_U32(fe_ber);
+      resp->add_U32(fe_unc);
+
+      resp->finaliseStream();
+      m_Socket->write(resp->getPtr(), resp->getLen());
+      delete resp;
+    }
+  }
+}
+
+void cLiveStreamer::sendStreamInfo()
+{
+  cResponsePacket *resp = new cResponsePacket();
+  if (!resp->initStream(VDR_STREAM_CONTENTINFO, 0, 0, 0, 0))
+  {
+    esyslog("VNSI-Error: stream response packet init fail");
+    delete resp;
+    return;
+  }
+
+  for (int idx = 0; idx < m_NumStreams; ++idx)
+  {
+    if (m_Streams[idx])
+    {
+      if (m_Streams[idx]->Type() == stMPEG2AUDIO ||
+          m_Streams[idx]->Type() == stAC3 ||
+          m_Streams[idx]->Type() == stEAC3 ||
+          m_Streams[idx]->Type() == stDTS ||
+          m_Streams[idx]->Type() == stAAC)
+      {
+        resp->add_U32(m_Streams[idx]->GetStreamID());
+        resp->add_String(m_Streams[idx]->GetLanguage());
+        resp->add_U32(m_Streams[idx]->GetChannels());
+        resp->add_U32(m_Streams[idx]->GetSampleRate());
+        resp->add_U32(m_Streams[idx]->GetBlockAlign());
+        resp->add_U32(m_Streams[idx]->GetBitRate());
+        resp->add_U32(m_Streams[idx]->GetBitsPerSample());
+      }
+      else if (m_Streams[idx]->Type() == stMPEG2VIDEO || m_Streams[idx]->Type() == stH264)
+      {
+        resp->add_U32(m_Streams[idx]->GetStreamID());
+        resp->add_U32(m_Streams[idx]->GetFpsScale());
+        resp->add_U32(m_Streams[idx]->GetFpsRate());
+        resp->add_U32(m_Streams[idx]->GetHeight());
+        resp->add_U32(m_Streams[idx]->GetWidth());
+        resp->add_double(m_Streams[idx]->GetAspect());
+      }
+      else if (m_Streams[idx]->Type() == stDVBSUB)
+      {
+        resp->add_U32(m_Streams[idx]->GetStreamID());
+        resp->add_String(m_Streams[idx]->GetLanguage());
+        resp->add_U32(m_Streams[idx]->CompositionPageId());
+        resp->add_U32(m_Streams[idx]->AncillaryPageId());
+      }
+    }
+  }
+
+  resp->finaliseStream();
+  m_Socket->write(resp->getPtr(), resp->getLen());
+  delete resp;
+}
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/receiver.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/receiver.h
new file mode 100644 (file)
index 0000000..16c273e
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef VNSIRECEIVER_H
+#define VNSIRECEIVER_H
+
+#include <linux/dvb/frontend.h>
+#include <linux/videodev2.h>
+#include <vdr/channels.h>
+#include <vdr/device.h>
+#include <vdr/receiver.h>
+#include <vdr/thread.h>
+#include <vdr/ringbuffer.h>
+
+#include "demuxer.h"
+
+class cxSocket;
+class cChannel;
+class cLiveReceiver;
+class cTSDemuxer;
+class cResponsePacket;
+class cLivePatFilter;
+
+class cLiveStreamer : public cThread
+                    , public cRingBufferLinear
+{
+private:
+  friend class cParser;
+  friend class cLivePatFilter;
+
+  cDevice *GetDevice(const cChannel *Channel, int Priority);
+  void Detach(void);
+  void Attach(void);
+  cTSDemuxer *FindStreamDemuxer(int Pid);
+
+  void sendStreamPacket(sStreamPacket *pkt);
+  void sendStreamChange();
+  void sendSignalInfo();
+  void sendStreamInfo();
+
+  const cChannel   *m_Channel;                      /*!> Channel to stream */
+  cDevice          *m_Device;                       /*!> The receiving device the channel depents to */
+  cLiveReceiver    *m_Receiver;                     /*!> Our stream transceiver */
+  cLivePatFilter   *m_PatFilter;                    /*!> Filter processor to get changed pid's */
+  int               m_Priority;                     /*!> The priority over other streamers */
+  int               m_Pids[MAXRECEIVEPIDS + 1];     /*!> PID for cReceiver also as extra array */
+  cTSDemuxer       *m_Streams[MAXRECEIVEPIDS + 1];  /*!> Stream information data (partly filled, rest is done by cLiveReceiver */
+  int               m_NumStreams;                   /*!> Number of streams selected */
+  cxSocket         *m_Socket;                       /*!> The socket class to communicate with client */
+  int               m_Frontend;                     /*!> File descriptor to access used receiving device  */
+  dvb_frontend_info m_FrontendInfo;                 /*!> DVB Information about the receiving device (DVB only) */
+  v4l2_capability   m_vcap;                         /*!> PVR Information about the receiving device (pvrinput only) */
+  cString           m_DeviceString;                 /*!> The name of the receiving device */
+  time_t            m_lastInfoSendet;               /*!> Last queue status report sent */
+  bool              m_streamChangeSendet;           /*!> Is false until the stream change message is sendet (no packets are sendet until this is set) */
+  bool              m_streamReady;                  /*!> Set by the video demuxer after we got video information */
+  bool              m_IsAudioOnly;                  /*!> Set to true if streams contains only audio */
+  bool              m_IsMPEGPS;                     /*!> TS Stream contains MPEG PS data like from pvrinput */
+
+protected:
+  virtual void Action(void);
+
+public:
+  cLiveStreamer();
+  virtual ~cLiveStreamer();
+
+  void Activate(bool On);
+
+  bool StreamChannel(const cChannel *channel, int priority, cxSocket *Socket, cResponsePacket *resp);
+  void SetReady() { m_streamReady = true; }
+  bool IsReady() { return m_streamReady; }
+  bool IsAudioOnly() { return m_IsAudioOnly; }
+  bool IsMPEGPS() { return m_IsMPEGPS; }
+  int HaveStreamDemuxer(int Pid, eStreamType streamType);
+};
+
+#endif  /* VNSIRECEIVER_H */
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/recplayer.c b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/recplayer.c
new file mode 100644 (file)
index 0000000..7156b10
--- /dev/null
@@ -0,0 +1,292 @@
+/*
+ *      Copyright (C) 2004-2005 Chris Tallon
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+/*
+ * This code is taken from VOMP for VDR plugin.
+ */
+
+#include "recplayer.h"
+
+#define _XOPEN_SOURCE 600
+#include <fcntl.h>
+
+cRecPlayer::cRecPlayer(cRecording* rec)
+{
+  m_file          = NULL;
+  m_fileOpen      = -1;
+  m_lastPosition  = 0;
+  m_recordingFilename = strdup(rec->FileName());
+
+  // FIXME find out max file path / name lengths
+#if VDRVERSNUM < 10703
+  m_pesrecording = false;
+  m_indexFile = new cIndexFile(m_recordingFilename, false);
+#else
+  m_pesrecording = rec->IsPesRecording();
+  m_indexFile = new cIndexFile(m_recordingFilename, false, m_pesrecording);
+#endif
+  esyslog("VNSI-Error: Failed to create indexfile!");
+
+  scan();
+}
+
+void cRecPlayer::cleanup() {
+  for(int i = 0; i != m_segments.Size(); i++) {
+    delete m_segments[i];
+  }
+  m_segments.Clear();
+}
+
+void cRecPlayer::scan()
+{
+  if (m_file) fclose(m_file);
+  m_totalLength = 0;
+  m_fileOpen    = -1;
+  m_totalFrames = 0;
+
+  cleanup();
+
+  for(int i = 0; i < 65535; i++) // i think we only need one possible loop
+  {
+    fileNameFromIndex(i);
+    LOGCONSOLE("FILENAME: %s", m_fileName);
+    m_file = fopen(m_fileName, "r");
+    if (!m_file) break;
+
+    cSegment* s = new cSegment();
+    s->start = m_totalLength;
+    fseek(m_file, 0, SEEK_END);
+    m_totalLength += ftell(m_file);
+    m_totalFrames = m_indexFile->Last();
+    s->end = m_totalLength;
+    m_segments.Append(s);
+    LOGCONSOLE("File %i found, totalLength now %llu, numFrames = %lu", i, m_totalLength, m_totalFrames);
+    fclose(m_file);
+  }
+
+  m_file = NULL;
+}
+
+cRecPlayer::~cRecPlayer()
+{
+  LOGCONSOLE("destructor");
+  cleanup();
+  if (m_file) fclose(m_file);
+  free(m_recordingFilename);
+}
+
+char* cRecPlayer::fileNameFromIndex(int index) {
+  if (m_pesrecording)
+    snprintf(m_fileName, sizeof(m_fileName), "%s/%03i.vdr", m_recordingFilename, index+1);
+  else
+    snprintf(m_fileName, sizeof(m_fileName), "%s/%05i.ts", m_recordingFilename, index+1);
+
+  return m_fileName;
+}
+bool cRecPlayer::openFile(int index)
+{
+  if (m_file) fclose(m_file);
+
+  fileNameFromIndex(index);
+  LOGCONSOLE("openFile called for index %i string:%s", index, m_fileName);
+
+  m_file = fopen(m_fileName, "r");
+  if (!m_file)
+  {
+    LOGCONSOLE("file failed to open");
+    m_fileOpen = -1;
+    return false;
+  }
+  m_fileOpen = index;
+  return true;
+}
+
+uint64_t cRecPlayer::getLengthBytes()
+{
+  return m_totalLength;
+}
+
+uint32_t cRecPlayer::getLengthFrames()
+{
+  return m_totalFrames;
+}
+
+unsigned long cRecPlayer::getBlock(unsigned char* buffer, uint64_t position, unsigned long amount)
+{
+  if ((amount > m_totalLength) || (amount > 500000))
+  {
+    LOGCONSOLE("Amount %lu requested and rejected", amount);
+    return 0;
+  }
+
+  if (position >= m_totalLength)
+  {
+    LOGCONSOLE("Client asked for data starting past end of recording!");
+    return 0;
+  }
+
+  if ((position + amount) > m_totalLength)
+  {
+    LOGCONSOLE("Client asked for some data past the end of recording, adjusting amount");
+    amount = m_totalLength - position;
+  }
+
+  // work out what block position is in
+  int segmentNumber;
+  for(segmentNumber = 0; segmentNumber < m_segments.Size(); segmentNumber++)
+  {
+    if ((position >= m_segments[segmentNumber]->start) && (position < m_segments[segmentNumber]->end)) break;
+    // position is in this block
+  }
+
+  // we could be seeking around
+  if (segmentNumber != m_fileOpen)
+  {
+    if (!openFile(segmentNumber)) return 0;
+  }
+
+  uint64_t currentPosition = position;
+  uint32_t yetToGet = amount;
+  uint32_t got = 0;
+  uint32_t getFromThisSegment = 0;
+  uint32_t filePosition;
+
+  while(got < amount)
+  {
+    if (got)
+    {
+      // if(got) then we have already got some and we are back around
+      // advance the file pointer to the next file
+      if (!openFile(++segmentNumber)) return 0;
+    }
+
+    // is the request completely in this block?
+    if ((currentPosition + yetToGet) <= m_segments[segmentNumber]->end)
+      getFromThisSegment = yetToGet;
+    else
+      getFromThisSegment = m_segments[segmentNumber]->end - currentPosition;
+
+    filePosition = currentPosition - m_segments[segmentNumber]->start;
+    fseek(m_file, filePosition, SEEK_SET);
+    fread(&buffer[got], getFromThisSegment, 1, m_file);
+
+    // Tell linux not to bother keeping the data in the FS cache
+    posix_fadvise(m_file->_fileno, filePosition, getFromThisSegment, POSIX_FADV_DONTNEED);
+
+    got += getFromThisSegment;
+    currentPosition += getFromThisSegment;
+    yetToGet -= getFromThisSegment;
+  }
+
+  m_lastPosition = position;
+  return got;
+}
+
+uint64_t cRecPlayer::getLastPosition()
+{
+  return m_lastPosition;
+}
+
+/*cRecording* cRecPlayer::getCurrentRecording()
+{
+  return NULL;
+}*/
+
+uint64_t cRecPlayer::positionFromFrameNumber(uint32_t frameNumber)
+{
+  if (!m_indexFile) return 0;
+#if VDRVERSNUM < 10703
+  unsigned char retFileNumber;
+  int retFileOffset;
+  unsigned char retPicType;
+#else
+  uint16_t retFileNumber;
+  off_t retFileOffset;
+  bool retPicType;
+#endif
+  int retLength;
+
+
+  if (!m_indexFile->Get((int)frameNumber, &retFileNumber, &retFileOffset, &retPicType, &retLength))
+  {
+    return 0;
+  }
+
+//  LOGCONSOLE("FN: %u FO: %i", retFileNumber, retFileOffset);
+  if (retFileNumber >= m_segments.Size()) return 0;
+//  if (!m_segments[retFileNumber]) return 0;
+  uint64_t position = m_segments[retFileNumber]->start + retFileOffset;
+//  LOGCONSOLE("Pos: %llu", position);
+
+  return position;
+}
+
+uint32_t cRecPlayer::frameNumberFromPosition(uint64_t position)
+{
+  if (!m_indexFile) return 0;
+
+  if (position >= m_totalLength)
+  {
+    LOGCONSOLE("Client asked for data starting past end of recording!");
+    return 0;
+  }
+
+  unsigned char segmentNumber;
+  for(segmentNumber = 0; segmentNumber < m_segments.Size(); segmentNumber++)
+  {
+    if ((position >= m_segments[segmentNumber]->start) && (position < m_segments[segmentNumber]->end)) break;
+    // position is in this block
+  }
+  uint32_t askposition = position - m_segments[segmentNumber]->start;
+  return m_indexFile->Get((int)segmentNumber, askposition);
+}
+
+
+bool cRecPlayer::getNextIFrame(uint32_t frameNumber, uint32_t direction, uint64_t* rfilePosition, uint32_t* rframeNumber, uint32_t* rframeLength)
+{
+  // 0 = backwards
+  // 1 = forwards
+
+  if (!m_indexFile) return false;
+
+#if VDRVERSNUM < 10703
+  unsigned char waste1;
+  int waste2;
+#else
+  uint16_t waste1;
+  off_t waste2;
+#endif
+
+  int iframeLength;
+  int indexReturnFrameNumber;
+
+  indexReturnFrameNumber = (uint32_t)m_indexFile->GetNextIFrame(frameNumber, (direction==1 ? true : false), &waste1, &waste2, &iframeLength);
+  LOGCONSOLE("GNIF input framenumber:%lu, direction=%lu, output:framenumber=%i, framelength=%i", frameNumber, direction, indexReturnFrameNumber, iframeLength);
+
+  if (indexReturnFrameNumber == -1) return false;
+
+  *rfilePosition = positionFromFrameNumber(indexReturnFrameNumber);
+  *rframeNumber = (uint32_t)indexReturnFrameNumber;
+  *rframeLength = (uint32_t)iframeLength;
+
+  return true;
+}
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/recplayer.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/recplayer.h
new file mode 100644 (file)
index 0000000..d748100
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ *      Copyright (C) 2004-2005 Chris Tallon
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+/*
+ * This code is taken from VOMP for VDR plugin.
+ */
+
+#ifndef RECPLAYER_H
+#define RECPLAYER_H
+
+#include <stdio.h>
+#include <vdr/recording.h>
+#include <vdr/tools.h>
+
+#include "config.h"
+
+class cSegment
+{
+  public:
+    uint64_t start;
+    uint64_t end;
+};
+
+class cRecPlayer
+{
+public:
+  cRecPlayer(cRecording* rec);
+  ~cRecPlayer();
+  uint64_t getLengthBytes();
+  uint32_t getLengthFrames();
+  unsigned long getBlock(unsigned char* buffer, uint64_t position, unsigned long amount);
+  bool openFile(int index);
+  uint64_t getLastPosition();
+  void scan();
+  uint64_t positionFromFrameNumber(uint32_t frameNumber);
+  uint32_t frameNumberFromPosition(uint64_t position);
+  bool getNextIFrame(uint32_t frameNumber, uint32_t direction, uint64_t* rfilePosition, uint32_t* rframeNumber, uint32_t* rframeLength);
+
+private:
+  void cleanup();
+  char* fileNameFromIndex(int index);
+  void checkBufferSize(int s);
+
+  char        m_fileName[512];
+  cIndexFile *m_indexFile;
+  FILE       *m_file;
+  int         m_fileOpen;
+  cVector<cSegment*> m_segments;
+  uint64_t    m_totalLength;
+  uint64_t    m_lastPosition;
+  uint32_t    m_totalFrames;
+  char       *m_recordingFilename;
+  bool        m_pesrecording;
+};
+
+#endif
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/requestpacket.c b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/requestpacket.c
new file mode 100644 (file)
index 0000000..64c9ae9
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "config.h"
+#include "requestpacket.h"
+#include "server.h"
+#include "vdrcommand.h"
+#include "tools.h"
+
+cRequestPacket::cRequestPacket(uint32_t requestID, uint32_t opcode, uint8_t* data, uint32_t dataLength, cConnection *cli)
+ : requestID(requestID), opCode(opcode), userData(data), userDataLength(dataLength), client(cli)
+{
+  packetPos       = 0;
+  ownBlock        = true;
+  channelID       = 0;
+  streamID        = 0;
+  flag            = 0;
+}
+
+cRequestPacket::~cRequestPacket()
+{
+  if (!ownBlock) return; // don't free if it's a getblock
+
+  if (userData) free(userData);
+}
+
+bool cRequestPacket::end()
+{
+  return (packetPos >= userDataLength);
+}
+
+int cRequestPacket::serverError()
+{
+  if ((packetPos == 0) && (userDataLength == 4) && !ntohl(*(uint32_t*)userData)) return 1;
+  else return 0;
+}
+
+char* cRequestPacket::extract_String()
+{
+  if (serverError()) return NULL;
+
+  int length = strlen((char*)&userData[packetPos]);
+  if ((packetPos + length) > userDataLength) return NULL;
+  char* str = new char[length + 1];
+  strcpy(str, (char*)&userData[packetPos]);
+  packetPos += length + 1;
+  return str;
+}
+
+uint8_t cRequestPacket::extract_U8()
+{
+  if ((packetPos + sizeof(uint8_t)) > userDataLength) return 0;
+  uint8_t uc = userData[packetPos];
+  packetPos += sizeof(uint8_t);
+  return uc;
+}
+
+uint32_t cRequestPacket::extract_U32()
+{
+  if ((packetPos + sizeof(uint32_t)) > userDataLength) return 0;
+  uint32_t ul = ntohl(*(uint32_t*)&userData[packetPos]);
+  packetPos += sizeof(uint32_t);
+  return ul;
+}
+
+uint64_t cRequestPacket::extract_U64()
+{
+  if ((packetPos + sizeof(uint64_t)) > userDataLength) return 0;
+  uint64_t ull = ntohll(*(uint64_t*)&userData[packetPos]);
+  packetPos += sizeof(uint64_t);
+  return ull;
+}
+
+double cRequestPacket::extract_Double()
+{
+  if ((packetPos + sizeof(uint64_t)) > userDataLength) return 0;
+  uint64_t ull = ntohll(*(uint64_t*)&userData[packetPos]);
+  double d;
+  memcpy(&d,&ull,sizeof(double));
+  packetPos += sizeof(uint64_t);
+  return d;
+}
+
+int32_t cRequestPacket::extract_S32()
+{
+  if ((packetPos + sizeof(int32_t)) > userDataLength) return 0;
+  int32_t l = ntohl(*(int32_t*)&userData[packetPos]);
+  packetPos += sizeof(int32_t);
+  return l;
+}
+
+uint8_t* cRequestPacket::getData()
+{
+  ownBlock = false;
+  return userData;
+}
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/requestpacket.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/requestpacket.h
new file mode 100644 (file)
index 0000000..c1c37de
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef REQUEST_PACKET_H
+#define REQUEST_PACKET_H
+
+class cConnection;
+
+class cRequestPacket
+{
+public:
+  cRequestPacket(uint32_t requestID, uint32_t opcode, uint8_t* data, uint32_t dataLength, cConnection *cli);
+  ~cRequestPacket();
+
+  int  serverError();
+
+  uint32_t  getDataLength()     { return userDataLength; }
+  uint32_t  getChannelID()      { return channelID; }
+  uint32_t  getRequestID()      { return requestID; }
+  uint32_t  getStreamID()       { return streamID; }
+  uint32_t  getFlag()           { return flag; }
+  uint32_t  getOpCode()         { return opCode; }
+  cConnection *getClient()      { return client; }
+
+  char*     extract_String();
+  uint8_t   extract_U8();
+  uint32_t  extract_U32();
+  uint64_t  extract_U64();
+  int32_t   extract_S32();
+  double    extract_Double();
+
+  bool      end();
+
+  // If you call this, the memory becomes yours. Free with free()
+  uint8_t* getData();
+
+private:
+  uint8_t* userData;
+  uint32_t userDataLength;
+  uint32_t packetPos;
+  uint32_t opCode;
+
+  uint32_t channelID;
+
+  uint32_t requestID;
+  uint32_t streamID;
+
+  uint32_t flag; // stream only
+
+  bool ownBlock;
+  cConnection *client;
+};
+
+#endif /* REQUEST_PACKET_H */
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/responsepacket.c b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/responsepacket.c
new file mode 100644 (file)
index 0000000..fe9b757
--- /dev/null
@@ -0,0 +1,214 @@
+/*
+ *      Copyright (C) 2007 Chris Tallon
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+/*
+ * This code is taken from VOMP for VDR plugin.
+ */
+
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "responsepacket.h"
+#include "vdrcommand.h"
+#include "config.h"
+#include "tools.h"
+
+/* Packet format for an RR channel response:
+
+4 bytes = channel ID = 1 (request/response channel)
+4 bytes = request ID (from serialNumber)
+4 bytes = length of the rest of the packet
+? bytes = rest of packet. depends on packet
+*/
+
+cResponsePacket::cResponsePacket()
+{
+  buffer = NULL;
+  bufSize = 0;
+  bufUsed = 0;
+}
+
+cResponsePacket::~cResponsePacket()
+{
+  if (buffer) free(buffer);
+}
+
+bool cResponsePacket::init(uint32_t requestID)
+{
+  if (buffer == NULL) {
+    bufSize = 512;
+    buffer = (uint8_t*)malloc(bufSize);
+  }
+
+  *(uint32_t*)&buffer[0] = htonl(CHANNEL_REQUEST_RESPONSE); // RR channel
+  *(uint32_t*)&buffer[4] = htonl(requestID);
+  *(uint32_t*)&buffer[userDataLenPos] = 0;
+  bufUsed = headerLength;
+
+  return true;
+}
+
+bool cResponsePacket::initScan(uint32_t opCode)
+{
+  if(buffer == NULL) {
+    bufSize = 512;
+    buffer = (uint8_t*)malloc(bufSize);
+  }
+
+  *(uint32_t*)&buffer[0] = htonl(CHANNEL_SCAN); // RR channel
+  *(uint32_t*)&buffer[4] = htonl(opCode);
+  *(uint32_t*)&buffer[userDataLenPos] = 0;
+  bufUsed = headerLength;
+
+  return true;
+}
+
+bool cResponsePacket::initStatus(uint32_t opCode)
+{
+  if(buffer == NULL) {
+    bufSize = 512;
+    buffer = (uint8_t*)malloc(bufSize);
+  }
+
+  *(uint32_t*)&buffer[0] = htonl(CHANNEL_STATUS); // RR channel
+  *(uint32_t*)&buffer[4] = htonl(opCode);
+  *(uint32_t*)&buffer[userDataLenPos] = 0;
+  bufUsed = headerLength;
+
+  return true;
+}
+
+bool cResponsePacket::initStream(uint32_t opCode, uint32_t streamID, uint32_t duration, int64_t dts, int64_t pts)
+{
+  if(buffer == NULL) {
+    bufSize = 512;
+    buffer = (uint8_t*)malloc(bufSize);
+  }
+
+  *(uint32_t*)&buffer[0]  = htonl(CHANNEL_STREAM); // stream channel
+  *(uint32_t*)&buffer[4]  = htonl(opCode);        // Stream packet operation code
+  *(uint32_t*)&buffer[8]  = htonl(streamID);              // Stream ID (unused here)
+  *(uint32_t*)&buffer[12] = htonl(duration);              // Duration (unused here)
+  *(int64_t*) &buffer[16] = htonl(dts);              // DTS (unused here)
+  *(int64_t*) &buffer[24] = htonl(pts);              // PTS (unused here)
+  *(uint32_t*)&buffer[userDataLenPosStream] = 0;
+  bufUsed = headerLengthStream;
+
+  return true;
+}
+
+void cResponsePacket::finalise()
+{
+  *(uint32_t*)&buffer[userDataLenPos] = htonl(bufUsed - headerLength);
+  //Log::getInstance()->log("Client", Log::DEBUG, "RP finalise %lu", bufUsed - headerLength);
+}
+
+void cResponsePacket::finaliseStream()
+{
+  *(uint32_t*)&buffer[userDataLenPosStream] = htonl(bufUsed - headerLengthStream);
+}
+
+
+bool cResponsePacket::copyin(const uint8_t* src, uint32_t len)
+{
+  if (!checkExtend(len)) return false;
+  memcpy(buffer + bufUsed, src, len);
+  bufUsed += len;
+  return true;
+}
+
+uint8_t* cResponsePacket::reserve(uint32_t len) {
+  if (!checkExtend(len)) return false;
+  uint8_t* result = buffer + bufUsed;
+  bufUsed += len;
+  return result;
+}
+
+bool cResponsePacket::unreserve(uint32_t len) {
+  if(bufUsed < len) return false;
+  bufUsed -= len;
+  return true;
+}
+
+bool cResponsePacket::add_String(const char* string)
+{
+  uint32_t len = strlen(string) + 1;
+  if (!checkExtend(len)) return false;
+  memcpy(buffer + bufUsed, string, len);
+  bufUsed += len;
+  return true;
+}
+
+bool cResponsePacket::add_U32(uint32_t ul)
+{
+  if (!checkExtend(sizeof(uint32_t))) return false;
+  *(uint32_t*)&buffer[bufUsed] = htonl(ul);
+  bufUsed += sizeof(uint32_t);
+  return true;
+}
+
+bool cResponsePacket::add_U8(uint8_t c)
+{
+  if (!checkExtend(sizeof(uint8_t))) return false;
+  buffer[bufUsed] = c;
+  bufUsed += sizeof(uint8_t);
+  return true;
+}
+
+bool cResponsePacket::add_S32(int32_t l)
+{
+  if (!checkExtend(sizeof(int32_t))) return false;
+  *(int32_t*)&buffer[bufUsed] = htonl(l);
+  bufUsed += sizeof(int32_t);
+  return true;
+}
+
+bool cResponsePacket::add_U64(uint64_t ull)
+{
+  if (!checkExtend(sizeof(uint64_t))) return false;
+  *(uint64_t*)&buffer[bufUsed] = htonll(ull);
+  bufUsed += sizeof(uint64_t);
+  return true;
+}
+
+bool cResponsePacket::add_double(double d)
+{
+  if (!checkExtend(sizeof(double))) return false;
+  uint64_t ull;
+  memcpy(&ull,&d,sizeof(double));
+  *(uint64_t*)&buffer[bufUsed] = htonll(ull);
+  bufUsed += sizeof(uint64_t);
+  return true;
+}
+
+
+bool cResponsePacket::checkExtend(uint32_t by)
+{
+  if ((bufUsed + by) < bufSize) return true;
+  if (512 > by) by = 512;
+  uint8_t* newBuf = (uint8_t*)realloc(buffer, bufSize + by);
+  if (!newBuf) return false;
+  buffer = newBuf;
+  bufSize += by;
+  return true;
+}
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/responsepacket.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/responsepacket.h
new file mode 100644 (file)
index 0000000..36edf13
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ *      Copyright (C) 2007 Chris Tallon
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+/*
+ * This code is taken from VOMP for VDR plugin.
+ */
+
+#ifndef RESPONSEPACKET_H
+#define RESPONSEPACKET_H
+
+class cResponsePacket
+{
+public:
+  cResponsePacket();
+  ~cResponsePacket();
+
+  bool init(uint32_t requestID);
+  bool initScan(uint32_t opCode);
+  bool initStatus(uint32_t opCode);
+  bool initStream(uint32_t opCode, uint32_t streamID, uint32_t duration, int64_t dts, int64_t pts);
+  void finalise();
+  void finaliseStream();
+  bool copyin(const uint8_t* src, uint32_t len);
+  uint8_t* reserve(uint32_t len);
+  bool unreserve(uint32_t len);
+
+  bool add_String(const char* string);
+  bool add_U32(uint32_t ul);
+  bool add_S32(int32_t l);
+  bool add_U8(uint8_t c);
+  bool add_U64(uint64_t ull);
+  bool add_double(double d);
+
+  uint8_t* getPtr() { return buffer; }
+  uint32_t getLen() { return bufUsed; }
+
+private:
+  uint8_t* buffer;
+  uint32_t bufSize;
+  uint32_t bufUsed;
+
+  bool checkExtend(uint32_t by);
+
+  const static uint32_t headerLength          = 12;
+  const static uint32_t userDataLenPos        = 8;
+  const static uint32_t headerLengthStream    = 36;
+  const static uint32_t userDataLenPosStream  = 32;
+};
+
+#endif
+
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/server.c b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/server.c
new file mode 100644 (file)
index 0000000..c1f0873
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <netdb.h>
+#include <poll.h>
+#include <assert.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+
+#include <vdr/plugin.h>
+
+#include "server.h"
+#include "connection.h"
+
+unsigned int cServer::m_IdCnt = 0;
+
+class cAllowedHosts : public cSVDRPhosts
+{
+public:
+  cAllowedHosts(const cString& AllowedHostsFile)
+  {
+    if (!Load(AllowedHostsFile, true, true))
+    {
+      isyslog("VNSI-Error: Invalid or missing '%s'. falling back to 'svdrphosts.conf'.", *AllowedHostsFile);
+      cString Base = cString::sprintf("%s/../svdrphosts.conf", *VNSIServerConfig.ConfigDirectory);
+      if (!Load(Base, true, true))
+      {
+        esyslog("VNSI-Error: Invalid or missing %s. Adding 127.0.0.1 to list of allowed hosts.", *Base);
+        cSVDRPhost *localhost = new cSVDRPhost;
+        if (localhost->Parse("127.0.0.1"))
+          Add(localhost);
+        else
+          delete localhost;
+      }
+    }
+  }
+};
+
+cServer::cServer(int listenPort) : cThread("VDR VNSI Server")
+{
+  m_ServerPort  = listenPort;
+  m_ServerId    = time(NULL) ^ getpid();
+
+  if(*VNSIServerConfig.ConfigDirectory)
+  {
+    m_AllowedHostsFile = cString::sprintf("%s/" ALLOWED_HOSTS_FILE, *VNSIServerConfig.ConfigDirectory);
+  }
+  else
+  {
+    esyslog("VNSI-Error: cServer: missing ConfigDirectory!");
+    m_AllowedHostsFile = cString::sprintf("/video/" ALLOWED_HOSTS_FILE);
+  }
+
+  m_ServerFD = socket(AF_INET, SOCK_STREAM, 0);
+  if(m_ServerFD == -1)
+    return;
+
+  fcntl(m_ServerFD, F_SETFD, fcntl(m_ServerFD, F_GETFD) | FD_CLOEXEC);
+
+  int one = 1;
+  setsockopt(m_ServerFD, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int));
+
+  struct sockaddr_in s;
+  memset(&s, 0, sizeof(s));
+  s.sin_family = AF_INET;
+  s.sin_port = htons(m_ServerPort);
+
+  int x = bind(m_ServerFD, (struct sockaddr *)&s, sizeof(s));
+  if (x < 0)
+  {
+    close(m_ServerFD);
+    isyslog("VNSI: Unable to start VNSI Server, port already in use ?");
+    m_ServerFD = -1;
+    return;
+  }
+
+  listen(m_ServerFD, 10);
+
+  Start();
+
+  isyslog("VNSI: VNSI Server started");
+  return;
+}
+
+cServer::~cServer()
+{
+  Cancel(-1);
+  for (Connections::iterator i = m_Connections.begin(); i != m_Connections.end(); i++)
+  {
+    delete (*i);
+  }
+  m_Connections.erase(m_Connections.begin(), m_Connections.end());
+  Cancel();
+  isyslog("VNSI: VNSI Server stopped");
+}
+
+void cServer::NewClientConnected(int fd)
+{
+  char buf[64];
+  struct sockaddr_in sin;
+  socklen_t len = sizeof(sin);
+
+  if (getpeername(fd, (struct sockaddr *)&sin, &len))
+  {
+    esyslog("VNSI-Error: getpeername() failed, dropping new incoming connection %d", m_IdCnt);
+    close(fd);
+    return;
+  }
+
+  cAllowedHosts AllowedHosts(m_AllowedHostsFile);
+  if (!AllowedHosts.Acceptable(sin.sin_addr.s_addr))
+  {
+    esyslog("VNSI: Address not allowed to connect (%s)", *m_AllowedHostsFile);
+    close(fd);
+    return;
+  }
+
+  if (fcntl(fd, F_SETFL, fcntl (fd, F_GETFL) | O_NONBLOCK) == -1)
+  {
+    esyslog("VNSI-Error: Error setting control socket to nonblocking mode");
+    close(fd);
+    return;
+  }
+
+  int val = 1;
+  setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val));
+
+  val = 30;
+  setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, &val, sizeof(val));
+
+  val = 15;
+  setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, &val, sizeof(val));
+
+  val = 5;
+  setsockopt(fd, SOL_TCP, TCP_KEEPCNT, &val, sizeof(val));
+
+  val = 1;
+  setsockopt(fd, SOL_TCP, TCP_NODELAY, &val, sizeof(val));
+
+  isyslog("VNSI: Client with ID %d connected: %s", m_IdCnt, cxSocket::ip2txt(sin.sin_addr.s_addr, sin.sin_port, buf));
+  cConnection *connection = new cConnection(this, fd, m_IdCnt, cxSocket::ip2txt(sin.sin_addr.s_addr, sin.sin_port, buf));
+  m_Connections.push_back(connection);
+  m_IdCnt++;
+}
+
+void cServer::Action(void)
+{
+  fd_set fds;
+  struct timeval tv;
+
+  while (Running())
+  {
+    FD_ZERO(&fds);
+    FD_SET(m_ServerFD, &fds);
+
+    tv.tv_sec = 5;
+    tv.tv_usec = 0;
+
+    int r = select(m_ServerFD + 1, &fds, NULL, NULL, &tv);
+    if (r == -1)
+    {
+      esyslog("VNSI-Error: failed during select");
+      continue;
+    }
+    if (r == 0)
+    {
+      for (Connections::iterator i = m_Connections.begin(); i != m_Connections.end();)
+      {
+        if (!(*i)->Active())
+        {
+          isyslog("VNSI: Client with ID %u seems to be disconnected, removing from client list", (*i)->GetID());
+          delete (*i);
+          i = m_Connections.erase(i);
+        }
+        else
+        {
+          i++;
+        }
+      }
+      continue;
+    }
+
+    int fd = accept(m_ServerFD, 0, 0);
+    if (fd >= 0)
+    {
+      NewClientConnected(fd);
+    }
+    else
+    {
+      esyslog("VNSI-Error: accept failed");
+    }
+  }
+  return;
+}
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/server.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/server.h
new file mode 100644 (file)
index 0000000..2103094
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef SERVER_H
+#define SERVER_H
+
+#include <list>
+#include <vdr/thread.h>
+
+#include "config.h"
+//#include "cmdcontrol.h"
+
+class cConnection;
+
+typedef std::list<cConnection*> Connections;
+
+class cServer : public cThread
+{
+protected:
+  virtual void Action(void);
+  void NewClientConnected(int fd);
+
+  int           m_ServerPort;
+  int           m_ServerId;
+  int           m_ServerFD;
+  cString       m_AllowedHostsFile;
+  Connections   m_Connections;
+  //cCmdControl   m_CmdControl;
+  static unsigned int m_IdCnt;
+
+public:
+  cServer(int listenPort);
+  virtual ~cServer();
+
+  //cCmdControl *GetCmdControl() { return &m_CmdControl; }
+};
+
+#endif /* SERVER_H */
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/suspend.c b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/suspend.c
new file mode 100644 (file)
index 0000000..11c3fa7
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ *  $Id: suspend.c,v 1.3 2008/10/22 11:59:32 schmirl Exp $
+ */
+
+#include "suspend.h"
+#include "suspend.dat"
+#include "config.h"
+
+cSuspendLive::cSuspendLive(void)
+               : cThread("Streamdev: server suspend")
+{
+}
+
+cSuspendLive::~cSuspendLive() {
+       Stop();
+       Detach();
+}
+
+void cSuspendLive::Activate(bool On) {
+       LOGCONSOLE("Activate cSuspendLive %d\n", On);
+       if (On)
+               Start();
+       else
+               Stop();
+}
+
+void cSuspendLive::Stop(void) {
+       if (Running())
+               Cancel(3);
+}
+
+void cSuspendLive::Action(void) {
+       while (Running()) {
+               DeviceStillPicture(suspend_mpg, sizeof(suspend_mpg));
+               cCondWait::SleepMs(100);
+       }
+}
+
+bool cSuspendCtl::m_Active = false;
+
+cSuspendCtl::cSuspendCtl(void):
+               cControl(m_Suspend = new cSuspendLive) {
+       m_Active = true;
+}
+
+cSuspendCtl::~cSuspendCtl() {
+       m_Active = false;
+       DELETENULL(m_Suspend);
+}
+
+eOSState cSuspendCtl::ProcessKey(eKeys Key) {
+       if (!m_Suspend->Active() || Key == kBack) {
+               DELETENULL(m_Suspend);
+               return osEnd;
+       }
+       return osContinue;
+}
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/suspend.dat b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/suspend.dat
new file mode 100644 (file)
index 0000000..7b1b890
--- /dev/null
@@ -0,0 +1,1206 @@
+const unsigned char suspend_mpg[] = {
+       0x00, 0x00, 0x01, 0xb3, 0x2d, 0x02, 0x40, 0x83, 0x02, 0xd0, 0x20, 0xa0, 
+       0x00, 0x00, 0x01, 0xb2, 0x4d, 0x50, 0x45, 0x47, 0x0a, 0x00, 0x00, 0x01, 
+       0xb8, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0a, 0xbb, 
+       0x58, 0x00, 0x00, 0x01, 0x01, 0x52, 0x97, 0xe6, 0x54, 0xa5, 0x2f, 0xdc, 
+       0xaf, 0x9a, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 
+       0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 
+       0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x45, 0xe2, 
+       0x2c, 0x52, 0x67, 0x98, 0x6c, 0x9a, 0x2e, 0xb9, 0x9e, 0xb4, 0x6c, 0xdd, 
+       0x34, 0x8c, 0x8b, 0xab, 0xa6, 0x61, 0xb5, 0xbd, 0x33, 0x8d, 0xa7, 0x22, 
+       0x6f, 0x75, 0x74, 0xcc, 0x36, 0xb4, 0x6c, 0xfd, 0x34, 0x64, 0x4d, 0xee, 
+       0x91, 0xb3, 0xf4, 0xd6, 0xf4, 0xcd, 0xd3, 0x46, 0x44, 0xde, 0xea, 0x1b, 
+       0x37, 0x4d, 0x77, 0x4c, 0xdd, 0x34, 0xe4, 0x43, 0xdd, 0x43, 0x66, 0xe9, 
+       0xad, 0x1b, 0x38, 0xda, 0x32, 0x26, 0xf7, 0x4f, 0x4c, 0xe3, 0x6b, 0x46, 
+       0xce, 0x36, 0x8c, 0x89, 0xbd, 0xd3, 0xd3, 0x3f, 0x4d, 0x68, 0xd9, 0x86, 
+       0xd3, 0x91, 0x37, 0xba, 0x86, 0xcc, 0x36, 0xbb, 0xa6, 0x6e, 0x9a, 0x32, 
+       0x26, 0xf7, 0x4f, 0x4c, 0xfd, 0x35, 0xbd, 0x33, 0xf4, 0xd1, 0x91, 0x37, 
+       0xba, 0x46, 0xcf, 0xd3, 0x5a, 0x36, 0x7e, 0x9a, 0x72, 0x26, 0xf7, 0x4f, 
+       0x4c, 0xfd, 0x35, 0xa3, 0x66, 0x1b, 0x46, 0x44, 0xde, 0xea, 0xe9, 0x9c, 
+       0x6d, 0x67, 0x4c, 0xfd, 0x34, 0x64, 0x43, 0xdd, 0x43, 0x66, 0xe9, 0xad, 
+       0xe9, 0x9c, 0x6d, 0x39, 0x13, 0x7b, 0xa7, 0xa6, 0x7e, 0x9a, 0xd1, 0xb3, 
+       0x0d, 0xa3, 0x22, 0x6f, 0x75, 0x74, 0xcd, 0xd3, 0x5c, 0x36, 0x61, 0xb4, 
+       0x0c, 0x9b, 0xdd, 0x43, 0x66, 0x1b, 0x5c, 0x36, 0x6e, 0x9a, 0x72, 0x26, 
+       0xf7, 0x50, 0xd9, 0xba, 0x6b, 0x86, 0xcd, 0xd3, 0x46, 0x44, 0xde, 0xea, 
+       0x1b, 0x30, 0xda, 0xd1, 0xb3, 0x8d, 0xa3, 0x22, 0x6f, 0x74, 0x8d, 0x9c, 
+       0x6d, 0x68, 0xd9, 0xba, 0x69, 0xc8, 0x87, 0xba, 0xba, 0x66, 0x1b, 0x5c, 
+       0x36, 0x6e, 0x9a, 0x32, 0x26, 0xf7, 0x57, 0x4c, 0xdd, 0x35, 0xdd, 0x33, 
+       0x74, 0xd1, 0x91, 0x37, 0xba, 0x9e, 0x6e, 0x9a, 0xde, 0x99, 0xfa, 0x68, 
+       0xc8, 0x9b, 0xdd, 0x23, 0x67, 0xe9, 0xad, 0x79, 0xc6, 0xd3, 0x91, 0x37, 
+       0xba, 0x7a, 0x67, 0xe9, 0xad, 0x1b, 0x30, 0xda, 0x32, 0x26, 0xf7, 0x50, 
+       0xd9, 0xba, 0x6b, 0x7a, 0x67, 0x1b, 0x46, 0x44, 0x3d, 0xd4, 0x36, 0x61, 
+       0xb5, 0xbd, 0x33, 0xbd, 0x39, 0x13, 0x7b, 0xa7, 0xa6, 0x71, 0xb5, 0xbf, 
+       0xcc, 0x36, 0x8c, 0x89, 0xbd, 0xd4, 0xf3, 0x0d, 0xae, 0xe9, 0x9b, 0xa6, 
+       0x9c, 0x89, 0xbd, 0xd4, 0xf3, 0x74, 0xd7, 0x74, 0xcc, 0x36, 0x8c, 0x89, 
+       0xbd, 0xd2, 0x36, 0x7e, 0x9a, 0xd1, 0xb3, 0x0d, 0xa3, 0x22, 0x6f, 0x75, 
+       0x74, 0xcf, 0xd3, 0x5b, 0xd3, 0x37, 0x4d, 0x19, 0x10, 0xf7, 0x53, 0xcc, 
+       0x36, 0xb7, 0xa6, 0x71, 0xb4, 0xe4, 0x4d, 0xee, 0x91, 0xb3, 0xf4, 0xd6, 
+       0x8d, 0x98, 0x6d, 0x03, 0x26, 0xf0, 0x00, 0x00, 0x01, 0x02, 0x2b, 0xf9, 
+       0x95, 0x29, 0x4b, 0xf7, 0x2b, 0xe6, 0xae, 0x52, 0x94, 0x88, 0xb9, 0x4a, 
+       0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 
+       0x88, 0xb9, 0x4a, 0x45, 0xe2, 0x2c, 0x52, 0x71, 0xb3, 0x74, 0xc9, 0xa2, 
+       0xeb, 0x79, 0x86, 0xd6, 0xf4, 0xcc, 0x36, 0x9c, 0x88, 0xba, 0xba, 0x66, 
+       0xe9, 0xad, 0x1b, 0x38, 0xda, 0x32, 0x26, 0xf7, 0x50, 0xd9, 0xba, 0x6b, 
+       0x7a, 0x67, 0xe9, 0xa7, 0x22, 0x6f, 0x74, 0x8d, 0x9f, 0xa6, 0xb4, 0x6c, 
+       0xc3, 0x68, 0xc8, 0x9b, 0xdd, 0x5d, 0x33, 0x0d, 0xae, 0x1b, 0x37, 0x4d, 
+       0x19, 0x13, 0x7b, 0xab, 0xa6, 0x61, 0xb5, 0xc3, 0x66, 0xe9, 0xa3, 0x22, 
+       0x1e, 0xea, 0x1b, 0x30, 0xda, 0xde, 0x99, 0xfa, 0x69, 0xc8, 0x9b, 0xdd, 
+       0x23, 0x67, 0x1b, 0x5b, 0xd3, 0x37, 0x4d, 0x19, 0x13, 0x7b, 0xa8, 0x6c, 
+       0xc3, 0x6b, 0x86, 0xcd, 0xd3, 0x46, 0x44, 0xde, 0xea, 0xe9, 0x9b, 0xa6, 
+       0xb7, 0xa6, 0x7e, 0x9a, 0x72, 0x26, 0xf7, 0x4f, 0x4c, 0xfd, 0x35, 0xa3, 
+       0x67, 0xe9, 0xa3, 0x22, 0x6f, 0x74, 0xf4, 0xce, 0x36, 0xb7, 0xa6, 0x67, 
+       0xa3, 0x22, 0x6f, 0x75, 0x74, 0xcc, 0xf5, 0xc3, 0x66, 0x1b, 0x4e, 0x44, 
+       0xde, 0xea, 0xe9, 0x9b, 0xa6, 0xb4, 0x6c, 0xfd, 0x34, 0x0c, 0x87, 0xba, 
+       0x46, 0xcf, 0xd3, 0x5a, 0x36, 0x61, 0xb4, 0x64, 0x4d, 0xee, 0xa1, 0xb3, 
+       0xf4, 0xd6, 0x8d, 0x9b, 0xa6, 0x9c, 0x89, 0xbd, 0xd5, 0xd3, 0x30, 0xda, 
+       0xee, 0x99, 0x86, 0xd1, 0x91, 0x37, 0xba, 0x86, 0xcc, 0x36, 0xb8, 0x6c, 
+       0xc3, 0x68, 0xc8, 0x9b, 0xdd, 0x5d, 0x33, 0x74, 0xd6, 0x8d, 0x9c, 0x6d, 
+       0x39, 0x13, 0x7b, 0xa4, 0x6c, 0xe3, 0x6b, 0x7a, 0x66, 0xe9, 0xa3, 0x22, 
+       0x6f, 0x75, 0x3c, 0xdd, 0x35, 0xdd, 0x33, 0x0d, 0xa3, 0x22, 0x1e, 0xea, 
+       0xe9, 0x9b, 0xa6, 0xb8, 0x6c, 0xdd, 0x34, 0xe4, 0x4d, 0xee, 0xa1, 0xb3, 
+       0x0d, 0xad, 0x1b, 0x3f, 0x4d, 0x19, 0x13, 0x7b, 0xa4, 0x6c, 0xe3, 0x6b, 
+       0x7a, 0x67, 0x1b, 0x46, 0x44, 0xde, 0xe9, 0x1b, 0x38, 0xda, 0xd1, 0xb3, 
+       0x3d, 0x39, 0x13, 0x7b, 0xab, 0xa6, 0x61, 0xb5, 0xdd, 0x33, 0x74, 0xd1, 
+       0x91, 0x37, 0xba, 0x5e, 0x7e, 0x9a, 0xee, 0x99, 0x9e, 0x8c, 0x88, 0x7b, 
+       0xa4, 0x6c, 0xfd, 0x35, 0xbf, 0xcc, 0x36, 0x9c, 0x89, 0xbd, 0xd5, 0xd3, 
+       0x33, 0xd7, 0x0d, 0x9b, 0xa6, 0x8c, 0x89, 0xbd, 0xd5, 0xd3, 0x37, 0x4d, 
+       0x68, 0xd9, 0xc6, 0xd1, 0x91, 0x37, 0xba, 0xba, 0x66, 0x1b, 0x5b, 0xd3, 
+       0x33, 0xd3, 0x91, 0x37, 0xba, 0x86, 0xce, 0x36, 0xb7, 0xa6, 0x61, 0xb4, 
+       0x64, 0x4d, 0xee, 0xa1, 0xb3, 0x74, 0xd7, 0x0d, 0x98, 0x6d, 0x19, 0x13, 
+       0x7b, 0xab, 0xa6, 0x6e, 0x9a, 0xd1, 0xb3, 0xf4, 0xd3, 0x91, 0x0f, 0x74, 
+       0x8d, 0x9f, 0xa6, 0xb4, 0x6c, 0xe3, 0x68, 0xc8, 0x9b, 0xdd, 0x23, 0x66, 
+       0x1b, 0x5c, 0x36, 0x6e, 0x9a, 0x32, 0x26, 0xf7, 0x57, 0x4c, 0xc3, 0x6b, 
+       0xba, 0x66, 0x1b, 0x4e, 0x44, 0xde, 0x00, 0x00, 0x01, 0x03, 0x2b, 0xf9, 
+       0x95, 0x29, 0x4b, 0xf7, 0x2b, 0xe6, 0xae, 0x52, 0x94, 0x88, 0xb9, 0x4a, 
+       0x52, 0x22, 0xe5, 0x28, 0xf3, 0x3c, 0x9b, 0xf8, 0xb5, 0xa5, 0x67, 0x4c, 
+       0xe3, 0x64, 0xd1, 0x75, 0x3c, 0xc3, 0x6b, 0x7a, 0x66, 0x1b, 0x4e, 0x44, 
+       0x3d, 0xd5, 0xd3, 0x30, 0xda, 0xe1, 0xb3, 0x74, 0xd1, 0x91, 0x37, 0xba, 
+       0x86, 0xcc, 0x36, 0xb4, 0x6c, 0xfd, 0x34, 0x64, 0x4d, 0xee, 0x9e, 0x99, 
+       0xc6, 0xd6, 0x8d, 0x9c, 0x6d, 0x38, 0xa9, 0xbd, 0xd2, 0x36, 0x71, 0xb5, 
+       0xbd, 0x33, 0x0d, 0xa3, 0x22, 0x6f, 0x75, 0x74, 0xcc, 0x36, 0xbb, 0xa6, 
+       0x6e, 0x9a, 0x31, 0x53, 0x7b, 0xab, 0xa6, 0x6e, 0x9a, 0xd1, 0xb3, 0x8d, 
+       0xa7, 0x15, 0x37, 0xba, 0xba, 0x66, 0x1b, 0x5a, 0x36, 0x6e, 0x9a, 0x06, 
+       0x43, 0xdd, 0x5d, 0x33, 0x0d, 0xae, 0x1b, 0x30, 0xda, 0x72, 0x26, 0xf7, 
+       0x57, 0x4c, 0xc3, 0x6b, 0x86, 0xcd, 0xd3, 0x46, 0x44, 0xde, 0xea, 0x1b, 
+       0x37, 0x4d, 0x6f, 0x4c, 0xe3, 0x68, 0x19, 0x37, 0xba, 0x86, 0xcd, 0xd3, 
+       0x5a, 0x36, 0x61, 0xb4, 0xe4, 0x4d, 0xee, 0xae, 0x99, 0x86, 0xd7, 0x74, 
+       0xcd, 0xd3, 0x46, 0x44, 0xde, 0xea, 0x1b, 0x30, 0xda, 0xde, 0x99, 0xfa, 
+       0x68, 0xc8, 0x9b, 0xdd, 0x5d, 0x33, 0x0d, 0xad, 0x1b, 0x3f, 0x4d, 0x23, 
+       0x26, 0xf7, 0x4f, 0x4c, 0xfd, 0x35, 0xbd, 0x33, 0x74, 0xd1, 0x91, 0x0f, 
+       0x75, 0x0d, 0x9f, 0xa6, 0xb7, 0xa6, 0x71, 0xb4, 0x64, 0x4d, 0xee, 0x9e, 
+       0x99, 0xba, 0x6b, 0x86, 0xcd, 0xd3, 0x4e, 0x44, 0xde, 0xea, 0x1b, 0x37, 
+       0x4d, 0x6f, 0x4c, 0xfd, 0x34, 0x64, 0x4d, 0xee, 0xa1, 0xb3, 0x0d, 0xad, 
+       0x1b, 0x38, 0xda, 0x32, 0x26, 0xf7, 0x4f, 0x4c, 0xe3, 0x6b, 0x46, 0xcd, 
+       0xd3, 0x4e, 0x44, 0xde, 0xea, 0xe9, 0x98, 0x6d, 0x70, 0xd9, 0xba, 0x68, 
+       0xc8, 0x87, 0xba, 0x86, 0xcd, 0xd3, 0x5d, 0xd3, 0x30, 0xda, 0x32, 0x26, 
+       0xf7, 0x57, 0x4c, 0xdd, 0x35, 0xa3, 0x67, 0x1b, 0x4e, 0x44, 0xde, 0xe9, 
+       0x1b, 0x38, 0xda, 0xde, 0x99, 0x86, 0xd1, 0x91, 0x37, 0xba, 0xba, 0x66, 
+       0x1b, 0x5c, 0x36, 0x6e, 0x9a, 0x32, 0x26, 0xf7, 0x57, 0x4c, 0xdd, 0x35, 
+       0xc3, 0x66, 0xe9, 0xa7, 0x22, 0x6f, 0x75, 0x74, 0xcc, 0x36, 0xb5, 0xe6, 
+       0xe9, 0xa3, 0x22, 0x1e, 0xea, 0xe9, 0x99, 0xeb, 0xba, 0x66, 0xe9, 0xa3, 
+       0x22, 0x6f, 0x75, 0x0d, 0x98, 0x6d, 0x70, 0xd9, 0x86, 0xd3, 0x91, 0x37, 
+       0xba, 0x86, 0xcd, 0xd3, 0x5a, 0x36, 0x61, 0xb4, 0x64, 0x4e, 0xea, 0xe9, 
+       0x9b, 0xa6, 0xbb, 0xa6, 0x6e, 0x9a, 0x32, 0x26, 0xf7, 0x53, 0xcc, 0x36, 
+       0xbb, 0xa6, 0x6e, 0x9a, 0x72, 0x26, 0xf7, 0x57, 0x4c, 0xc3, 0x6b, 0x46, 
+       0xcf, 0xd3, 0x46, 0x44, 0xde, 0xea, 0x1b, 0x37, 0x4d, 0x6f, 0x4c, 0xfd, 
+       0x34, 0x64, 0x43, 0xdd, 0x23, 0x67, 0xe9, 0xad, 0xe9, 0x98, 0x6d, 0x39, 
+       0x13, 0x7b, 0xab, 0xa6, 0x6e, 0x9a, 0xe1, 0xb3, 0x74, 0xd1, 0x91, 0x37, 
+       0xba, 0xba, 0x66, 0xe9, 0xad, 0x1b, 0x3f, 0x4d, 0x19, 0x13, 0x7b, 0xa4, 
+       0x6c, 0xfd, 0x35, 0xa3, 0x67, 0x1b, 0x4e, 0x44, 0xde, 0xe9, 0xe9, 0x9c, 
+       0x6d, 0x6f, 0x4c, 0xdd, 0x34, 0x64, 0x4d, 0xe0, 0x00, 0x00, 0x01, 0x04, 
+       0x2b, 0xf9, 0x95, 0x22, 0xf3, 0x74, 0xdf, 0xb8, 0xb5, 0xf3, 0x56, 0x91, 
+       0x7a, 0xce, 0x99, 0xc6, 0xc8, 0x64, 0x5d, 0x23, 0x67, 0xe9, 0xad, 0xe9, 
+       0x9b, 0xa6, 0x9c, 0x89, 0xdd, 0xe0, 0x19, 0xf4, 0x80, 0x54, 0x5f, 0x41, 
+       0x2c, 0x20, 0x61, 0x7f, 0x14, 0x4b, 0x25, 0xa5, 0x05, 0x27, 0x25, 0x25, 
+       0x20, 0x95, 0xd0, 0x58, 0xd2, 0xd1, 0xba, 0x33, 0x73, 0x4d, 0xb1, 0xe0, 
+       0x0c, 0x00, 0x60, 0x1a, 0x8d, 0xf2, 0x40, 0xaf, 0xc9, 0xcf, 0xbe, 0x01, 
+       0x88, 0x0e, 0xce, 0xe6, 0xe0, 0x0a, 0x6d, 0x1d, 0x32, 0x00, 0x36, 0xf8, 
+       0xa2, 0x62, 0x40, 0x74, 0xb0, 0x42, 0x08, 0x49, 0x29, 0x0f, 0xbe, 0x00, 
+       0xa3, 0xe1, 0xa1, 0x8c, 0xff, 0xff, 0x90, 0x8e, 0x06, 0x06, 0xba, 0xd2, 
+       0x57, 0xbe, 0x6c, 0x01, 0xa2, 0x51, 0xd0, 0x58, 0x67, 0xdb, 0xfd, 0xc9, 
+       0x79, 0xf6, 0xea, 0x3b, 0x91, 0x2f, 0x54, 0x03, 0x1c, 0x9d, 0x90, 0x5a, 
+       0x3f, 0xc6, 0x75, 0xde, 0xc0, 0x13, 0x10, 0xd2, 0x94, 0x16, 0x50, 0x6f, 
+       0x1a, 0x1a, 0x05, 0x0b, 0x49, 0x30, 0x37, 0xb3, 0x3f, 0x43, 0x74, 0x24, 
+       0x90, 0x84, 0x01, 0x72, 0xba, 0x0b, 0xe3, 0x30, 0xcd, 0xd2, 0xc6, 0x59, 
+       0x7a, 0x6d, 0x20, 0x21, 0xc0, 0x07, 0xc0, 0x19, 0xe4, 0xac, 0x60, 0x09, 
+       0xca, 0x18, 0x49, 0xed, 0x89, 0xa4, 0x27, 0xea, 0x43, 0x30, 0xd2, 0xc3, 
+       0x3f, 0x7e, 0xa5, 0x13, 0x31, 0xf0, 0x7b, 0xbc, 0x03, 0x10, 0x29, 0x88, 
+       0x40, 0x55, 0x39, 0x19, 0x90, 0x90, 0x94, 0x08, 0xe4, 0x0f, 0x72, 0x00, 
+       0xe8, 0x34, 0x0c, 0x86, 0x23, 0x9a, 0x77, 0xba, 0xba, 0x66, 0xe9, 0xad, 
+       0xe9, 0x9f, 0xa6, 0xec, 0xc8, 0xb9, 0xde, 0xe9, 0xe9, 0xb4, 0x13, 0x12, 
+       0x1a, 0x02, 0x14, 0x96, 0x80, 0x28, 0xdb, 0x06, 0x27, 0x20, 0xb2, 0x19, 
+       0x78, 0x68, 0x66, 0xfb, 0xf2, 0x59, 0x45, 0x6c, 0x94, 0x24, 0xb2, 0xd0, 
+       0xcc, 0x96, 0xf9, 0x08, 0xfd, 0x69, 0xe9, 0x47, 0x45, 0x79, 0x0d, 0x25, 
+       0xfc, 0x82, 0x83, 0x3e, 0xc1, 0xa4, 0xd2, 0xd0, 0x18, 0x57, 0x7d, 0xbe, 
+       0xe9, 0xeb, 0x2d, 0x19, 0x3b, 0xe4, 0xf0, 0xc2, 0x86, 0x86, 0x8c, 0x6e, 
+       0xf9, 0x1e, 0xf2, 0xc8, 0x41, 0x40, 0x3a, 0x0c, 0xdb, 0x29, 0x05, 0x63, 
+       0x7b, 0x87, 0xff, 0xd7, 0xff, 0x37, 0xf5, 0xfe, 0xbf, 0x78, 0x80, 0x0f, 
+       0x80, 0x34, 0x40, 0x6e, 0x03, 0x25, 0xa0, 0x6e, 0x76, 0x46, 0x1f, 0xf1, 
+       0xbc, 0x7e, 0xbd, 0x10, 0x10, 0x80, 0xef, 0x86, 0x32, 0x10, 0x7f, 0x27, 
+       0x55, 0x3d, 0x0c, 0x02, 0x02, 0x94, 0x35, 0x21, 0xa5, 0xf6, 0x67, 0xe5, 
+       0xa5, 0x0e, 0x02, 0x20, 0xce, 0x52, 0x1c, 0x5b, 0x55, 0x80, 0x1d, 0x13, 
+       0x07, 0x16, 0x09, 0x3f, 0x81, 0xa0, 0x9b, 0xfb, 0x64, 0x09, 0x4a, 0xdc, 
+       0x65, 0xeb, 0x50, 0x03, 0xb0, 0x1d, 0x97, 0x9c, 0x96, 0x56, 0x5a, 0x73, 
+       0x12, 0xbf, 0x5a, 0x3f, 0xdd, 0x60, 0x65, 0x6b, 0xc7, 0xaa, 0xf0, 0x53, 
+       0x8a, 0x2b, 0x3a, 0x7a, 0x36, 0x38, 0xe2, 0x05, 0xe9, 0xa0, 0x96, 0x5f, 
+       0x57, 0xe1, 0x75, 0x9f, 0x9d, 0x01, 0xb2, 0x05, 0x80, 0x46, 0x92, 0x0d, 
+       0x6f, 0x21, 0x15, 0xc0, 0x98, 0x24, 0xfe, 0x96, 0x00, 0x31, 0xbb, 0x24, 
+       0xb0, 0xd0, 0x26, 0x80, 0x08, 0xb8, 0x00, 0xc6, 0x80, 0x64, 0x37, 0x1a, 
+       0x8e, 0x1d, 0xf1, 0x17, 0xdb, 0x0d, 0x28, 0x7e, 0x0a, 0xb1, 0x18, 0x9b, 
+       0xce, 0x1a, 0x05, 0x06, 0xaf, 0x06, 0x96, 0x80, 0xa6, 0xaf, 0x46, 0x42, 
+       0x3e, 0x41, 0x7b, 0xef, 0xce, 0xe3, 0x52, 0xdc, 0x9e, 0x78, 0xab, 0x22, 
+       0x08, 0x60, 0x12, 0xe6, 0x17, 0xbb, 0xe2, 0x25, 0xc5, 0x2c, 0xac, 0x5a, 
+       0x3e, 0xeb, 0xde, 0xe6, 0xcc, 0x29, 0xae, 0x8e, 0xf8, 0xfb, 0xd2, 0x03, 
+       0x62, 0x85, 0x80, 0x46, 0x92, 0x0d, 0x00, 0x6c, 0x50, 0xee, 0x01, 0x1a, 
+       0x48, 0x37, 0xad, 0x1a, 0x43, 0x43, 0xa0, 0x97, 0x94, 0x3d, 0x0b, 0xf8, 
+       0xce, 0x49, 0x02, 0x34, 0x25, 0x93, 0x40, 0x25, 0x40, 0x04, 0x5c, 0x00, 
+       0x63, 0x69, 0xc6, 0x63, 0x70, 0x55, 0x8b, 0x26, 0xfc, 0x98, 0x0d, 0x8a, 
+       0x1d, 0xc0, 0x23, 0x49, 0x06, 0x80, 0x36, 0x28, 0x77, 0x00, 0x8d, 0x24, 
+       0x1b, 0xd7, 0x92, 0xc8, 0x60, 0x12, 0xa0, 0x02, 0x2e, 0x00, 0x31, 0x89, 
+       0x2c, 0x86, 0x01, 0x2a, 0x00, 0x22, 0x00, 0x17, 0xd4, 0xe5, 0x0f, 0xc1, 
+       0x54, 0x8c, 0x4d, 0xf9, 0x40, 0x0b, 0x0a, 0x30, 0x6e, 0x01, 0x1f, 0xe4, 
+       0x7f, 0xf5, 0xd9, 0x2c, 0x31, 0x25, 0x2f, 0x04, 0xe0, 0x08, 0x44, 0xa9, 
+       0x5c, 0x39, 0xab, 0xb1, 0x0c, 0x35, 0x07, 0xb2, 0x44, 0x2b, 0x91, 0xfd, 
+       0xe3, 0xd0, 0x9c, 0x5e, 0xdf, 0x1b, 0x0c, 0x8e, 0x3b, 0xd6, 0x12, 0x8a, 
+       0x9f, 0xdd, 0xe2, 0xf2, 0x76, 0xfb, 0xef, 0xbe, 0x7d, 0xf2, 0xf7, 0xdc, 
+       0xdd, 0xe2, 0xf5, 0x00, 0x1a, 0x86, 0x00, 0x1f, 0x00, 0xef, 0xa3, 0x12, 
+       0x03, 0x77, 0x0d, 0x28, 0x53, 0x7d, 0x8b, 0x46, 0x51, 0xdb, 0xfd, 0xf3, 
+       0xf0, 0x3e, 0xd9, 0x5a, 0xdc, 0xf7, 0xb6, 0x1a, 0x18, 0x06, 0x13, 0xf0, 
+       0x50, 0x8b, 0xc9, 0x28, 0xb2, 0x5a, 0x37, 0xba, 0x9e, 0x77, 0xad, 0x79, 
+       0x9e, 0xd1, 0xa8, 0xba, 0x9e, 0x67, 0xae, 0x79, 0x9e, 0x86, 0x9d, 0xd4, 
+       0xf3, 0x3d, 0x6b, 0xce, 0xf4, 0x34, 0xee, 0x97, 0x9d, 0xeb, 0x5e, 0x77, 
+       0xa1, 0xa7, 0x74, 0xbc, 0xef, 0x5a, 0xf3, 0xbd, 0x2d, 0x17, 0x4b, 0xce, 
+       0xf5, 0xaf, 0x33, 0xd0, 0xd3, 0xba, 0x9e, 0x67, 0xad, 0x79, 0xde, 0x86, 
+       0x9d, 0xd4, 0xf3, 0x3d, 0x6b, 0xce, 0xf4, 0xb4, 0xee, 0x97, 0x9d, 0xeb, 
+       0x5e, 0x67, 0xa1, 0xa7, 0x75, 0x3c, 0xef, 0x5a, 0xf3, 0x3d, 0x0d, 0x3b, 
+       0xa9, 0xe6, 0x7a, 0xe7, 0x99, 0xe9, 0x69, 0xdd, 0x4f, 0x33, 0xd6, 0xbc, 
+       0xef, 0x43, 0x45, 0xd4, 0xf3, 0x3d, 0x6b, 0xcc, 0xf4, 0x34, 0xee, 0xa7, 
+       0x99, 0xeb, 0x9e, 0x67, 0xa5, 0xa7, 0x75, 0x3c, 0xcf, 0x5c, 0xf3, 0x3d, 
+       0x0d, 0x3b, 0xa9, 0xe6, 0x7a, 0xd7, 0x9d, 0xe8, 0x69, 0xdd, 0x2f, 0x33, 
+       0xd7, 0x3c, 0xcf, 0x4b, 0x4e, 0xea, 0x79, 0x9e, 0xb9, 0xe6, 0x7a, 0x1a, 
+       0x2e, 0xa7, 0x99, 0xeb, 0x5e, 0x67, 0xa1, 0xa7, 0x75, 0x3c, 0xef, 0x5a, 
+       0xf3, 0x3d, 0x2d, 0x3b, 0xa9, 0xe6, 0x7a, 0xe7, 0x99, 0xe8, 0x69, 0xdd, 
+       0x4f, 0x33, 0xd7, 0x3c, 0xcf, 0x43, 0x4e, 0xea, 0x79, 0x9e, 0xb5, 0xe7, 
+       0x7a, 0x5a, 0x77, 0x4b, 0xce, 0xf5, 0xaf, 0x33, 0xd0, 0xd1, 0x75, 0x3c, 
+       0xef, 0x5a, 0xf3, 0x3d, 0x0d, 0x3b, 0xa9, 0xe6, 0x7a, 0xe7, 0x99, 0xe9, 
+       0x69, 0xdd, 0x2f, 0x3b, 0xd6, 0xbc, 0xef, 0x43, 0x4e, 0xe9, 0x79, 0xde, 
+       0xb5, 0xe6, 0x7a, 0x1a, 0x77, 0x53, 0xce, 0xf5, 0xaf, 0x33, 0xd2, 0xd3, 
+       0xba, 0x9e, 0x67, 0xac, 0x8d, 0x2d, 0x16, 0xaf, 0x17, 0x8d, 0x29, 0x9c, 
+       0x00, 0x00, 0x01, 0x05, 0x3b, 0xf9, 0xd3, 0xce, 0xf5, 0xaf, 0x33, 0xdf, 
+       0xb5, 0x35, 0xf3, 0x97, 0x53, 0xcc, 0xf5, 0xcf, 0x33, 0xd2, 0xd1, 0x75, 
+       0x3c, 0xcf, 0x5a, 0xf3, 0xbd, 0x0d, 0x3b, 0xa9, 0xe7, 0x28, 0xb0, 0x1b, 
+       0x00, 0xec, 0x31, 0x26, 0x06, 0xb9, 0x31, 0x21, 0x06, 0x9e, 0x57, 0xe9, 
+       0x1d, 0xbb, 0xa3, 0x61, 0xe1, 0xb5, 0x8f, 0x68, 0x26, 0xee, 0x59, 0x49, 
+       0xdb, 0xed, 0xc0, 0xf7, 0xf9, 0xc6, 0x27, 0x2d, 0x09, 0x61, 0xe8, 0xed, 
+       0xf0, 0xbd, 0x71, 0x01, 0x44, 0x81, 0x80, 0xd4, 0x24, 0xc3, 0xb8, 0x8f, 
+       0x6e, 0x1a, 0x82, 0x5a, 0x3e, 0xab, 0x78, 0x80, 0x3b, 0x47, 0x48, 0x67, 
+       0xf9, 0x7d, 0x01, 0x38, 0xc7, 0x63, 0x03, 0xf3, 0xe0, 0xfb, 0xab, 0xbe, 
+       0xfb, 0x7d, 0xdf, 0x73, 0x6f, 0x3a, 0x52, 0x49, 0x68, 0xdc, 0x7e, 0x5e, 
+       0x1d, 0xcd, 0xc1, 0xdd, 0x57, 0xae, 0xc0, 0x5b, 0xa4, 0xa0, 0x25, 0xc5, 
+       0x12, 0x3a, 0xd0, 0x1c, 0xca, 0x0e, 0xf7, 0x8a, 0x43, 0x43, 0xa3, 0x71, 
+       0xc4, 0x6b, 0xd2, 0x26, 0x76, 0x7d, 0x53, 0xe3, 0x79, 0x9e, 0xb9, 0xeb, 
+       0x12, 0x9c, 0x8d, 0xff, 0xfb, 0x6c, 0xbf, 0xfa, 0xb6, 0xc3, 0xfd, 0xef, 
+       0x64, 0x3d, 0xe4, 0xbd, 0x6d, 0xce, 0x2b, 0x3a, 0x5c, 0x09, 0x62, 0x7f, 
+       0x20, 0x62, 0x3c, 0x84, 0x34, 0xb3, 0x6c, 0x69, 0x1b, 0x00, 0xfa, 0xe6, 
+       0x86, 0x66, 0x46, 0x73, 0xfa, 0xf8, 0x07, 0xc7, 0x5e, 0xb5, 0x3c, 0x6a, 
+       0x53, 0xc7, 0x2b, 0x11, 0x40, 0x3e, 0xbe, 0x47, 0x04, 0x6b, 0xdd, 0x3f, 
+       0xd4, 0xfe, 0x02, 0xfa, 0x09, 0x33, 0xa8, 0xb0, 0x47, 0xfb, 0x21, 0x82, 
+       0x5f, 0xd9, 0x77, 0xcf, 0xaf, 0xa3, 0x06, 0x70, 0x1c, 0x82, 0x3f, 0xd9, 
+       0x0c, 0x12, 0xfe, 0xd3, 0x74, 0x80, 0x9c, 0x10, 0xc1, 0x81, 0x23, 0xf7, 
+       0x11, 0xbd, 0x00, 0x30, 0x24, 0xf1, 0x17, 0x8d, 0xee, 0x70, 0x60, 0x05, 
+       0xc0, 0x21, 0x26, 0x95, 0x83, 0x71, 0x41, 0x85, 0x15, 0xf3, 0x80, 0x52, 
+       0x5a, 0x50, 0x5b, 0x0f, 0x19, 0x83, 0x0b, 0x65, 0x2f, 0x64, 0xf4, 0x73, 
+       0x3e, 0xd7, 0x7c, 0x9a, 0x9d, 0x8a, 0x2d, 0xd7, 0x86, 0x77, 0x10, 0xd7, 
+       0x9b, 0x21, 0x80, 0x27, 0x2c, 0xa4, 0x96, 0x05, 0x39, 0x34, 0x07, 0x41, 
+       0x84, 0xde, 0x4d, 0x6c, 0x94, 0xe2, 0xf9, 0x41, 0x85, 0x37, 0x7c, 0x5e, 
+       0x35, 0x39, 0x38, 0x61, 0xed, 0xdd, 0xec, 0x00, 0x1e, 0x00, 0x60, 0x80, 
+       0x18, 0xe2, 0xfa, 0x51, 0xd2, 0x30, 0xcc, 0x47, 0xb9, 0x08, 0x64, 0xd2, 
+       0x53, 0x2d, 0xab, 0x4e, 0xe7, 0x37, 0x7c, 0x1d, 0xc7, 0x72, 0x27, 0xb8, 
+       0x84, 0x24, 0x9e, 0x81, 0xc7, 0x08, 0x1c, 0x1c, 0x40, 0xaf, 0x21, 0xfe, 
+       0x94, 0x10, 0x8b, 0x3b, 0xa7, 0x37, 0x0a, 0x7b, 0xa0, 0x19, 0xd8, 0x35, 
+       0x02, 0xdf, 0x13, 0x9c, 0x89, 0x57, 0x41, 0x29, 0x2c, 0x67, 0xb5, 0xeb, 
+       0xd5, 0x47, 0x28, 0x01, 0xe9, 0x4d, 0x83, 0x09, 0xb8, 0x0f, 0x24, 0x94, 
+       0x93, 0xc9, 0x05, 0xf6, 0xc1, 0x45, 0xb1, 0xe7, 0x6f, 0xee, 0xfb, 0xde, 
+       0x34, 0x03, 0x32, 0x80, 0xc8, 0x0c, 0x08, 0x68, 0xca, 0xe4, 0xa4, 0xb7, 
+       0xc7, 0xef, 0xb3, 0xef, 0x83, 0xef, 0x6f, 0xb2, 0x53, 0xb7, 0xc8, 0x4f, 
+       0xdf, 0x3e, 0x57, 0x36, 0xe9, 0x02, 0xa5, 0x38, 0xc4, 0xe1, 0x1c, 0x3a, 
+       0x60, 0x51, 0x2a, 0xfb, 0xda, 0x3c, 0xef, 0x02, 0x51, 0x60, 0x49, 0x21, 
+       0x5e, 0x7e, 0x50, 0x6a, 0xc0, 0xb1, 0x7d, 0x87, 0x21, 0x3f, 0x85, 0x6d, 
+       0xff, 0xbb, 0xc9, 0x49, 0x1d, 0xee, 0x21, 0xb8, 0x7e, 0xb8, 0xf7, 0x97, 
+       0x00, 0x70, 0xe0, 0x60, 0x9a, 0xee, 0x08, 0x7f, 0x6a, 0x70, 0x47, 0x04, 
+       0x28, 0xe1, 0x44, 0x57, 0xbd, 0x08, 0x60, 0x09, 0x80, 0x76, 0x43, 0x61, 
+       0x85, 0x72, 0xb7, 0x3d, 0x21, 0xbb, 0xa1, 0x81, 0x20, 0x0f, 0xb1, 0x7f, 
+       0x8a, 0x6d, 0x85, 0xeb, 0xc2, 0x10, 0x88, 0x48, 0x02, 0x81, 0x88, 0x0c, 
+       0x41, 0xd8, 0xd1, 0xff, 0xf7, 0xff, 0xb3, 0x67, 0xfd, 0x7e, 0xf3, 0x08, 
+       0x41, 0x9d, 0x0f, 0xfa, 0x9f, 0x33, 0x5e, 0x81, 0x0c, 0xbd, 0xf3, 0x65, 
+       0xdf, 0xa4, 0x00, 0x66, 0xa0, 0x03, 0xee, 0x00, 0x29, 0x4e, 0x13, 0xb9, 
+       0x49, 0xec, 0x17, 0xb7, 0x0e, 0xbd, 0x2b, 0xdc, 0xa0, 0x07, 0x64, 0xdc, 
+       0x1a, 0x43, 0x28, 0xac, 0xbc, 0x4c, 0xdd, 0x3c, 0xf2, 0x4a, 0x73, 0x61, 
+       0x44, 0x87, 0x71, 0x3a, 0xee, 0xbc, 0xc0, 0x77, 0x82, 0x76, 0x1c, 0x85, 
+       0xd8, 0x10, 0x94, 0x55, 0xd4, 0xf3, 0x3d, 0x6b, 0xce, 0xf7, 0x6b, 0x5c, 
+       0xd7, 0x4b, 0xce, 0xf5, 0xaf, 0x3b, 0xd0, 0xd3, 0xba, 0x5e, 0x77, 0xad, 
+       0x79, 0x9e, 0x86, 0x9d, 0xd4, 0xf3, 0x3d, 0x6b, 0xce, 0xf4, 0xb4, 0x5d, 
+       0x4f, 0x33, 0xd6, 0xbc, 0xef, 0x43, 0x4e, 0xe9, 0x79, 0xde, 0xb5, 0xe7, 
+       0x7a, 0x1a, 0x77, 0x4b, 0xce, 0xf5, 0xaf, 0x33, 0xd2, 0xd3, 0xba, 0x9e, 
+       0x67, 0xae, 0x79, 0x9e, 0x86, 0x9d, 0xd4, 0xf3, 0x3d, 0x6b, 0xce, 0xf4, 
+       0x34, 0xee, 0xa7, 0x99, 0xeb, 0x5e, 0x67, 0xa5, 0xa2, 0xea, 0x79, 0x9e, 
+       0xb9, 0xe7, 0x7a, 0x1a, 0x77, 0x4b, 0xcc, 0xf5, 0xcf, 0x33, 0xd0, 0xd3, 
+       0xba, 0x9e, 0x67, 0xad, 0x79, 0xde, 0x96, 0x9d, 0xd2, 0xf3, 0x3d, 0x73, 
+       0xcc, 0xf4, 0x34, 0xee, 0xa7, 0x9d, 0xeb, 0x5e, 0x67, 0xa1, 0xa7, 0x75, 
+       0x3c, 0xcf, 0x5a, 0xf3, 0xbd, 0x2d, 0x17, 0x4b, 0xce, 0xf5, 0xaf, 0x3b, 
+       0xd0, 0xd3, 0xba, 0x5e, 0x77, 0xad, 0x79, 0x9e, 0x86, 0x9d, 0xd4, 0xf3, 
+       0x3d, 0x73, 0xcc, 0xf4, 0xb4, 0xee, 0xa7, 0x99, 0xeb, 0x5e, 0x77, 0xa1, 
+       0xa7, 0x74, 0xbc, 0xef, 0x5a, 0xf3, 0x3d, 0x0d, 0x3b, 0xa9, 0xe7, 0x7a, 
+       0xd7, 0x99, 0xe9, 0x69, 0xdd, 0x4f, 0x33, 0xd7, 0x3c, 0xcf, 0x43, 0x45, 
+       0xd4, 0xf3, 0x3d, 0x6b, 0xce, 0xf4, 0x34, 0xee, 0x97, 0x9d, 0xeb, 0x5e, 
+       0x77, 0xa5, 0xa7, 0x74, 0xbc, 0xef, 0x5a, 0xf3, 0x3d, 0x0d, 0x3b, 0xa9, 
+       0xe6, 0x7a, 0xca, 0x52, 0xd3, 0xb5, 0x79, 0xa9, 0x49, 0x45, 0xca, 0x52, 
+       0x91, 0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, 0x11, 0x00, 0x00, 0x00, 
+       0x01, 0x06, 0x43, 0xf1, 0x4f, 0x33, 0xd6, 0xbc, 0xef, 0x7e, 0xc4, 0xd7, 
+       0xcf, 0x5d, 0x2f, 0x3b, 0xd6, 0xbc, 0xcf, 0x43, 0x4e, 0xea, 0x79, 0xde, 
+       0xb5, 0xe6, 0x7a, 0x1a, 0x77, 0x53, 0xcc, 0xf5, 0xaf, 0x3b, 0xd2, 0xd3, 
+       0xad, 0xf9, 0x04, 0xce, 0xdf, 0xe0, 0xfc, 0x45, 0x27, 0xac, 0x3e, 0xb4, 
+       0x9b, 0x99, 0x3b, 0x8f, 0x57, 0x23, 0xe2, 0x2d, 0xd3, 0x71, 0x9f, 0x71, 
+       0xc4, 0x63, 0x48, 0x21, 0xd7, 0x94, 0x0d, 0x40, 0xd4, 0x7c, 0x69, 0x82, 
+       0x71, 0xd7, 0xcd, 0x60, 0x9c, 0x4e, 0xbd, 0x9e, 0xaf, 0x54, 0x7a, 0x97, 
+       0xa1, 0x48, 0x00, 0xb4, 0x9a, 0x18, 0x18, 0x3f, 0x8d, 0x4a, 0xd8, 0x20, 
+       0xa6, 0xfc, 0x4e, 0xfd, 0x91, 0xf0, 0xe4, 0xda, 0xd0, 0x58, 0xd4, 0x64, 
+       0xe7, 0x5e, 0xdd, 0x67, 0xae, 0xf3, 0xe0, 0x0f, 0x8a, 0x72, 0x59, 0x34, 
+       0x7f, 0x25, 0x21, 0x6a, 0x64, 0x3b, 0xfd, 0x8c, 0xfd, 0x2f, 0xb8, 0xf1, 
+       0x9a, 0xf4, 0x40, 0xa0, 0x60, 0x17, 0x58, 0x1e, 0x58, 0x7d, 0xc4, 0x4d, 
+       0x2d, 0x8a, 0x13, 0x7c, 0xa0, 0x13, 0x93, 0x40, 0x2c, 0x21, 0x16, 0x9c, 
+       0x7b, 0x32, 0x30, 0xf4, 0xf5, 0x6c, 0xb1, 0xfe, 0xf7, 0x28, 0xc4, 0xa0, 
+       0xcc, 0x83, 0xfe, 0xcb, 0xec, 0xcf, 0xcd, 0xec, 0x2f, 0xaf, 0xdc, 0x80, 
+       0x07, 0xa1, 0xa4, 0xb2, 0x68, 0x61, 0x33, 0x1c, 0x80, 0x14, 0xa5, 0x4e, 
+       0x29, 0x18, 0x52, 0xc2, 0x77, 0x01, 0xe5, 0xd8, 0xfe, 0xf3, 0xc0, 0xaf, 
+       0x59, 0x45, 0x00, 0x8f, 0x89, 0xf7, 0x41, 0x31, 0x0c, 0x57, 0xe3, 0xef, 
+       0xe6, 0x97, 0xd3, 0x11, 0xc0, 0x72, 0x08, 0xff, 0x84, 0xd0, 0x4b, 0xfc, 
+       0x4d, 0xf3, 0x4b, 0xea, 0x3b, 0xac, 0xb0, 0x47, 0xfc, 0x26, 0x82, 0x5f, 
+       0xe2, 0x68, 0x02, 0xab, 0x0c, 0x01, 0x1e, 0x23, 0x7b, 0x60, 0x1d, 0x12, 
+       0x78, 0x8b, 0xcf, 0xca, 0x01, 0xbe, 0x4a, 0x3b, 0xe4, 0x0c, 0x4f, 0x0d, 
+       0x4e, 0x01, 0x21, 0x5b, 0x71, 0xa9, 0x16, 0xd8, 0xec, 0xcb, 0xff, 0x6b, 
+       0xe9, 0xc0, 0x54, 0x01, 0xb9, 0x41, 0xa9, 0x0d, 0x0d, 0xc4, 0xc0, 0x2e, 
+       0x92, 0xd3, 0xf3, 0x99, 0xbf, 0xff, 0x28, 0xe7, 0x61, 0xd7, 0x89, 0x00, 
+       0x6e, 0x05, 0x4b, 0x00, 0xd0, 0x01, 0xd0, 0x40, 0x42, 0x40, 0x35, 0x4b, 
+       0x3e, 0x3c, 0x02, 0xc2, 0x97, 0x97, 0xbf, 0x24, 0xfe, 0xfb, 0x81, 0x24, 
+       0xdf, 0x6b, 0xbe, 0x60, 0x03, 0x04, 0x24, 0x06, 0x01, 0x8a, 0xff, 0x74, 
+       0xaf, 0xc1, 0x35, 0x8b, 0xeb, 0x32, 0xbc, 0xfc, 0xd6, 0x4c, 0xe0, 0x16, 
+       0x90, 0x80, 0x7a, 0x82, 0x21, 0x07, 0x5b, 0xca, 0x01, 0xca, 0x04, 0xf2, 
+       0x37, 0xbd, 0x80, 0x06, 0x45, 0xf0, 0x32, 0x30, 0x7e, 0x23, 0x5f, 0x1c, 
+       0x49, 0x4c, 0x1a, 0xe6, 0x33, 0x87, 0x9c, 0x46, 0xba, 0x13, 0x9d, 0x70, 
+       0x65, 0x63, 0x8d, 0x24, 0x8e, 0x23, 0xda, 0x03, 0x3e, 0xe5, 0x21, 0xbe, 
+       0x3b, 0x4c, 0x43, 0x48, 0xc0, 0xc2, 0xf8, 0xed, 0xef, 0xaa, 0x64, 0x6d, 
+       0x99, 0x6a, 0x1d, 0xfe, 0x22, 0xde, 0xab, 0xed, 0x96, 0xf6, 0xe8, 0x43, 
+       0xd5, 0x3f, 0x1c, 0x7c, 0x3f, 0x49, 0x7d, 0x98, 0x95, 0xb0, 0x1f, 0x0d, 
+       0x04, 0x9f, 0xb0, 0xd0, 0x01, 0x8d, 0xc2, 0x01, 0xd0, 0x05, 0xe5, 0x80, 
+       0xe9, 0x3b, 0x06, 0xfe, 0x59, 0x33, 0x9e, 0x9e, 0x49, 0x0c, 0x16, 0x2d, 
+       0x03, 0xcd, 0xc7, 0xae, 0xac, 0x92, 0x92, 0xad, 0xfe, 0xe2, 0xec, 0xd2, 
+       0x4c, 0xfb, 0x56, 0xfe, 0x09, 0x25, 0xa1, 0x07, 0xf6, 0xbd, 0x59, 0x60, 
+       0x55, 0xd4, 0x80, 0xbe, 0x03, 0x5b, 0xc9, 0xa5, 0x18, 0xbc, 0xde, 0xfa, 
+       0x71, 0x68, 0xe5, 0x76, 0xe1, 0xdc, 0x78, 0x0b, 0xe8, 0x76, 0xab, 0x35, 
+       0x53, 0xab, 0x29, 0xcd, 0xff, 0xbf, 0xfc, 0xff, 0xc7, 0xfa, 0x3d, 0x6b, 
+       0xcc, 0xf7, 0xab, 0xdf, 0xf7, 0xf7, 0x93, 0x75, 0x3c, 0xef, 0x5a, 0xf3, 
+       0x3c, 0x9a, 0x77, 0x53, 0xcc, 0xf5, 0xcf, 0x33, 0xd0, 0xd1, 0x75, 0x3c, 
+       0xcf, 0x5c, 0xf3, 0x3d, 0x2d, 0x3b, 0xa5, 0xe7, 0x7a, 0xd7, 0x9d, 0xe8, 
+       0x69, 0xdd, 0x2f, 0x3b, 0xd6, 0xbc, 0xcf, 0x43, 0x4e, 0xea, 0x79, 0xde, 
+       0xb1, 0xe7, 0x7a, 0x5a, 0x77, 0x53, 0xcc, 0xf5, 0xaf, 0x3b, 0xd0, 0xd3, 
+       0xba, 0x5e, 0x67, 0xae, 0x79, 0x9e, 0x86, 0x8b, 0xa9, 0xe7, 0x7a, 0xd7, 
+       0x99, 0xe9, 0x69, 0xdd, 0x4f, 0x33, 0xd6, 0xbc, 0xef, 0x43, 0x4e, 0xe9, 
+       0x79, 0xde, 0xb5, 0xe7, 0x7a, 0x1a, 0x77, 0x4b, 0xce, 0xf5, 0xaf, 0x33, 
+       0xd2, 0xd3, 0xba, 0x9e, 0x67, 0xad, 0x79, 0xde, 0x86, 0x9d, 0xd2, 0xf3, 
+       0xbd, 0x6b, 0xce, 0xf4, 0x34, 0x5d, 0x2f, 0x3b, 0xd6, 0xbc, 0xcf, 0x4b, 
+       0x4e, 0xea, 0x79, 0xde, 0xb5, 0xe6, 0x7a, 0x1a, 0x77, 0x53, 0xcc, 0xf5, 
+       0xcf, 0x33, 0xd0, 0xd3, 0xba, 0x9e, 0x67, 0xad, 0x79, 0xde, 0x96, 0x9d, 
+       0xd2, 0xf3, 0xbd, 0x6b, 0xcc, 0xf4, 0x34, 0xee, 0xa7, 0x99, 0xeb, 0x9e, 
+       0x67, 0xa1, 0xa7, 0x75, 0x3c, 0xcf, 0x5c, 0xf3, 0x3d, 0x2d, 0x17, 0x53, 
+       0xcc, 0xf5, 0xaf, 0x3b, 0xd0, 0xd3, 0xba, 0x5e, 0x77, 0xad, 0x78, 0xd2, 
+       0xd3, 0xba, 0xde, 0x67, 0x9e, 0x94, 0xce, 0xc4, 0x69, 0x49, 0x45, 0xca, 
+       0x52, 0x91, 0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94, 
+       0xa4, 0x45, 0xca, 0x52, 0x91, 0x17, 0x29, 0x4a, 0x44, 0x40, 0x00, 0x00, 
+       0x01, 0x07, 0x4b, 0xf3, 0x2f, 0x33, 0xd6, 0xbc, 0xef, 0x7e, 0xb2, 0xd7, 
+       0x83, 0x74, 0xbc, 0xef, 0x5a, 0xf3, 0xbd, 0x0d, 0x3b, 0xa5, 0xe7, 0x7a, 
+       0xd7, 0x9d, 0xe9, 0x69, 0xdd, 0x2f, 0x3b, 0xd6, 0xbc, 0xcf, 0x43, 0x4e, 
+       0xd3, 0xb7, 0xdf, 0x76, 0xfb, 0xab, 0xee, 0x77, 0xbc, 0x80, 0x06, 0x41, 
+       0xa0, 0x64, 0x85, 0xb7, 0x3c, 0xae, 0x4a, 0xef, 0xf7, 0xea, 0x3b, 0xf6, 
+       0x35, 0x28, 0x6e, 0x1f, 0x7b, 0x47, 0xb9, 0x40, 0x19, 0x06, 0x92, 0x88, 
+       0x63, 0x3f, 0x50, 0x6e, 0x6d, 0x80, 0x7a, 0x87, 0xea, 0x12, 0x87, 0x71, 
+       0x1a, 0xf3, 0x40, 0xa7, 0x6f, 0xf0, 0xf2, 0x3e, 0xbb, 0x48, 0x68, 0x74, 
+       0x38, 0xea, 0xe3, 0x7a, 0x70, 0xc4, 0xfd, 0x87, 0xbe, 0x0f, 0x03, 0xb7, 
+       0x83, 0xd8, 0x62, 0x36, 0x1d, 0xad, 0xa9, 0x28, 0x69, 0x68, 0x48, 0xe7, 
+       0x0f, 0xb4, 0x90, 0xd2, 0xc8, 0x17, 0xd5, 0x79, 0x3b, 0x5e, 0x8b, 0xbb, 
+       0x55, 0x3e, 0xc3, 0x86, 0x80, 0xdc, 0xb2, 0xc3, 0x40, 0x25, 0x4f, 0x65, 
+       0x01, 0xec, 0xac, 0x27, 0x75, 0xa7, 0x09, 0x8b, 0xd7, 0xab, 0x66, 0xca, 
+       0xd8, 0xc8, 0x1f, 0x7a, 0xff, 0x92, 0xcb, 0x71, 0xfe, 0xf2, 0x19, 0x90, 
+       0xc2, 0xaf, 0xe4, 0xf7, 0xde, 0x40, 0x4e, 0x43, 0x0d, 0x03, 0x21, 0xa1, 
+       0x85, 0x27, 0x14, 0x12, 0x5f, 0x4a, 0x59, 0xd2, 0xb0, 0xd2, 0xd4, 0x83, 
+       0x31, 0xc7, 0xf1, 0xea, 0xbe, 0x0f, 0x7d, 0x30, 0x10, 0x46, 0x8b, 0x48, 
+       0x15, 0x26, 0x96, 0x94, 0x95, 0x90, 0x1b, 0xdd, 0x5d, 0x3d, 0x2e, 0x37, 
+       0xf5, 0xee, 0x8d, 0xb2, 0xbb, 0x2b, 0x99, 0x7c, 0xb0, 0x09, 0x80, 0x2c, 
+       0xe1, 0xb8, 0x02, 0xd2, 0x46, 0xef, 0x7b, 0xa3, 0x7a, 0x32, 0x58, 0xfa, 
+       0xf3, 0xd6, 0xb0, 0x0d, 0xd9, 0x09, 0xe0, 0x2a, 0xe3, 0x80, 0xe5, 0xf4, 
+       0x94, 0xa3, 0xa3, 0xfd, 0xcc, 0x55, 0xf0, 0xa0, 0x03, 0xce, 0x3f, 0x5c, 
+       0x20, 0x03, 0xc2, 0x11, 0xfd, 0x0f, 0xc4, 0xf6, 0xbe, 0x5c, 0xbe, 0x51, 
+       0x9e, 0xd3, 0x57, 0xff, 0x58, 0x01, 0xd7, 0x25, 0x7c, 0x5f, 0x17, 0xaf, 
+       0x90, 0xa0, 0x87, 0x8b, 0x4f, 0xc7, 0x0e, 0x89, 0x34, 0x98, 0x35, 0x86, 
+       0x66, 0xd8, 0xca, 0xdc, 0x4c, 0x28, 0x31, 0x69, 0x5f, 0xc1, 0x5a, 0xcc, 
+       0x0b, 0x74, 0x0b, 0xad, 0x6f, 0x54, 0xa0, 0x03, 0x72, 0x68, 0xc2, 0x90, 
+       0x19, 0xba, 0xcb, 0x4e, 0x70, 0x15, 0x21, 0x7c, 0x2c, 0xe0, 0xfb, 0xdb, 
+       0xbc, 0xa4, 0x32, 0x1f, 0x01, 0xd9, 0x37, 0x93, 0x7b, 0xe7, 0x52, 0x37, 
+       0x03, 0xc8, 0x20, 0xf3, 0xf8, 0x76, 0x32, 0xd2, 0xf7, 0xe8, 0xe4, 0xdf, 
+       0xbf, 0x6c, 0x72, 0x15, 0x78, 0x41, 0x88, 0x19, 0xae, 0xa7, 0x99, 0xeb, 
+       0x9e, 0x67, 0xba, 0x5a, 0xc2, 0xb4, 0xf1, 0x00, 0x3a, 0x0d, 0x01, 0x27, 
+       0x01, 0x11, 0x00, 0x81, 0x79, 0xc2, 0x68, 0x60, 0x19, 0x43, 0x1a, 0x67, 
+       0x17, 0x7d, 0x75, 0xcd, 0xc7, 0xe5, 0xe1, 0x37, 0xc8, 0x09, 0x81, 0xb8, 
+       0x35, 0x0e, 0x1d, 0xc4, 0xde, 0x7d, 0xea, 0x27, 0x35, 0x55, 0xf5, 0xbe, 
+       0xf9, 0xf7, 0xdf, 0x7c, 0xaf, 0xb9, 0xfb, 0xe3, 0xf4, 0xef, 0x78, 0xd0, 
+       0x07, 0x20, 0x3a, 0x19, 0xc0, 0x2d, 0x46, 0x3d, 0xb1, 0x6d, 0x8d, 0x3b, 
+       0x24, 0x89, 0xc3, 0x85, 0xde, 0xe9, 0xed, 0xfa, 0x1a, 0xe1, 0x42, 0x46, 
+       0xeb, 0xa5, 0xe7, 0x7a, 0xd7, 0x9d, 0xe6, 0xd6, 0x77, 0x4b, 0xce, 0xf5, 
+       0xaf, 0x33, 0xd2, 0xd3, 0xba, 0x9e, 0x67, 0xae, 0x79, 0x9e, 0x86, 0x9d, 
+       0xd4, 0xf3, 0x3d, 0x6b, 0xce, 0xf4, 0x34, 0xee, 0x97, 0x9d, 0xeb, 0x5e, 
+       0x77, 0xa5, 0xa7, 0x74, 0xbc, 0xef, 0x5a, 0xf3, 0x3d, 0x0d, 0x17, 0x53, 
+       0xcc, 0xf5, 0xcf, 0x33, 0xd0, 0xd3, 0xba, 0x5e, 0x77, 0xad, 0x79, 0x9e, 
+       0x96, 0x9d, 0xd4, 0xf3, 0xbd, 0x6b, 0xcc, 0xf4, 0x34, 0xee, 0xa7, 0x99, 
+       0xeb, 0x5e, 0x77, 0xa1, 0xa7, 0x75, 0x3c, 0xcf, 0x5a, 0xf3, 0xbd, 0x2d, 
+       0x3b, 0xa5, 0xe7, 0x7a, 0xd7, 0x99, 0xe8, 0x69, 0xdd, 0x4f, 0x3b, 0xd6, 
+       0xbc, 0xcf, 0x43, 0x45, 0xd4, 0xf3, 0x3d, 0x73, 0xcc, 0xf4, 0xb4, 0xee, 
+       0xa7, 0x99, 0xeb, 0x9e, 0x67, 0xa1, 0xa7, 0x74, 0xbc, 0xef, 0x5a, 0xf3, 
+       0x3d, 0x0d, 0x3b, 0xa9, 0xe6, 0x7a, 0xe7, 0x99, 0xe9, 0x69, 0xdd, 0x4f, 
+       0x33, 0xd7, 0x3c, 0xcf, 0x43, 0x4e, 0xea, 0x79, 0x9e, 0xb5, 0xe7, 0x7a, 
+       0x1a, 0x77, 0x53, 0xcc, 0xf5, 0xb1, 0xa5, 0xa2, 0xeb, 0x79, 0x9e, 0x6a, 
+       0x4a, 0x77, 0x29, 0x4a, 0x4a, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 
+       0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 
+       0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 
+       0x52, 0x94, 0x88, 0x80, 0x00, 0x00, 0x01, 0x08, 0x53, 0xe4, 0x3c, 0xef, 
+       0x5a, 0xf3, 0x3d, 0xfa, 0x83, 0x5e, 0x25, 0xd4, 0xf3, 0x3d, 0x6b, 0xce, 
+       0xf4, 0xb4, 0xee, 0xa7, 0x99, 0xeb, 0x5e, 0x77, 0xa1, 0xa7, 0x74, 0xbc, 
+       0xef, 0x69, 0x49, 0xc1, 0x41, 0xf9, 0xd7, 0x8c, 0x67, 0xca, 0x73, 0x2d, 
+       0x3c, 0x6a, 0x0b, 0x42, 0x3a, 0x73, 0xaf, 0xf0, 0x1e, 0xa0, 0xed, 0xc7, 
+       0x39, 0xc9, 0x53, 0x29, 0x2c, 0x65, 0xc4, 0x51, 0x30, 0x6a, 0x3f, 0x7c, 
+       0xa3, 0xed, 0x7a, 0x5b, 0xb5, 0xf3, 0xde, 0x61, 0xbd, 0x1f, 0x7c, 0x7e, 
+       0xfb, 0xe3, 0x17, 0x96, 0xb1, 0x43, 0xed, 0x43, 0x06, 0x97, 0xca, 0x61, 
+       0xa4, 0x95, 0x30, 0xd7, 0x00, 0xa3, 0xa4, 0xef, 0xbe, 0xed, 0xce, 0x54, 
+       0xe4, 0x81, 0x9b, 0xab, 0x8c, 0xef, 0x8e, 0x61, 0x9d, 0x95, 0x9b, 0x33, 
+       0xee, 0x3b, 0x65, 0x3e, 0xcb, 0xb9, 0x30, 0x14, 0xf9, 0x6e, 0xbd, 0xed, 
+       0x49, 0x6e, 0xaa, 0xf7, 0xe7, 0x00, 0x37, 0x28, 0x7e, 0xbd, 0x70, 0x6a, 
+       0x72, 0x52, 0xa5, 0xec, 0x47, 0xba, 0xe9, 0xcc, 0x93, 0x42, 0xb6, 0x23, 
+       0x4e, 0xc4, 0x63, 0xc3, 0x88, 0x26, 0x5e, 0x5b, 0x5d, 0x95, 0x55, 0x2f, 
+       0x11, 0xac, 0xbb, 0x5a, 0x12, 0x35, 0x1b, 0x8f, 0xdb, 0x85, 0xd4, 0x96, 
+       0x8e, 0x82, 0x5f, 0xe8, 0x6c, 0x4a, 0x42, 0x50, 0x96, 0xc8, 0x74, 0xed, 
+       0x94, 0xf8, 0xa0, 0x97, 0xcf, 0x97, 0xff, 0x37, 0x1d, 0x7a, 0xa1, 0x84, 
+       0xce, 0x97, 0x15, 0x71, 0x25, 0x2c, 0x1f, 0x5c, 0x7c, 0xd8, 0x02, 0xd0, 
+       0xc0, 0x27, 0x82, 0xf4, 0xa0, 0x37, 0x0c, 0x02, 0x78, 0x2f, 0x5f, 0x54, 
+       0xea, 0xce, 0x24, 0x89, 0x64, 0x4e, 0x6c, 0xe7, 0x2c, 0xdc, 0xc0, 0x6c, 
+       0x4d, 0xe5, 0x30, 0xab, 0xd0, 0x6a, 0xbf, 0xe6, 0xc0, 0x07, 0xa4, 0x20, 
+       0x45, 0xfc, 0x40, 0x5e, 0xad, 0xe0, 0x27, 0x21, 0x60, 0x12, 0xa3, 0x88, 
+       0xee, 0x46, 0xbe, 0x9b, 0xca, 0x01, 0x42, 0x3e, 0x4a, 0xfa, 0x0a, 0xe9, 
+       0x14, 0xfb, 0x2f, 0x61, 0xce, 0x78, 0x1b, 0xb5, 0xa7, 0xab, 0x88, 0xab, 
+       0xb2, 0x19, 0x44, 0xfa, 0x72, 0x37, 0xaf, 0xbe, 0x7b, 0xc0, 0xae, 0x33, 
+       0x7e, 0x4e, 0xbd, 0x8a, 0x53, 0xd2, 0x78, 0x55, 0x52, 0x76, 0xce, 0xc6, 
+       0x71, 0xd9, 0x41, 0x56, 0xaf, 0x82, 0x1d, 0x66, 0x99, 0x87, 0x8f, 0x3e, 
+       0xcd, 0x74, 0x55, 0x57, 0xbc, 0xcf, 0x56, 0x82, 0x93, 0xd0, 0x35, 0x08, 
+       0x25, 0x81, 0x76, 0xfb, 0xed, 0xce, 0xdb, 0xe3, 0xd1, 0xd8, 0xd5, 0xe6, 
+       0xe2, 0xd5, 0x5e, 0x19, 0x89, 0x68, 0x2d, 0xb3, 0xfd, 0xbb, 0x65, 0x25, 
+       0xf0, 0xd6, 0x01, 0xee, 0xea, 0x1e, 0xea, 0xff, 0xb2, 0xae, 0xc0, 0x0b, 
+       0x0e, 0x59, 0xf7, 0x38, 0xd3, 0xee, 0x3c, 0xcf, 0x63, 0x42, 0x30, 0x0d, 
+       0xc0, 0xa2, 0x3a, 0x18, 0x97, 0xf0, 0xd2, 0x94, 0xa6, 0x2f, 0x20, 0xe3, 
+       0xb6, 0xe8, 0x6e, 0x31, 0xa6, 0x26, 0x94, 0x18, 0x51, 0x48, 0x25, 0x7d, 
+       0xc0, 0xba, 0x50, 0xf9, 0x05, 0x21, 0x94, 0x48, 0xf8, 0xa5, 0x3a, 0x9f, 
+       0x7e, 0x22, 0x90, 0x0b, 0x0f, 0x15, 0x50, 0xd3, 0xaf, 0x63, 0xdc, 0x80, 
+       0x10, 0x00, 0x9c, 0x00, 0xf0, 0x35, 0xff, 0x02, 0xc9, 0x51, 0x62, 0x87, 
+       0x24, 0x38, 0xd1, 0x42, 0x2b, 0x80, 0xc0, 0x46, 0x74, 0x36, 0xe4, 0x20, 
+       0xc4, 0xe4, 0xb6, 0x40, 0x17, 0x25, 0x6e, 0x78, 0xc7, 0x4b, 0x9d, 0xdf, 
+       0xf3, 0xff, 0x32, 0x25, 0x13, 0x06, 0x90, 0x89, 0x79, 0x09, 0xfc, 0x6a, 
+       0x70, 0x40, 0x61, 0x49, 0x03, 0xc8, 0x35, 0x39, 0xf0, 0x75, 0x8e, 0x26, 
+       0x32, 0x4f, 0xaf, 0xfd, 0xf5, 0x77, 0xf3, 0x2e, 0x1a, 0x19, 0xf3, 0x0d, 
+       0xc1, 0x7a, 0xfa, 0xef, 0x6d, 0xfe, 0xdd, 0xf0, 0xfa, 0xb2, 0xb6, 0xfb, 
+       0xbf, 0x02, 0x4b, 0x6d, 0xcf, 0x71, 0xf6, 0x84, 0xa9, 0x3e, 0x8f, 0x95, 
+       0x5e, 0xaa, 0xae, 0x79, 0x9e, 0xb9, 0xe6, 0x7b, 0xb5, 0xae, 0x5b, 0xa5, 
+       0xe7, 0x7a, 0xd7, 0x99, 0xe8, 0x69, 0xdd, 0x4f, 0x3b, 0xd6, 0xbc, 0xcf, 
+       0x43, 0x4e, 0xea, 0x79, 0x9e, 0xb9, 0xe6, 0x7a, 0x5a, 0x2e, 0xa7, 0x99, 
+       0xeb, 0x5e, 0x77, 0xa1, 0xa7, 0x75, 0x3c, 0xcf, 0x5a, 0xf3, 0x3d, 0x0d, 
+       0x3b, 0xa9, 0xe6, 0x7a, 0xe7, 0x99, 0xe9, 0x69, 0xdd, 0x4f, 0x33, 0xd6, 
+       0xbc, 0xef, 0x43, 0x4e, 0xea, 0x79, 0x9e, 0xb5, 0xe7, 0x7a, 0x5a, 0x77, 
+       0x4b, 0xce, 0xf5, 0xaf, 0x33, 0xd0, 0xd3, 0xba, 0x9e, 0x67, 0xae, 0x79, 
+       0x9e, 0x86, 0x8b, 0xa9, 0xe6, 0x7a, 0xd7, 0x9d, 0xe8, 0x69, 0xdd, 0x2f, 
+       0x3b, 0xd6, 0xbc, 0xef, 0x4b, 0x4e, 0xe9, 0x79, 0xde, 0xb5, 0xe6, 0x7a, 
+       0x1a, 0x77, 0x53, 0xcc, 0xf5, 0xcf, 0x33, 0xd0, 0xd3, 0xba, 0x9e, 0x67, 
+       0xad, 0x79, 0xde, 0x96, 0x9d, 0xd4, 0xf3, 0x3d, 0x64, 0x69, 0x68, 0xb4, 
+       0x79, 0xa3, 0x4a, 0x67, 0x72, 0x94, 0xa4, 0x45, 0xca, 0x52, 0x91, 0x17, 
+       0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94, 0xa4, 0x45, 0xca, 
+       0x52, 0x91, 0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94, 
+       0xa4, 0x45, 0xca, 0x52, 0x91, 0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, 
+       0x11, 0x72, 0x94, 0xa4, 0x44, 0x00, 0x00, 0x01, 0x09, 0x53, 0xa1, 0xe6, 
+       0x7a, 0xd7, 0x9d, 0xef, 0xd2, 0x1a, 0xf1, 0xee, 0x97, 0x9d, 0xeb, 0x5e, 
+       0x67, 0xa1, 0xa7, 0x75, 0x3c, 0xef, 0x5a, 0xf3, 0x3d, 0x0d, 0x3b, 0xa9, 
+       0xed, 0x08, 0x46, 0x41, 0xc1, 0x46, 0x6c, 0x3b, 0x0a, 0xa3, 0xcc, 0xf6, 
+       0x59, 0xf0, 0x7c, 0xed, 0xf1, 0x5b, 0x66, 0xca, 0x5b, 0x88, 0x1c, 0xc6, 
+       0x28, 0xea, 0xc4, 0x14, 0x84, 0x66, 0x3c, 0xf3, 0xf6, 0x34, 0xfc, 0xdb, 
+       0x6c, 0xb3, 0x54, 0xba, 0xa7, 0x9d, 0xec, 0x46, 0xa5, 0x99, 0xfa, 0xeb, 
+       0x6d, 0x76, 0xe2, 0x99, 0x23, 0x46, 0xff, 0xc4, 0x21, 0x62, 0x7e, 0x46, 
+       0xe4, 0xf8, 0xf2, 0xb7, 0xee, 0xfc, 0x42, 0x8d, 0x3d, 0x6c, 0xf8, 0x3b, 
+       0xea, 0xb7, 0x9d, 0xec, 0xc9, 0x69, 0x77, 0x7e, 0xe7, 0x4d, 0xb5, 0xd4, 
+       0xf6, 0xb2, 0x56, 0x0e, 0x66, 0x66, 0x43, 0x30, 0xe3, 0x9d, 0x85, 0x3d, 
+       0x5b, 0xcc, 0xf1, 0xbf, 0x41, 0xdd, 0x9e, 0x47, 0xd8, 0x65, 0xbd, 0x68, 
+       0xcc, 0xe3, 0xce, 0xcc, 0xa3, 0x0e, 0x5b, 0x3f, 0x61, 0x7e, 0x0f, 0x33, 
+       0xd1, 0x92, 0x9c, 0x6f, 0xa9, 0xb5, 0xbf, 0x2b, 0x2f, 0xaf, 0x7e, 0xa7, 
+       0x14, 0xb5, 0x08, 0x5f, 0xa9, 0x7a, 0xe7, 0x99, 0xe4, 0xff, 0x9f, 0xe2, 
+       0xd4, 0x3f, 0x81, 0x17, 0x62, 0x75, 0x6a, 0x50, 0xea, 0x75, 0x2d, 0x4c, 
+       0x8e, 0x79, 0xe7, 0xa3, 0xc1, 0xe6, 0x7b, 0x2c, 0x94, 0xe3, 0x66, 0xda, 
+       0xd7, 0x81, 0xb3, 0xbb, 0x89, 0x7d, 0xe2, 0x1f, 0xc4, 0x89, 0xac, 0x79, 
+       0x9e, 0x4f, 0xc7, 0x9d, 0x3b, 0x53, 0x18, 0x0e, 0x11, 0x9b, 0x4c, 0x1f, 
+       0xcf, 0x7e, 0x2f, 0xaf, 0xac, 0x3f, 0xce, 0xf3, 0x3d, 0x0f, 0xc7, 0xf9, 
+       0xdd, 0x7f, 0xf1, 0xe2, 0x8e, 0xc7, 0x0e, 0x76, 0x72, 0x7c, 0xcf, 0xce, 
+       0x75, 0x0e, 0x64, 0x75, 0x8f, 0x5e, 0xf5, 0x8f, 0x3b, 0xd0, 0xe9, 0x1f, 
+       0x36, 0xd6, 0x86, 0x35, 0x1e, 0xb5, 0xe6, 0x79, 0x2e, 0x2e, 0xa7, 0x99, 
+       0xeb, 0x9e, 0x67, 0x86, 0x8b, 0xa9, 0xe6, 0x7a, 0xd7, 0x9d, 0xe9, 0x69, 
+       0xdd, 0x4f, 0x33, 0xd6, 0xbc, 0xcf, 0x43, 0x45, 0xd4, 0xf3, 0x3d, 0x73, 
+       0xcc, 0xf4, 0x34, 0xee, 0xa7, 0x99, 0xeb, 0x9e, 0x67, 0xa5, 0xa7, 0x75, 
+       0x3c, 0xcf, 0x5a, 0xf3, 0xbd, 0x0d, 0x3b, 0xa9, 0xe6, 0x7a, 0xd7, 0x99, 
+       0xe8, 0x69, 0xdd, 0x4f, 0x3b, 0xd6, 0xbc, 0xcf, 0x43, 0x4e, 0xea, 0x79, 
+       0x9e, 0xb9, 0xe6, 0x7a, 0x5a, 0x2e, 0xa7, 0x99, 0xeb, 0x5e, 0x77, 0xa1, 
+       0xa7, 0x74, 0xbc, 0xef, 0x5a, 0xf3, 0xbd, 0x0d, 0x3b, 0xa5, 0xe7, 0x7a, 
+       0xd7, 0x99, 0xe9, 0x69, 0xdd, 0x4f, 0x33, 0xd7, 0x3c, 0x68, 0x69, 0xdd, 
+       0x6f, 0x33, 0xd6, 0x52, 0x99, 0xda, 0xbc, 0xd4, 0xa4, 0xa2, 0xe5, 0x29, 
+       0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 
+       0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 
+       0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 
+       0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 
+       0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00, 
+       0x01, 0x0a, 0x53, 0xd0, 0xf3, 0xbd, 0x6b, 0xcc, 0xf7, 0xdf, 0xb5, 0xe5, 
+       0x5d, 0x4f, 0x33, 0xd6, 0xbc, 0xef, 0x43, 0x45, 0xd2, 0xf3, 0xbd, 0x6b, 
+       0xce, 0xf4, 0xb4, 0xee, 0x97, 0x9d, 0xeb, 0x5e, 0x67, 0xa1, 0xa7, 0x75, 
+       0x3c, 0xcf, 0x5c, 0xf3, 0x3d, 0x0d, 0x3b, 0xa9, 0xe6, 0x7a, 0xe7, 0x99, 
+       0xe9, 0x69, 0xdd, 0x4f, 0x33, 0xd6, 0xbc, 0xcf, 0x43, 0x4e, 0xea, 0x79, 
+       0xde, 0xb5, 0xe6, 0x7a, 0x1a, 0x77, 0x53, 0xcc, 0xf5, 0xcf, 0x33, 0xd2, 
+       0xd1, 0x75, 0x3c, 0xcf, 0x5a, 0xf3, 0x3d, 0x0d, 0x3b, 0xad, 0xe6, 0x7a, 
+       0xd7, 0x99, 0xe8, 0x69, 0xdd, 0x4f, 0x33, 0xd7, 0x3c, 0xcf, 0x4b, 0x4e, 
+       0xea, 0x79, 0x9e, 0xb5, 0xe7, 0x7a, 0x1a, 0x77, 0x53, 0xcc, 0xf5, 0xaf, 
+       0x33, 0xd0, 0xd3, 0xba, 0x9e, 0x77, 0xad, 0x79, 0x9e, 0x96, 0x8b, 0xa9, 
+       0xe6, 0x7a, 0xe7, 0x99, 0xe8, 0x69, 0xdd, 0x4f, 0x33, 0xd6, 0xbc, 0xef, 
+       0x43, 0x4e, 0xea, 0x79, 0x9e, 0xb5, 0xe7, 0x7a, 0x5a, 0x77, 0x4b, 0xce, 
+       0xf5, 0xaf, 0x33, 0xd0, 0xd3, 0xba, 0x9e, 0x67, 0xad, 0x79, 0xde, 0x86, 
+       0x9d, 0xd4, 0xf3, 0x3d, 0x6b, 0xce, 0xf4, 0xb4, 0xee, 0x97, 0x9d, 0xeb, 
+       0x5e, 0x77, 0xa1, 0xa2, 0xe9, 0x79, 0xde, 0xb5, 0xe6, 0x7a, 0x1a, 0x77, 
+       0x53, 0xcc, 0xf5, 0xcf, 0x1a, 0x5a, 0x77, 0x5b, 0xcc, 0xf3, 0xd2, 0x99, 
+       0xd8, 0x8d, 0x29, 0x28, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 
+       0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 
+       0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 
+       0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 
+       0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 
+       0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 
+       0x94, 0xa5, 0x22, 0x20, 0x00, 0x00, 0x01, 0x0b, 0x53, 0xeb, 0xbc, 0xef, 
+       0x5a, 0xf3, 0xbd, 0xf6, 0xcd, 0x79, 0x97, 0x4b, 0xce, 0xf5, 0xaf, 0x33, 
+       0xd2, 0xd3, 0xba, 0x9e, 0x67, 0xae, 0x79, 0x9e, 0x86, 0x9d, 0xd4, 0xf3, 
+       0x3d, 0x6b, 0xce, 0xf4, 0x34, 0xee, 0xa7, 0x99, 0xeb, 0x5e, 0x67, 0xa5, 
+       0xa7, 0x75, 0x3c, 0xcf, 0x5c, 0xf3, 0x3d, 0x0d, 0x3b, 0xa9, 0xe6, 0x7a, 
+       0xd7, 0x9d, 0xe8, 0x68, 0xba, 0x9e, 0x67, 0xad, 0x79, 0xde, 0x96, 0x9d, 
+       0xd2, 0xf3, 0xbd, 0x6b, 0xcc, 0xf4, 0x34, 0xee, 0xa7, 0x99, 0xeb, 0x5e, 
+       0x77, 0xa1, 0xa7, 0x75, 0x3c, 0xcf, 0x5a, 0xf3, 0xbd, 0x2d, 0x3b, 0xa5, 
+       0xe7, 0x7a, 0xd7, 0x9d, 0xe8, 0x69, 0xdd, 0x2f, 0x3b, 0xd6, 0xbc, 0xcf, 
+       0x43, 0x45, 0xd4, 0xf3, 0x3d, 0x73, 0xcc, 0xf4, 0xb4, 0xee, 0xa7, 0x99, 
+       0xeb, 0x5e, 0x77, 0xa1, 0xa7, 0x74, 0xbc, 0xef, 0x5a, 0xf3, 0x3d, 0x0d, 
+       0x3b, 0xa9, 0xe7, 0x7a, 0xd7, 0x99, 0xe9, 0x69, 0xdd, 0x4f, 0x33, 0xd6, 
+       0xbc, 0xef, 0x43, 0x4e, 0xea, 0x79, 0x9e, 0xb5, 0xe7, 0x7a, 0x1a, 0x77, 
+       0x4b, 0xce, 0xf5, 0xaf, 0x33, 0xd2, 0xd3, 0xba, 0x9e, 0x77, 0xac, 0x9a, 
+       0x86, 0x8b, 0xad, 0xe6, 0x79, 0xa9, 0x29, 0xd8, 0x8d, 0x29, 0x28, 0xb9, 
+       0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 
+       0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 
+       0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 
+       0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 
+       0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 
+       0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 
+       0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x88, 0x00, 0x00, 
+       0x01, 0x0c, 0x53, 0xf4, 0xef, 0x33, 0xd6, 0xbc, 0xef, 0x7d, 0x73, 0x5e, 
+       0x7d, 0xd2, 0xf3, 0xbd, 0x6b, 0xce, 0xf4, 0x34, 0xee, 0x97, 0x99, 0xeb, 
+       0x9e, 0x67, 0xa1, 0xa7, 0x75, 0x3c, 0xef, 0x58, 0xf3, 0xbd, 0x2d, 0x3b, 
+       0xa9, 0xe6, 0x7a, 0xd7, 0x9d, 0xe8, 0x68, 0xba, 0x5e, 0x77, 0xad, 0x79, 
+       0x9e, 0x86, 0x9d, 0xd4, 0xf3, 0xbd, 0x6b, 0xcc, 0xf4, 0xb4, 0xee, 0xa7, 
+       0x99, 0xeb, 0x5e, 0x77, 0xa1, 0xa7, 0x74, 0xbc, 0xef, 0x5a, 0xf3, 0xbd, 
+       0x0d, 0x3b, 0xa5, 0xe7, 0x7a, 0xd7, 0x99, 0xe9, 0x69, 0xdd, 0x4f, 0x33, 
+       0xd7, 0x3c, 0xcf, 0x43, 0x45, 0xd4, 0xf3, 0x3d, 0x6b, 0xce, 0xf4, 0x34, 
+       0xee, 0x97, 0x9d, 0xeb, 0x5e, 0x67, 0xa5, 0xa7, 0x75, 0x3c, 0xef, 0x5a, 
+       0xf3, 0x3d, 0x0d, 0x3b, 0xa9, 0xe6, 0x7a, 0xe7, 0x99, 0xe8, 0x69, 0xdd, 
+       0x4f, 0x33, 0xd6, 0xbc, 0xef, 0x4b, 0x4e, 0xea, 0x79, 0x9e, 0xb5, 0xe6, 
+       0x7a, 0x1a, 0x77, 0x53, 0xce, 0xf5, 0x91, 0xa5, 0xa2, 0xd1, 0xe6, 0x8d, 
+       0x29, 0x9d, 0xca, 0x52, 0x91, 0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, 
+       0x11, 0x72, 0x94, 0xa4, 0x45, 0xca, 0x52, 0x91, 0x17, 0x29, 0x4a, 0x44, 
+       0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94, 0xa4, 0x45, 0xca, 0x52, 0x91, 0x17, 
+       0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94, 0xa4, 0x45, 0xca, 
+       0x52, 0x91, 0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94, 
+       0xa4, 0x45, 0xca, 0x52, 0x91, 0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, 
+       0x11, 0x72, 0x94, 0xa4, 0x45, 0xca, 0x52, 0x91, 0x17, 0x29, 0x4a, 0x44, 
+       0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94, 0xa4, 0x45, 0xca, 0x52, 0x91, 0x17, 
+       0x29, 0x4a, 0x44, 0x40, 0x00, 0x00, 0x01, 0x0d, 0x53, 0xf6, 0xcf, 0x3b, 
+       0xd6, 0x3c, 0xef, 0x7d, 0x2b, 0x5c, 0x57, 0x53, 0xcc, 0xf5, 0xaf, 0x3b, 
+       0xd0, 0xd3, 0xba, 0x5e, 0x77, 0xad, 0x79, 0x9e, 0x96, 0x8b, 0xa9, 0xe7, 
+       0x7a, 0xd7, 0x99, 0xe8, 0x69, 0xdd, 0x4f, 0x33, 0xd6, 0xbc, 0xef, 0x43, 
+       0x4e, 0xe9, 0x79, 0xde, 0xb5, 0xe7, 0x7a, 0x5a, 0x77, 0x4b, 0xce, 0xf5, 
+       0xaf, 0x33, 0xd0, 0xd3, 0xba, 0x9e, 0x67, 0xae, 0x79, 0x9e, 0x86, 0x9d, 
+       0xd4, 0xf3, 0x3d, 0x73, 0xcc, 0xf4, 0xb4, 0xee, 0xa7, 0x99, 0xeb, 0x5e, 
+       0x77, 0xa1, 0xa2, 0xe9, 0x79, 0xde, 0xb5, 0xe6, 0x7a, 0x1a, 0x77, 0x53, 
+       0xcc, 0xf5, 0xcf, 0x33, 0xd2, 0xd3, 0xba, 0x9e, 0x67, 0xae, 0x79, 0x9e, 
+       0x86, 0x9d, 0xd4, 0xf3, 0x3d, 0x6b, 0xcc, 0xf4, 0x34, 0xee, 0xa7, 0x9d, 
+       0xeb, 0x29, 0x4c, 0xed, 0x5e, 0x31, 0xa4, 0xa2, 0xe5, 0x29, 0x48, 0x8b, 
+       0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 
+       0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 
+       0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 
+       0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 
+       0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 
+       0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 
+       0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 
+       0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 
+       0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x88, 0x00, 0x00, 0x01, 
+       0x0e, 0x53, 0xfa, 0x2b, 0xce, 0xf5, 0xaf, 0x33, 0xde, 0xe3, 0x5c, 0xb7, 
+       0x53, 0xcc, 0xf5, 0xcf, 0x33, 0xd2, 0xd1, 0x75, 0x3c, 0xcf, 0x5a, 0xf3, 
+       0xbd, 0x0d, 0x3b, 0xa9, 0xe6, 0x7a, 0xd7, 0x9d, 0xe8, 0x69, 0xdd, 0x2f, 
+       0x3b, 0xd6, 0xbc, 0xcf, 0x4b, 0x4e, 0xea, 0x79, 0x9e, 0xb9, 0xe6, 0x7a, 
+       0x1a, 0x77, 0x53, 0xcc, 0xf5, 0xaf, 0x3b, 0xd0, 0xd3, 0xba, 0x5e, 0x77, 
+       0xad, 0x79, 0x9e, 0x96, 0x8b, 0xa9, 0xe7, 0x7a, 0xd7, 0x99, 0xe8, 0x69, 
+       0xdd, 0x4f, 0x33, 0xd7, 0x3c, 0xcf, 0x43, 0x4e, 0xea, 0x79, 0x9e, 0xb5, 
+       0xe6, 0xa5, 0xa7, 0x75, 0x3c, 0xef, 0x3d, 0x29, 0x9d, 0x88, 0xd2, 0x92, 
+       0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 
+       0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 
+       0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 
+       0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 
+       0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 
+       0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 
+       0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 
+       0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 
+       0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 
+       0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 
+       0x88, 0x00, 0x00, 0x01, 0x0f, 0x53, 0xfa, 0xab, 0xcc, 0xf5, 0xaf, 0x3b, 
+       0xde, 0xa3, 0x5c, 0xf7, 0x4b, 0xce, 0xf5, 0xaf, 0x33, 0xd0, 0xd3, 0xba, 
+       0x9e, 0x67, 0xae, 0x79, 0x9e, 0x86, 0x9d, 0xd4, 0xf3, 0x3d, 0x6b, 0xce, 
+       0xf4, 0xb4, 0xee, 0xa7, 0x99, 0xeb, 0x5e, 0x77, 0xa1, 0xa7, 0x74, 0xbc, 
+       0xef, 0x5a, 0xf3, 0x3d, 0x0d, 0x3b, 0xa9, 0xe7, 0x7a, 0xd7, 0x99, 0xe9, 
+       0x68, 0xba, 0x9e, 0x67, 0xad, 0x78, 0xd0, 0xd3, 0xb4, 0x79, 0x9e, 0x6a, 
+       0x4a, 0x76, 0x23, 0x4a, 0x4a, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 
+       0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x97, 0xcf, 
+       0xca, 0x00, 0x58, 0x90, 0xd0, 0x1d, 0x00, 0x1f, 0xa0, 0x0a, 0x86, 0x01, 
+       0x81, 0x89, 0x4b, 0xb1, 0x6e, 0xc5, 0x3b, 0xa9, 0x9c, 0xcf, 0x6e, 0x58, 
+       0x6a, 0x03, 0x18, 0x6f, 0x67, 0xed, 0x84, 0xd3, 0xb7, 0xf7, 0xf4, 0x0a, 
+       0x5c, 0x22, 0x10, 0x0c, 0x0a, 0x21, 0x93, 0x40, 0x6c, 0x82, 0x1a, 0x1f, 
+       0xf4, 0x21, 0x1c, 0x61, 0x6c, 0x9e, 0x90, 0xe5, 0x31, 0xaf, 0x54, 0x01, 
+       0xa8, 0x06, 0x98, 0x03, 0x44, 0x92, 0xb9, 0x34, 0xa2, 0xcb, 0xcc, 0xe8, 
+       0x40, 0xd5, 0x38, 0xff, 0xf3, 0x07, 0x1e, 0xb9, 0x12, 0xd0, 0x34, 0x61, 
+       0xac, 0xec, 0xdc, 0xe8, 0x6e, 0x7d, 0xfa, 0x2a, 0x52, 0x96, 0x35, 0x5c, 
+       0xa5, 0x29, 0x11, 0x72, 0x94, 0xa4, 0x45, 0xca, 0x52, 0xf1, 0xa0, 0x18, 
+       0x00, 0x38, 0x48, 0x05, 0xe8, 0x4f, 0x28, 0x9a, 0x56, 0xe8, 0x61, 0xa8, 
+       0xf9, 0x2a, 0x77, 0x65, 0xb3, 0x73, 0xad, 0x8b, 0x2d, 0x01, 0x98, 0x6a, 
+       0x7b, 0x61, 0xf4, 0xec, 0xf7, 0xec, 0xa9, 0x72, 0x80, 0x62, 0x05, 0x10, 
+       0x03, 0xa2, 0xf9, 0x40, 0x67, 0x16, 0x94, 0x25, 0x28, 0x4b, 0x64, 0xe6, 
+       0xe8, 0xec, 0x6e, 0xce, 0x1d, 0x71, 0x09, 0x80, 0x0f, 0x31, 0x34, 0x07, 
+       0x5c, 0x07, 0x60, 0x15, 0x86, 0x20, 0xa4, 0x29, 0x23, 0x72, 0x5f, 0x25, 
+       0xb7, 0x18, 0x72, 0xfa, 0xcc, 0x3e, 0x7c, 0xb4, 0x6d, 0xbf, 0xea, 0xe7, 
+       0x2b, 0x9b, 0x26, 0xf7, 0xec, 0x29, 0x78, 0x00, 0x13, 0x00, 0x2e, 0x48, 
+       0x06, 0xa1, 0x9c, 0x34, 0x98, 0x43, 0x0c, 0xfb, 0x71, 0x9d, 0xf1, 0x79, 
+       0x6d, 0xb8, 0xe1, 0x76, 0xde, 0x00, 0xe9, 0xdb, 0x13, 0x00, 0x62, 0x03, 
+       0xa2, 0x6a, 0x7f, 0x24, 0x62, 0xdb, 0x9d, 0xfb, 0x84, 0xb6, 0xb5, 0x00, 
+       0xb4, 0x31, 0xfe, 0x1a, 0xbc, 0x7a, 0xba, 0x84, 0xaa, 0x43, 0x3d, 0xfb, 
+       0x2a, 0x5c, 0xd0, 0x1d, 0x80, 0xc0, 0x9a, 0x02, 0x62, 0xc9, 0x44, 0xd2, 
+       0xba, 0x39, 0x8e, 0x77, 0x5b, 0x3e, 0xe6, 0xeb, 0x20, 0x06, 0x80, 0x26, 
+       0x00, 0x37, 0x0d, 0xc4, 0xcc, 0x5a, 0x0a, 0x4f, 0x24, 0x6f, 0xba, 0xdb, 
+       0x7f, 0xd7, 0xb2, 0x9b, 0x63, 0x6c, 0x39, 0x68, 0xc8, 0x1a, 0xea, 0x67, 
+       0x73, 0xd5, 0x53, 0x2d, 0xaf, 0xbb, 0x4b, 0xe7, 0x25, 0x80, 0x2a, 0xe1, 
+       0x81, 0xa8, 0x0c, 0x48, 0x0c, 0x40, 0xa2, 0x0b, 0x4b, 0x0c, 0x47, 0x1a, 
+       0xfd, 0xfb, 0x66, 0x51, 0xbe, 0xf5, 0x60, 0x27, 0x00, 0xc8, 0x0a, 0x80, 
+       0x65, 0xd1, 0x89, 0xa8, 0x61, 0xbb, 0xfd, 0xf7, 0xdf, 0x76, 0x3d, 0x78, 
+       0xdd, 0x6c, 0x82, 0x18, 0x61, 0x7f, 0x6c, 0x9d, 0xdd, 0x78, 0x58, 0x81, 
+       0xf2, 0xe8, 0xfa, 0xfb, 0x74, 0xa5, 0xe4, 0x00, 0x60, 0x00, 0xd8, 0x0a, 
+       0x80, 0x9c, 0x0a, 0x16, 0x43, 0x43, 0x64, 0x23, 0x76, 0xdc, 0xf2, 0xc6, 
+       0x65, 0xec, 0xc1, 0x4b, 0xb1, 0x49, 0x68, 0x2b, 0x3a, 0x7b, 0x63, 0xaa, 
+       0xd9, 0xef, 0xd7, 0xd2, 0x94, 0xb2, 0x9d, 0xca, 0x52, 0x91, 0x17, 0x29, 
+       0x4a, 0x44, 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94, 0xa4, 0x45, 0xca, 0x52, 
+       0x91, 0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94, 0xb8, 
+       0x40, 0x1a, 0x00, 0x98, 0x00, 0xdc, 0x37, 0x13, 0x31, 0x68, 0x29, 0x3c, 
+       0x91, 0xbe, 0xeb, 0x6d, 0xff, 0x5e, 0xca, 0x6d, 0x8d, 0xad, 0x29, 0xc8, 
+       0xdf, 0xf5, 0x6c, 0x7c, 0x8f, 0xbe, 0xed, 0x2f, 0x22, 0x00, 0xec, 0x03, 
+       0x04, 0x00, 0x5c, 0x90, 0xc2, 0x80, 0xcf, 0x2c, 0xb4, 0xa0, 0x6a, 0x73, 
+       0x36, 0xc6, 0x66, 0xe6, 0xbf, 0xdc, 0x2a, 0xf5, 0xd5, 0x14, 0x5e, 0x2d, 
+       0x19, 0x28, 0x5e, 0xe7, 0x4b, 0xe6, 0xb9, 0x4a, 0x52, 0xca, 0xab, 0x94, 
+       0xbe, 0x6a, 0x03, 0xa0, 0x05, 0x88, 0x26, 0x90, 0xb8, 0x0e, 0xd2, 0x05, 
+       0x70, 0x0d, 0x86, 0x25, 0x2e, 0x84, 0x24, 0x68, 0x16, 0xfd, 0xd4, 0x96, 
+       0x1d, 0xef, 0xa5, 0x5d, 0x25, 0x06, 0xa0, 0xbe, 0x9c, 0x8e, 0xe3, 0x3b, 
+       0x71, 0x23, 0xa8, 0xfb, 0xeb, 0x94, 0xa5, 0x2e, 0x6a, 0xee, 0x52, 0x94, 
+       0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 
+       0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x88, 
+       0x00, 0x00, 0x01, 0x10, 0x53, 0xfb, 0x23, 0xcc, 0xf5, 0xcf, 0x33, 0xdd, 
+       0x6d, 0x65, 0x75, 0x3c, 0xcf, 0x5a, 0xf3, 0xbd, 0x2d, 0x3b, 0xa5, 0xe7, 
+       0x7a, 0xd7, 0x9d, 0xe8, 0x69, 0xdd, 0x2f, 0x3b, 0xd6, 0xbc, 0xcf, 0x43, 
+       0x4e, 0xea, 0x79, 0x9e, 0xb6, 0x34, 0xb4, 0x5a, 0x3c, 0xd1, 0xa5, 0x33, 
+       0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 
+       0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 
+       0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x7c, 0x24, 0x00, 0xb8, 0x37, 
+       0x06, 0x80, 0xe8, 0x9a, 0x4c, 0xf9, 0xc6, 0x25, 0xd6, 0xbc, 0x30, 0xbc, 
+       0xcc, 0xc0, 0x22, 0xee, 0x26, 0xfb, 0xb5, 0xf2, 0xb0, 0x02, 0xac, 0x90, 
+       0x03, 0xf2, 0x6f, 0x58, 0xc0, 0x30, 0xdc, 0x73, 0xf4, 0xe6, 0x71, 0x6c, 
+       0x35, 0x83, 0xef, 0x64, 0x03, 0x54, 0x38, 0x14, 0x2c, 0x53, 0x7c, 0x27, 
+       0x7b, 0x00, 0xce, 0xc9, 0xd5, 0xff, 0x41, 0x00, 0x84, 0x0c, 0x8e, 0x9e, 
+       0x49, 0xef, 0x91, 0x8d, 0xc3, 0xfd, 0xea, 0x4a, 0x29, 0x21, 0xa9, 0x6c, 
+       0x1f, 0xee, 0x70, 0x15, 0x4f, 0x48, 0x6a, 0x45, 0x76, 0x0a, 0xbe, 0x58, 
+       0x03, 0xad, 0x8a, 0x26, 0x72, 0xc7, 0xbf, 0xe1, 0xd6, 0xbf, 0x8c, 0xce, 
+       0x6f, 0xa2, 0xfd, 0xa0, 0x05, 0x48, 0xdf, 0x23, 0x96, 0x13, 0xcc, 0x65, 
+       0x0a, 0xbd, 0x29, 0x64, 0xce, 0x18, 0x96, 0x4f, 0x0f, 0xb9, 0x64, 0x2d, 
+       0xc3, 0x49, 0xbb, 0xf7, 0xeb, 0x7f, 0xc0, 0x3f, 0xbc, 0x88, 0x0e, 0xd8, 
+       0xb0, 0xd2, 0x8a, 0x0b, 0x0f, 0x6a, 0x9f, 0xe4, 0xec, 0xee, 0xa7, 0xe2, 
+       0xa2, 0xfd, 0x29, 0x60, 0x50, 0x94, 0x50, 0xde, 0x91, 0xcc, 0x2e, 0x72, 
+       0x60, 0x14, 0xc1, 0xa8, 0xe9, 0xfd, 0xb6, 0xbc, 0x58, 0x09, 0xfb, 0x92, 
+       0x8a, 0xc6, 0xf7, 0x20, 0x11, 0x6e, 0x71, 0x60, 0x28, 0x02, 0x8d, 0xbb, 
+       0xbe, 0x61, 0x2e, 0x7b, 0x13, 0xee, 0xd1, 0xbc, 0x66, 0x1f, 0xf9, 0x3a, 
+       0xcd, 0xaf, 0xec, 0xa1, 0x85, 0x01, 0x94, 0xb3, 0xb5, 0xe3, 0x80, 0x0f, 
+       0x00, 0xbb, 0x6e, 0xac, 0x7f, 0xe4, 0x6c, 0x1d, 0x56, 0x02, 0x62, 0x80, 
+       0xbf, 0xff, 0x12, 0x09, 0xdc, 0x07, 0xf7, 0x64, 0x0a, 0x86, 0xfe, 0x1b, 
+       0xdb, 0x92, 0x15, 0x88, 0xb7, 0x07, 0xff, 0xa5, 0xb7, 0x32, 0xd0, 0xca, 
+       0xb3, 0xd4, 0x00, 0xc4, 0x99, 0x80, 0xa1, 0x0c, 0xb0, 0xcc, 0x9c, 0x5e, 
+       0x47, 0x61, 0xb8, 0xd0, 0x3b, 0x8e, 0xe2, 0xef, 0x00, 0x00, 0x91, 0x3c, 
+       0x02, 0xb0, 0xd4, 0x3a, 0xb8, 0x63, 0xfc, 0x2f, 0x27, 0x31, 0x1f, 0xdf, 
+       0x53, 0xbe, 0x7e, 0x00, 0xb0, 0x34, 0x94, 0x02, 0x1c, 0x86, 0x01, 0xca, 
+       0x12, 0x81, 0x0d, 0x86, 0x80, 0x0c, 0xae, 0x50, 0x2b, 0x9c, 0x84, 0x5e, 
+       0x3c, 0x23, 0x93, 0xad, 0x10, 0x49, 0xd5, 0xc6, 0xf0, 0x1d, 0x94, 0x08, 
+       0x5f, 0x29, 0xd8, 0xdf, 0x82, 0xfa, 0xc8, 0xfe, 0xa4, 0x0a, 0xa7, 0x23, 
+       0x20, 0x99, 0x8e, 0x28, 0xfe, 0x3d, 0x26, 0x85, 0x1d, 0x78, 0x52, 0x1a, 
+       0x00, 0xc0, 0x69, 0x64, 0x3e, 0x3b, 0x0c, 0x01, 0x5f, 0xcc, 0x4e, 0x67, 
+       0xbe, 0xd8, 0x7f, 0xb4, 0x28, 0x27, 0x61, 0x5b, 0x05, 0xd3, 0xea, 0xdf, 
+       0x81, 0x00, 0x98, 0x10, 0x82, 0x40, 0x1d, 0x72, 0xc0, 0x70, 0x9e, 0x51, 
+       0x69, 0x70, 0x1e, 0xf7, 0xc8, 0xe1, 0xe1, 0x0d, 0x7e, 0x81, 0xbe, 0x00, 
+       0x00, 0xec, 0x10, 0x81, 0xc0, 0x30, 0x41, 0x30, 0xf7, 0x25, 0x06, 0x39, 
+       0xac, 0x8c, 0xee, 0xc0, 0x22, 0x27, 0xdf, 0x68, 0x01, 0x80, 0x14, 0xe0, 
+       0x3b, 0x26, 0x94, 0x19, 0x80, 0xff, 0x5f, 0x1d, 0x8d, 0xe1, 0x57, 0x50, 
+       0x0e, 0xbe, 0x18, 0x1a, 0x1b, 0xc0, 0xfa, 0x7f, 0x71, 0x5a, 0x84, 0x9f, 
+       0xb5, 0x7f, 0xf2, 0x32, 0x18, 0x49, 0x09, 0x1f, 0x9e, 0x11, 0xc2, 0xf5, 
+       0xe9, 0xc0, 0x40, 0x87, 0x2f, 0xf6, 0x3b, 0x13, 0xc0, 0x7f, 0x79, 0x80, 
+       0x13, 0xa7, 0x64, 0x64, 0x63, 0x56, 0x1d, 0xa0, 0x02, 0x0c, 0x10, 0x9e, 
+       0x3f, 0xb0, 0x50, 0x1b, 0xbd, 0x27, 0x01, 0x47, 0x16, 0x70, 0x7d, 0x91, 
+       0xb7, 0xeb, 0xc8, 0x63, 0x4b, 0x21, 0xfc, 0x85, 0xb3, 0x6c, 0x28, 0x08, 
+       0xde, 0x4c, 0x04, 0x29, 0xc3, 0x36, 0x35, 0xdc, 0x3a, 0x50, 0x28, 0x4d, 
+       0xe1, 0xa5, 0xf5, 0x6c, 0xaf, 0xd5, 0x96, 0x7e, 0x36, 0xfa, 0x81, 0x0d, 
+       0x03, 0x03, 0x53, 0x9f, 0xf1, 0x22, 0x2e, 0x3d, 0x82, 0x76, 0x38, 0x66, 
+       0xae, 0xfb, 0xbb, 0x3f, 0x1d, 0x7c, 0x9f, 0x86, 0x24, 0x30, 0x99, 0xf9, 
+       0x2d, 0x5c, 0xcc, 0x77, 0x7b, 0xea, 0x48, 0x40, 0xd4, 0xec, 0x3f, 0xb5, 
+       0xf1, 0x90, 0x13, 0xf5, 0x21, 0x1b, 0x0b, 0x11, 0xb6, 0xb8, 0x53, 0xd6, 
+       0x8d, 0x6b, 0xaf, 0xe8, 0x85, 0x13, 0x1d, 0x04, 0xb4, 0xf6, 0x45, 0x9c, 
+       0xb2, 0x16, 0x42, 0x31, 0x68, 0x48, 0xdd, 0x79, 0x50, 0x28, 0x5e, 0xd8, 
+       0x0c, 0x17, 0x8f, 0x39, 0xc5, 0x99, 0x7a, 0xa0, 0x2a, 0x18, 0x03, 0x6e, 
+       0xdb, 0xf6, 0xeb, 0xad, 0xf8, 0xd6, 0x0a, 0x1d, 0xf4, 0xaf, 0xd9, 0x6c, 
+       0xfd, 0xb5, 0xf3, 0xc0, 0x0a, 0x91, 0xbe, 0x41, 0x69, 0x64, 0xf1, 0x4a, 
+       0x0b, 0xbd, 0xda, 0x1d, 0xd4, 0x7e, 0x58, 0x55, 0xf0, 0xa0, 0x2c, 0x03, 
+       0x14, 0x0e, 0x4e, 0x0b, 0xc0, 0x72, 0x82, 0xfb, 0x08, 0x34, 0x54, 0xf5, 
+       0xfd, 0x40, 0x02, 0xc6, 0x2c, 0x95, 0xfe, 0xdc, 0xc3, 0xd7, 0x12, 0x19, 
+       0x33, 0x13, 0x06, 0x67, 0x7f, 0xcf, 0xb8, 0x60, 0x3a, 0x47, 0xe4, 0xd5, 
+       0xbb, 0xa3, 0x72, 0x05, 0x8c, 0x37, 0x00, 0xdc, 0x86, 0xcf, 0x9b, 0x36, 
+       0x03, 0x82, 0x5f, 0xda, 0x3f, 0x1a, 0x7a, 0xb6, 0x7f, 0x4d, 0xf6, 0x52, 
+       0x05, 0x32, 0x03, 0x31, 0x7d, 0x2b, 0xec, 0xa1, 0x20, 0x72, 0xe8, 0x94, 
+       0x4c, 0x74, 0x12, 0xd3, 0xd9, 0x17, 0xcf, 0x00, 0x4f, 0xff, 0x18, 0x8c, 
+       0x2f, 0xf0, 0xe0, 0x23, 0x74, 0x40, 0xa1, 0x7b, 0x60, 0x30, 0x5e, 0x3c, 
+       0xe7, 0x16, 0x65, 0xd1, 0x92, 0x91, 0x8e, 0x2c, 0x9d, 0x49, 0xb7, 0xea, 
+       0x8b, 0x21, 0x64, 0x23, 0x16, 0x84, 0x8d, 0xd7, 0x19, 0x00, 0x3a, 0xdc, 
+       0xa1, 0xbd, 0x2a, 0x5f, 0x56, 0x33, 0x5d, 0x90, 0x2a, 0x18, 0x03, 0x6f, 
+       0xb6, 0xfd, 0xba, 0xef, 0x98, 0x80, 0x4c, 0x03, 0xac, 0x23, 0x70, 0xff, 
+       0xdc, 0x0d, 0x8a, 0x8e, 0x9e, 0xd9, 0xb9, 0xfe, 0x2f, 0xe8, 0xc1, 0x80, 
+       0x50, 0x96, 0x7f, 0xdb, 0xb3, 0xec, 0x68, 0x1a, 0xbb, 0xc0, 0x36, 0x49, 
+       0x2f, 0xa3, 0x67, 0xf8, 0x07, 0xf7, 0xce, 0x00, 0x4d, 0x9f, 0x96, 0x5f, 
+       0x0b, 0x27, 0xfb, 0xc6, 0x06, 0x77, 0xc9, 0x2d, 0x41, 0x3f, 0xf7, 0x15, 
+       0x95, 0x9e, 0xd3, 0x97, 0xf7, 0xe6, 0x9c, 0x17, 0xe4, 0xd7, 0xf5, 0xa0, 
+       0x0a, 0xb0, 0xd2, 0x51, 0x2f, 0x0d, 0x30, 0x70, 0x7d, 0xaf, 0x10, 0x80, 
+       0xb9, 0x2b, 0xa7, 0x6e, 0xe4, 0xe6, 0x7b, 0x86, 0x18, 0x92, 0x8a, 0xef, 
+       0x9c, 0xb4, 0xfb, 0x92, 0x03, 0xa7, 0x2c, 0x34, 0xb4, 0xa4, 0xfc, 0xa0, 
+       0x0e, 0x2c, 0x3f, 0xfb, 0x7e, 0x28, 0x9f, 0x55, 0xfa, 0xa2, 0xc0, 0xa0, 
+       0xc4, 0xb6, 0x49, 0x8f, 0xff, 0x56, 0xcb, 0x69, 0x49, 0xa0, 0x50, 0x06, 
+       0xc8, 0xdd, 0xdc, 0xcf, 0x72, 0x12, 0x02, 0x90, 0x2a, 0x30, 0xc7, 0x58, 
+       0xb6, 0x01, 0xf5, 0x84, 0x06, 0x2e, 0x5a, 0x72, 0x31, 0x87, 0x89, 0xfd, 
+       0x96, 0x02, 0xf9, 0xef, 0xdd, 0x19, 0xcc, 0x7f, 0x2b, 0xf7, 0x01, 0x84, 
+       0x24, 0x86, 0xbf, 0x3b, 0x63, 0x7d, 0xe1, 0x48, 0x41, 0x85, 0x12, 0xdd, 
+       0x09, 0xfc, 0x63, 0xba, 0x83, 0xae, 0x78, 0x0e, 0xf8, 0x18, 0x0c, 0xc5, 
+       0x63, 0x54, 0x1d, 0xae, 0x68, 0x0e, 0x91, 0x8a, 0x2b, 0xf3, 0x4f, 0xe2, 
+       0x80, 0x82, 0xee, 0xd4, 0xa7, 0xf4, 0x7e, 0x3d, 0x5b, 0x50, 0xd7, 0xf4, 
+       0xd2, 0x1b, 0x86, 0x93, 0x7e, 0xcf, 0xd9, 0x41, 0x47, 0x93, 0xe4, 0x00, 
+       0xab, 0x23, 0xec, 0x94, 0xed, 0xf9, 0xdb, 0x2d, 0x62, 0xef, 0x0c, 0x03, 
+       0x04, 0x70, 0x92, 0xf9, 0xe9, 0x02, 0x37, 0xa4, 0xc9, 0x0c, 0x41, 0x7c, 
+       0xbf, 0xfa, 0x52, 0xe7, 0x63, 0x8d, 0x1e, 0x7d, 0xc6, 0xe6, 0x63, 0x35, 
+       0x77, 0xec, 0x0b, 0x26, 0x24, 0xa4, 0x8c, 0x3f, 0xf3, 0x9a, 0xf3, 0xdc, 
+       0x0a, 0x62, 0xb0, 0x17, 0x4b, 0x39, 0x99, 0x77, 0xcf, 0x88, 0x43, 0x4b, 
+       0x2d, 0x25, 0xa7, 0x37, 0xc1, 0x1b, 0x0e, 0x36, 0xfa, 0x2e, 0x04, 0x2f, 
+       0xa2, 0x6b, 0xf3, 0x9c, 0x9c, 0x66, 0x0f, 0xa4, 0x6f, 0xe1, 0xd9, 0x73, 
+       0xab, 0x7c, 0x28, 0x0e, 0xf0, 0x01, 0xb0, 0x03, 0xac, 0x18, 0x37, 0xbf, 
+       0xd9, 0x0e, 0xcc, 0x03, 0x9e, 0x35, 0x39, 0x8c, 0x53, 0xeb, 0xef, 0x77, 
+       0xc4, 0x40, 0x16, 0xf4, 0xa4, 0x06, 0xc1, 0x88, 0x5a, 0x8b, 0x2d, 0x28, 
+       0xc3, 0xb6, 0x52, 0x78, 0x70, 0xdb, 0xec, 0x25, 0x93, 0x03, 0x43, 0x18, 
+       0x6a, 0x3a, 0x1f, 0x6f, 0x9f, 0x1f, 0xbd, 0x90, 0x09, 0x80, 0x70, 0x37, 
+       0x9e, 0x5f, 0xe9, 0xa8, 0xb3, 0xc4, 0x5e, 0xda, 0x58, 0xf9, 0x30, 0x35, 
+       0x08, 0xc5, 0xf4, 0x27, 0x21, 0x27, 0x76, 0x57, 0x68, 0x00, 0xd8, 0x34, 
+       0x94, 0x4b, 0x19, 0xc6, 0x3b, 0x0d, 0x65, 0x3d, 0xe2, 0x3e, 0x1f, 0x74, 
+       0x5e, 0xda, 0x52, 0x94, 0x4e, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 
+       0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 
+       0x94, 0xa5, 0x22, 0x20, 0x00, 0x00, 0x01, 0x11, 0x6b, 0xfb, 0x9b, 0xce, 
+       0xf5, 0xaf, 0x35, 0x4d, 0x4d, 0xd4, 0xf3, 0x3d, 0x64, 0x65, 0x16, 0xaf, 
+       0x18, 0xd2, 0x99, 0xdc, 0xa5, 0x29, 0x11, 0x72, 0x94, 0xa4, 0x45, 0xca, 
+       0x52, 0x91, 0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94, 
+       0xa4, 0x45, 0xca, 0x52, 0x91, 0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, 
+       0x11, 0x72, 0x94, 0xa4, 0x45, 0xca, 0x52, 0x91, 0x17, 0x2f, 0x99, 0x00, 
+       0x64, 0x01, 0x98, 0x0e, 0x80, 0x34, 0xc5, 0xb2, 0x09, 0xa9, 0xe5, 0xfc, 
+       0xff, 0x8c, 0xc7, 0x1c, 0x68, 0xbf, 0x7d, 0x36, 0x97, 0x49, 0x65, 0x60, 
+       0xdc, 0xdc, 0xea, 0x37, 0x6b, 0xc4, 0x1b, 0xd1, 0xd2, 0xe8, 0xe9, 0xee, 
+       0x7f, 0x4b, 0x3b, 0x87, 0x7f, 0x79, 0x60, 0x13, 0x00, 0x5f, 0xc0, 0xa1, 
+       0x31, 0x24, 0x24, 0xa0, 0x96, 0x51, 0x6e, 0xeb, 0x6f, 0xb2, 0x73, 0x25, 
+       0x8c, 0x10, 0x2e, 0xf6, 0xd4, 0xa5, 0x25, 0x64, 0xe6, 0xeb, 0x9b, 0xb5, 
+       0xfc, 0xd4, 0x30, 0x01, 0xf9, 0x08, 0x04, 0xc8, 0x4e, 0x01, 0xd1, 0x31, 
+       0x2e, 0x93, 0xf9, 0x7d, 0xce, 0x38, 0xe1, 0xcb, 0xbd, 0x38, 0x14, 0x01, 
+       0x3a, 0x49, 0x80, 0x31, 0x40, 0x15, 0x2d, 0xcb, 0xef, 0xdc, 0x7f, 0x7d, 
+       0xf1, 0x15, 0x57, 0xa3, 0xa5, 0xae, 0x26, 0x23, 0x0d, 0x51, 0x25, 0x5f, 
+       0xf3, 0xf8, 0x7d, 0x2f, 0xc2, 0xaf, 0xc0, 0x80, 0x6a, 0x00, 0xff, 0x00, 
+       0xef, 0xf2, 0x50, 0x66, 0x43, 0xa0, 0x60, 0xde, 0x49, 0x18, 0xee, 0xc3, 
+       0xb2, 0xdc, 0x4e, 0xf7, 0x2c, 0x86, 0x00, 0xcc, 0x0c, 0x86, 0x21, 0x08, 
+       0x26, 0x64, 0x27, 0x2f, 0x73, 0xf1, 0x5c, 0xed, 0xd1, 0x87, 0xb7, 0xe7, 
+       0x9d, 0xef, 0xa9, 0x52, 0xd0, 0x02, 0xc2, 0x99, 0x19, 0x89, 0x07, 0x2f, 
+       0xc8, 0x6f, 0xbf, 0x9c, 0x80, 0x64, 0x00, 0xfc, 0xa0, 0x13, 0x06, 0x06, 
+       0xb9, 0x49, 0x48, 0x69, 0x7d, 0xc6, 0xb9, 0xea, 0x56, 0xe1, 0xee, 0x17, 
+       0x7d, 0x01, 0x29, 0xe5, 0xa7, 0xa5, 0x3d, 0xfa, 0x7f, 0xff, 0x8f, 0xfe, 
+       0xba, 0x96, 0x18, 0xa4, 0x23, 0xa5, 0x3b, 0x2d, 0x27, 0x87, 0x54, 0xf7, 
+       0x2e, 0x00, 0x06, 0x80, 0x57, 0x00, 0xc0, 0x0a, 0x94, 0x1a, 0x9d, 0xfe, 
+       0x40, 0xd5, 0x63, 0xd6, 0x8f, 0x88, 0xb7, 0x7e, 0x96, 0x5f, 0x64, 0xef, 
+       0xb9, 0xdd, 0x55, 0x1d, 0x7e, 0x18, 0x01, 0xf8, 0x06, 0x20, 0x06, 0xfc, 
+       0xac, 0x8d, 0xd0, 0x84, 0x63, 0xb8, 0x13, 0xe6, 0xa8, 0x51, 0xc1, 0xf7, 
+       0x7c, 0x06, 0x05, 0x86, 0x06, 0x06, 0xa0, 0xb2, 0xc6, 0x81, 0x64, 0xa1, 
+       0x3b, 0x23, 0x65, 0xb7, 0x75, 0xbf, 0xd7, 0x6a, 0x96, 0xa8, 0x29, 0x08, 
+       0x40, 0xcc, 0xad, 0x4b, 0xbf, 0x7b, 0xf9, 0x28, 0x0c, 0x40, 0x1f, 0x80, 
+       0x80, 0x04, 0xf9, 0x18, 0x34, 0x98, 0x4d, 0x08, 0xfd, 0xbe, 0xfb, 0x75, 
+       0x9f, 0x76, 0x4a, 0x00, 0x7e, 0x50, 0x0c, 0x32, 0x70, 0x0e, 0x88, 0x59, 
+       0x0e, 0xc0, 0x5c, 0xbe, 0xe9, 0x1e, 0xb3, 0x02, 0xaf, 0xa0, 0xd2, 0xe9, 
+       0xc0, 0x50, 0xad, 0x86, 0x2d, 0x87, 0x3f, 0x6a, 0x06, 0xfb, 0xe7, 0x16, 
+       0x4d, 0x49, 0x0c, 0xb1, 0xa4, 0xd4, 0xf4, 0x9c, 0xfc, 0xd6, 0x14, 0x65, 
+       0xf3, 0xa2, 0x10, 0x03, 0xf0, 0x28, 0x42, 0x01, 0xd7, 0x21, 0x21, 0x01, 
+       0xab, 0xc9, 0xc4, 0xb0, 0x8d, 0xdd, 0x2e, 0xb3, 0xcf, 0xc4, 0x4b, 0xea, 
+       0xd4, 0xb1, 0x48, 0x62, 0x0b, 0xdb, 0x73, 0xb2, 0xf6, 0xca, 0x3a, 0xad, 
+       0xfe, 0xbf, 0x2e, 0x02, 0x00, 0x13, 0x80, 0x1c, 0x10, 0xc9, 0x7b, 0x0d, 
+       0x47, 0x74, 0x7d, 0xcf, 0x19, 0xf9, 0x1d, 0x55, 0x60, 0x19, 0x81, 0x52, 
+       0x60, 0x0c, 0x43, 0x78, 0x69, 0x58, 0xb4, 0x27, 0xb3, 0xee, 0xcc, 0xac, 
+       0xcf, 0x9b, 0x88, 0xbd, 0x65, 0x28, 0x25, 0x20, 0x69, 0x2c, 0xd6, 0x60, 
+       0xe9, 0xba, 0x9a, 0xf8, 0x80, 0x20, 0x02, 0xa0, 0x07, 0x80, 0x54, 0xa0, 
+       0xd6, 0x71, 0x8e, 0xf8, 0x57, 0xdf, 0x11, 0x6f, 0x2e, 0x02, 0x10, 0x07, 
+       0x7c, 0x86, 0x90, 0x14, 0x20, 0x94, 0x76, 0xe9, 0xeb, 0x4a, 0x76, 0x58, 
+       0xce, 0x63, 0x3b, 0xa8, 0xd3, 0x6f, 0xa1, 0xd2, 0xae, 0x18, 0x8e, 0x9c, 
+       0x9f, 0x97, 0xb3, 0x49, 0xfb, 0x35, 0xfc, 0xcc, 0x34, 0x02, 0xf0, 0x28, 
+       0x02, 0x74, 0x20, 0x00, 0xfc, 0x98, 0x92, 0x5a, 0x00, 0xf9, 0x5f, 0x65, 
+       0x30, 0xb1, 0x66, 0x5e, 0x98, 0x03, 0x50, 0x0d, 0x4b, 0x00, 0xd5, 0x3b, 
+       0xa7, 0x64, 0x64, 0x12, 0x11, 0x8c, 0x73, 0xd6, 0x2e, 0xf4, 0xd4, 0xa9, 
+       0x04, 0xc4, 0x23, 0xef, 0xb1, 0xce, 0xc2, 0x9c, 0xf6, 0x8f, 0xff, 0xbf, 
+       0x36, 0x01, 0xa0, 0x08, 0x52, 0x01, 0xa1, 0x7d, 0x28, 0xd9, 0x3b, 0xe6, 
+       0xfb, 0x90, 0x4f, 0xd7, 0x97, 0x00, 0x13, 0x81, 0x42, 0x11, 0x65, 0x93, 
+       0x40, 0xa1, 0x31, 0x28, 0x2c, 0xbd, 0xf0, 0x4f, 0xc4, 0xac, 0xeb, 0x7d, 
+       0xb0, 0xbd, 0x95, 0x7d, 0x32, 0x92, 0x49, 0x31, 0x09, 0xef, 0xf1, 0xf9, 
+       0xba, 0xcf, 0xc2, 0xe1, 0xfd, 0xf8, 0x00, 0x0c, 0x00, 0x1f, 0x80, 0x1e, 
+       0x16, 0x4d, 0x48, 0x6e, 0xe5, 0x81, 0x96, 0xff, 0xe6, 0x77, 0xdf, 0x91, 
+       0xdf, 0xda, 0x80, 0x04, 0xe0, 0x85, 0xf3, 0x89, 0xb8, 0x98, 0x56, 0xd9, 
+       0x29, 0x2c, 0x62, 0xd6, 0x94, 0xe6, 0x7f, 0xcd, 0x6e, 0xbc, 0xa5, 0x5e, 
+       0xd6, 0x93, 0x01, 0xb1, 0x4f, 0xd2, 0xcb, 0x4e, 0x6e, 0x22, 0x06, 0xfb, 
+       0xf9, 0xe0, 0x08, 0x40, 0x1d, 0xf2, 0x1a, 0x40, 0x50, 0x82, 0x51, 0xdb, 
+       0xa7, 0xad, 0x29, 0xd9, 0x63, 0x39, 0x8c, 0xee, 0xa3, 0x4d, 0xb3, 0x06, 
+       0x80, 0x5e, 0x05, 0x00, 0x4e, 0x84, 0x00, 0x1f, 0x93, 0x12, 0x4b, 0x40, 
+       0x1f, 0x2b, 0xec, 0xa6, 0x16, 0x2c, 0xcb, 0xe9, 0x94, 0xac, 0x02, 0xc2, 
+       0xb6, 0xcd, 0x98, 0x61, 0xdf, 0x7e, 0xe1, 0x72, 0x1b, 0xeb, 0xff, 0x1a, 
+       0x58, 0x6a, 0x40, 0xa9, 0x68, 0xc7, 0x19, 0x83, 0xef, 0x97, 0x00, 0xc5, 
+       0x28, 0xc3, 0x32, 0x77, 0xfb, 0xbf, 0x51, 0xea, 0x03, 0x77, 0xdb, 0x8a, 
+       0x49, 0x29, 0x39, 0x2c, 0xec, 0x65, 0xcd, 0x26, 0x86, 0x80, 0xdd, 0x2e, 
+       0xcc, 0xa6, 0xbb, 0x3a, 0x17, 0x87, 0x67, 0xb3, 0x7a, 0xbb, 0x92, 0x1a, 
+       0x01, 0x7b, 0x13, 0x00, 0x2c, 0x02, 0x85, 0x01, 0x82, 0x99, 0xbb, 0xad, 
+       0x3d, 0xcf, 0x71, 0x2b, 0x0e, 0xaa, 0x01, 0x00, 0x05, 0xe0, 0x16, 0x06, 
+       0x24, 0x98, 0x1a, 0x8e, 0x59, 0x5f, 0x76, 0xd8, 0xfc, 0xe7, 0xe1, 0x1e, 
+       0xfa, 0x15, 0x2e, 0x20, 0x1b, 0x15, 0xf3, 0x31, 0xae, 0x2d, 0xc7, 0xb8, 
+       0xfb, 0x47, 0xf7, 0xe5, 0xb8, 0x15, 0x0c, 0x0c, 0xe9, 0x00, 0xb0, 0x33, 
+       0xa3, 0xf5, 0x64, 0xa5, 0x2e, 0x9d, 0xba, 0x3e, 0xec, 0xcd, 0xb7, 0x59, 
+       0xeb, 0xbc, 0xf0, 0x06, 0x80, 0x17, 0x86, 0x00, 0x80, 0x99, 0xd0, 0x4a, 
+       0x4a, 0x53, 0xff, 0xee, 0xad, 0x8e, 0x56, 0x73, 0xd8, 0x83, 0x7d, 0x2a, 
+       0x94, 0x24, 0x31, 0x09, 0xcd, 0xd6, 0x1e, 0x3a, 0x6f, 0xf5, 0xf9, 0x20, 
+       0x0c, 0x80, 0x4e, 0x00, 0x78, 0x43, 0xe5, 0xa7, 0x74, 0x8d, 0xdf, 0x71, 
+       0xeb, 0xfb, 0xba, 0xc5, 0xeb, 0x30, 0x0c, 0x40, 0x2f, 0x49, 0x34, 0xb7, 
+       0x2b, 0x01, 0x8c, 0x37, 0x25, 0x47, 0xa7, 0xa0, 0x91, 0xd2, 0x23, 0x6c, 
+       0xd8, 0xeb, 0xdb, 0x52, 0x40, 0x61, 0x03, 0x46, 0xb2, 0xd9, 0xa1, 0xfb, 
+       0x35, 0xfc, 0xbc, 0x07, 0x60, 0x0e, 0xca, 0x21, 0xe2, 0x81, 0x08, 0x1b, 
+       0x74, 0xa1, 0x6e, 0x95, 0xed, 0xd4, 0x76, 0x34, 0xe3, 0x6b, 0xc0, 0xa0, 
+       0x03, 0xdc, 0x4c, 0x02, 0x9c, 0x98, 0x90, 0xc2, 0x5f, 0xee, 0xb7, 0x24, 
+       0x8d, 0xfd, 0xc6, 0x9a, 0x72, 0xaf, 0xa4, 0xd2, 0xdc, 0x02, 0xc2, 0x86, 
+       0x0c, 0x65, 0x3b, 0x9f, 0xe9, 0x1b, 0xef, 0xe7, 0x00, 0x30, 0x00, 0x72, 
+       0x1a, 0x4c, 0x2d, 0x3c, 0xb0, 0x94, 0x24, 0xcc, 0xa1, 0x8a, 0x50, 0xc1, 
+       0x46, 0x35, 0xd6, 0xe0, 0x3b, 0x02, 0x85, 0x16, 0x4b, 0x21, 0x13, 0x3e, 
+       0xe7, 0xe2, 0x5f, 0x7c, 0x8e, 0xee, 0xad, 0x89, 0xcf, 0xef, 0x63, 0x4b, 
+       0x20, 0x30, 0x8d, 0xff, 0xd9, 0x9c, 0xf6, 0x36, 0xa7, 0xf7, 0xf3, 0xe0, 
+       0x0c, 0x00, 0x2f, 0x00, 0x3c, 0x25, 0xe2, 0xf2, 0x4b, 0xc9, 0xe3, 0x45, 
+       0x8d, 0x37, 0xac, 0x79, 0x12, 0xe8, 0xa0, 0x03, 0x5c, 0x51, 0x34, 0xa2, 
+       0xd0, 0x4c, 0x29, 0x08, 0x47, 0xfc, 0x95, 0x9d, 0x7f, 0xa1, 0x9f, 0xfe, 
+       0x7b, 0x1f, 0x87, 0x5e, 0xc2, 0x90, 0x06, 0x10, 0xff, 0xb1, 0x24, 0xf8, 
+       0x7f, 0x7f, 0x36, 0x01, 0xd8, 0x03, 0xb0, 0x32, 0x5e, 0xe5, 0x15, 0xb6, 
+       0xc3, 0x06, 0xb9, 0xe5, 0xa3, 0x77, 0x19, 0xc7, 0x67, 0x77, 0x1f, 0x6a, 
+       0x00, 0xcc, 0x01, 0xf8, 0x01, 0xd9, 0x45, 0x15, 0x90, 0x33, 0x66, 0x4f, 
+       0x01, 0x5b, 0x32, 0x85, 0x2e, 0xf7, 0xf4, 0xab, 0x13, 0x0a, 0xc3, 0x1b, 
+       0x12, 0x0e, 0x90, 0xdf, 0x7f, 0x36, 0x01, 0x88, 0x03, 0xb0, 0x0a, 0xc0, 
+       0xa6, 0x26, 0x62, 0xb1, 0x4e, 0x49, 0x1c, 0xdb, 0xbe, 0xc8, 0xa0, 0x00, 
+       0x84, 0x10, 0x82, 0x80, 0x2c, 0xc5, 0x6d, 0xc6, 0x12, 0x9c, 0xc1, 0x8c, 
+       0xdf, 0x89, 0xf7, 0xd2, 0x69, 0x30, 0x0b, 0x0a, 0xdb, 0x66, 0x3b, 0x8d, 
+       0x34, 0xf8, 0x1b, 0xee, 0x52, 0x94, 0xbc, 0xfb, 0x5b, 0x94, 0xa5, 0x22, 
+       0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 
+       0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0x80, 0x00, 0x00, 0x01, 0x12, 
+       0x73, 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 
+       0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 
+       0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 
+       0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 
+       0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 
+       0x94, 0x88, 0xb9, 0x4b, 0x31, 0x5b, 0x25, 0x3b, 0x7f, 0xf3, 0x8e, 0xce, 
+       0x45, 0xd7, 0x9c, 0x0d, 0x01, 0x32, 0x03, 0x08, 0x60, 0x16, 0x01, 0x90, 
+       0x97, 0xd9, 0xfa, 0x19, 0x0b, 0xdb, 0x9f, 0xbf, 0xe6, 0x81, 0x2b, 0x5e, 
+       0x9d, 0xbb, 0x07, 0xd2, 0xd7, 0xeb, 0x29, 0x63, 0x0d, 0x0c, 0x49, 0x30, 
+       0xbe, 0x52, 0x73, 0x74, 0x76, 0xf6, 0xcb, 0x33, 0xe7, 0x72, 0x94, 0xa4, 
+       0xa7, 0x72, 0x94, 0xa4, 0x45, 0xca, 0x5e, 0x5c, 0x06, 0x00, 0x3a, 0x28, 
+       0x9a, 0x51, 0x34, 0xb4, 0xa5, 0x38, 0x0c, 0x21, 0xdb, 0x6c, 0xb1, 0xcb, 
+       0xfc, 0x2d, 0x57, 0xa8, 0xb4, 0xdf, 0x7f, 0xf0, 0xea, 0x6e, 0x52, 0x94, 
+       0xb3, 0xaa, 0xe5, 0x29, 0x79, 0x70, 0x1d, 0x00, 0xe8, 0xa2, 0x69, 0x44, 
+       0xd2, 0xd2, 0x94, 0xe0, 0x30, 0x87, 0x6d, 0xb2, 0xc7, 0x2f, 0xf0, 0xbb, 
+       0x4f, 0xf2, 0x33, 0xfa, 0x4f, 0xbf, 0x51, 0x4a, 0x52, 0xce, 0xab, 0x94, 
+       0xa9, 0x4a, 0x3a, 0x0f, 0xcc, 0xa7, 0x0e, 0xf5, 0xd1, 0x17, 0x29, 0x4a, 
+       0x44, 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94, 0xa4, 0x45, 0xca, 0x52, 0x91, 
+       0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x97, 0x9a, 0x00, 
+       0x3e, 0x40, 0x01, 0xe7, 0x21, 0x8d, 0xe4, 0x9c, 0x8d, 0x98, 0xfe, 0x69, 
+       0xbc, 0xc1, 0x16, 0xe0, 0x1b, 0x60, 0x0b, 0x30, 0x69, 0x2d, 0x0e, 0x34, 
+       0x61, 0x28, 0x0f, 0x89, 0xb6, 0x1b, 0x89, 0xec, 0x1f, 0x4b, 0x5f, 0x5e, 
+       0x94, 0xa5, 0x8d, 0x57, 0x29, 0x4b, 0x20, 0x61, 0x5c, 0x34, 0xb4, 0x23, 
+       0x01, 0xff, 0xb4, 0xe2, 0xf5, 0xd2, 0xf2, 0x21, 0x80, 0x26, 0x28, 0x9b, 
+       0xcb, 0x18, 0x1b, 0xd8, 0x33, 0x7f, 0xd2, 0x4a, 0xdb, 0x36, 0x1c, 0x05, 
+       0xdd, 0x66, 0x2a, 0xb0, 0x98, 0x03, 0xa0, 0xc2, 0x19, 0xc4, 0xdf, 0x82, 
+       0x50, 0x53, 0x72, 0x8d, 0x49, 0xc7, 0x27, 0x8e, 0xba, 0x09, 0x79, 0x62, 
+       0x85, 0x7a, 0x1a, 0xfd, 0x55, 0x2e, 0x50, 0x15, 0x26, 0x62, 0x10, 0x6a, 
+       0x4a, 0x1a, 0x37, 0xa3, 0xb7, 0xec, 0xcb, 0x14, 0xbf, 0x64, 0x26, 0x81, 
+       0x44, 0x13, 0x0a, 0x42, 0x49, 0x7d, 0xcb, 0xcf, 0x91, 0xd0, 0xdd, 0x9d, 
+       0x9f, 0x84, 0xf6, 0x39, 0x47, 0xd9, 0x8d, 0xca, 0x16, 0x74, 0xda, 0xfd, 
+       0x05, 0x29, 0x4b, 0x2a, 0xae, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 
+       0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 
+       0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x20, 0x00, 
+       0x00, 0x01, 0x13, 0x73, 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, 
+       0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 
+       0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 
+       0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 
+       0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 
+       0x22, 0x2e, 0x52, 0x94, 0x88, 0xad, 0xfc, 0xc8, 0x0e, 0x90, 0x34, 0x9a, 
+       0x50, 0xae, 0xe1, 0x4b, 0xb8, 0x5c, 0xaf, 0xc8, 0x7c, 0xb4, 0x84, 0x20, 
+       0x6b, 0xbf, 0x22, 0x0f, 0xb7, 0x80, 0xeb, 0xa0, 0xa1, 0xa9, 0xff, 0x6e, 
+       0x33, 0xe4, 0x09, 0xbd, 0x87, 0xbd, 0x13, 0xd3, 0xc4, 0x35, 0x8d, 0x7f, 
+       0xf3, 0x07, 0x41, 0x35, 0x25, 0xa7, 0xec, 0xdb, 0x1f, 0xdc, 0x89, 0xee, 
+       0xea, 0x32, 0x0b, 0x3b, 0x36, 0x46, 0x74, 0xf3, 0x78, 0x1d, 0xb4, 0xe4, 
+       0xec, 0x4c, 0xf9, 0xd6, 0x94, 0x93, 0xb5, 0x90, 0x84, 0x5e, 0xd8, 0xb4, 
+       0x7e, 0x30, 0x6e, 0xcd, 0xc5, 0xeb, 0xa1, 0x98, 0x89, 0x45, 0xb8, 0xcc, 
+       0x4a, 0xfd, 0x38, 0x60, 0x12, 0x1e, 0x06, 0xaa, 0xba, 0x10, 0x8c, 0x8f, 
+       0xf0, 0xd4, 0x7c, 0x79, 0xaf, 0x76, 0xc2, 0x1f, 0x3f, 0x75, 0xaf, 0x90, 
+       0x2e, 0x08, 0xd4, 0x21, 0x05, 0xa1, 0xc6, 0xee, 0x71, 0x06, 0xca, 0xaa, 
+       0xdf, 0x6a, 0x0a, 0xc0, 0x60, 0x97, 0xb0, 0xd6, 0xfc, 0x97, 0xb9, 0x98, 
+       0xc0, 0x37, 0x7a, 0x42, 0xc6, 0x74, 0xf1, 0x9b, 0xec, 0x66, 0xf7, 0x88, 
+       0x28, 0xb0, 0xc2, 0x62, 0x72, 0x50, 0x97, 0x47, 0x40, 0x4a, 0x39, 0xee, 
+       0xc7, 0x01, 0xdb, 0xe8, 0x1e, 0xc3, 0x1f, 0xea, 0xaf, 0xfe, 0x7e, 0x8c, 
+       0xc4, 0x2f, 0xf8, 0x1f, 0xe4, 0xec, 0x38, 0x3e, 0xd6, 0x33, 0x24, 0x98, 
+       0x37, 0x6d, 0xb8, 0xd6, 0x53, 0xac, 0x2f, 0xd4, 0x27, 0xa1, 0x21, 0x85, 
+       0x23, 0x76, 0x18, 0x7b, 0x8b, 0x97, 0x06, 0xa3, 0x24, 0xac, 0x07, 0x90, 
+       0x4f, 0xe3, 0xda, 0xec, 0x59, 0x3e, 0xce, 0xf7, 0x96, 0x8c, 0x19, 0xc6, 
+       0x6c, 0x35, 0x26, 0x89, 0x1d, 0xae, 0x70, 0x68, 0x66, 0x03, 0x38, 0xa4, 
+       0x36, 0x52, 0xc8, 0xff, 0xda, 0xb7, 0x42, 0x03, 0x77, 0x1a, 0xeb, 0x3c, 
+       0x70, 0x59, 0xf7, 0x3f, 0x06, 0xa3, 0x13, 0x7f, 0x77, 0xdd, 0xdf, 0xb0, 
+       0xf9, 0xac, 0x9c, 0x4f, 0x8b, 0xf4, 0x19, 0x25, 0x6f, 0xf0, 0xc7, 0xc6, 
+       0x0f, 0x12, 0x7d, 0xe6, 0x83, 0x0a, 0xfc, 0x85, 0xc0, 0x7a, 0x95, 0x88, 
+       0xcb, 0x70, 0xb5, 0x5e, 0xcd, 0x39, 0x08, 0x3b, 0x8d, 0xbc, 0x40, 0x0d, 
+       0xf1, 0x40, 0x63, 0x65, 0xed, 0x82, 0x43, 0xcf, 0xa3, 0x8a, 0x01, 0xf4, 
+       0xef, 0xda, 0xa5, 0x25, 0x33, 0x21, 0x01, 0xff, 0xac, 0x81, 0x02, 0x66, 
+       0x1a, 0x5e, 0xc3, 0xfc, 0x4b, 0xc5, 0x23, 0x84, 0x71, 0xa2, 0xee, 0x58, 
+       0x14, 0x4e, 0x42, 0x7f, 0xfd, 0x95, 0xd8, 0x4d, 0xcc, 0x65, 0x77, 0xcd, 
+       0x04, 0x3f, 0x90, 0xde, 0x73, 0x1e, 0xe2, 0xb5, 0x68, 0x62, 0x10, 0x4d, 
+       0x4f, 0x46, 0xcd, 0x9b, 0xac, 0xd1, 0x35, 0xa1, 0x85, 0xa1, 0x1d, 0x38, 
+       0xc4, 0xa3, 0xa0, 0x69, 0x15, 0xa7, 0x42, 0x50, 0x18, 0x84, 0x9a, 0x94, 
+       0xa8, 0x46, 0xba, 0x96, 0x46, 0x6b, 0x3a, 0xc3, 0xda, 0xde, 0x19, 0x08, 
+       0x0c, 0x40, 0x0c, 0x30, 0x6a, 0x4a, 0x43, 0xf2, 0xdb, 0x12, 0x5d, 0x3b, 
+       0x61, 0xef, 0xf6, 0x16, 0x4e, 0xbd, 0xd5, 0xe4, 0xc0, 0x62, 0x1a, 0x84, 
+       0xa0, 0x84, 0x52, 0x3b, 0xe2, 0x8a, 0x58, 0x0a, 0xf9, 0xf7, 0x12, 0x5f, 
+       0xb6, 0x16, 0xbb, 0x5d, 0x5f, 0xe3, 0x19, 0xbe, 0xe5, 0xe4, 0x1f, 0xd9, 
+       0x08, 0x23, 0xd1, 0x28, 0x46, 0x25, 0x6e, 0xaf, 0xc9, 0x2c, 0x66, 0x20, 
+       0xd9, 0x72, 0x72, 0x49, 0x99, 0x03, 0x46, 0x74, 0x7e, 0x3d, 0x7b, 0x88, 
+       0xbb, 0xc8, 0xc5, 0x7e, 0xdf, 0xfc, 0xa5, 0x71, 0x63, 0xfd, 0xd6, 0x16, 
+       0xab, 0x3b, 0xca, 0x5e, 0xc5, 0x3a, 0x12, 0x86, 0xe6, 0x30, 0xe2, 0x2c, 
+       0x03, 0x72, 0x32, 0x0a, 0x53, 0x7f, 0x9f, 0x91, 0x0f, 0x90, 0xb2, 0xf7, 
+       0x41, 0x48, 0xeb, 0xf8, 0xd1, 0x43, 0xae, 0x68, 0x6a, 0x4a, 0xe5, 0x8d, 
+       0x1a, 0xe7, 0x32, 0x8e, 0x85, 0xa8, 0x3e, 0x2f, 0xa2, 0x43, 0x46, 0x28, 
+       0x66, 0x65, 0xb3, 0xfb, 0x0a, 0x50, 0x8c, 0x18, 0x57, 0x66, 0xfd, 0x64, 
+       0x03, 0x42, 0xed, 0xc4, 0x34, 0xa0, 0xb2, 0xdf, 0x77, 0x16, 0x2a, 0x62, 
+       0x6a, 0x50, 0x92, 0xf3, 0x70, 0x8c, 0xda, 0x79, 0xd4, 0x17, 0xe2, 0xf9, 
+       0xa1, 0xa8, 0x42, 0x32, 0x3b, 0x33, 0xa0, 0xe7, 0x34, 0x8f, 0x10, 0xd0, 
+       0xcd, 0xd2, 0x52, 0x11, 0x9b, 0xfd, 0x9c, 0x73, 0xdb, 0x8b, 0x2f, 0x16, 
+       0x4d, 0x70, 0x3d, 0x94, 0x1d, 0xae, 0x20, 0x19, 0x42, 0x08, 0x63, 0x5d, 
+       0xf2, 0xed, 0x16, 0x4e, 0xf4, 0xd6, 0x1b, 0x0a, 0x26, 0x06, 0x0d, 0x4f, 
+       0xe8, 0x62, 0x12, 0x1f, 0x72, 0xfb, 0x8d, 0x4a, 0x94, 0xc2, 0xdd, 0xba, 
+       0xb3, 0x5f, 0x42, 0xbc, 0xd1, 0x34, 0x94, 0x8f, 0xc0, 0x52, 0x03, 0x7e, 
+       0xb6, 0x3c, 0xd3, 0x7f, 0xe7, 0xf1, 0x43, 0xef, 0x47, 0x8f, 0x5d, 0xe7, 
+       0xa0, 0x7a, 0xed, 0xbd, 0x7f, 0xe1, 0xcb, 0xe3, 0x03, 0x03, 0x10, 0x02, 
+       0x3f, 0x69, 0x0d, 0xc8, 0x42, 0x0a, 0x6c, 0xdf, 0xe7, 0xe1, 0xe7, 0x59, 
+       0x88, 0x69, 0x29, 0x29, 0xcd, 0xdf, 0x2b, 0x08, 0xd3, 0x90, 0xd2, 0x84, 
+       0x96, 0xfb, 0xb9, 0x19, 0xae, 0xcc, 0x61, 0xe6, 0xfb, 0x2b, 0xb8, 0x9b, 
+       0xb6, 0xc5, 0x6d, 0xb6, 0xec, 0xfc, 0xfb, 0x9b, 0xc3, 0x10, 0xe2, 0x90, 
+       0x8c, 0x34, 0x48, 0xea, 0xe2, 0x1a, 0x51, 0xcb, 0x1b, 0xbb, 0x9c, 0xd8, 
+       0x74, 0xc4, 0xc2, 0xf0, 0xc2, 0xd1, 0xd2, 0x33, 0xa5, 0x97, 0x52, 0xdb, 
+       0x45, 0x69, 0xa5, 0x25, 0x23, 0x13, 0x3f, 0x2d, 0x19, 0x09, 0xff, 0x12, 
+       0xb6, 0x3c, 0x5b, 0x9e, 0xa6, 0x1f, 0x7b, 0x17, 0x6b, 0x9c, 0x58, 0xcc, 
+       0xad, 0xc6, 0x80, 0xf4, 0xe0, 0xec, 0x41, 0xba, 0x37, 0x9b, 0xad, 0xaf, 
+       0xc0, 0x93, 0x00, 0xb0, 0x0c, 0x08, 0x69, 0x02, 0x7f, 0x23, 0x11, 0x49, 
+       0xd7, 0xad, 0x26, 0x24, 0x61, 0x68, 0xe3, 0x93, 0xb8, 0x57, 0xbc, 0xa8, 
+       0x6f, 0x46, 0x02, 0x85, 0x14, 0x82, 0xd1, 0xfb, 0xf4, 0xa7, 0x0b, 0x22, 
+       0xde, 0xef, 0x2a, 0xe9, 0x42, 0xc6, 0x8b, 0xfc, 0x81, 0x47, 0x32, 0xb0, 
+       0xf0, 0x00, 0x3b, 0x26, 0x20, 0x00, 0xe4, 0x34, 0x6f, 0x08, 0xc0, 0x5d, 
+       0x22, 0x3e, 0xdd, 0x82, 0xfb, 0x5e, 0xc4, 0x9a, 0x1b, 0xc9, 0xa5, 0xf4, 
+       0xf3, 0x49, 0xc2, 0xaf, 0x1a, 0x01, 0x98, 0x15, 0x28, 0x37, 0xb1, 0x4d, 
+       0xc6, 0xe4, 0x72, 0xd2, 0x6f, 0xfb, 0x33, 0x08, 0x22, 0xdf, 0x42, 0x90, 
+       0x63, 0x8c, 0xdf, 0x8e, 0x69, 0xfb, 0x94, 0xa5, 0x2e, 0x0b, 0x5b, 0x94, 
+       0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 
+       0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 
+       0x22, 0xe5, 0x29, 0x48, 0x88, 0x00, 0x00, 0x01, 0x14, 0x83, 0xfb, 0xfd, 
+       0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 
+       0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 
+       0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 
+       0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 
+       0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 
+       0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 
+       0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 
+       0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 
+       0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 
+       0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 
+       0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 
+       0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 
+       0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 
+       0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 
+       0x00, 0x00, 0x01, 0x15, 0x83, 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, 
+       0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 
+       0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 
+       0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 
+       0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 
+       0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 
+       0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 
+       0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 
+       0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 
+       0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 
+       0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 
+       0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 
+       0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 
+       0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 
+       0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00, 0x01, 0x16, 0x83, 
+       0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 
+       0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 
+       0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 
+       0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 
+       0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 
+       0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 
+       0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 
+       0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 
+       0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 
+       0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 
+       0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 
+       0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 
+       0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 
+       0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 
+       0x52, 0x22, 0x00, 0x00, 0x01, 0x17, 0x83, 0xfb, 0xfd, 0x29, 0x49, 0x4a, 
+       0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 
+       0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 
+       0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 
+       0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 
+       0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 
+       0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 
+       0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 
+       0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 
+       0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 
+       0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 
+       0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 
+       0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 
+       0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 
+       0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00, 0x01, 
+       0x18, 0x83, 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94, 
+       0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 
+       0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 
+       0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 
+       0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 
+       0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 
+       0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 
+       0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 
+       0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 
+       0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 
+       0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 
+       0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 
+       0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 
+       0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 
+       0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00, 0x01, 0x19, 0x83, 0xfb, 0xfd, 0x29, 
+       0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 
+       0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 
+       0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 
+       0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 
+       0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 
+       0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 
+       0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 
+       0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 
+       0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 
+       0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 
+       0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 
+       0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 
+       0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 
+       0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0x00, 
+       0x00, 0x01, 0x1a, 0x83, 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, 
+       0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 
+       0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 
+       0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 
+       0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 
+       0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 
+       0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 
+       0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 
+       0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 
+       0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 
+       0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 
+       0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 
+       0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 
+       0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 
+       0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00, 0x01, 0x1b, 0x83, 0xfb, 
+       0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 
+       0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 
+       0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 
+       0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 
+       0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 
+       0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 
+       0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 
+       0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 
+       0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 
+       0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 
+       0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 
+       0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 
+       0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 
+       0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 
+       0x22, 0x00, 0x00, 0x01, 0x1c, 0x83, 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 
+       0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 
+       0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 
+       0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 
+       0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 
+       0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 
+       0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 
+       0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 
+       0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 
+       0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 
+       0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 
+       0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 
+       0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 
+       0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 
+       0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00, 0x01, 0x1d, 
+       0x83, 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 
+       0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 
+       0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 
+       0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 
+       0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 
+       0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 
+       0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 
+       0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 
+       0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 
+       0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 
+       0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 
+       0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 
+       0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 
+       0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 
+       0x4a, 0x52, 0x22, 0x00, 0x00, 0x01, 0x1e, 0x83, 0xfb, 0xfd, 0x29, 0x49, 
+       0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 
+       0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 
+       0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 
+       0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 
+       0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 
+       0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 
+       0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 
+       0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 
+       0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 
+       0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 
+       0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 
+       0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 
+       0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 
+       0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00, 
+       0x01, 0x1f, 0x83, 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b, 
+       0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 
+       0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 
+       0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 
+       0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 
+       0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 
+       0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 
+       0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 
+       0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 
+       0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 
+       0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 
+       0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 
+       0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 
+       0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 
+       0x88, 0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00, 0x01, 0x20, 0x83, 0xfb, 0xfd, 
+       0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 
+       0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 
+       0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 
+       0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 
+       0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 
+       0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 
+       0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 
+       0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 
+       0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 
+       0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 
+       0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 
+       0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 
+       0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 
+       0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 
+       0x00, 0x00, 0x01, 0x21, 0x83, 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, 
+       0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 
+       0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 
+       0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 
+       0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 
+       0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 
+       0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 
+       0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 
+       0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 
+       0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 
+       0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 
+       0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 
+       0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 
+       0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 
+       0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00, 0x01, 0x22, 0x83, 
+       0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 
+       0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 
+       0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 
+       0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 
+       0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 
+       0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 
+       0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 
+       0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 
+       0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 
+       0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 
+       0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 
+       0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 
+       0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 
+       0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 
+       0x52, 0x22, 0x00, 0x00, 0x01, 0x23, 0x83, 0xfb, 0xfd, 0x29, 0x49, 0x4a, 
+       0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 
+       0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 
+       0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 
+       0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 
+       0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 
+       0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 
+       0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 
+       0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 
+       0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 
+       0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 
+       0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 
+       0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 
+       0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 
+       0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00, 0x01, 
+       0x24, 0x83, 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94, 
+       0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 
+       0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 
+       0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 
+       0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 
+       0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 
+       0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 
+       0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 
+       0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 
+       0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 
+       0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 
+       0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 
+       0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 
+       0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 
+       0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00, 0x01, 0xb7, 
+};
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/suspend.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/suspend.h
new file mode 100644 (file)
index 0000000..bea25ee
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ *  $Id: suspend.h,v 1.2 2008/10/22 11:59:32 schmirl Exp $
+ */
+#ifndef VDR_STREAMDEV_SUSPEND_H
+#define VDR_STREAMDEV_SUSPEND_H
+
+#include <vdr/player.h>
+
+class cSuspendLive: public cPlayer, public cThread {
+protected:
+       virtual void Activate(bool On);
+       virtual void Action(void);
+
+       void Stop(void);
+
+public:
+       cSuspendLive(void);
+       virtual ~cSuspendLive();
+};
+
+class cSuspendCtl: public cControl {
+private:
+  cSuspendLive *m_Suspend;
+       static bool m_Active;
+
+public:
+  cSuspendCtl(void);
+  virtual ~cSuspendCtl();
+  virtual void Hide(void) {}
+  virtual eOSState ProcessKey(eKeys Key);
+
+       static bool IsActive(void) { return m_Active; }
+};
+
+#endif // VDR_STREAMDEV_SUSPEND_H
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/tools.c b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/tools.c
new file mode 100644 (file)
index 0000000..1c7cc18
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "tools.h"
+
+
+uint64_t ntohll(uint64_t a)
+{
+  return htonll(a);
+}
+
+uint64_t htonll(uint64_t a)
+{
+#if BYTE_ORDER == BIG_ENDIAN
+  return a;
+#else
+  uint64_t b = 0;
+
+  b = ((a << 56) & 0xFF00000000000000ULL)
+    | ((a << 40) & 0x00FF000000000000ULL)
+    | ((a << 24) & 0x0000FF0000000000ULL)
+    | ((a <<  8) & 0x000000FF00000000ULL)
+    | ((a >>  8) & 0x00000000FF000000ULL)
+    | ((a >> 24) & 0x0000000000FF0000ULL)
+    | ((a >> 40) & 0x000000000000FF00ULL)
+    | ((a >> 56) & 0x00000000000000FFULL) ;
+
+  return b;
+#endif
+}
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/tools.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/tools.h
new file mode 100644 (file)
index 0000000..fda1785
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef __VNSI_TOOLS_H
+#define __VNSI_TOOLS_H
+
+#include <vdr/tools.h>
+
+#define TRANSPONDER(c1, c2) (c1->Transponder() == c2->Transponder())
+
+uint64_t ntohll(uint64_t a);
+uint64_t htonll(uint64_t a);
+
+#endif //__VNSI_TOOLS_H
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/vdrcommand.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/vdrcommand.h
new file mode 100644 (file)
index 0000000..4826158
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef VDRCOMMAND_H
+#define VDRCOMMAND_H
+
+/** Current VNSI Protocol Version number */
+const static uint32_t VNSIProtocolVersion      = 1;
+
+/** Packet types */
+const static uint32_t CHANNEL_REQUEST_RESPONSE = 1;
+const static uint32_t CHANNEL_STREAM           = 2;
+const static uint32_t CHANNEL_KEEPALIVE        = 3;
+const static uint32_t CHANNEL_NETLOG           = 4;
+const static uint32_t CHANNEL_STATUS           = 5;
+const static uint32_t CHANNEL_SCAN             = 6;
+
+
+/** Response packets operation codes */
+
+/* OPCODE 1 - 19: VNSI network functions for general purpose */
+const static uint32_t VDR_LOGIN                 = 1;
+const static uint32_t VDR_GETTIME               = 2;
+const static uint32_t VDR_ENABLESTATUSINTERFACE = 3;
+const static uint32_t VDR_ENABLEOSDINTERFACE    = 4;
+
+/* OPCODE 20 - 39: VNSI network functions for live streaming */
+const static uint32_t VDR_CHANNELSTREAM_OPEN    = 20;
+const static uint32_t VDR_CHANNELSTREAM_CLOSE   = 21;
+
+/* OPCODE 40 - 59: VNSI network functions for recording streaming */
+const static uint32_t VDR_RECSTREAM_OPEN        = 40;
+const static uint32_t VDR_RECSTREAM_CLOSE       = 41;
+const static uint32_t VDR_RECSTREAM_GETBLOCK    = 42;
+const static uint32_t VDR_RECSTREAM_POSTOFRAME  = 43;
+const static uint32_t VDR_RECSTREAM_FRAMETOPOS  = 44;
+const static uint32_t VDR_RECSTREAM_GETIFRAME   = 45;
+
+/* OPCODE 60 - 79: VNSI network functions for channel access */
+const static uint32_t VDR_CHANNELS_GROUPSCOUNT  = 60;
+const static uint32_t VDR_CHANNELS_GETCOUNT     = 61;
+const static uint32_t VDR_CHANNELS_GETGROUPS    = 62;
+const static uint32_t VDR_CHANNELS_GETCHANNELS  = 63;
+
+/* OPCODE 80 - 99: VNSI network functions for timer access */
+const static uint32_t VDR_TIMER_GETCOUNT        = 80;
+const static uint32_t VDR_TIMER_GET             = 81;
+const static uint32_t VDR_TIMER_GETLIST         = 82;
+const static uint32_t VDR_TIMER_ADD             = 83;
+const static uint32_t VDR_TIMER_DELETE          = 84;
+const static uint32_t VDR_TIMER_UPDATE          = 85;
+
+/* OPCODE 100 - 119: VNSI network functions for recording access */
+const static uint32_t VDR_RECORDINGS_DISKSIZE   = 100;
+const static uint32_t VDR_RECORDINGS_GETCOUNT   = 101;
+const static uint32_t VDR_RECORDINGS_GETLIST    = 102;
+const static uint32_t VDR_RECORDINGS_GETINFO    = 103;
+const static uint32_t VDR_RECORDINGS_DELETE     = 104;
+const static uint32_t VDR_RECORDINGS_MOVE       = 105;
+
+/* OPCODE 120 - 139: VNSI network functions for epg access and manipulating */
+const static uint32_t VDR_EPG_GETFORCHANNEL     = 120;
+
+/* OPCODE 140 - 159: VNSI network functions for channel scanning */
+const static uint32_t VDR_SCAN_SUPPORTED        = 140;
+const static uint32_t VDR_SCAN_GETCOUNTRIES     = 141;
+const static uint32_t VDR_SCAN_GETSATELLITES    = 142;
+const static uint32_t VDR_SCAN_START            = 143;
+const static uint32_t VDR_SCAN_STOP             = 144;
+
+
+/** Stream packet types (server -> client) */
+const static uint32_t VDR_STREAM_CHANGE       = 1;
+const static uint32_t VDR_STREAM_STATUS       = 2;
+const static uint32_t VDR_STREAM_QUEUESTATUS  = 3;
+const static uint32_t VDR_STREAM_MUXPKT       = 4;
+const static uint32_t VDR_STREAM_SIGNALINFO   = 5;
+const static uint32_t VDR_STREAM_CONTENTINFO  = 6;
+
+/** Scan packet types (server -> client) */
+const static uint32_t VDR_SCANNER_PERCENTAGE  = 1;
+const static uint32_t VDR_SCANNER_SIGNAL      = 2;
+const static uint32_t VDR_SCANNER_DEVICE      = 3;
+const static uint32_t VDR_SCANNER_TRANSPONDER = 4;
+const static uint32_t VDR_SCANNER_NEWCHANNEL  = 5;
+const static uint32_t VDR_SCANNER_FINISHED    = 6;
+const static uint32_t VDR_SCANNER_STATUS      = 7;
+
+/** Status packet types (server -> client) */
+const static uint32_t VDR_STATUS_TIMERCHANGE  = 1;
+const static uint32_t VDR_STATUS_RECORDING    = 2;
+const static uint32_t VDR_STATUS_MESSAGE      = 3;
+
+/** Packet return codes */
+const static uint32_t VDR_RET_OK              = 0;
+const static uint32_t VDR_RET_RECRUNNING      = 1;
+const static uint32_t VDR_RET_NOTSUPPORTED    = 995;
+const static uint32_t VDR_RET_DATAUNKNOWN     = 996;
+const static uint32_t VDR_RET_DATALOCKED      = 997;
+const static uint32_t VDR_RET_DATAINVALID     = 998;
+const static uint32_t VDR_RET_ERROR           = 999;
+
+#endif
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/vnsiserver.c b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/vnsiserver.c
new file mode 100644 (file)
index 0000000..2ee3522
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * vnsiserver.c: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id$
+ */
+
+#include <vdr/plugin.h>
+#include "server.h"
+
+static const char *VERSION        = "0.0.1";
+static const char *DESCRIPTION    = "VDR-Network-Streaming-Interface (VNSI) Server";
+
+class cPluginVNSIServer : public cPlugin {
+private:
+  cServer *Server;
+
+public:
+  cPluginVNSIServer(void);
+  virtual ~cPluginVNSIServer();
+  virtual const char *Version(void) { return VERSION; }
+  virtual const char *Description(void) { return DESCRIPTION; }
+  virtual const char *CommandLineHelp(void);
+  virtual bool ProcessArgs(int argc, char *argv[]);
+  virtual bool Initialize(void);
+  virtual bool Start(void);
+  virtual void Stop(void);
+  virtual void Housekeeping(void);
+  virtual void MainThreadHook(void);
+  virtual cString Active(void);
+  virtual time_t WakeupTime(void);
+  virtual const char *MainMenuEntry(void) { return NULL; }
+  virtual cOsdObject *MainMenuAction(void) { return NULL; }
+  virtual cMenuSetupPage *SetupMenu(void);
+  virtual bool SetupParse(const char *Name, const char *Value);
+  virtual bool Service(const char *Id, void *Data = NULL);
+  virtual const char **SVDRPHelpPages(void);
+  virtual cString SVDRPCommand(const char *Command, const char *Option, int &ReplyCode);
+  };
+
+cPluginVNSIServer::cPluginVNSIServer(void)
+{
+  Server = NULL;
+}
+
+cPluginVNSIServer::~cPluginVNSIServer()
+{
+  // Clean up after yourself!
+}
+
+const char *cPluginVNSIServer::CommandLineHelp(void)
+{
+  // Return a string that describes all known command line options.
+  return NULL;
+}
+
+bool cPluginVNSIServer::ProcessArgs(int argc, char *argv[])
+{
+  // Implement command line argument processing here if applicable.
+  return true;
+}
+
+bool cPluginVNSIServer::Initialize(void)
+{
+  // Initialize any background activities the plugin shall perform.
+  VNSIServerConfig.ConfigDirectory = ConfigDirectory(PLUGIN_NAME_I18N);
+  return true;
+}
+
+bool cPluginVNSIServer::Start(void)
+{
+  VNSIServerConfig.readNoSignalStream();
+  Server = new cServer(VNSIServerConfig.listen_port);
+
+  return true;
+}
+
+void cPluginVNSIServer::Stop(void)
+{
+  delete Server;
+  Server = NULL;
+}
+
+void cPluginVNSIServer::Housekeeping(void)
+{
+  // Perform any cleanup or other regular tasks.
+}
+
+void cPluginVNSIServer::MainThreadHook(void)
+{
+  // Perform actions in the context of the main program thread.
+  // WARNING: Use with great care - see PLUGINS.html!
+}
+
+cString cPluginVNSIServer::Active(void)
+{
+  // Return a message string if shutdown should be postponed
+  return NULL;
+}
+
+time_t cPluginVNSIServer::WakeupTime(void)
+{
+  // Return custom wakeup time for shutdown script
+  return 0;
+}
+
+cMenuSetupPage *cPluginVNSIServer::SetupMenu(void)
+{
+  // Return a setup menu in case the plugin supports one.
+  return NULL;
+}
+
+bool cPluginVNSIServer::SetupParse(const char *Name, const char *Value)
+{
+  // Parse your own setup parameters and store their values.
+  return false;
+}
+
+bool cPluginVNSIServer::Service(const char *Id, void *Data)
+{
+  // Handle custom service requests from other plugins
+  return false;
+}
+
+const char **cPluginVNSIServer::SVDRPHelpPages(void)
+{
+  // Return help text for SVDRP commands this plugin implements
+  return NULL;
+}
+
+cString cPluginVNSIServer::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode)
+{
+  // Process SVDRP commands this plugin implements
+  return NULL;
+}
+
+VDRPLUGINCREATOR(cPluginVNSIServer); // Don't touch this!
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/vnsiserver/allowed_hosts.conf b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/vnsiserver/allowed_hosts.conf
new file mode 100644 (file)
index 0000000..b9fa1af
--- /dev/null
@@ -0,0 +1,13 @@
+#
+# allowed_hosts.conf  This file describes a number of host addresses that
+#                     are allowed to connect to the streamdev server running 
+#                     with the Video Disk Recorder (VDR) on this system.
+# Syntax:
+#
+# IP-Address[/Netmask]
+#
+
+127.0.0.1             # always accept localhost
+#192.168.0.0/24       # any host on the local net
+#204.152.189.113      # a specific host
+#0.0.0.0/0            # any host on any net (USE THIS WITH CARE!)
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/vnsiserver/noSignal.mpg b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/vnsiserver/noSignal.mpg
new file mode 100644 (file)
index 0000000..ebf1ff1
Binary files /dev/null and b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/vnsiserver/noSignal.mpg differ
diff --git a/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/wirbelscanservice.h b/xbmc/pvrclients/vdr-vnsi/vdr-plugin-vnsiserver/wirbelscanservice.h
new file mode 100644 (file)
index 0000000..625e0d3
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * wirbelscan.c: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id$
+ */
+
+#ifndef __WIRBELSCAN_SERVICE_H
+#define __WIRBELSCAN_SERVICE_H
+
+typedef enum scantype
+{
+  DVB_TERR    = 0,
+  DVB_CABLE   = 1,
+  DVB_SAT     = 2,
+  PVRINPUT    = 3,
+  PVRINPUT_FM = 4,
+  DVB_ATSC    = 5,
+} scantype_t;
+
+typedef void (*WirbelScanService_GetCountries_v1_0)(int index, const char *isoName, const char *longName);
+typedef void (*WirbelScanService_GetSatellites_v1_0)(int index, const char *shortName, const char *longName);
+
+struct WirbelScanService_DoScan_v1_0
+{
+  scantype_t  type;
+
+  bool        scan_tv;
+  bool        scan_radio;
+  bool        scan_fta;
+  bool        scan_scrambled;
+  bool        scan_hd;
+
+  int         CountryIndex;
+
+  int         DVBC_Inversion;
+  int         DVBC_Symbolrate;
+  int         DVBC_QAM;
+
+  int         DVBT_Inversion;
+
+  int         SatIndex;
+
+  int         ATSC_Type;
+
+  void (*SetPercentage)(int percent);
+  void (*SetSignalStrength)(int strenght, bool locked);
+  void (*SetDeviceInfo)(const char *Info);
+  void (*SetTransponder)(const char *Info);
+  void (*NewChannel)(const char *Name, bool isRadio, bool isEncrypted, bool isHD);
+  void (*IsFinished)();
+  void (*SetStatus)(int status);
+};
+
+#endif //__WIRBELSCAN_SERVICE_H
+
diff --git a/xbmc/pvrclients/vdr-vnsi/vdrcommand.h b/xbmc/pvrclients/vdr-vnsi/vdrcommand.h
new file mode 100644 (file)
index 0000000..4532482
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ *      Copyright (C) 2010 Alwin Esch (Team XBMC)
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef VDRCOMMAND_H
+#define VDRCOMMAND_H
+
+/** Current VNSI Protocol Version number */
+const static uint32_t VNSIProtocolVersion      = 1;
+
+
+/** Packet types */
+const static uint32_t CHANNEL_REQUEST_RESPONSE = 1;
+const static uint32_t CHANNEL_STREAM           = 2;
+const static uint32_t CHANNEL_KEEPALIVE        = 3;
+const static uint32_t CHANNEL_NETLOG           = 4;
+const static uint32_t CHANNEL_STATUS           = 5;
+const static uint32_t CHANNEL_SCAN             = 6;
+
+
+/** Response packets operation codes */
+
+/* OPCODE 1 - 19: VNSI network functions for general purpose */
+const static uint32_t VDR_LOGIN                 = 1;
+const static uint32_t VDR_GETTIME               = 2;
+const static uint32_t VDR_ENABLESTATUSINTERFACE = 3;
+const static uint32_t VDR_ENABLEOSDINTERFACE    = 4;
+
+/* OPCODE 20 - 39: VNSI network functions for live streaming */
+const static uint32_t VDR_CHANNELSTREAM_OPEN    = 20;
+const static uint32_t VDR_CHANNELSTREAM_CLOSE   = 21;
+
+/* OPCODE 40 - 59: VNSI network functions for recording streaming */
+const static uint32_t VDR_RECSTREAM_OPEN        = 40;
+const static uint32_t VDR_RECSTREAM_CLOSE       = 41;
+const static uint32_t VDR_RECSTREAM_GETBLOCK    = 42;
+const static uint32_t VDR_RECSTREAM_POSTOFRAME  = 43;
+const static uint32_t VDR_RECSTREAM_FRAMETOPOS  = 44;
+const static uint32_t VDR_RECSTREAM_GETIFRAME   = 45;
+
+/* OPCODE 60 - 79: VNSI network functions for channel access */
+const static uint32_t VDR_CHANNELS_GROUPSCOUNT  = 60;
+const static uint32_t VDR_CHANNELS_GETCOUNT     = 61;
+const static uint32_t VDR_CHANNELS_GETGROUPS    = 62;
+const static uint32_t VDR_CHANNELS_GETCHANNELS  = 63;
+
+/* OPCODE 80 - 99: VNSI network functions for timer access */
+const static uint32_t VDR_TIMER_GETCOUNT        = 80;
+const static uint32_t VDR_TIMER_GET             = 81;
+const static uint32_t VDR_TIMER_GETLIST         = 82;
+const static uint32_t VDR_TIMER_ADD             = 83;
+const static uint32_t VDR_TIMER_DELETE          = 84;
+const static uint32_t VDR_TIMER_UPDATE          = 85;
+
+/* OPCODE 100 - 119: VNSI network functions for recording access */
+const static uint32_t VDR_RECORDINGS_DISKSIZE   = 100;
+const static uint32_t VDR_RECORDINGS_GETCOUNT   = 101;
+const static uint32_t VDR_RECORDINGS_GETLIST    = 102;
+const static uint32_t VDR_RECORDINGS_GETINFO    = 103;
+const static uint32_t VDR_RECORDINGS_DELETE     = 104;
+const static uint32_t VDR_RECORDINGS_MOVE       = 105;
+
+/* OPCODE 120 - 139: VNSI network functions for epg access and manipulating */
+const static uint32_t VDR_EPG_GETFORCHANNEL     = 120;
+
+/* OPCODE 140 - 159: VNSI network functions for channel scanning */
+const static uint32_t VDR_SCAN_SUPPORTED        = 140;
+const static uint32_t VDR_SCAN_GETCOUNTRIES     = 141;
+const static uint32_t VDR_SCAN_GETSATELLITES    = 142;
+const static uint32_t VDR_SCAN_START            = 143;
+const static uint32_t VDR_SCAN_STOP             = 144;
+
+
+/** Stream packet types (server -> client) */
+const static uint32_t VDR_STREAM_CHANGE       = 1;
+const static uint32_t VDR_STREAM_STATUS       = 2;
+const static uint32_t VDR_STREAM_QUEUESTATUS  = 3;
+const static uint32_t VDR_STREAM_MUXPKT       = 4;
+const static uint32_t VDR_STREAM_SIGNALINFO   = 5;
+const static uint32_t VDR_STREAM_CONTENTINFO  = 6;
+
+/** Scan packet types (server -> client) */
+const static uint32_t VDR_SCANNER_PERCENTAGE  = 1;
+const static uint32_t VDR_SCANNER_SIGNAL      = 2;
+const static uint32_t VDR_SCANNER_DEVICE      = 3;
+const static uint32_t VDR_SCANNER_TRANSPONDER = 4;
+const static uint32_t VDR_SCANNER_NEWCHANNEL  = 5;
+const static uint32_t VDR_SCANNER_FINISHED    = 6;
+const static uint32_t VDR_SCANNER_STATUS      = 7;
+
+/** Status packet types (server -> client) */
+const static uint32_t VDR_STATUS_TIMERCHANGE  = 1;
+const static uint32_t VDR_STATUS_RECORDING    = 2;
+const static uint32_t VDR_STATUS_MESSAGE      = 3;
+
+/** Packet return codes */
+const static uint32_t VDR_RET_OK              = 0;
+const static uint32_t VDR_RET_RECRUNNING      = 1;
+const static uint32_t VDR_RET_NOTSUPPORTED    = 995;
+const static uint32_t VDR_RET_DATAUNKNOWN     = 996;
+const static uint32_t VDR_RET_DATALOCKED      = 997;
+const static uint32_t VDR_RET_DATAINVALID     = 998;
+const static uint32_t VDR_RET_ERROR           = 999;
+
+#endif
index e5124f4..b1ef215 100644 (file)
@@ -51,6 +51,7 @@ enum EINTERLACEMETHOD
   VS_INTERLACEMETHOD_VDPAU_TEMPORAL_HALF=13,
   VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL=14,
   VS_INTERLACEMETHOD_VDPAU_TEMPORAL_SPATIAL_HALF=15,
+  VS_INTERLACEMETHOD_AUTO_ION=16,
 };
 
 enum ESCALINGMETHOD
index 8a50960..64822ee 100644 (file)
 #include "SingleLock.h"
 #include "log.h"
 
+#include "pvr/PVRManager.h"
+#include "pvr/PVRChannel.h"
+#include "pvr/PVREpgInfoTag.h"
+#include "pvr/PVRChannelGroups.h"
+#include "pvr/PVRTimerInfoTag.h"
+
 #include "addons/AddonManager.h"
 
 #define SYSHEATUPDATEINTERVAL 60000
@@ -241,6 +247,54 @@ int CGUIInfoManager::TranslateSingleString(const CStdString &strCondition)
     else if (strTest.Equals("weather.fanartcode")) ret = WEATHER_FANART_CODE;
     else if (strTest.Equals("weather.plugin")) ret = WEATHER_PLUGIN;
   }
+  else if (strCategory.Equals("pvr"))
+  {
+    if (strTest.Equals("pvr.isrecording")) ret = PVR_IS_RECORDING;
+    else if (strTest.Equals("pvr.hastimer")) ret = PVR_HAS_TIMER;
+    else if (strTest.Equals("pvr.nowrecordingtitle")) ret = PVR_NOW_RECORDING_TITLE;
+    else if (strTest.Equals("pvr.nowrecordingdatetime")) ret = PVR_NOW_RECORDING_DATETIME;
+    else if (strTest.Equals("pvr.nowrecordingchannel")) ret = PVR_NOW_RECORDING_CHANNEL;
+    else if (strTest.Equals("pvr.nextrecordingtitle")) ret = PVR_NEXT_RECORDING_TITLE;
+    else if (strTest.Equals("pvr.nextrecordingdatetime")) ret = PVR_NEXT_RECORDING_DATETIME;
+    else if (strTest.Equals("pvr.nextrecordingchannel")) ret = PVR_NEXT_RECORDING_CHANNEL;
+    else if (strTest.Equals("pvr.backendname")) ret = PVR_BACKEND_NAME;
+    else if (strTest.Equals("pvr.backendversion")) ret = PVR_BACKEND_VERSION;
+    else if (strTest.Equals("pvr.backendhost")) ret = PVR_BACKEND_HOST;
+    else if (strTest.Equals("pvr.backenddiskspace")) ret = PVR_BACKEND_DISKSPACE;
+    else if (strTest.Equals("pvr.backendchannels")) ret = PVR_BACKEND_CHANNELS;
+    else if (strTest.Equals("pvr.backendtimers")) ret = PVR_BACKEND_TIMERS;
+    else if (strTest.Equals("pvr.backendrecordings")) ret = PVR_BACKEND_RECORDINGS;
+    else if (strTest.Equals("pvr.backendnumber")) ret = PVR_BACKEND_NUMBER;
+    else if (strTest.Equals("pvr.hasepg")) ret = PVR_HAS_EPG;
+    else if (strTest.Equals("pvr.hastxt")) ret = PVR_HAS_TXT;
+    else if (strTest.Equals("pvr.hasdirector")) ret = PVR_HAS_DIRECTOR;
+    else if (strTest.Equals("pvr.totaldiscspace")) ret = PVR_TOTAL_DISKSPACE;
+    else if (strTest.Equals("pvr.nexttimer")) ret = PVR_NEXT_TIMER;
+    else if (strTest.Equals("pvr.isplayingtv")) ret = PVR_IS_PLAYING_TV;
+    else if (strTest.Equals("pvr.isplayingradio")) ret = PVR_IS_PLAYING_RADIO;
+    else if (strTest.Equals("pvr.isplayingrecording")) ret = PVR_IS_PLAYING_RECORDING;
+    else if (strTest.Equals("pvr.duration")) ret = PVR_PLAYING_DURATION;
+    else if (strTest.Equals("pvr.time")) ret = PVR_PLAYING_TIME;
+    else if (strTest.Equals("pvr.progress")) ret = PVR_PLAYING_PROGRESS;
+    else if (strTest.Equals("pvr.actstreamclient")) ret = PVR_ACTUAL_STREAM_CLIENT;
+    else if (strTest.Equals("pvr.actstreamdevice")) ret = PVR_ACTUAL_STREAM_DEVICE;
+    else if (strTest.Equals("pvr.actstreamstatus")) ret = PVR_ACTUAL_STREAM_STATUS;
+    else if (strTest.Equals("pvr.actstreamsignal")) ret = PVR_ACTUAL_STREAM_SIG;
+    else if (strTest.Equals("pvr.actstreamsnr")) ret = PVR_ACTUAL_STREAM_SNR;
+    else if (strTest.Equals("pvr.actstreamber")) ret = PVR_ACTUAL_STREAM_BER;
+    else if (strTest.Equals("pvr.actstreamunc")) ret = PVR_ACTUAL_STREAM_UNC;
+    else if (strTest.Equals("pvr.actstreamvideobitrate")) ret = PVR_ACTUAL_STREAM_VIDEO_BR;
+    else if (strTest.Equals("pvr.actstreamaudiobitrate")) ret = PVR_ACTUAL_STREAM_AUDIO_BR;
+    else if (strTest.Equals("pvr.actstreamdolbybitrate")) ret = PVR_ACTUAL_STREAM_DOLBY_BR;
+    else if (strTest.Equals("pvr.actstreamprogrsignal")) ret = PVR_ACTUAL_STREAM_SIG_PROGR;
+    else if (strTest.Equals("pvr.actstreamprogrsnr")) ret = PVR_ACTUAL_STREAM_SNR_PROGR;
+    else if (strTest.Equals("pvr.actstreamisencrypted")) ret = PVR_ACTUAL_STREAM_ENCRYPTED;
+    else if (strTest.Equals("pvr.actstreamencryptionname")) ret = PVR_ACTUAL_STREAM_CRYPTION;
+  }
+  else if (strCategory.Equals("addon"))
+  {
+    if (strTest.Equals("addon.rating")) ret = ADDON_STAR_RATING;
+  }
   else if (strCategory.Equals("bar"))
   {
     if (strTest.Equals("bar.gputemperature")) ret = SYSTEM_GPU_TEMPERATURE;
@@ -583,6 +637,19 @@ int CGUIInfoManager::TranslateSingleString(const CStdString &strCondition)
     else if (strTest.Equals("videoplayer.audiocodec")) return VIDEOPLAYER_AUDIO_CODEC;
     else if (strTest.Equals("videoplayer.audiochannels")) return VIDEOPLAYER_AUDIO_CHANNELS;
     else if (strTest.Equals("videoplayer.hasteletext")) return VIDEOPLAYER_HASTELETEXT;
+    else if (strTest.Equals("videoplayer.starttime")) return VIDEOPLAYER_STARTTIME;
+    else if (strTest.Equals("videoplayer.endtime")) return VIDEOPLAYER_ENDTIME;
+    else if (strTest.Equals("videoplayer.nexttitle")) return VIDEOPLAYER_NEXT_TITLE;
+    else if (strTest.Equals("videoplayer.nextgenre")) return VIDEOPLAYER_NEXT_GENRE;
+    else if (strTest.Equals("videoplayer.nextplot")) return VIDEOPLAYER_NEXT_PLOT;
+    else if (strTest.Equals("videoplayer.nextplotoutline")) return VIDEOPLAYER_NEXT_PLOT_OUTLINE;
+    else if (strTest.Equals("videoplayer.nextstarttime")) return VIDEOPLAYER_NEXT_STARTTIME;
+    else if (strTest.Equals("videoplayer.nextendtime")) return VIDEOPLAYER_NEXT_ENDTIME;
+    else if (strTest.Equals("videoplayer.nextduration")) return VIDEOPLAYER_NEXT_DURATION;
+    else if (strTest.Equals("videoplayer.channelname")) return VIDEOPLAYER_CHANNEL_NAME;
+    else if (strTest.Equals("videoplayer.channelnumber")) return VIDEOPLAYER_CHANNEL_NUMBER;
+    else if (strTest.Equals("videoplayer.channelgroup")) return VIDEOPLAYER_CHANNEL_GROUP;
+    else if (strTest.Equals("videoplayer.parentalrating")) return VIDEOPLAYER_PARENTAL_RATING;
   }
   else if (strCategory.Equals("playlist"))
   {
@@ -945,6 +1012,25 @@ int CGUIInfoManager::TranslateListItem(const CStdString &info)
   else if (info.Equals("audiolanguage")) return LISTITEM_AUDIO_LANGUAGE;
   else if (info.Equals("subtitlelanguage")) return LISTITEM_SUBTITLE_LANGUAGE;
   else if (info.Equals("isfolder")) return LISTITEM_IS_FOLDER;
+  else if (info.Equals("starttime")) return LISTITEM_STARTTIME;
+  else if (info.Equals("endtime")) return LISTITEM_ENDTIME;
+  else if (info.Equals("startdate")) return LISTITEM_STARTDATE;
+  else if (info.Equals("enddate")) return LISTITEM_ENDDATE;
+  else if (info.Equals("nexttitle")) return LISTITEM_NEXT_TITLE;
+  else if (info.Equals("nextgenre")) return LISTITEM_NEXT_GENRE;
+  else if (info.Equals("nextplot")) return LISTITEM_NEXT_PLOT;
+  else if (info.Equals("nextplotoutline")) return LISTITEM_NEXT_PLOT_OUTLINE;
+  else if (info.Equals("nextstarttime")) return LISTITEM_NEXT_STARTTIME;
+  else if (info.Equals("nextendtime")) return LISTITEM_NEXT_ENDTIME;
+  else if (info.Equals("nextstartdate")) return LISTITEM_NEXT_STARTDATE;
+  else if (info.Equals("nextenddate")) return LISTITEM_NEXT_ENDDATE;
+  else if (info.Equals("channelname")) return LISTITEM_CHANNEL_NAME;
+  else if (info.Equals("channelnumber")) return LISTITEM_CHANNEL_NUMBER;
+  else if (info.Equals("channelgroup")) return LISTITEM_CHANNEL_GROUP;
+  else if (info.Equals("hastimer")) return LISTITEM_HASTIMER;
+  else if (info.Equals("isrecording")) return LISTITEM_ISRECORDING;
+  else if (info.Equals("isencrypted")) return LISTITEM_ISENCRYPTED;
+  else if (info.Equals("parentalrating")) return LISTITEM_PARENTALRATING;
   else if (info.Equals("originaltitle")) return LISTITEM_ORIGINALTITLE;
   else if (info.Left(9).Equals("property(")) return AddListItemProp(info.Mid(9, info.GetLength() - 10));
   return 0;
@@ -976,6 +1062,9 @@ int CGUIInfoManager::TranslateMusicPlayerString(const CStdString &info) const
   else if (info.Equals("exists")) return MUSICPLAYER_EXISTS;
   else if (info.Equals("hasprevious")) return MUSICPLAYER_HASPREVIOUS;
   else if (info.Equals("hasnext")) return MUSICPLAYER_HASNEXT;
+  else if (info.Equals("channelname")) return MUSICPLAYER_CHANNEL_NAME;
+  else if (info.Equals("channelnumber")) return MUSICPLAYER_CHANNEL_NUMBER;
+  else if (info.Equals("channelgroup")) return MUSICPLAYER_CHANNEL_GROUP;
   return 0;
 }
 
@@ -1024,6 +1113,37 @@ CStdString CGUIInfoManager::GetLabel(int info, int contextWindow)
 
   switch (info)
   {
+  case PVR_NOW_RECORDING_CHANNEL:
+  case PVR_NOW_RECORDING_DATETIME:
+  case PVR_NOW_RECORDING_TITLE:
+  case PVR_NEXT_RECORDING_CHANNEL:
+  case PVR_NEXT_RECORDING_DATETIME:
+  case PVR_NEXT_RECORDING_TITLE:
+  case PVR_BACKEND_NAME:
+  case PVR_BACKEND_VERSION:
+  case PVR_BACKEND_HOST:
+  case PVR_BACKEND_DISKSPACE:
+  case PVR_BACKEND_CHANNELS:
+  case PVR_BACKEND_TIMERS:
+  case PVR_BACKEND_RECORDINGS:
+  case PVR_BACKEND_NUMBER:
+  case PVR_TOTAL_DISKSPACE:
+  case PVR_NEXT_TIMER:
+  case PVR_PLAYING_TIME:
+  case PVR_PLAYING_DURATION:
+  case PVR_ACTUAL_STREAM_CLIENT:
+  case PVR_ACTUAL_STREAM_DEVICE:
+  case PVR_ACTUAL_STREAM_STATUS:
+  case PVR_ACTUAL_STREAM_BER:
+  case PVR_ACTUAL_STREAM_UNC:
+  case PVR_ACTUAL_STREAM_VIDEO_BR:
+  case PVR_ACTUAL_STREAM_AUDIO_BR:
+  case PVR_ACTUAL_STREAM_DOLBY_BR:
+  case PVR_ACTUAL_STREAM_CRYPTION:
+  case PVR_ACTUAL_STREAM_SIG:
+  case PVR_ACTUAL_STREAM_SNR:
+    strLabel = g_PVRManager.TranslateCharInfo(info);
+    break;
   case WEATHER_CONDITIONS:
     strLabel = g_weatherManager.GetInfo(WEATHER_LABEL_CURRENT_COND);
     strLabel = strLabel.Trim();
@@ -1124,6 +1244,9 @@ CStdString CGUIInfoManager::GetLabel(int info, int contextWindow)
   case MUSICPLAYER_RATING:
   case MUSICPLAYER_COMMENT:
   case MUSICPLAYER_LYRICS:
+  case MUSICPLAYER_CHANNEL_NAME:
+  case MUSICPLAYER_CHANNEL_NUMBER:
+  case MUSICPLAYER_CHANNEL_GROUP:
     strLabel = GetMusicLabel(info);
   break;
   case VIDEOPLAYER_TITLE:
@@ -1152,6 +1275,19 @@ CStdString CGUIInfoManager::GetLabel(int info, int contextWindow)
   case VIDEOPLAYER_WRITER:
   case VIDEOPLAYER_TAGLINE:
   case VIDEOPLAYER_TRAILER:
+  case VIDEOPLAYER_STARTTIME:
+  case VIDEOPLAYER_ENDTIME:
+  case VIDEOPLAYER_NEXT_TITLE:
+  case VIDEOPLAYER_NEXT_GENRE:
+  case VIDEOPLAYER_NEXT_PLOT:
+  case VIDEOPLAYER_NEXT_PLOT_OUTLINE:
+  case VIDEOPLAYER_NEXT_STARTTIME:
+  case VIDEOPLAYER_NEXT_ENDTIME:
+  case VIDEOPLAYER_NEXT_DURATION:
+  case VIDEOPLAYER_CHANNEL_NAME:
+  case VIDEOPLAYER_CHANNEL_NUMBER:
+  case VIDEOPLAYER_CHANNEL_GROUP:
+  case VIDEOPLAYER_PARENTAL_RATING:
     strLabel = GetVideoLabel(info);
   break;
   case VIDEOPLAYER_VIDEO_CODEC:
@@ -1689,6 +1825,10 @@ int CGUIInfoManager::GetInt(int info, int contextWindow) const
       }
     case SYSTEM_CPU_USAGE:
       return g_cpuInfo.getUsedPercentage();
+    case PVR_PLAYING_PROGRESS:
+    case PVR_ACTUAL_STREAM_SIG_PROGR:
+    case PVR_ACTUAL_STREAM_SNR_PROGR:
+      return g_PVRManager.TranslateIntInfo(info);
   }
   return 0;
 }
@@ -1812,6 +1952,9 @@ bool CGUIInfoManager::GetBool(int condition1, int contextWindow, const CGUIListI
     bReturn = g_settings.UsingLoginScreen();
   else if (condition == WEATHER_IS_FETCHED)
     bReturn = g_weatherManager.IsFetched();
+  else if (condition >= PVR_IS_RECORDING && condition <= PVR_IS_RECORDING+20)
+    bReturn = g_PVRManager.TranslateBoolInfo(condition);
+
   else if (condition == SYSTEM_INTERNET_STATE)
   {
     g_sysinfo.GetInfo(condition);
@@ -1860,7 +2003,8 @@ bool CGUIInfoManager::GetBool(int condition1, int contextWindow, const CGUIListI
       bReturn = ((CGUIMediaWindow*)pWindow)->CurrentDirectory().HasThumbnail();
   }
   else if (condition == VIDEOPLAYER_HAS_INFO)
-    bReturn = (m_currentFile->HasVideoInfoTag() && !m_currentFile->GetVideoInfoTag()->IsEmpty());
+    bReturn = ((m_currentFile->HasVideoInfoTag() && !m_currentFile->GetVideoInfoTag()->IsEmpty()) ||
+               (m_currentFile->HasPVRChannelInfoTag()  && !m_currentFile->GetPVRChannelInfoTag()->IsEmpty()));
   else if (condition >= CONTAINER_SCROLL_PREVIOUS && condition <= CONTAINER_SCROLL_NEXT)
   {
     // no parameters, so we assume it's just requested for a media window.  It therefore
@@ -2374,6 +2518,8 @@ bool CGUIInfoManager::GetMultiInfoBool(const GUIInfo &info, int contextWindow, c
             strContent = "musicvideos";
           if (m_currentFile->HasVideoInfoTag() && m_currentFile->GetVideoInfoTag()->m_strStatus == "livetv")
             strContent = "livetv";
+          if (m_currentFile->HasPVRChannelInfoTag())
+            strContent = "livetv";
           bReturn = m_stringParameters[info.GetData1()].Equals(strContent);
         }
         break;
@@ -3069,6 +3215,31 @@ CStdString CGUIInfoManager::GetMusicTagLabel(int info, const CFileItem *item) co
     return GetItemLabel(item, LISTITEM_COMMENT);
   case MUSICPLAYER_DURATION:
     return GetItemLabel(item, LISTITEM_DURATION);
+  case MUSICPLAYER_CHANNEL_NAME:
+    {
+      CPVRChannel* channeltag = m_currentFile->GetPVRChannelInfoTag();
+      if (channeltag)
+        return channeltag->ChannelName();
+    }
+    break;
+  case MUSICPLAYER_CHANNEL_NUMBER:
+    {
+      CPVRChannel* channeltag = m_currentFile->GetPVRChannelInfoTag();
+      if (channeltag)
+      {
+        CStdString strNumber;
+        strNumber.Format("%i", channeltag->ChannelNumber());
+        return strNumber;
+      }
+    }
+    break;
+  case MUSICPLAYER_CHANNEL_GROUP:
+    {
+      CPVRChannel* channeltag = m_currentFile->GetPVRChannelInfoTag();
+      if (channeltag && channeltag->IsRadio())
+        return PVRChannelGroupsRadio.GetGroupName(g_PVRManager.GetPlayingGroup());
+    }
+    break;
   }
   return "";
 }
@@ -3080,6 +3251,8 @@ CStdString CGUIInfoManager::GetVideoLabel(int item)
 
   if (item == VIDEOPLAYER_TITLE)
   {
+    if (m_currentFile->HasPVRChannelInfoTag())
+      return m_currentFile->GetPVRChannelInfoTag()->GetEPGNow()->Title();
     if (m_currentFile->HasVideoInfoTag() && !m_currentFile->GetVideoInfoTag()->m_strTitle.IsEmpty())
       return m_currentFile->GetVideoInfoTag()->m_strTitle;
     // don't have the title, so use dvdplayer, label, or drop down to title from path
@@ -3099,6 +3272,73 @@ CStdString CGUIInfoManager::GetVideoLabel(int item)
     if (g_playlistPlayer.GetCurrentPlaylist() == PLAYLIST_VIDEO)
       return GetPlaylistLabel(PLAYLIST_POSITION);
   }
+  else if (m_currentFile->HasPVRChannelInfoTag())
+  {
+    CPVRChannel* tag = m_currentFile->GetPVRChannelInfoTag();
+
+    switch (item)
+    {
+    /* Now playing infos */
+    case VIDEOPLAYER_ORIGINALTITLE:
+      return tag->GetEPGNow()->Title();
+    case VIDEOPLAYER_GENRE:
+      return tag->GetEPGNow()->Genre();
+    case VIDEOPLAYER_PLOT:
+      return tag->GetEPGNow()->Plot();
+    case VIDEOPLAYER_PLOT_OUTLINE:
+      return tag->GetEPGNow()->PlotOutline();
+    case VIDEOPLAYER_STARTTIME:
+      return tag->GetEPGNow()->Start().GetAsLocalizedTime("", false);
+    case VIDEOPLAYER_ENDTIME:
+      return tag->GetEPGNow()->End().GetAsLocalizedTime("", false);
+
+    /* Next playing infos */
+    case VIDEOPLAYER_NEXT_TITLE:
+      return tag->GetEPGNext()->Title();
+    case VIDEOPLAYER_NEXT_GENRE:
+      return tag->GetEPGNext()->Genre();
+    case VIDEOPLAYER_NEXT_PLOT:
+      return tag->GetEPGNext()->Plot();
+    case VIDEOPLAYER_NEXT_PLOT_OUTLINE:
+      return tag->GetEPGNext()->PlotOutline();
+    case VIDEOPLAYER_NEXT_STARTTIME:
+      return tag->GetEPGNext()->Start().GetAsLocalizedTime("", false);
+    case VIDEOPLAYER_NEXT_ENDTIME:
+      return tag->GetEPGNext()->End().GetAsLocalizedTime("", false);
+    case VIDEOPLAYER_NEXT_DURATION:
+      {
+        CStdString duration;
+        if (tag->GetEPGNext()->GetDuration() > 0)
+          duration = StringUtils::SecondsToTimeString(tag->GetEPGNext()->GetDuration());
+
+        return duration;
+      }
+
+    case VIDEOPLAYER_PARENTAL_RATING:
+      {
+        CStdString rating;
+        if (tag->GetEPGNow()->ParentalRating() > 0)
+          rating.Format("%i", tag->GetEPGNow()->ParentalRating());
+        return rating;
+      }
+      break;
+
+    /* General channel infos */
+    case VIDEOPLAYER_CHANNEL_NAME:
+      return tag->ChannelName();
+    case VIDEOPLAYER_CHANNEL_NUMBER:
+      {
+        CStdString strNumber;
+        strNumber.Format("%i", tag->ChannelNumber());
+        return strNumber;
+      }
+    case VIDEOPLAYER_CHANNEL_GROUP:
+      {
+        if (tag && !tag->IsRadio())
+          return PVRChannelGroupsTV.GetGroupName(g_PVRManager.GetPlayingGroup());
+      }
+    }
+  }
   else if (m_currentFile->HasVideoInfoTag())
   {
     switch (item)
@@ -3317,7 +3557,7 @@ void CGUIInfoManager::SetCurrentMovie(CFileItem &item)
   CLog::Log(LOGDEBUG,"CGUIInfoManager::SetCurrentMovie(%s)",item.m_strPath.c_str());
   *m_currentFile = item;
 
-  if (!m_currentFile->HasVideoInfoTag() || m_currentFile->GetVideoInfoTag()->IsEmpty())
+  if (!m_currentFile->HasPVRChannelInfoTag() && (!m_currentFile->HasVideoInfoTag() || m_currentFile->GetVideoInfoTag()->IsEmpty()))
   { // attempt to get some information
     CVideoDatabase dbs;
     dbs.Open();
@@ -3712,6 +3952,14 @@ CStdString CGUIInfoManager::GetItemLabel(const CFileItem *item, int info) const
   case LISTITEM_LABEL2:
     return item->GetLabel2();
   case LISTITEM_TITLE:
+    if (item->HasPVRChannelInfoTag())
+      return item->GetPVRChannelInfoTag()->GetEPGNow()->Title();
+    if (item->HasPVRRecordingInfoTag())
+      return item->GetPVRRecordingInfoTag()->m_strTitle;
+    if (item->HasEPGInfoTag())
+      return item->GetEPGInfoTag()->Title();
+    if (item->HasPVRTimerInfoTag())
+      return item->GetPVRTimerInfoTag()->Title();
     if (item->HasVideoInfoTag())
       return item->GetVideoInfoTag()->m_strTitle;
     if (item->HasMusicInfoTag())
@@ -3773,6 +4021,12 @@ CStdString CGUIInfoManager::GetItemLabel(const CFileItem *item, int info) const
       return item->GetVideoInfoTag()->m_strGenre;
     if (item->HasMusicInfoTag())
       return item->GetMusicInfoTag()->GetGenre();
+    if (item->HasPVRChannelInfoTag())
+      return item->GetPVRChannelInfoTag()->GetEPGNow()->Genre();
+    if (item->HasPVRRecordingInfoTag())
+      return item->GetPVRRecordingInfoTag()->m_strGenre;
+    if (item->HasEPGInfoTag())
+      return item->GetEPGInfoTag()->Genre();
     break;
   case LISTITEM_FILENAME:
     if (item->IsMusicDb() && item->HasMusicInfoTag())
@@ -3781,6 +4035,14 @@ CStdString CGUIInfoManager::GetItemLabel(const CFileItem *item, int info) const
       return CUtil::GetFileName(item->GetVideoInfoTag()->m_strFileNameAndPath);
     return CUtil::GetFileName(item->m_strPath);
   case LISTITEM_DATE:
+    if (item->HasEPGInfoTag())
+      return item->GetEPGInfoTag()->Start().GetAsLocalizedDateTime(false, false);
+    if (item->HasPVRChannelInfoTag())
+      return item->GetPVRChannelInfoTag()->GetEPGNow()->Start().GetAsLocalizedDateTime(false, false);
+    if (item->HasPVRRecordingInfoTag())
+      return item->GetPVRRecordingInfoTag()->RecordingTime().GetAsLocalizedDateTime(false, false);
+    if (item->HasPVRTimerInfoTag())
+      return item->GetPVRTimerInfoTag()->Summary();
     if (item->m_dateTime.IsValid())
       return item->m_dateTime.GetAsLocalizedDate();
     break;
@@ -3821,14 +4083,30 @@ CStdString CGUIInfoManager::GetItemLabel(const CFileItem *item, int info) const
   case LISTITEM_DURATION:
     {
       CStdString duration;
-      if (item->HasVideoInfoTag())
+      if (item->HasPVRChannelInfoTag())
+      {
+        if (item->GetPVRChannelInfoTag()->GetEPGNow()->GetDuration() > 0)
+          duration = StringUtils::SecondsToTimeString(item->GetPVRChannelInfoTag()->GetEPGNow()->GetDuration());
+      }
+      else if (item->HasPVRRecordingInfoTag())
+      {
+        if (item->GetPVRRecordingInfoTag()->GetDuration() > 0)
+          duration = StringUtils::SecondsToTimeString(item->GetPVRRecordingInfoTag()->GetDuration());
+      }
+      else if (item->HasEPGInfoTag())
       {
+        if (item->GetEPGInfoTag()->GetDuration() > 0)
+          duration = StringUtils::SecondsToTimeString(item->GetEPGInfoTag()->GetDuration());
+      }
+      else if (item->HasVideoInfoTag())
+      {
+        duration = item->GetVideoInfoTag()->m_strRuntime;
         if (item->GetVideoInfoTag()->m_streamDetails.GetVideoDuration() > 0)
           duration.Format("%i", item->GetVideoInfoTag()->m_streamDetails.GetVideoDuration() / 60);
         else if (!item->GetVideoInfoTag()->m_strRuntime.IsEmpty())
           duration = item->GetVideoInfoTag()->m_strRuntime;
       }
-      if (item->HasMusicInfoTag())
+      else if (item->HasMusicInfoTag())
       {
         if (item->GetMusicInfoTag()->GetDuration() > 0)
           duration = StringUtils::SecondsToTimeString(item->GetMusicInfoTag()->GetDuration());
@@ -3836,6 +4114,12 @@ CStdString CGUIInfoManager::GetItemLabel(const CFileItem *item, int info) const
       return duration;
     }
   case LISTITEM_PLOT:
+    if (item->HasPVRChannelInfoTag())
+      return item->GetPVRChannelInfoTag()->GetEPGNow()->Plot();
+    if (item->HasEPGInfoTag())
+      return item->GetEPGInfoTag()->Plot();
+    if (item->HasPVRRecordingInfoTag())
+      return item->GetPVRRecordingInfoTag()->Plot();
     if (item->HasVideoInfoTag())
     {
       if (!(!item->GetVideoInfoTag()->m_strShowTitle.IsEmpty() && item->GetVideoInfoTag()->m_iSeason == -1)) // dont apply to tvshows
@@ -3846,6 +4130,12 @@ CStdString CGUIInfoManager::GetItemLabel(const CFileItem *item, int info) const
     }
     break;
   case LISTITEM_PLOT_OUTLINE:
+    if (item->HasPVRChannelInfoTag())
+      return item->GetPVRChannelInfoTag()->GetEPGNow()->PlotOutline();
+    if (item->HasEPGInfoTag())
+      return item->GetEPGInfoTag()->PlotOutline();
+    if (item->HasPVRRecordingInfoTag())
+      return item->GetPVRRecordingInfoTag()->PlotOutline();
     if (item->HasVideoInfoTag())
       return item->GetVideoInfoTag()->m_strPlotOutline;
     break;
@@ -3876,6 +4166,8 @@ CStdString CGUIInfoManager::GetItemLabel(const CFileItem *item, int info) const
       return item->GetVideoInfoTag()->m_strShowTitle;
     break;
   case LISTITEM_COMMENT:
+    if (item->HasPVRTimerInfoTag())
+      return item->GetPVRTimerInfoTag()->GetStatus();
     if (item->HasMusicInfoTag())
       return item->GetMusicInfoTag()->GetComment();
     break;
@@ -4028,6 +4320,116 @@ CStdString CGUIInfoManager::GetItemLabel(const CFileItem *item, int info) const
     if (item->HasVideoInfoTag())
       return item->GetVideoInfoTag()->m_streamDetails.GetSubtitleLanguage();
     break;
+  case LISTITEM_STARTTIME:
+    if (item->HasPVRChannelInfoTag())
+      return item->GetPVRChannelInfoTag()->GetEPGNow()->Start().GetAsLocalizedTime("", false);
+    if (item->HasEPGInfoTag())
+      return item->GetEPGInfoTag()->Start().GetAsLocalizedTime("", false);
+    if (item->HasPVRTimerInfoTag())
+      return item->GetPVRTimerInfoTag()->Start().GetAsLocalizedTime("", false);
+    if (item->HasPVRRecordingInfoTag())
+      return item->GetPVRRecordingInfoTag()->RecordingTime().GetAsLocalizedTime("", false);
+    if (item->m_dateTime.IsValid())
+      return item->m_dateTime.GetAsLocalizedTime("", false);
+    break;
+  case LISTITEM_ENDTIME:
+    if (item->HasPVRChannelInfoTag())
+      return item->GetPVRChannelInfoTag()->GetEPGNow()->End().GetAsLocalizedTime("", false);
+    if (item->HasEPGInfoTag())
+      return item->GetEPGInfoTag()->End().GetAsLocalizedTime("", false);
+    if (item->HasPVRTimerInfoTag())
+      return item->GetPVRTimerInfoTag()->Stop().GetAsLocalizedTime("", false);
+    break;
+  case LISTITEM_STARTDATE:
+    if (item->HasPVRChannelInfoTag())
+      return item->GetPVRChannelInfoTag()->GetEPGNow()->Start().GetAsLocalizedDate(true);
+    if (item->HasEPGInfoTag())
+      return item->GetEPGInfoTag()->Start().GetAsLocalizedDate(true);
+    if (item->HasPVRTimerInfoTag())
+      return item->GetPVRTimerInfoTag()->Start().GetAsLocalizedDate(true);
+    if (item->HasPVRRecordingInfoTag())
+      return item->GetPVRRecordingInfoTag()->RecordingTime().GetAsLocalizedDate(true);
+    if (item->m_dateTime.IsValid())
+      return item->m_dateTime.GetAsLocalizedDate(true);
+    break;
+  case LISTITEM_ENDDATE:
+    if (item->HasPVRChannelInfoTag())
+      return item->GetPVRChannelInfoTag()->GetEPGNow()->End().GetAsLocalizedDate(true);
+    if (item->HasEPGInfoTag())
+      return item->GetEPGInfoTag()->End().GetAsLocalizedDate(true);
+    if (item->HasPVRTimerInfoTag())
+      return item->GetPVRTimerInfoTag()->Stop().GetAsLocalizedDate(true);
+    break;
+  case LISTITEM_CHANNEL_NUMBER:
+    {
+      CStdString number;
+      if (item->HasPVRChannelInfoTag())
+        number.Format("%i", item->GetPVRChannelInfoTag()->ChannelNumber());
+      if (item->HasEPGInfoTag())
+        number.Format("%i", item->GetEPGInfoTag()->ChannelTag()->ChannelNumber());
+      if (item->HasPVRTimerInfoTag())
+        number.Format("%i", item->GetPVRTimerInfoTag()->ChannelNumber());
+
+      return number;
+    }
+    break;
+  case LISTITEM_CHANNEL_NAME:
+    if (item->HasPVRChannelInfoTag())
+      return item->GetPVRChannelInfoTag()->ChannelName();
+    if (item->HasEPGInfoTag())
+      return item->GetEPGInfoTag()->ChannelTag()->ChannelName();
+    if (item->HasPVRRecordingInfoTag())
+      return item->GetPVRRecordingInfoTag()->ChannelName();
+    if (item->HasPVRTimerInfoTag())
+      return item->GetPVRTimerInfoTag()->ChannelName();
+    break;
+  case LISTITEM_NEXT_STARTTIME:
+    if (item->HasPVRChannelInfoTag())
+      return item->GetPVRChannelInfoTag()->GetEPGNext()->Start().GetAsLocalizedTime("", false);
+    break;
+  case LISTITEM_NEXT_ENDTIME:
+    if (item->HasPVRChannelInfoTag())
+      return item->GetPVRChannelInfoTag()->GetEPGNext()->End().GetAsLocalizedTime("", false);
+    break;
+  case LISTITEM_NEXT_STARTDATE:
+    if (item->HasPVRChannelInfoTag())
+      return item->GetPVRChannelInfoTag()->GetEPGNext()->Start().GetAsLocalizedDate(true);
+    break;
+  case LISTITEM_NEXT_ENDDATE:
+    if (item->HasPVRChannelInfoTag())
+      return item->GetPVRChannelInfoTag()->GetEPGNext()->End().GetAsLocalizedDate(true);
+    break;
+  case LISTITEM_NEXT_PLOT:
+    if (item->HasPVRChannelInfoTag())
+      return item->GetPVRChannelInfoTag()->GetEPGNext()->Plot();
+    break;
+  case LISTITEM_NEXT_PLOT_OUTLINE:
+    if (item->HasPVRChannelInfoTag())
+      return item->GetPVRChannelInfoTag()->GetEPGNext()->PlotOutline();
+    break;
+  case LISTITEM_NEXT_DURATION:
+    {
+      CStdString duration;
+      if (item->HasPVRChannelInfoTag())
+      {
+        if (item->GetPVRChannelInfoTag()->GetEPGNext()->GetDuration() > 0)
+          duration = StringUtils::SecondsToTimeString(item->GetPVRChannelInfoTag()->GetEPGNext()->GetDuration());
+      }
+      return duration;
+    }
+    break;
+  case LISTITEM_NEXT_GENRE:
+    if (item->HasPVRChannelInfoTag())
+      return item->GetPVRChannelInfoTag()->GetEPGNext()->Genre();
+    break;
+  case LISTITEM_PARENTALRATING:
+    {
+      CStdString rating;
+      if (item->HasEPGInfoTag() && item->GetEPGInfoTag()->ParentalRating() > 0)
+        rating.Format("%i", item->GetEPGInfoTag()->ParentalRating());
+      return rating;
+    }
+    break;
   }
   return "";
 }
@@ -4060,6 +4462,14 @@ CStdString CGUIInfoManager::GetItemImage(const CFileItem *item, int info) const
       return rating;
     }
     break;
+  case ADDON_STAR_RATING:
+    {
+      CStdString rating;
+      //TODO need to check item is an addon
+      rating.Format("rating%d.png", item->GetPropertyInt("Addon.Rating"));
+      return rating;
+    }
+    break;
   }  /* switch (info) */
 
   return GetItemLabel(item, info);
@@ -4090,6 +4500,59 @@ bool CGUIInfoManager::GetItemBool(const CGUIListItem *item, int condition) const
     return item->IsSelected();
   else if (condition == LISTITEM_IS_FOLDER)
     return item->m_bIsFolder;
+
+  if (item->IsFileItem())
+  {
+    const CFileItem *pItem = (const CFileItem *)item;
+    if (condition == LISTITEM_ISRECORDING)
+    {
+      if (pItem->HasPVRChannelInfoTag())
+      {
+        return pItem->GetPVRChannelInfoTag()->IsRecording();
+      }
+      else if (pItem->HasEPGInfoTag())
+      {
+        const CPVRTimerInfoTag *timer = pItem->GetEPGInfoTag()->Timer();
+        if (timer)
+        {
+          CDateTime now = CDateTime::GetCurrentDateTime();
+          if ((timer->Start() <= now) && (timer->Stop() >= now) && timer->Active())
+            return true;
+        }
+      }
+      else if (pItem->HasPVRTimerInfoTag())
+      {
+        const CPVRTimerInfoTag *timer = pItem->GetPVRTimerInfoTag();
+        CDateTime now = CDateTime::GetCurrentDateTime();
+        if ((timer->Start() <= now) && (timer->Stop() >= now) && timer->Active())
+          return true;
+      }
+    }
+    else if (condition == LISTITEM_HASTIMER)
+    {
+      if (pItem->HasEPGInfoTag())
+      {
+        const CPVRTimerInfoTag *timer = pItem->GetEPGInfoTag()->Timer();
+        if (timer)
+        {
+          if (timer->Start() > CDateTime::GetCurrentDateTime() && timer->Active())
+            return true;
+        }
+      }
+    }
+    else if (condition == LISTITEM_ISENCRYPTED)
+    {
+      if (pItem->HasPVRChannelInfoTag())
+      {
+        return pItem->GetPVRChannelInfoTag()->IsEncrypted();
+      }
+      else if (pItem->HasEPGInfoTag())
+      {
+        return pItem->GetEPGInfoTag()->ChannelTag()->IsEncrypted();
+      }
+    }
+  }
+
   return false;
 }
 
index f2b2920..75f9090 100644 (file)
@@ -211,7 +211,10 @@ class CDateTime;
 #define MUSICPLAYER_HASNEXT         223
 #define MUSICPLAYER_EXISTS          224
 #define MUSICPLAYER_PLAYLISTPLAYING 225
-#define MUSICPLAYER_ALBUM_ARTIST    226
+#define MUSICPLAYER_CHANNEL_NAME    226
+#define MUSICPLAYER_CHANNEL_NUMBER  227
+#define MUSICPLAYER_CHANNEL_GROUP   228
+#define MUSICPLAYER_ALBUM_ARTIST    229
 
 #define VIDEOPLAYER_TITLE             250
 #define VIDEOPLAYER_GENRE             251
@@ -251,16 +254,29 @@ class CDateTime;
 #define VIDEOPLAYER_AUDIO_CHANNELS    289
 #define VIDEOPLAYER_VIDEO_ASPECT      290
 #define VIDEOPLAYER_HASTELETEXT       291
-#define VIDEOPLAYER_COUNTRY           292
-
-#define AUDIOSCROBBLER_ENABLED      300
-#define AUDIOSCROBBLER_CONN_STATE   301
-#define AUDIOSCROBBLER_SUBMIT_INT   302
-#define AUDIOSCROBBLER_FILES_CACHED 303
-#define AUDIOSCROBBLER_SUBMIT_STATE 304
-#define LASTFM_RADIOPLAYING         305
-#define LASTFM_CANLOVE              306
-#define LASTFM_CANBAN               307
+#define VIDEOPLAYER_STARTTIME         292
+#define VIDEOPLAYER_ENDTIME           293
+#define VIDEOPLAYER_NEXT_TITLE        294
+#define VIDEOPLAYER_NEXT_GENRE        295
+#define VIDEOPLAYER_NEXT_PLOT         296
+#define VIDEOPLAYER_NEXT_PLOT_OUTLINE 297
+#define VIDEOPLAYER_NEXT_STARTTIME    298
+#define VIDEOPLAYER_NEXT_ENDTIME      299
+#define VIDEOPLAYER_NEXT_DURATION     300
+#define VIDEOPLAYER_CHANNEL_NAME      301
+#define VIDEOPLAYER_CHANNEL_NUMBER    302
+#define VIDEOPLAYER_CHANNEL_GROUP     303
+#define VIDEOPLAYER_PARENTAL_RATING   304
+#define VIDEOPLAYER_COUNTRY           305
+
+#define AUDIOSCROBBLER_ENABLED      320
+#define AUDIOSCROBBLER_CONN_STATE   321
+#define AUDIOSCROBBLER_SUBMIT_INT   322
+#define AUDIOSCROBBLER_FILES_CACHED 323
+#define AUDIOSCROBBLER_SUBMIT_STATE 324
+#define LASTFM_RADIOPLAYING         325
+#define LASTFM_CANLOVE              326
+#define LASTFM_CANBAN               327
 
 #define CONTAINER_SCROLL_PREVIOUS   345 // NOTE: These 5 must be kept in this consecutive order
 #define CONTAINER_MOVE_PREVIOUS     346
@@ -414,6 +430,51 @@ class CDateTime;
 #define FANART_COLOR3               1002
 #define FANART_IMAGE                1003
 
+#define PVR_IS_RECORDING            1101
+#define PVR_HAS_TIMER               1102
+#define PVR_HAS_EPG                 1103
+#define PVR_HAS_TXT                 1104
+#define PVR_HAS_DIRECTOR            1105
+#define PVR_IS_PLAYING_TV           1106
+#define PVR_IS_PLAYING_RADIO        1107
+#define PVR_IS_PLAYING_RECORDING    1108
+#define PVR_ACTUAL_STREAM_ENCRYPTED 1110
+
+#define PVR_NEXT_RECORDING_CHANNEL  1120
+#define PVR_NEXT_RECORDING_DATETIME 1121
+#define PVR_NEXT_RECORDING_TITLE    1122
+#define PVR_NOW_RECORDING_CHANNEL   1123
+#define PVR_NOW_RECORDING_DATETIME  1124
+#define PVR_NOW_RECORDING_TITLE     1125
+#define PVR_BACKEND_NAME            1126
+#define PVR_BACKEND_VERSION         1127
+#define PVR_BACKEND_HOST            1128
+#define PVR_BACKEND_DISKSPACE       1129
+#define PVR_BACKEND_CHANNELS        1130
+#define PVR_BACKEND_TIMERS          1131
+#define PVR_BACKEND_RECORDINGS      1132
+#define PVR_BACKEND_NUMBER          1133
+#define PVR_TOTAL_DISKSPACE         1134
+#define PVR_NEXT_TIMER              1135
+#define PVR_PLAYING_DURATION        1136
+#define PVR_PLAYING_TIME            1137
+#define PVR_PLAYING_PROGRESS        1138
+#define PVR_ACTUAL_STREAM_CLIENT    1142
+#define PVR_ACTUAL_STREAM_DEVICE    1143
+#define PVR_ACTUAL_STREAM_STATUS    1144
+#define PVR_ACTUAL_STREAM_SIG       1145
+#define PVR_ACTUAL_STREAM_SNR       1146
+#define PVR_ACTUAL_STREAM_SIG_PROGR 1147
+#define PVR_ACTUAL_STREAM_SNR_PROGR 1148
+#define PVR_ACTUAL_STREAM_BER       1149
+#define PVR_ACTUAL_STREAM_UNC       1150
+#define PVR_ACTUAL_STREAM_VIDEO_BR  1151
+#define PVR_ACTUAL_STREAM_AUDIO_BR  1152
+#define PVR_ACTUAL_STREAM_DOLBY_BR  1153
+#define PVR_ACTUAL_STREAM_CRYPTION  1154
+
+#define ADDON_STAR_RATING           1200
+
 #define WINDOW_PROPERTY             9993
 #define WINDOW_IS_TOPMOST           9994
 #define WINDOW_IS_VISIBLE           9995
@@ -491,8 +552,28 @@ class CDateTime;
 #define LISTITEM_AUDIO_LANGUAGE     (LISTITEM_START + 51)
 #define LISTITEM_SUBTITLE_LANGUAGE  (LISTITEM_START + 52)
 #define LISTITEM_IS_FOLDER          (LISTITEM_START + 53)
-#define LISTITEM_ORIGINALTITLE      (LISTITEM_START + 54)
-#define LISTITEM_COUNTRY            (LISTITEM_START + 55)
+#define LISTITEM_STARTTIME          (LISTITEM_START + 54)
+#define LISTITEM_ENDTIME            (LISTITEM_START + 55)
+#define LISTITEM_STARTDATE          (LISTITEM_START + 56)
+#define LISTITEM_ENDDATE            (LISTITEM_START + 57)
+#define LISTITEM_NEXT_TITLE         (LISTITEM_START + 58)
+#define LISTITEM_NEXT_GENRE         (LISTITEM_START + 59)
+#define LISTITEM_NEXT_PLOT          (LISTITEM_START + 60)
+#define LISTITEM_NEXT_PLOT_OUTLINE  (LISTITEM_START + 61)
+#define LISTITEM_NEXT_STARTTIME     (LISTITEM_START + 62)
+#define LISTITEM_NEXT_ENDTIME       (LISTITEM_START + 63)
+#define LISTITEM_NEXT_STARTDATE     (LISTITEM_START + 64)
+#define LISTITEM_NEXT_ENDDATE       (LISTITEM_START + 65)
+#define LISTITEM_NEXT_DURATION      (LISTITEM_START + 66)
+#define LISTITEM_CHANNEL_NAME       (LISTITEM_START + 67)
+#define LISTITEM_CHANNEL_NUMBER     (LISTITEM_START + 68)
+#define LISTITEM_CHANNEL_GROUP      (LISTITEM_START + 69)
+#define LISTITEM_HASTIMER           (LISTITEM_START + 70)
+#define LISTITEM_ISRECORDING        (LISTITEM_START + 71)
+#define LISTITEM_ISENCRYPTED        (LISTITEM_START + 72)
+#define LISTITEM_PARENTALRATING     (LISTITEM_START + 73)
+#define LISTITEM_ORIGINALTITLE      (LISTITEM_START + 74)
+#define LISTITEM_COUNTRY            (LISTITEM_START + 75)
 
 #define LISTITEM_PROPERTY_START     (LISTITEM_START + 200)
 #define LISTITEM_PROPERTY_END       (LISTITEM_PROPERTY_START + 1000)
index 7ea6ec7..a6391b9 100644 (file)
@@ -57,6 +57,7 @@ SRCS=AlarmClock.cpp \
      DbusServer.cpp \
      Atomics.cpp \
      LockFree.cpp \
+     Observer.cpp \
      StreamDetails.cpp \
      TimeUtils.cpp \
      JobManager.cpp \
@@ -64,6 +65,7 @@ SRCS=AlarmClock.cpp \
      fastmemcpy.c \
      PasswordManager.cpp \
      AliasShortcutUtils.cpp \
+     TextSearch.cpp \
      WebServer.cpp \
      AnnouncementManager.cpp \
      Semaphore.cpp \
diff --git a/xbmc/utils/Observer.cpp b/xbmc/utils/Observer.cpp
new file mode 100644 (file)
index 0000000..84e3cd6
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "Observer.h"
+
+Observable::Observable()
+{
+  erase(begin(), end());
+}
+
+Observable::~Observable()
+{
+  erase(begin(), end());
+}
+
+void Observable::AddObserver(Observer *o)
+{
+  push_back(o);
+}
+
+void Observable::RemoveObserver(Observer *o)
+{
+  for (unsigned int ptr = 0; ptr < size(); ptr++)
+  {
+    if (at(ptr) == o)
+    {
+      erase(begin() + ptr);
+      break;
+    }
+  }
+}
+
+void Observable::NotifyObservers(const CStdString& msg)
+{
+  if (m_bObservableChanged)
+  {
+    for(unsigned int ptr = 0; ptr < size(); ptr++)
+    {
+      Observer *obs = at(ptr);
+      if (obs)
+        at(ptr)->Notify(*this, msg);
+    }
+
+    m_bObservableChanged = false;
+  }
+}
+
+void Observable::SetChanged(bool SetTo)
+{
+  m_bObservableChanged = SetTo;
+}
diff --git a/xbmc/utils/Observer.h b/xbmc/utils/Observer.h
new file mode 100644 (file)
index 0000000..dd2bb9d
--- /dev/null
@@ -0,0 +1,48 @@
+#pragma once
+
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "guilib/StdString.h"
+
+class Observable;
+
+class Observer
+{
+public:
+  virtual void Notify(const Observable &obs, const CStdString& msg) = 0;
+};
+
+class Observable : private std::vector<Observer *>
+{
+
+public:
+  Observable();
+  virtual ~Observable();
+
+  void AddObserver(Observer *o);
+  void RemoveObserver(Observer *o);
+  void NotifyObservers(const CStdString& msg = CStdString());
+  void SetChanged(bool bSetTo = true);
+
+private:
+  bool  m_bObservableChanged;
+};
diff --git a/xbmc/utils/TextSearch.cpp b/xbmc/utils/TextSearch.cpp
new file mode 100644 (file)
index 0000000..7b75591
--- /dev/null
@@ -0,0 +1,265 @@
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#include "TextSearch.h"
+
+using namespace std;
+
+cTextSearch::cTextSearch(void)
+{
+  m_text          = "";
+  m_searchText    = "";
+  m_CaseSensitive = false;
+}
+
+cTextSearch::cTextSearch(CStdString text, CStdString searchText, bool caseSensitive)
+{
+  m_CaseSensitive = caseSensitive;
+  m_text          = text;
+  m_searchText    = searchText;
+}
+
+cTextSearch::~cTextSearch(void)
+{
+  m_AND.clear();
+  m_OR.clear();
+  m_NOT.clear();
+}
+
+void cTextSearch::SetText(CStdString text, CStdString searchText, bool caseSensitive)
+{
+  m_CaseSensitive = caseSensitive;
+  m_text          = text;
+  m_searchText    = searchText;
+}
+
+bool cTextSearch::DoSearch()
+{
+  m_AND.clear();
+  m_OR.clear();
+  m_NOT.clear();
+
+  CStdString text       = m_text;
+  CStdString searchStr  = m_searchText;
+
+  if (text == "")
+    return false;
+
+  if (searchStr == "")
+    return true;
+
+  if (!m_CaseSensitive)
+    text.ToLower();
+
+  /* Only one word search */
+  if (searchStr.Find(" ") == CStdString::npos)
+  {
+    if (searchStr[0] == '"')
+      searchStr.erase(0, 1);
+    if (searchStr[searchStr.size()-1] == '"')
+      searchStr.erase(searchStr.size()-1, 1);
+
+    if (!m_CaseSensitive)
+      searchStr.ToLower();
+
+    if (text.Find(searchStr) == CStdString::npos)
+      return false;
+    else
+      return true;
+  }
+  else
+  {
+    while (searchStr.length() > 3)
+    {
+      /* Remove white spaces and begin of search line */
+      if (searchStr[0] == ' ')
+      {
+        searchStr.erase(0, 1);
+      }
+      /* Search for AND */
+      else if (searchStr.Find("AND") != CStdString::npos || searchStr.Find("+") != CStdString::npos)
+      {
+        size_t firstPos     = searchStr.Find("AND");
+        size_t lastPos      = CStdString::npos;
+
+        if (firstPos != CStdString::npos)
+          searchStr.erase(firstPos, 3);
+        else
+        {
+          firstPos = searchStr.Find("+");
+          searchStr.erase(firstPos, 1);
+        }
+
+        ClearBlankCharacter(&searchStr, firstPos);
+
+        if (searchStr[firstPos] == '"')
+        {
+          firstPos++; // Remove the phrase
+          lastPos = searchStr.Find("\"", firstPos);
+        }
+        else
+          lastPos = searchStr.Find(" ");
+
+        size_t strSize = CStdString::npos;
+        if (lastPos != CStdString::npos)
+          strSize = lastPos-firstPos;
+
+        m_AND.push_back(searchStr.substr(firstPos, strSize));
+        searchStr.erase(firstPos, lastPos-firstPos);
+      }
+      /* Search for OR */
+      else if (searchStr.Find("OR") != CStdString::npos || searchStr.Find("|") != CStdString::npos)
+      {
+        size_t firstPos     = searchStr.Find("OR");
+        size_t lastPos      = CStdString::npos;
+
+        if (firstPos != CStdString::npos)
+          searchStr.erase(firstPos, 2);
+        else
+        {
+          firstPos = searchStr.Find("|");
+          searchStr.erase(firstPos, 1);
+        }
+
+        ClearBlankCharacter(&searchStr, firstPos);
+
+        if (searchStr[firstPos] == '"')
+        {
+          firstPos++; // Remove the phrase
+          lastPos = searchStr.Find("\"", firstPos);
+        }
+        else
+          lastPos = searchStr.Find(" ");
+
+        size_t strSize = CStdString::npos;
+        if (lastPos != CStdString::npos)
+          strSize = lastPos-firstPos;
+
+        m_OR.push_back(searchStr.substr(firstPos, strSize));
+        searchStr.erase(firstPos, lastPos-firstPos);
+      }
+      /* Search for OR */
+      else if (searchStr.Find("NOT") != CStdString::npos || searchStr.Find("-") != CStdString::npos)
+      {
+        size_t firstPos     = searchStr.Find("NOT");
+        size_t lastPos      = CStdString::npos;
+
+        if (firstPos != CStdString::npos)
+          searchStr.erase(firstPos, 3);
+        else
+        {
+          firstPos = searchStr.Find("-");
+          searchStr.erase(firstPos, 1);
+        }
+
+        ClearBlankCharacter(&searchStr, firstPos);
+
+        if (searchStr[firstPos] == '"')
+        {
+          firstPos++; // Remove the phrase
+          lastPos = searchStr.Find("\"", firstPos);
+        }
+        else
+          lastPos = searchStr.Find(" ");
+
+        size_t strSize = CStdString::npos;
+        if (lastPos != CStdString::npos)
+          strSize = lastPos-firstPos;
+
+        m_NOT.push_back(searchStr.substr(firstPos, strSize));
+        searchStr.erase(firstPos, lastPos-firstPos);
+      }
+      else
+      {
+        size_t lastPos = CStdString::npos;
+
+        if (searchStr[0] == '"')
+        {
+          searchStr.erase(0, 1);
+          lastPos = searchStr.Find("\"");
+        }
+        else
+        {
+          lastPos = searchStr.Find(" ");
+        }
+
+        m_AND.push_back(searchStr.substr(0, lastPos));
+        searchStr.erase(0, lastPos);
+      }
+    }
+
+    /* If no search words are present everything is true */
+    if (m_AND.size() == 0 && m_OR.size() == 0 && m_NOT.size() == 0)
+      return true;
+
+    for (unsigned int i = 0; i < m_NOT.size(); i++)
+    {
+      CStdString word = m_NOT[i];
+      if (!m_CaseSensitive)
+        word.ToLower();
+
+      if (text.Find(word) != CStdString::npos)
+        return false;
+    }
+
+    for (unsigned int i = 0; i < m_OR.size(); i++)
+    {
+      CStdString word = m_OR[i];
+      if (!m_CaseSensitive)
+        word.ToLower();
+
+      if (text.Find(word) != CStdString::npos)
+        return true;
+    }
+
+    for (unsigned int i = 0; i < m_AND.size(); i++)
+    {
+      CStdString word = m_AND[i];
+      if (!m_CaseSensitive)
+        word.ToLower();
+
+      if (text.Find(word) == CStdString::npos)
+        return false;
+    }
+
+    return true;
+  }
+  return false;
+}
+
+bool cTextSearch::SearchText(CStdString text, CStdString searchText, bool caseSensitive)
+{
+  cTextSearch search(text, searchText, caseSensitive);
+  return search.DoSearch();
+}
+
+void cTextSearch::ClearBlankCharacter(CStdString *text, int pos)
+{
+  if (text->at(pos) > ' ')
+    return;
+  while (text->length() > 0 && text->at(pos) == ' ')
+    text->erase(pos, 1);
+}
diff --git a/xbmc/utils/TextSearch.h b/xbmc/utils/TextSearch.h
new file mode 100644 (file)
index 0000000..cfcade6
--- /dev/null
@@ -0,0 +1,48 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2009 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <vector>
+#include "StringUtils.h"
+
+class cTextSearch
+{
+public:
+  cTextSearch(void);
+  cTextSearch(CStdString text, CStdString searchText, bool caseSensitive);
+  virtual ~cTextSearch(void);
+
+  void SetText(CStdString text, CStdString searchText, bool caseSensitive);
+  bool DoSearch();
+
+  static bool SearchText(CStdString text, CStdString searchText, bool caseSensitive);
+
+private:
+  void ClearBlankCharacter(CStdString *text, int pos);
+
+  CStdString m_text;
+  CStdString m_searchText;
+  std::vector<CStdString>  m_AND;
+  std::vector<CStdString>  m_OR;
+  std::vector<CStdString>  m_NOT;
+  bool m_CaseSensitive;
+};
+
diff --git a/xbmc/win32/stdbool.h b/xbmc/win32/stdbool.h
new file mode 100644 (file)
index 0000000..87a5575
--- /dev/null
@@ -0,0 +1,53 @@
+/* Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC 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.
+
+GCC 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 GCC; see the file COPYING.  If not, write to
+the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA.  */
+
+/* As a special exception, if you include this header file into source
+   files compiled by GCC, this header file does not by itself cause
+   the resulting executable to be covered by the GNU General Public
+   License.  This exception does not however invalidate any other
+   reasons why the executable file might be covered by the GNU General
+   Public License.  */
+
+/*
+* ISO C Standard:  7.16  Boolean type and values  <stdbool.h>
+*/
+
+#ifndef _STDBOOL_H
+#define _STDBOOL_H
+
+#ifndef __cplusplus
+
+#define bool    _Bool
+#define true    1
+#define false   0
+
+#else /* __cplusplus */
+
+/* Supporting <stdbool.h> in C++ is a GCC extension.  */
+#define _Bool   bool
+#define bool    bool
+#define false   false
+#define true    true
+
+#endif /* __cplusplus */
+
+/* Signal that all the definitions are present.  */
+#define __bool_true_false_are_defined   1
+
+#endif  /* stdbool.h */