Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

[dvbviewer] Version 0.1.7

  • Loading branch information...
commit 9e6aca3ac0ff688f132ce0e8a4494b61b9b3ddac 1 parent 1dc2b52
@A600 A600 authored
View
1  addons/Makefile.am
@@ -3,6 +3,7 @@ if ADDON_MYTHTV
endif
SUBDIRS = pvr.demo \
+ pvr.dvbviewer \
pvr.hts \
pvr.mediaportal.tvserver \
pvr.nextpvr \
View
18 addons/pvr.dvbviewer/Makefile.am
@@ -0,0 +1,18 @@
+#
+# Makefile for the DVBViewer add-on for XBMC PVR
+#
+# See the README for copyright information and
+# how to reach the author.
+#
+
+ADDONBINNAME = XBMC_dvbviewer
+ADDONNAME = pvr.dvbviewer
+LIBNAME = libdvbviewer-addon
+lib_LTLIBRARIES = libdvbviewer-addon.la
+
+include ../Makefile.include.am
+
+libdvbviewer_addon_la_SOURCES = src/client.cpp \
+ src/DvbData.cpp \
+ src/xmlParser.cpp
+
View
24 addons/pvr.dvbviewer/addon/addon.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<addon
+ id="pvr.dvbviewer"
+ version="0.1.7"
+ name="DVBViewer Client"
+ provider-name="jdembski, A600">
+ <requires>
+ <c-pluff version="0.1"/>
+ </requires>
+ <extension
+ point="xbmc.pvrclient"
+ library_linux="XBMC_dvbviewer.pvr"
+ library_osx="XBMC_dvbviewer.pvr"
+ library_wingl="XBMC_dvbviewer_win32.pvr"
+ library_windx="XBMC_dvbviewer_win32.pvr"
+ library_android="libXBMC_dvbviewer.so"/>
+ <extension point="xbmc.addon.metadata">
+ <summary>XBMC's frontend for DVBViewer</summary>
+ <description>DVBViewer Recording Service frontend; supporting streaming of LiveTV, Timers, Recordings &amp; EPG. For parsing the XML output from the RS webinterface it uses the xmlParser library developed by Frank Vanden Berghen (http://www.applied-mathematics.net/tools/xmlParser.html). Many thanks for this!
+</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>
View
27 addons/pvr.dvbviewer/addon/changelog.txt
@@ -0,0 +1,27 @@
+0.1.7
+
+[added] The Recording Service version 1.21 or higher is now required. Download the latest version from the DVBViewer members area and install it.
+[added] Display a notification if the add-on can't connect to the Recording Service. Please, check that the RS is enabled and the IP, webinterface port, username and pass are correct.
+[added] If the favourites.xml selector is empty, the favourites are loaded from the web interface.
+[added] Don't fetch recording thumbnails at startup if there are more than 20 recordings.
+[fixed] The add-on crashed if favourites.xml had entries with the old 32 bits ID.
+[fixed] Channel names with ansi chars are converted to utf8 so they can be displayed properly (a reset of the PVR database may be required).
+[fixed] Channel names with more than 25 chars.
+
+
+0.1.5
+
+[added] Timers support.
+[added] An option to load the channels from favourites.xml instead of from channels.dat (a reset of the PVR database is required).
+ It is usually located at c:\ProgramData\CMUV\DVBViewer\
+ This is a hidden folder so be sure to enable the "Settings\Appearance\File Lists\Show hidden files and directories" option.
+[added] Always grab the EPG data after a channel switch.
+[added] Recording thumbnails.
+[added] Receiving device name to the status info.
+[fixed] The preferred language is loaded from the DVBViewer config and used to get the correct EPG in case it supports multi language entries.
+[fixed] When the EPG is missing the description entry, the event entry is used instead.
+
+
+0.1.0
+
+First version.
View
BIN  addons/pvr.dvbviewer/addon/icon.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
16 addons/pvr.dvbviewer/addon/resources/language/English/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<strings>
+ <!-- settings labels -->
+ <string id="30000">DVBViewer Recording Service IP Address</string>
+ <string id="30001">Webinterface Port</string>
+ <string id="30002">Streaming Port</string>
+ <string id="30003">Recordings Port</string>
+ <string id="30004">Username</string>
+ <string id="30005">Password</string>
+ <string id="30006">Load channels from favourites.xml instead of from channels.dat</string>
+ <string id="30007">Select the favourites.xml file</string>
+
+ <!-- notifications -->
+ <string id="30500">Can't connect to the Recording Service</string>
+ <string id="30501">Recording Service version 1.%d or higher required</string>
+</strings>
View
11 addons/pvr.dvbviewer/addon/resources/settings.xml
@@ -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="webport" type="number" label="30001" default="8089" />
+ <setting id="streamport" type="number" label="30002" default="7522" />
+ <setting id="recordingport" type="number" label="30003" default="8090" />
+ <setting id="user" type="text" label="30004" default="" />
+ <setting id="pass" type="text" label="30005" option="hidden" default="" />
+ <setting label="30006" type="bool" id="usefavourites" default="false"/>
+ <setting id="favouritespath" type="file" label="30007" default="" />
+</settings>
View
105 addons/pvr.dvbviewer/project/VS2010Express/pvr.dvbviewer.vcxproj
@@ -0,0 +1,105 @@
+<?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>{D31D6BEB-0285-47CF-9817-F8AD2CFDF0E8}</ProjectGuid>
+ <RootNamespace>pvrdvbviewer</RootNamespace>
+ </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>..\..\addon\</OutDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <TargetExt>.pvr</TargetExt>
+ <TargetName>XBMC_dvbviewer_win32</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <OutDir>..\..\addon\</OutDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <TargetExt>.pvr</TargetExt>
+ <TargetName>XBMC_dvbviewer_win32</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <AdditionalIncludeDirectories>..\..\..\..\xbmc;..\..\..\..\lib;..\..\..\..\lib\platform\windows</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WINDLL;TARGET_WINDOWS;_WINSOCKAPI_;_USE_32BIT_TIME_T;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalLibraryDirectories>
+ </AdditionalLibraryDirectories>
+ <AdditionalDependencies>ws2_32.lib;kernel32.lib;user32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <AdditionalIncludeDirectories>..\..\..\..\xbmc;..\..\..\..\lib;..\..\..\..\lib\platform\windows</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WINDLL;TARGET_WINDOWS;_WINSOCKAPI_;_USE_32BIT_TIME_T;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <AdditionalLibraryDirectories>
+ </AdditionalLibraryDirectories>
+ <AdditionalDependencies>ws2_32.lib;kernel32.lib;user32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\src\client.cpp" />
+ <ClCompile Include="..\..\src\DvbData.cpp" />
+ <ClCompile Include="..\..\src\xmlParser.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\src\client.h" />
+ <ClInclude Include="..\..\src\DvbData.h" />
+ <ClInclude Include="..\..\src\xmlParser.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\..\..\project\VS2010Express\platform\platform.vcxproj">
+ <Project>{fe4573f6-a794-4ad3-b37f-49e51f1140e6}</Project>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
View
39 addons/pvr.dvbviewer/project/VS2010Express/pvr.dvbviewer.vcxproj.filters
@@ -0,0 +1,39 @@
+<?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="..\..\src\client.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\xmlParser.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\src\DvbData.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\src\client.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\xmlParser.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\src\DvbData.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+</Project>
View
1,219 addons/pvr.dvbviewer/src/DvbData.cpp
@@ -0,0 +1,1219 @@
+#include "DvbData.h"
+
+#include "client.h"
+
+using namespace ADDON;
+using namespace PLATFORM;
+
+void Dvb::TimerUpdates()
+{
+ std::vector<DvbTimer> newtimer = LoadTimers();
+
+ for (unsigned int i=0; i<m_timers.size(); i++)
+ {
+ m_timers[i].iUpdateState = DVB_UPDATE_STATE_NONE;
+ }
+
+ unsigned int iUpdated=0;
+ unsigned int iUnchanged=0;
+
+ for (unsigned int j=0;j<newtimer.size(); j++) {
+ for (unsigned int i=0; i<m_timers.size(); i++)
+ {
+ if (m_timers[i].like(newtimer[j]))
+ {
+ if(m_timers[i] == newtimer[j])
+ {
+ m_timers[i].iUpdateState = DVB_UPDATE_STATE_FOUND;
+ newtimer[j].iUpdateState = DVB_UPDATE_STATE_FOUND;
+ iUnchanged++;
+ }
+ else
+ {
+ newtimer[j].iUpdateState = DVB_UPDATE_STATE_UPDATED;
+ m_timers[i].iUpdateState = DVB_UPDATE_STATE_UPDATED;
+ m_timers[i].strTitle = newtimer[j].strTitle;
+ m_timers[i].strPlot = newtimer[j].strPlot;
+ m_timers[i].iChannelId = newtimer[j].iChannelId;
+ m_timers[i].startTime = newtimer[j].startTime;
+ m_timers[i].endTime = newtimer[j].endTime;
+ m_timers[i].bRepeating = newtimer[j].bRepeating;
+ m_timers[i].iWeekdays = newtimer[j].iWeekdays;
+ m_timers[i].iEpgID = newtimer[j].iEpgID;
+ m_timers[i].iTimerID = newtimer[j].iTimerID;
+ m_timers[i].iPriority = newtimer[j].iPriority;
+ m_timers[i].iFirstDay = newtimer[j].iFirstDay;
+ m_timers[i].state = newtimer[j].state;
+
+ iUpdated++;
+
+ }
+ }
+ }
+ }
+
+ unsigned int iRemoved = 0;
+
+ for (unsigned int i=0; i<m_timers.size(); i++)
+ {
+ if (m_timers.at(i).iUpdateState == DVB_UPDATE_STATE_NONE)
+ {
+ XBMC->Log(LOG_INFO, "%s Removed timer: '%s', ClientIndex: '%d'", __FUNCTION__, m_timers.at(i).strTitle.c_str(), m_timers.at(i).iClientIndex);
+ m_timers.erase(m_timers.begin()+i);
+ i=0;
+ iRemoved++;
+ }
+ }
+ unsigned int iNew=0;
+
+ for (unsigned int i=0; i<newtimer.size();i++)
+ {
+ if(newtimer.at(i).iUpdateState == DVB_UPDATE_STATE_NEW)
+ {
+ DvbTimer &timer = newtimer.at(i);
+ timer.iClientIndex = m_iClientIndexCounter;
+ XBMC->Log(LOG_INFO, "%s New timer: '%s', ClientIndex: '%d'", __FUNCTION__, timer.strTitle.c_str(), m_iClientIndexCounter);
+ m_timers.push_back(timer);
+ m_iClientIndexCounter++;
+ iNew++;
+ }
+ }
+
+ XBMC->Log(LOG_INFO, "%s No of timers: removed [%d], untouched [%d], updated '%d', new '%d'", __FUNCTION__, iRemoved, iUnchanged, iUpdated, iNew);
+
+ if (iRemoved != 0 || iUpdated != 0 || iNew != 0)
+ {
+ XBMC->Log(LOG_INFO, "%s Changes in timerlist detected, trigger an update!", __FUNCTION__);
+ PVR->TriggerTimerUpdate();
+ }
+}
+
+Dvb::Dvb()
+{
+ m_bIsConnected = false;
+ m_strServerName = "DVBViewer";
+ CStdString strURL = "", strURLStream = "", strURLRecording = "";
+
+ // simply add user@pass in front of the URL if username/password is set
+ if ((g_strUsername.length() > 0) && (g_strPassword.length() > 0))
+ strURL.Format("%s:%s@", g_strUsername.c_str(), g_strPassword.c_str());
+ strURL.Format("http://%s%s:%u/", strURL.c_str(), g_strHostname.c_str(), g_iPortWeb);
+ m_strURL = strURL.c_str();
+
+ if ((g_strUsername.length() > 0) && (g_strPassword.length() > 0))
+ strURLRecording.Format("%s:%s@", g_strUsername.c_str(), g_strPassword.c_str());
+ strURLRecording.Format("http://%s%s:%u/", strURLRecording.c_str(), g_strHostname.c_str(), g_iPortRecording);
+ m_strURLRecording = strURLRecording.c_str();
+
+ if ((g_strUsername.length() > 0) && (g_strPassword.length() > 0))
+ strURLStream.Format("%s:%s@", g_strUsername.c_str(), g_strPassword.c_str());
+ strURLStream.Format("http://%s%s:%u/", strURLStream.c_str(), g_strHostname.c_str(), g_iPortStream);
+ m_strURLStream = strURLStream.c_str();
+
+ m_iNumRecordings = 0;
+ m_iNumChannelGroups = 0;
+ m_iCurrentChannel = -1;
+ m_iClientIndexCounter = 1;
+
+ m_iUpdateTimer = 0;
+ m_bUpdateTimers = false;
+ m_bUpdateEPG = false;
+}
+
+bool Dvb::Open()
+{
+ CLockObject lock(m_mutex);
+
+ XBMC->Log(LOG_NOTICE, "%s - DVBViewer Addon Configuration options", __FUNCTION__);
+ XBMC->Log(LOG_NOTICE, "%s - Hostname: '%s'", __FUNCTION__, g_strHostname.c_str());
+ XBMC->Log(LOG_NOTICE, "%s - WebPort: '%d'", __FUNCTION__, g_iPortWeb);
+ XBMC->Log(LOG_NOTICE, "%s - StreamPort: '%d'", __FUNCTION__, g_iPortStream);
+
+ m_bIsConnected = GetDeviceInfo();
+
+ if (!m_bIsConnected)
+ return false;
+
+ if (!LoadChannels())
+ return false;
+
+ m_strEPGLanguage = GetPreferredLanguage();
+
+ CStdString url;
+ url.Format("%s%s", m_strURL.c_str(), "api/status.html");
+
+ CStdString strXML;
+ strXML = GetHttpXML(url);
+
+ XMLResults xe;
+ XMLNode xMainNode = XMLNode::parseString(strXML.c_str(), NULL, &xe);
+
+ if(xe.error != 0) {
+ XBMC->Log(LOG_ERROR, "%s Unable to parse XML. Error: '%s' ", __FUNCTION__, XMLNode::getError(xe.error));
+ }
+ else {
+ XMLNode xNode = xMainNode.getChildNode("status");
+ GetInt(xNode, "timezone", m_iTimezone);
+ }
+
+ TimerUpdates();
+
+ XBMC->Log(LOG_INFO, "%s Starting separate client update thread...", __FUNCTION__);
+ CreateThread();
+
+ return IsRunning();
+}
+
+void *Dvb::Process()
+{
+ XBMC->Log(LOG_DEBUG, "%s - starting", __FUNCTION__);
+
+ while(!IsStopped())
+ {
+ Sleep(1000);
+ m_iUpdateTimer++;
+
+ if (m_bUpdateEPG)
+ {
+ Sleep(8000); /* Sleep enough time to let the recording service grab the EPG data */
+ PVR->TriggerEpgUpdate(m_iCurrentChannel);
+ m_bUpdateEPG = false;
+ }
+
+ if ((m_iUpdateTimer > 60) || m_bUpdateTimers)
+ {
+ m_iUpdateTimer = 0;
+
+ // Trigger Timer and Recording updates acording to the addon settings
+ CLockObject lock(m_mutex);
+ XBMC->Log(LOG_INFO, "%s Perform Updates!", __FUNCTION__);
+
+ if (m_bUpdateTimers)
+ {
+ Sleep(500);
+ m_bUpdateTimers = false;
+ }
+ TimerUpdates();
+ PVR->TriggerRecordingUpdate();
+ }
+ }
+
+ CLockObject lock(m_mutex);
+ m_started.Broadcast();
+ //XBMC->Log(LOG_DEBUG, "%s - exiting", __FUNCTION__);
+
+ return NULL;
+}
+
+bool Dvb::LoadChannels()
+{
+ ChannelsDat *channel;
+ int num_channels, channel_pos = 0;
+
+ m_groups.clear();
+ m_iNumChannelGroups = 0;
+
+ CStdString url;
+ url.Format("%sapi/getchannelsdat.html", m_strURL.c_str());
+ CStdString strBIN;
+ strBIN = GetHttpXML(url);
+ if (strBIN.IsEmpty())
+ {
+ XBMC->Log(LOG_ERROR, "%s Unable to load channels.dat", __FUNCTION__);
+ return false;
+ }
+ strBIN.erase(0, CHANNELDAT_HEADER_SIZE);
+ channel = (ChannelsDat*)strBIN.data();
+ num_channels = strBIN.size() / sizeof(ChannelsDat);
+
+ std::vector<DvbChannel> channelsdat;
+ std::vector<DvbChannelGroup> groupsdat;
+ DvbChannel datChannel;
+ DvbChannelGroup datGroup;
+
+ for (int i=0; i<num_channels; i++)
+ {
+ if (i) channel++;
+ if (channel->Flags & ADDITIONAL_AUDIO_TRACK_FLAG) continue;
+ char channel_group[26] = "";
+ if ((strcmp(channel_group, channel->Category) != 0) && (channel->Flags & VIDEO_FLAG))
+ {
+ datGroup.strGroupName = strncpy(channel_group, channel->Category, channel->Category_len);
+#ifdef WIN32
+ AnsiToUtf8(datGroup.strGroupName);
+#else
+ XBMC->UnknownToUTF8(datGroup.strGroupName);
+#endif
+ groupsdat.push_back(datGroup);
+ m_iNumChannelGroups++;
+ }
+ channel_pos++;
+
+ /* EPGChannelID = (TunerType + 1)*2^48 + NID*2^32 + TID*2^16 + SID */
+ datChannel.llEpgId = ((uint64_t)(channel->TunerType + 1)<<48) + ((uint64_t)channel->OriginalNetwork_ID<<32) + ((uint64_t)channel->TransportStream_ID<<16) + channel->Service_ID;
+ datChannel.Encrypted = channel->Flags & ENCRYPTED_FLAG;
+ /* ChannelID = (tunertype + 1) * 536870912 + APID * 65536 + SID */
+ datChannel.iChannelId = ((channel->TunerType + 1) << 29) + (channel->Audio_PID << 16) + channel->Service_ID;
+ datChannel.bRadio = (channel->Flags & VIDEO_FLAG) == 0;
+ datChannel.strGroupName = datGroup.strGroupName;
+ datChannel.iUniqueId = channelsdat.size()+1;
+ datChannel.iChannelNumber = channelsdat.size()+1;
+
+ char channel_name[26] = "";
+ CStdString strChannelName = strncpy(channel_name, channel->ChannelName, channel->ChannelName_len);
+ if (channel->ChannelName_len == 25)
+ {
+ if (channel->Root[channel->Root_len] > 0)
+ {
+ char channel_root[26] = "";
+ CStdString strRoot = strncpy(channel_root, channel->Root, 25);
+ strChannelName.append(strRoot.substr(channel->Root_len + 1, channel->Root[channel->Root_len]));
+ if (channel->Category[channel->Category_len] > 0)
+ {
+ char channel_category[26] = "";
+ CStdString strCategory = strncpy(channel_category, channel->Category, 25);
+ strChannelName.append(strCategory.substr(channel->Category_len + 1, channel->Category[channel->Category_len]));
+ }
+ }
+ }
+ datChannel.strChannelName = strChannelName;
+#ifdef WIN32
+ AnsiToUtf8(datChannel.strChannelName);
+#else
+ XBMC->UnknownToUTF8(datChannel.strChannelName);
+#endif
+
+ CStdString strTmp;
+ strTmp.Format("%supnp/channelstream/%d.ts", m_strURLStream.c_str(), channel_pos-1);
+ datChannel.strStreamURL = strTmp;
+
+ strTmp.Format("%sLogos/%s.png", m_strURL.c_str(), URLEncodeInline(datChannel.strChannelName.c_str()));
+ datChannel.strIconPath = strTmp;
+
+ channelsdat.push_back(datChannel);
+ }
+
+ if (g_bUseFavourites)
+ {
+ CStdString urlFav = g_strFavouritesPath;
+ if (!XBMC->FileExists(urlFav.c_str(), false))
+ urlFav.Format("%sapi/getfavourites.html", m_strURL.c_str());
+
+ CStdString strXML;
+ strXML = GetHttpXML(urlFav);
+
+ if(!strXML.size())
+ {
+ XBMC->Log(LOG_ERROR, "%s Unable to load favourites.xml.", __FUNCTION__);
+ return false;
+ }
+
+ RemoveNullChars(strXML);
+ XMLResults xe;
+ XMLNode xMainNode = XMLNode::parseString(strXML.c_str(), NULL, &xe);
+
+ if(xe.error != 0) {
+ XBMC->Log(LOG_ERROR, "%s Unable to parse favourites.xml. Error: '%s' ", __FUNCTION__, XMLNode::getError(xe.error));
+ return false;
+ }
+
+ XMLNode xNode = xMainNode.getChildNode("settings");
+ int m = xNode.nChildNode("section");
+
+ std::vector<DvbChannel> channelsfav;
+ std::vector<DvbChannelGroup> groupsfav;
+ DvbChannel favChannel;
+ DvbChannelGroup favGroup;
+
+ for (int i = 0; i<m; i++)
+ {
+ CStdString groupName;
+ XMLNode xTmp = xNode.getChildNode("section", i);
+ int n = xTmp.nChildNode("entry");
+ for (int j = 0; j<n; j++)
+ {
+ CStdString strTmp;
+ XMLNode xTmp2 = xTmp.getChildNode("entry", j);
+ strTmp = xTmp2.getText();
+ uint64_t llFavId;
+ if (sscanf(strTmp, "%lld|", &llFavId))
+ {
+ int iChannelId = llFavId & 0xFFFFFFFF;
+ if (n == 1)
+ groupName = "";
+ for (unsigned int i = 0; i<channelsdat.size(); i++)
+ {
+ if (channelsdat[i].iChannelId == iChannelId)
+ {
+ favChannel.llEpgId = channelsdat[i].llEpgId;
+ favChannel.Encrypted = channelsdat[i].Encrypted;
+ favChannel.iChannelId = channelsdat[i].iChannelId;
+ favChannel.bRadio = channelsdat[i].bRadio;
+ favChannel.strGroupName = groupName;
+ favChannel.iUniqueId = channelsfav.size()+1;
+ favChannel.iChannelNumber = channelsfav.size()+1;
+ favChannel.strChannelName = channelsdat[i].strChannelName;
+ favChannel.strStreamURL = channelsdat[i].strStreamURL;
+ favChannel.strIconPath = channelsdat[i].strIconPath;
+ channelsfav.push_back(favChannel);
+ break;
+ }
+ }
+ }
+ else
+ {
+ groupName = strTmp;
+ favGroup.strGroupName = groupName;
+#ifdef WIN32
+ AnsiToUtf8(favGroup.strGroupName);
+#else
+ XBMC->UnknownToUTF8(favGroup.strGroupName);
+#endif
+ groupsfav.push_back(favGroup);
+ }
+ }
+ }
+ m_channels = channelsfav;
+ m_groups = groupsfav;
+ }
+ else
+ {
+ m_channels = channelsdat;
+ m_groups = groupsdat;
+ }
+
+ XBMC->Log(LOG_INFO, "%s Loaded %d Channelsgroups", __FUNCTION__, m_iNumChannelGroups);
+ return true;
+}
+
+bool Dvb::IsConnected()
+{
+ return m_bIsConnected;
+}
+
+CStdString Dvb::GetHttpXML(CStdString& url)
+{
+ CStdString strResult;
+ void* fileHandle = XBMC->OpenFile(url.c_str(), 0);
+ if (fileHandle)
+ {
+ char buffer[1024];
+ while (int bytesRead = XBMC->ReadFile(fileHandle, buffer, 1024))
+ strResult.append(buffer, bytesRead);
+ XBMC->CloseFile(fileHandle);
+ }
+ return strResult;
+}
+
+const char * Dvb::GetServerName()
+{
+ return m_strServerName.c_str();
+}
+
+int Dvb::GetChannelsAmount()
+{
+ return m_channels.size();
+}
+
+int Dvb::GetTimersAmount()
+{
+ return m_timers.size();
+}
+
+unsigned int Dvb::GetRecordingsAmount() {
+ return m_iNumRecordings;
+}
+
+PVR_ERROR Dvb::GetChannels(ADDON_HANDLE handle, bool bRadio)
+{
+ for (unsigned int iChannelPtr = 0; iChannelPtr < m_channels.size(); iChannelPtr++)
+ {
+ DvbChannel &channel = m_channels.at(iChannelPtr);
+ if (channel.bRadio == bRadio)
+ {
+ PVR_CHANNEL xbmcChannel;
+ memset(&xbmcChannel, 0, sizeof(PVR_CHANNEL));
+
+ xbmcChannel.iUniqueId = channel.iUniqueId;
+ xbmcChannel.bIsRadio = channel.bRadio;
+ xbmcChannel.iChannelNumber = channel.iChannelNumber;
+ strncpy(xbmcChannel.strChannelName, channel.strChannelName.c_str(), sizeof(xbmcChannel.strChannelName));
+ strncpy(xbmcChannel.strInputFormat, "", sizeof(xbmcChannel.strInputFormat)); // unused
+
+ CStdString strStream;
+ strStream.Format("pvr://stream/tv/%i.ts", channel.iUniqueId);
+ strncpy(xbmcChannel.strStreamURL, strStream.c_str(), sizeof(xbmcChannel.strStreamURL)); //channel.strStreamURL.c_str();
+ xbmcChannel.iEncryptionSystem = channel.Encrypted;
+
+ strncpy(xbmcChannel.strIconPath, channel.strIconPath.c_str(), sizeof(xbmcChannel.strIconPath));
+ xbmcChannel.bIsHidden = false;
+
+ PVR->TransferChannelEntry(handle, &xbmcChannel);
+ }
+ }
+
+ return PVR_ERROR_NO_ERROR;
+}
+
+Dvb::~Dvb()
+{
+ StopThread();
+
+ m_channels.clear();
+ m_timers.clear();
+ m_recordings.clear();
+ m_groups.clear();
+ m_bIsConnected = false;
+}
+
+int Dvb::ParseDateTime(CStdString strDate, bool iDateFormat)
+{
+ struct tm timeinfo;
+
+ memset(&timeinfo, 0, sizeof(tm));
+ if (iDateFormat)
+ sscanf(strDate, "%04d%02d%02d%02d%02d%02d", &timeinfo.tm_year, &timeinfo.tm_mon, &timeinfo.tm_mday, &timeinfo.tm_hour, &timeinfo.tm_min, &timeinfo.tm_sec);
+ else
+ sscanf(strDate, "%02d.%02d.%04d%02d:%02d:%02d", &timeinfo.tm_mday, &timeinfo.tm_mon, &timeinfo.tm_year, &timeinfo.tm_hour, &timeinfo.tm_min, &timeinfo.tm_sec);
+ timeinfo.tm_mon -= 1;
+ timeinfo.tm_year -= 1900;
+ timeinfo.tm_isdst = -1;
+
+ return mktime(&timeinfo);
+}
+
+PVR_ERROR Dvb::GetEPGForChannel(ADDON_HANDLE handle, const PVR_CHANNEL &channel, time_t iStart, time_t iEnd)
+{
+ DvbChannel &myChannel = m_channels.at(channel.iUniqueId-1);
+
+ CStdString url;
+ url.Format("%sapi/epg.html?lvl=2&channel=%lld&start=%f&end=%f", m_strURL.c_str(), myChannel.llEpgId, iStart/86400.0 + DELPHI_DATE, iEnd/86400.0 + DELPHI_DATE);
+
+ CStdString strXML;
+ strXML = GetHttpXML(url);
+
+ int iNumEPG = 0;
+
+ XMLResults xe;
+ XMLNode xMainNode = XMLNode::parseString(strXML.c_str(), NULL, &xe);
+
+ if(xe.error != 0) {
+ XBMC->Log(LOG_ERROR, "%s Unable to parse XML. Error: '%s' ", __FUNCTION__, XMLNode::getError(xe.error));
+ return PVR_ERROR_SERVER_ERROR;
+ }
+
+ XMLNode xNode = xMainNode.getChildNode("epg");
+ int n = xNode.nChildNode("programme");
+
+ XBMC->Log(LOG_INFO, "%s Number of elements: '%d'", __FUNCTION__, n);
+
+ for (int i = 0; i<n; i++)
+ {
+ CStdString strTmp;
+ int iTmpStart;
+ int iTmpEnd;
+
+ XMLNode xTmp = xNode.getChildNode("programme", i);
+ iTmpStart = ParseDateTime(xNode.getChildNode("programme", i).getAttribute("start"));
+ iTmpEnd = ParseDateTime(xNode.getChildNode("programme", i).getAttribute("stop"));
+
+ if ((iEnd > 1) && (iEnd < iTmpEnd))
+ continue;
+
+ DvbEPGEntry entry;
+ entry.startTime = iTmpStart;
+ entry.endTime = iTmpEnd;
+
+ if (!GetInt(xTmp, "eventid", entry.iEventId))
+ continue;
+
+ entry.iChannelId = channel.iUniqueId;
+
+ if(!GetStringLng(xTmp.getChildNode("titles"), "title", strTmp))
+ continue;
+
+ entry.strTitle = strTmp;
+
+ CStdString strTmp2;
+ GetStringLng(xTmp.getChildNode("descriptions"), "description", strTmp);
+ GetStringLng(xTmp.getChildNode("events"), "event", strTmp2);
+ if (strTmp.size() > strTmp2.size())
+ entry.strPlot = strTmp;
+ else
+ entry.strPlot = strTmp2;
+
+ EPG_TAG broadcast;
+ memset(&broadcast, 0, sizeof(EPG_TAG));
+
+ broadcast.iUniqueBroadcastId = entry.iEventId;
+ broadcast.strTitle = entry.strTitle.c_str();
+ broadcast.iChannelNumber = channel.iChannelNumber;
+ broadcast.startTime = entry.startTime;
+ broadcast.endTime = entry.endTime;
+ broadcast.strPlotOutline = entry.strPlotOutline.c_str();
+ broadcast.strPlot = entry.strPlot.c_str();
+ broadcast.strIconPath = ""; // unused
+ broadcast.iGenreType = 0; // unused
+ broadcast.iGenreSubType = 0; // unused
+ broadcast.strGenreDescription = "";
+ broadcast.firstAired = 0; // unused
+ broadcast.iParentalRating = 0; // unused
+ broadcast.iStarRating = 0; // unused
+ broadcast.bNotify = false;
+ broadcast.iSeriesNumber = 0; // unused
+ broadcast.iEpisodeNumber = 0; // unused
+ broadcast.iEpisodePartNumber = 0; // unused
+ broadcast.strEpisodeName = ""; // unused
+
+ PVR->TransferEpgEntry(handle, &broadcast);
+
+ iNumEPG++;
+
+ XBMC->Log(LOG_INFO, "%s loaded EPG entry '%d:%s' channel '%d' start '%d' end '%d'", __FUNCTION__, broadcast.iUniqueBroadcastId, broadcast.strTitle, entry.iChannelId, entry.startTime, entry.endTime);
+ }
+
+ XBMC->Log(LOG_INFO, "%s Loaded %u EPG Entries for channel '%s'", __FUNCTION__, iNumEPG, channel.strChannelName);
+ return PVR_ERROR_NO_ERROR;
+}
+
+int Dvb::GetChannelNumber(CStdString strChannelId)
+{
+ int channel;
+ sscanf(strChannelId.c_str(), "%d|", &channel);
+ for (unsigned int i = 0;i<m_channels.size(); i++)
+ {
+ if (m_channels[i].iChannelId == channel)
+ return i+1;
+ }
+ return -1;
+}
+
+PVR_ERROR Dvb::GetTimers(ADDON_HANDLE handle)
+{
+ XBMC->Log(LOG_INFO, "%s - timers available '%d'", __FUNCTION__, m_timers.size());
+ for (unsigned int i=0; i<m_timers.size(); i++)
+ {
+ DvbTimer &timer = m_timers.at(i);
+ XBMC->Log(LOG_INFO, "%s - Transfer timer '%s', ClientIndex '%d'", __FUNCTION__, timer.strTitle.c_str(), timer.iClientIndex);
+ PVR_TIMER tag;
+ memset(&tag, 0, sizeof(PVR_TIMER));
+ tag.iClientChannelUid = timer.iChannelId;
+ tag.startTime = timer.startTime;
+ tag.endTime = timer.endTime;
+ strncpy(tag.strTitle, timer.strTitle.c_str(), sizeof(tag.strTitle));
+ strncpy(tag.strDirectory, "/", sizeof(tag.strDirectory)); // unused
+ strncpy(tag.strSummary, timer.strPlot.c_str(), sizeof(tag.strSummary));
+ tag.state = timer.state;
+ tag.iPriority = timer.iPriority;
+ tag.iLifetime = 0; // unused
+ tag.bIsRepeating = timer.bRepeating;
+ tag.firstDay = timer.iFirstDay;
+ tag.iWeekdays = timer.iWeekdays;
+ tag.iEpgUid = timer.iEpgID;
+ tag.iMarginStart = 0; // unused
+ tag.iMarginEnd = 0; // unused
+ tag.iGenreType = 0; // unused
+ tag.iGenreSubType = 0; // unused
+ tag.iClientIndex = timer.iClientIndex;
+
+ PVR->TransferTimerEntry(handle, &tag);
+ }
+ return PVR_ERROR_NO_ERROR;
+}
+
+std::vector<DvbTimer> Dvb::LoadTimers()
+{
+ CStdString url;
+ url.Format("%s%s", m_strURL.c_str(), "api/timerlist.html");
+
+ CStdString strXML;
+ strXML = GetHttpXML(url);
+ RemoveNullChars(strXML);
+
+ XMLResults xe;
+ XMLNode xMainNode = XMLNode::parseString(strXML.c_str(), NULL, &xe);
+
+ std::vector<DvbTimer> timers;
+
+ if(xe.error != 0) {
+ XBMC->Log(LOG_ERROR, "%s Unable to parse XML. Error: '%s' ", __FUNCTION__, XMLNode::getError(xe.error));
+ return timers;
+ }
+
+ XMLNode xNode = xMainNode.getChildNode("Timers");
+ int n = xNode.nChildNode("Timer");
+
+ XBMC->Log(LOG_INFO, "%s Number of elements: '%d'", __FUNCTION__, n);
+
+
+ while(n>0)
+ {
+ int i = n-1;
+ n--;
+ XMLNode xTmp = xNode.getChildNode("Timer", i);
+
+ CStdString strTmp;
+ int iTmp;
+
+ if (GetString(xTmp, "Descr", strTmp))
+ XBMC->Log(LOG_DEBUG, "%s Processing timer '%s'", __FUNCTION__, strTmp.c_str());
+
+ DvbTimer timer;
+
+ timer.strTitle = strTmp;
+ timer.iChannelId = GetChannelNumber(xTmp.getChildNode("Channel").getAttribute("ID"));
+ timer.state = PVR_TIMER_STATE_SCHEDULED;
+
+ CStdString DateTime;
+ DateTime = xNode.getChildNode("timer", i).getAttribute("Date");
+ DateTime.append(xNode.getChildNode("timer", i).getAttribute("Start"));
+ timer.startTime = ParseDateTime(DateTime, false);
+ timer.endTime = timer.startTime + atoi(xNode.getChildNode("timer", i).getAttribute("Dur"))*60;
+
+ CStdString Weekdays;
+ Weekdays = xNode.getChildNode("timer", i).getAttribute("Days");
+ timer.iWeekdays = 0;
+ for (unsigned int i = 0; i<Weekdays.size(); i++)
+ {
+ if (Weekdays.data()[i] != '-')
+ timer.iWeekdays += (1 << i);
+ }
+
+ if (timer.iWeekdays != 0)
+ {
+ timer.iFirstDay = timer.startTime;
+ timer.bRepeating = true;
+ }
+ else
+ timer.bRepeating = false;
+
+ timer.iPriority = atoi(xNode.getChildNode("Timer", i).getAttribute("Priority"));
+
+ if (xNode.getChildNode("Timer", i).getAttribute("EPGEventID"))
+ timer.iEpgID = atoi(xNode.getChildNode("Timer", i).getAttribute("EPGEventID"));
+
+ if (xNode.getChildNode("Timer", i).getAttribute("Enabled")[0] == '0')
+ timer.state = PVR_TIMER_STATE_CANCELLED;
+
+ if (GetInt(xTmp, "Recording", iTmp))
+ {
+ if (iTmp == -1) timer.state = PVR_TIMER_STATE_RECORDING;
+ }
+
+ if (GetInt(xTmp, "ID", iTmp))
+ timer.iTimerID = iTmp;
+
+ timers.push_back(timer);
+
+ XBMC->Log(LOG_INFO, "%s fetched Timer entry '%s', begin '%d', end '%d'", __FUNCTION__, timer.strTitle.c_str(), timer.startTime, timer.endTime);
+ }
+
+ XBMC->Log(LOG_INFO, "%s fetched %u Timer Entries", __FUNCTION__, timers.size());
+ return timers;
+}
+
+int Dvb::GetTimerID(const PVR_TIMER &timer)
+{
+ unsigned int i=0;
+ while (i<m_timers.size())
+ {
+ if (m_timers.at(i).iClientIndex == timer.iClientIndex)
+ break;
+ else
+ i++;
+ }
+ DvbTimer &Timer = m_timers.at(i);
+ return Timer.iTimerID;
+}
+
+void Dvb::GenerateTimer(const PVR_TIMER &timer, bool bNewTimer)
+{
+ XBMC->Log(LOG_DEBUG, "%s - channelUid=%d title=%s epgid=%d", __FUNCTION__, timer.iClientChannelUid, timer.strTitle, timer.iEpgUid);
+
+ struct tm * timeinfo;
+ time_t startTime = timer.startTime;
+ if (startTime == 0)
+ startTime = time(NULL);
+ int dor = ((startTime + m_iTimezone*60 - timer.iMarginStart) / DAY_SECS) + DELPHI_DATE;
+ timeinfo = gmtime (&startTime);
+ int start = (timeinfo->tm_hour*60 + m_iTimezone + timeinfo->tm_min - timer.iMarginStart) % DAY_MINS;
+ timeinfo = gmtime (&timer.endTime);
+ int stop = (timeinfo->tm_hour*60 + m_iTimezone + timeinfo->tm_min + timer.iMarginEnd) % DAY_MINS;
+
+ char strWeek[8] = "-------";
+ for (int i = 0; i<7; i++)
+ {
+ if (timer.iWeekdays & (1 << i)) strWeek[i] = 'T';
+ }
+
+ int iChannelId = m_channels.at(timer.iClientChannelUid-1).iChannelId;
+ CStdString strTmp;
+ if (bNewTimer)
+ {
+ if (timer.startTime)
+ strTmp.Format("api/timeradd.html?ch=%d&dor=%d&enable=1&start=%d&stop=%d&prio=%d&days=%s&title=%s", iChannelId, dor, start, stop, timer.iPriority, strWeek, URLEncodeInline(timer.strTitle));
+ else
+ strTmp.Format("api/timeradd.html?ch=%d&dor=%d&enable=1&start=%d&stop=%d&prio=%d&title=%s", iChannelId, dor, start+timer.iMarginStart, stop-timer.iMarginEnd, timer.iPriority, URLEncodeInline(timer.strTitle));
+ }
+ else
+ {
+ int enabled = 1;
+ if (timer.state == PVR_TIMER_STATE_CANCELLED)
+ enabled = 0;
+ strTmp.Format("api/timeredit.html?id=%d&ch=%d&dor=%d&enable=%d&start=%d&stop=%d&prio=%d&days=%s&title=%s", GetTimerID(timer), iChannelId, dor, enabled, start, stop, timer.iPriority, strWeek, URLEncodeInline(timer.strTitle));
+ }
+
+ SendSimpleCommand(strTmp);
+ m_bUpdateTimers = true;
+}
+
+PVR_ERROR Dvb::AddTimer(const PVR_TIMER &timer)
+{
+ GenerateTimer(timer);
+
+ return PVR_ERROR_NO_ERROR;
+}
+
+PVR_ERROR Dvb::UpdateTimer(const PVR_TIMER &timer)
+{
+ GenerateTimer(timer, false);
+
+ return PVR_ERROR_NO_ERROR;
+}
+
+PVR_ERROR Dvb::DeleteTimer(const PVR_TIMER &timer)
+{
+ CStdString strTmp;
+ strTmp.Format("api/timerdelete.html?id=%d", GetTimerID(timer));
+ SendSimpleCommand(strTmp);
+
+ if (timer.state == PVR_TIMER_STATE_RECORDING)
+ PVR->TriggerRecordingUpdate();
+
+ m_bUpdateTimers = true;
+
+ return PVR_ERROR_NO_ERROR;
+}
+
+PVR_ERROR Dvb::GetRecordings(ADDON_HANDLE handle)
+{
+ m_iNumRecordings = 0;
+ std::vector<DvbRecording> recthumbs;
+ recthumbs = m_recordings;
+ m_recordings.clear();
+
+ CStdString url;
+ url.Format("%s%s", m_strURL.c_str(), "api/recordings.html?utf8=");
+
+ CStdString strXML;
+ strXML = GetHttpXML(url);
+ RemoveNullChars(strXML);
+
+ XMLResults xe;
+ XMLNode xMainNode = XMLNode::parseString(strXML.c_str(), NULL, &xe);
+
+ if(xe.error != 0) {
+ XBMC->Log(LOG_ERROR, "%s Unable to parse XML. Error: '%s' ", __FUNCTION__, XMLNode::getError(xe.error));
+ return PVR_ERROR_SERVER_ERROR;
+ }
+
+ XMLNode xNode = xMainNode.getChildNode("recordings");
+ int n = xNode.nChildNode("recording");
+
+ XBMC->Log(LOG_INFO, "%s Number of elements: '%d'", __FUNCTION__, n);
+
+ int iNumRecording = 0;
+ static int iGetRecordingsCount = 0;
+ int j = n;
+
+ while(n>0)
+ {
+ int i = n-1;
+ n--;
+ XMLNode xTmp = xNode.getChildNode("recording", i);
+ CStdString strTmp;
+
+ DvbRecording recording;
+ if (xNode.getChildNode("recording", i).getAttribute("id"))
+ recording.strRecordingId = xNode.getChildNode("recording", i).getAttribute("id");
+
+ if (GetString(xTmp, "title", strTmp))
+ recording.strTitle = strTmp;
+
+ CStdString strTmp2;
+ GetString(xTmp, "desc", strTmp);
+ GetString(xTmp, "info", strTmp2);
+ if (strTmp.size() > strTmp2.size())
+ recording.strPlot = strTmp;
+ else
+ recording.strPlot = strTmp2;
+
+ if (GetString(xTmp, "channel", strTmp))
+ recording.strChannelName = strTmp;
+
+ strTmp.Format("%supnp/recordings/%d.ts", m_strURLRecording.c_str(), atoi(recording.strRecordingId.c_str()));
+ recording.strStreamURL = strTmp;
+
+ recording.startTime = ParseDateTime(xNode.getChildNode("recording", i).getAttribute("start"));
+
+ int hours, mins, secs;
+ sscanf(xNode.getChildNode("recording", i).getAttribute("duration"), "%02d%02d%02d", &hours, &mins, &secs);
+ recording.iDuration = hours*60*60 + mins*60 + secs;
+
+ bool bGetThumbnails = true;
+ if ((iGetRecordingsCount == 0) && (j > MAX_RECORDING_THUMBS - 1))
+ bGetThumbnails = false;
+
+ for (unsigned int i=0; i<recthumbs.size(); i++)
+ {
+ if ((recthumbs[i].strRecordingId == recording.strRecordingId) && (recthumbs[i].strThumbnailPath.size() > 20) && (recthumbs[i].strThumbnailPath.size() < 100))
+ {
+ recording.strThumbnailPath = recthumbs[i].strThumbnailPath;
+ bGetThumbnails = false;
+ break;
+ }
+ }
+
+ if (bGetThumbnails)
+ {
+ url.Format("%sepg_details.html?aktion=epg_details&recID=%s", m_strURL.c_str(), recording.strRecordingId.c_str());
+ CStdString strThumb;
+ strThumb = GetHttpXML(url);
+
+ unsigned int iThumbnailPos;
+ iThumbnailPos = strThumb.find_first_of('_', RECORDING_THUMB_POS);
+ strTmp.Format("%sthumbnails/video/%s_SM.jpg", m_strURL.c_str(), strThumb.substr(RECORDING_THUMB_POS, iThumbnailPos - RECORDING_THUMB_POS).c_str());
+ recording.strThumbnailPath = strTmp;
+ }
+
+ PVR_RECORDING tag;
+ memset(&tag, 0, sizeof(PVR_RECORDING));
+ strncpy(tag.strRecordingId, recording.strRecordingId.c_str(), sizeof(tag.strRecordingId));
+ strncpy(tag.strTitle, recording.strTitle.c_str(), sizeof(tag.strTitle));
+ strncpy(tag.strStreamURL, recording.strStreamURL.c_str(), sizeof(tag.strStreamURL));
+ strncpy(tag.strPlotOutline, recording.strPlotOutline.c_str(), sizeof(tag.strPlotOutline));
+ strncpy(tag.strPlot, recording.strPlot.c_str(), sizeof(tag.strPlot));
+ strncpy(tag.strChannelName, recording.strChannelName.c_str(), sizeof(tag.strChannelName));
+ strncpy(tag.strThumbnailPath, recording.strThumbnailPath.c_str(), sizeof(tag.strThumbnailPath));
+ tag.recordingTime = recording.startTime;
+ tag.iDuration = recording.iDuration;
+ strncpy(tag.strDirectory, "/", sizeof(tag.strDirectory)); // unused
+
+ PVR->TransferRecordingEntry(handle, &tag);
+
+ m_iNumRecordings++;
+ iNumRecording++;
+ m_recordings.push_back(recording);
+
+ XBMC->Log(LOG_INFO, "%s loaded Recording entry '%s', start '%d', length '%d'", __FUNCTION__, tag.strTitle, recording.startTime, recording.iDuration);
+ }
+ iGetRecordingsCount++;
+
+ XBMC->Log(LOG_INFO, "%s Loaded %u Recording Entries", __FUNCTION__, iNumRecording);
+
+ return PVR_ERROR_NO_ERROR;
+}
+
+PVR_ERROR Dvb::DeleteRecording(const PVR_RECORDING &recinfo)
+{
+ CStdString strTmp;
+ strTmp.Format("rec_list.html?aktion=delete_rec&recid=%s", recinfo.strRecordingId);
+ SendSimpleCommand(strTmp);
+
+ PVR->TriggerRecordingUpdate();
+
+ return PVR_ERROR_NO_ERROR;
+}
+
+void Dvb::SendSimpleCommand(const CStdString& strCommandURL)
+{
+ CStdString url;
+ url.Format("%s%s", m_strURL.c_str(), strCommandURL.c_str());
+ GetHttpXML(url);
+}
+
+CStdString Dvb::URLEncodeInline(const CStdString& strData)
+{
+ /* Copied from xbmc/URL.cpp */
+
+ CStdString strResult;
+
+ /* wonder what a good value is here is, depends on how often it occurs */
+ strResult.reserve( strData.length() * 2 );
+
+ for (int i = 0; i < (int)strData.size(); ++i)
+ {
+ int kar = (unsigned char)strData[i];
+ //if (kar == ' ') strResult += '+'; // obsolete
+ if (isalnum(kar) || strchr("-_.!()" , kar) ) // Don't URL encode these according to RFC1738
+ {
+ strResult += kar;
+ }
+ else
+ {
+ CStdString strTmp;
+ strTmp.Format("%%%02.2x", kar);
+ strResult += strTmp;
+ }
+ }
+ return strResult;
+}
+
+bool Dvb::GetInt(XMLNode xRootNode, const char* strTag, int& iIntValue)
+{
+ XMLNode xNode = xRootNode.getChildNode(strTag );
+ if (xNode.isEmpty())
+ return false;
+ iIntValue = atoi(xNode.getText());
+ return true;
+}
+
+bool Dvb::GetBoolean(XMLNode xRootNode, const char* strTag, bool& bBoolValue)
+{
+ XMLNode xNode = xRootNode.getChildNode(strTag );
+ if (xNode.isEmpty())
+ return false;
+
+ CStdString strEnabled = xNode.getText();
+
+ strEnabled.ToLower();
+ if (strEnabled == "off" || strEnabled == "no" || strEnabled == "disabled" || strEnabled == "false" || strEnabled == "0" )
+ bBoolValue = false;
+ else
+ {
+ bBoolValue = true;
+ if (strEnabled != "on" && strEnabled != "yes" && strEnabled != "enabled" && strEnabled != "true")
+ return false; // invalid bool switch - it's probably some other string.
+ }
+ return true;
+}
+
+bool Dvb::GetString(XMLNode xRootNode, const char* strTag, CStdString& strStringValue)
+{
+ XMLNode xNode = xRootNode.getChildNode(strTag );
+ if (!xNode.isEmpty())
+ {
+ strStringValue = xNode.getText();
+ return true;
+ }
+ strStringValue.Empty();
+ return false;
+}
+
+bool Dvb::GetStringLng(XMLNode xRootNode, const char* strTag, CStdString& strStringValue)
+{
+ XMLNode xNode;
+ bool found = false;
+ int n = xRootNode.nChildNode(strTag);
+
+ if (n > 1)
+ {
+ for (int i = 0; i<n; i++)
+ {
+ xNode = xRootNode.getChildNode(strTag, i);
+ CStdString strTmp;
+ strTmp = xNode.getAttribute("lng");
+ if (strTmp == m_strEPGLanguage)
+ {
+ found = true;
+ break;
+ }
+ }
+ }
+
+ if (!found) xNode = xRootNode.getChildNode(strTag);
+ if (!xNode.isEmpty())
+ {
+ strStringValue = xNode.getText();
+ return true;
+ }
+ strStringValue.Empty();
+ return false;
+}
+
+CStdString Dvb::GetPreferredLanguage()
+{
+ CStdString strXML, url;
+ url.Format("%s%s", m_strURL.c_str(), "config.html?aktion=config");
+ strXML = GetHttpXML(url);
+ unsigned int iLanguagePos = strXML.find("EPGLanguage");
+ iLanguagePos = strXML.find(" selected>", iLanguagePos);
+ return (strXML.substr(iLanguagePos-4, 3));
+}
+
+void Dvb::RemoveNullChars(CStdString &String)
+{
+ /* favourites.xml and timers.xml sometimes have null chars that screw the xml */
+ for (unsigned int i = 0; i<String.size()-1; i++)
+ {
+ if (String.data()[i] == '\0')
+ {
+ String.erase(i, 1);
+ i--;
+ }
+ }
+}
+
+#ifdef WIN32
+void Dvb::AnsiToUtf8(std::string &String)
+{
+ const int iLenW = MultiByteToWideChar(CP_ACP, 0, String.c_str(), -1, 0, 0);
+ wchar_t *wcTmp = new wchar_t[iLenW + 1];
+ memset(wcTmp, 0, sizeof(wchar_t)*(iLenW + 1));
+ if (MultiByteToWideChar(CP_ACP, 0, String.c_str(), -1, wcTmp, iLenW + 1))
+ {
+ const int iLenU = WideCharToMultiByte(CP_UTF8, 0, wcTmp, -1, NULL, 0, 0, FALSE);
+ char *cTmp = new char[iLenU + 1];
+ memset(cTmp, 0, sizeof(char)*(iLenU + 1));
+ WideCharToMultiByte(CP_UTF8, 0, wcTmp, -1, cTmp, iLenU + 1, 0, FALSE);
+ String = cTmp;
+ delete[] cTmp;
+ }
+ delete[] wcTmp;
+}
+#endif
+
+PVR_ERROR Dvb::GetChannelGroups(ADDON_HANDLE handle)
+{
+ for(unsigned int iTagPtr = 0; iTagPtr < m_groups.size(); iTagPtr++)
+ {
+ PVR_CHANNEL_GROUP tag;
+ memset(&tag, 0 , sizeof(PVR_CHANNEL_GROUP));
+
+ tag.bIsRadio = false;
+ strncpy(tag.strGroupName, m_groups[iTagPtr].strGroupName.c_str(), sizeof(tag.strGroupName));
+
+ PVR->TransferChannelGroup(handle, &tag);
+ }
+
+ return PVR_ERROR_NO_ERROR;
+}
+
+
+unsigned int Dvb::GetNumChannelGroups() {
+ return m_iNumChannelGroups;
+}
+
+PVR_ERROR Dvb::GetChannelGroupMembers(ADDON_HANDLE handle, const PVR_CHANNEL_GROUP &group)
+{
+ XBMC->Log(LOG_DEBUG, "%s - group '%s'", __FUNCTION__, group.strGroupName);
+ CStdString strTmp = group.strGroupName;
+ for (unsigned int i = 0;i<m_channels.size(); i++)
+ {
+ DvbChannel &myChannel = m_channels.at(i);
+ if (!strTmp.compare(myChannel.strGroupName))
+ {
+ PVR_CHANNEL_GROUP_MEMBER tag;
+ memset(&tag,0 , sizeof(PVR_CHANNEL_GROUP_MEMBER));
+
+ strncpy(tag.strGroupName, group.strGroupName, sizeof(tag.strGroupName));
+ tag.iChannelUniqueId = myChannel.iUniqueId;
+ tag.iChannelNumber = myChannel.iChannelNumber;
+
+ XBMC->Log(LOG_DEBUG, "%s - add channel %s (%d) to group '%s' channel number %d",
+ __FUNCTION__, myChannel.strChannelName.c_str(), tag.iChannelUniqueId, group.strGroupName, myChannel.iChannelNumber);
+
+ PVR->TransferChannelGroupMember(handle, &tag);
+ }
+ }
+ return PVR_ERROR_NO_ERROR;
+}
+
+int Dvb::GetCurrentClientChannel(void)
+{
+ return m_iCurrentChannel;
+}
+
+const char* Dvb::GetLiveStreamURL(const PVR_CHANNEL &channelinfo)
+{
+ SwitchChannel(channelinfo);
+
+ return m_channels.at(channelinfo.iUniqueId-1).strStreamURL.c_str();
+}
+
+bool Dvb::OpenLiveStream(const PVR_CHANNEL &channelinfo)
+{
+ XBMC->Log(LOG_INFO, "%s channel '%u'", __FUNCTION__, channelinfo.iUniqueId);
+
+ if ((int)channelinfo.iUniqueId == m_iCurrentChannel)
+ return true;
+
+ return SwitchChannel(channelinfo);
+}
+
+void Dvb::CloseLiveStream(void)
+{
+ m_iCurrentChannel = -1;
+}
+
+bool Dvb::SwitchChannel(const PVR_CHANNEL &channel)
+{
+ m_iCurrentChannel = channel.iUniqueId;
+ m_bUpdateEPG = true;
+ return true;
+}
+
+bool Dvb::GetDeviceInfo()
+{
+ CStdString url;
+ url.Format("%s%s", m_strURL.c_str(), "api/version.html");
+
+ CStdString strXML;
+ strXML = GetHttpXML(url);
+
+ XMLResults xe;
+ XMLNode xMainNode = XMLNode::parseString(strXML.c_str(), NULL, &xe);
+
+ if(xe.error != 0)
+ {
+ XBMC->Log(LOG_ERROR, "%s Can't connect to the Recording Service", __FUNCTION__);
+ XBMC->QueueNotification(QUEUE_ERROR, XBMC->GetLocalizedString(30500));
+ Sleep(10000);
+ return false;
+ }
+
+ XMLNode xNode = xMainNode.getChildNode("version");
+
+ CStdString strTmp;;
+
+ XBMC->Log(LOG_NOTICE, "%s - DeviceInfo", __FUNCTION__);
+
+ // Get Version
+ if (!xNode.getText()) {
+ XBMC->Log(LOG_ERROR, "%s Could not parse version from result!", __FUNCTION__);
+ return false;
+ }
+ m_strDVBViewerVersion = xNode.getText();
+ CStdString strVersion = m_strDVBViewerVersion.substr(30, 2);
+ if (atoi(strVersion) < RS_MIN_VERSION)
+ {
+ XBMC->Log(LOG_ERROR, "%s - Recording Service version 1.%d or higher required", __FUNCTION__, RS_MIN_VERSION);
+ XBMC->QueueNotification(QUEUE_ERROR, XBMC->GetLocalizedString(30501), RS_MIN_VERSION);
+ Sleep(10000);
+ return false;
+ }
+ XBMC->Log(LOG_NOTICE, "%s - Version: %s", __FUNCTION__, m_strDVBViewerVersion.c_str());
+
+ return true;
+}
+
+PVR_ERROR Dvb::SignalStatus(PVR_SIGNAL_STATUS &signalStatus)
+{
+ CStdString strXML, url;
+ url.Format("%s%s", m_strURL.c_str(), "status.html?aktion=status");
+ strXML = GetHttpXML(url);
+ unsigned int iSignalStartPos, iSignalEndPos;
+ iSignalEndPos = strXML.find("%</th>");
+
+ unsigned int iAdapterStartPos, iAdapterEndPos;
+ iAdapterStartPos = strXML.rfind("3\">", iSignalEndPos) + 3;
+ iAdapterEndPos = strXML.find("<", iAdapterStartPos);
+ strncpy(signalStatus.strAdapterName, strXML.substr(iAdapterStartPos, iAdapterEndPos - iAdapterStartPos).c_str(), sizeof(signalStatus.strAdapterName));
+
+ iSignalStartPos = strXML.find_last_of(">", iSignalEndPos) + 1;
+ if (iSignalEndPos < strXML.size())
+ signalStatus.iSignal = (int)(atoi(strXML.substr(iSignalStartPos, iSignalEndPos - iSignalStartPos).c_str()) * 655.35);
+ strncpy(signalStatus.strAdapterStatus, "OK", sizeof(signalStatus.strAdapterStatus));
+
+ return PVR_ERROR_NO_ERROR;
+}
View
286 addons/pvr.dvbviewer/src/DvbData.h
@@ -0,0 +1,286 @@
+#pragma once
+
+#include "platform/util/StdString.h"
+#include "xmlParser.h"
+#include "client.h"
+#include "platform/threads/threads.h"
+
+#define CHANNELDAT_HEADER_SIZE (7)
+#define ENCRYPTED_FLAG (1 << 0)
+#define VIDEO_FLAG (1 << 3)
+#define ADDITIONAL_AUDIO_TRACK_FLAG (1 << 7)
+#define DAY_MINS (24 * 60)
+#define DAY_SECS (24 * 60 * 60)
+#define DELPHI_DATE (25569)
+#define RECORDING_THUMB_POS (143)
+#define MAX_RECORDING_THUMBS (20)
+#define RS_MIN_VERSION (21)
+
+struct ChannelsDat
+{
+ byte TunerType;
+ byte ChannelGroup;
+ byte SatModulationSystem;
+ byte Flags;
+ unsigned int Frequency;
+ unsigned int Simbolrate;
+ unsigned short LNB_LOF;
+ unsigned short PMT_PID;
+ unsigned short Reserved1;
+ byte SatModulation;
+ byte AVFormat;
+ byte FEC;
+ byte Reserved2;
+ unsigned short Reserved3;
+ byte Polarity;
+ byte Reserved4;
+ unsigned short Reserved5;
+ byte Tone;
+ byte Reserved6;
+ unsigned short DiSEqCExt;
+ byte DiSEqC;
+ byte Reserved7;
+ unsigned short Reserved8;
+ unsigned short Audio_PID;
+ unsigned short Reserved9;
+ unsigned short Video_PID;
+ unsigned short TransportStream_ID;
+ unsigned short Teletext_PID;
+ unsigned short OriginalNetwork_ID;
+ unsigned short Service_ID;
+ unsigned short PCR_PID;
+ byte Root_len;
+ char Root[25];
+ byte ChannelName_len;
+ char ChannelName[25];
+ byte Category_len;
+ char Category[25];
+ byte Encrypted;
+ byte Reserved10;
+};
+
+typedef enum DVB_UPDATE_STATE
+{
+ DVB_UPDATE_STATE_NONE,
+ DVB_UPDATE_STATE_FOUND,
+ DVB_UPDATE_STATE_UPDATED,
+ DVB_UPDATE_STATE_NEW
+} DVB_UPDATE_STATE;
+
+struct DvbChannelGroup {
+ std::string strGroupName;
+ int iGroupState;
+
+ DvbChannelGroup()
+ {
+ iGroupState = DVB_UPDATE_STATE_NEW;
+ }
+
+ bool operator==(const DvbChannelGroup &right) const
+ {
+ return (! strGroupName.compare(right.strGroupName));
+ }
+
+};
+
+struct DvbChannel
+{
+ bool bRadio;
+ int iUniqueId;
+ int iChannelNumber;
+ int iChannelId;
+ uint64_t llEpgId;
+ byte Encrypted;
+ std::string strGroupName;
+ std::string strChannelName;
+ std::string strStreamURL;
+ std::string strIconPath;
+ int iChannelState;
+
+ DvbChannel()
+ {
+ iChannelState = DVB_UPDATE_STATE_NEW;
+ }
+
+ bool operator==(const DvbChannel &right) const
+ {
+ bool bChanged = true;
+ bChanged = bChanged && (bRadio == right.bRadio);
+ bChanged = bChanged && (iUniqueId == right.iUniqueId);
+ bChanged = bChanged && (iChannelNumber == right.iChannelNumber);
+ bChanged = bChanged && (! strGroupName.compare(right.strGroupName));
+ bChanged = bChanged && (! strChannelName.compare(right.strChannelName));
+ bChanged = bChanged && (! strStreamURL.compare(right.strStreamURL));
+ bChanged = bChanged && (! strIconPath.compare(right.strIconPath));
+
+ return bChanged;
+ }
+
+};
+
+struct DvbEPGEntry
+{
+ int iEventId;
+ std::string strTitle;
+ int iChannelId;
+ time_t startTime;
+ time_t endTime;
+ std::string strPlotOutline;
+ std::string strPlot;
+};
+
+struct DvbTimer
+{
+ std::string strTitle;
+ std::string strPlot;
+ int iChannelId;
+ time_t startTime;
+ time_t endTime;
+ bool bRepeating;
+ int iWeekdays;
+ int iEpgID;
+ int iTimerID;
+ int iPriority;
+ int iFirstDay;
+ PVR_TIMER_STATE state;
+ int iUpdateState;
+ unsigned int iClientIndex;
+
+ DvbTimer()
+ {
+ iUpdateState = DVB_UPDATE_STATE_NEW;
+ }
+
+ bool like(const DvbTimer &right) const
+ {
+ bool bChanged = true;
+ bChanged = bChanged && (startTime == right.startTime);
+ bChanged = bChanged && (endTime == right.endTime);
+ bChanged = bChanged && (iChannelId == right.iChannelId);
+ bChanged = bChanged && (bRepeating == right.bRepeating);
+ bChanged = bChanged && (iWeekdays == right.iWeekdays);
+ bChanged = bChanged && (iEpgID == right.iEpgID);
+
+ return bChanged;
+ }
+
+ bool operator==(const DvbTimer &right) const
+ {
+ bool bChanged = true;
+ bChanged = bChanged && (startTime == right.startTime);
+ bChanged = bChanged && (endTime == right.endTime);
+ bChanged = bChanged && (iChannelId == right.iChannelId);
+ bChanged = bChanged && (bRepeating == right.bRepeating);
+ bChanged = bChanged && (iWeekdays == right.iWeekdays);
+ bChanged = bChanged && (iEpgID == right.iEpgID);
+ bChanged = bChanged && (state == right.state);
+ bChanged = bChanged && (! strTitle.compare(right.strTitle));
+ bChanged = bChanged && (! strPlot.compare(right.strPlot));
+
+ return bChanged;
+ }
+};
+
+struct DvbRecording
+{
+ std::string strRecordingId;
+ time_t startTime;
+ int iDuration;
+ std::string strTitle;
+ std::string strStreamURL;
+ std::string strPlot;
+ std::string strPlotOutline;
+ std::string strChannelName;
+ std::string strThumbnailPath;
+};
+
+class Dvb : public PLATFORM::CThread
+{
+private:
+
+ // members
+ std::string m_strDVBViewerVersion;
+ bool m_bIsConnected;
+ std::string m_strServerName;
+ std::string m_strURL;
+ std::string m_strURLStream;
+ std::string m_strURLRecording;
+ std::string m_strEPGLanguage;
+ int m_iTimezone;
+ int m_iNumRecordings;
+ int m_iNumChannelGroups;
+ int m_iCurrentChannel;
+ unsigned int m_iUpdateTimer;
+ bool m_bUpdateTimers;
+ bool m_bUpdateEPG;
+ std::vector<DvbChannel> m_channels;
+ std::vector<DvbTimer> m_timers;
+ std::vector<DvbRecording> m_recordings;
+ std::vector<DvbChannelGroup> m_groups;
+ std::vector<std::string> m_locations;
+
+ unsigned int m_iClientIndexCounter;
+
+ PLATFORM::CMutex m_mutex;
+ PLATFORM::CCondition<bool> m_started;
+
+
+ // functions
+
+ CStdString GetHttpXML(CStdString& url);
+ int GetChannelNumber(CStdString strChannelId);
+ CStdString URLEncodeInline(const CStdString& strData);
+ void SendSimpleCommand(const CStdString& strCommandURL);
+ bool LoadChannels();
+ std::vector<DvbTimer> LoadTimers();
+ void TimerUpdates();
+ void GenerateTimer(const PVR_TIMER &timer, bool bNewtimer = true);
+ int GetTimerID(const PVR_TIMER &timer);
+
+ // helper functions
+ static bool GetInt(XMLNode xRootNode, const char* strTag, int& iIntValue);
+ static bool GetBoolean(XMLNode xRootNode, const char* strTag, bool& bBoolValue);
+ static bool GetString(XMLNode xRootNode, const char* strTag, CStdString& strStringValue);
+ bool GetStringLng(XMLNode xRootNode, const char* strTag, CStdString& strStringValue);
+ CStdString GetPreferredLanguage();
+ void RemoveNullChars(CStdString &String);
+#ifdef WIN32
+ void AnsiToUtf8(std::string &String);
+#endif
+ bool GetDeviceInfo();
+
+
+protected:
+ virtual void *Process(void);
+
+public:
+ Dvb(void);
+ ~Dvb();
+
+ const char * GetServerName();
+ bool IsConnected();
+ int GetChannelsAmount(void);
+ PVR_ERROR GetChannels(ADDON_HANDLE handle, bool bRadio);
+ int ParseDateTime(CStdString strDate, bool iDateFormat = true);
+ PVR_ERROR GetEPGForChannel(ADDON_HANDLE handle, const PVR_CHANNEL &channel, time_t iStart, time_t iEnd);
+ int GetCurrentClientChannel(void);
+ int GetTimersAmount(void);
+ PVR_ERROR GetTimers(ADDON_HANDLE handle);
+ PVR_ERROR AddTimer(const PVR_TIMER &timer);
+ PVR_ERROR UpdateTimer(const PVR_TIMER &timer);
+ PVR_ERROR DeleteTimer(const PVR_TIMER &timer);
+ unsigned int GetRecordingsAmount();
+ PVR_ERROR GetRecordings(ADDON_HANDLE handle);
+ PVR_ERROR DeleteRecording(const PVR_RECORDING &recinfo);
+ unsigned int GetNumChannelGroups(void);
+ PVR_ERROR GetChannelGroups(ADDON_HANDLE handle);
+ PVR_ERROR GetChannelGroupMembers(ADDON_HANDLE handle, const PVR_CHANNEL_GROUP &group);
+ PVR_ERROR SignalStatus(PVR_SIGNAL_STATUS &signalStatus);
+ const char* GetLiveStreamURL(const PVR_CHANNEL &channelinfo);
+ bool OpenLiveStream(const PVR_CHANNEL &channelinfo);
+ void CloseLiveStream();
+ bool SwitchChannel(const PVR_CHANNEL &channel);
+ bool Open();
+ void Action();
+};
+
View
508 addons/pvr.dvbviewer/src/client.cpp
@@ -0,0 +1,508 @@
+/*
+ * Copyright (C) 2005-2011 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 <stdlib.h>
+#include "DvbData.h"
+#include "platform/util/util.h"
+
+using namespace std;
+using namespace ADDON;
+
+bool m_bCreated = false;
+ADDON_STATUS m_CurStatus = ADDON_STATUS_UNKNOWN;
+//int g_iClientId = -1;
+
+/* User adjustable settings are saved here.
+ * Default values are defined inside client.h
+ * and exported to the other source files.
+ */
+std::string g_strHostname = DEFAULT_HOST;
+int g_iConnectTimeout = DEFAULT_CONNECT_TIMEOUT;
+int g_iPortWeb = DEFAULT_WEB_PORT;
+int g_iPortStream = DEFAULT_STREAM_PORT;
+int g_iPortRecording = DEFAULT_RECORDING_PORT;
+std::string g_strUsername = "";
+std::string g_strPassword = "";
+bool g_bUseFavourites = false;
+std::string g_strFavouritesPath = "";
+
+CHelper_libXBMC_addon *XBMC = NULL;
+CHelper_libXBMC_pvr *PVR = NULL;
+Dvb *DvbData = NULL;
+
+extern "C" {
+
+void ADDON_ReadSettings(void)
+{
+ /* 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_strHostname = buffer;
+ else
+ g_strHostname = DEFAULT_HOST;
+ buffer[0] = 0; /* Set the end of string */
+
+ /* read setting "user" from settings.xml */
+ if (XBMC->GetSetting("user", buffer))
+ g_strUsername = buffer;
+ else
+ g_strUsername = "";
+ buffer[0] = 0; /* Set the end of string */
+
+ /* read setting "pass" from settings.xml */
+ if (XBMC->GetSetting("pass", buffer))
+ g_strPassword = buffer;
+ else
+ g_strPassword = "";
+
+ /* read setting "streamport" from settings.xml */
+ if (!XBMC->GetSetting("streamport", &g_iPortStream))
+ g_iPortStream = DEFAULT_STREAM_PORT;
+
+ /* read setting "webport" from settings.xml */
+ if (!XBMC->GetSetting("webport", &g_iPortWeb))
+ g_iPortWeb = DEFAULT_WEB_PORT;
+
+ /* read setting "recordingport" from settings.xml */
+ if (!XBMC->GetSetting("recordingport", &g_iPortRecording))
+ g_iPortRecording = DEFAULT_RECORDING_PORT;
+
+ /* read setting "usefavourites" from settings.xml */
+ if (!XBMC->GetSetting("usefavourites", &g_bUseFavourites))
+ g_bUseFavourites = false;
+
+ /* read setting "favouritespath" from settings.xml */
+ if (XBMC->GetSetting("favouritespath", buffer))
+ g_strFavouritesPath = buffer;
+ else
+ g_strFavouritesPath = "";
+
+ free (buffer);
+}
+
+ADDON_STATUS ADDON_Create(void* hdl, void* props)
+{
+ if (!hdl || !props)
+ return ADDON_STATUS_UNKNOWN;
+
+ PVR_PROPERTIES* pvrprops = (PVR_PROPERTIES*)props;
+
+ XBMC = new CHelper_libXBMC_addon;
+ if (!XBMC->RegisterMe(hdl))
+ {
+ SAFE_DELETE(XBMC);
+ return ADDON_STATUS_PERMANENT_FAILURE;
+ }
+
+ PVR = new CHelper_libXBMC_pvr;
+ if (!PVR->RegisterMe(hdl))
+ {
+ SAFE_DELETE(PVR);
+ SAFE_DELETE(XBMC);
+ return ADDON_STATUS_PERMANENT_FAILURE;
+ }
+
+ XBMC->Log(LOG_DEBUG, "%s - Creating DVBViewer PVR-Client", __FUNCTION__);
+
+ m_CurStatus = ADDON_STATUS_UNKNOWN;
+ //g_iClientId = pvrprops->iClientId; //removed from Frodo PVR API
+
+ ADDON_ReadSettings();
+
+ DvbData = new Dvb;
+ if (!DvbData->Open())
+ {
+ SAFE_DELETE(DvbData);
+ SAFE_DELETE(PVR);
+ SAFE_DELETE(XBMC);
+ m_CurStatus = ADDON_STATUS_LOST_CONNECTION;
+ return m_CurStatus;
+ }
+
+ m_CurStatus = ADDON_STATUS_OK;
+ m_bCreated = true;
+ return m_CurStatus;
+}
+
+ADDON_STATUS ADDON_GetStatus()
+{
+ /* check whether we're still connected */
+ if (m_CurStatus == ADDON_STATUS_OK && !DvbData->IsConnected())
+ m_CurStatus = ADDON_STATUS_LOST_CONNECTION;
+
+ return m_CurStatus;
+}
+
+void ADDON_Destroy()
+{
+ SAFE_DELETE(DvbData);
+ SAFE_DELETE(PVR);
+ SAFE_DELETE(XBMC);
+
+ m_CurStatus = ADDON_STATUS_UNKNOWN;
+}
+
+bool ADDON_HasSettings()
+{
+ return true;
+}
+
+unsigned int ADDON_GetSettings(ADDON_StructSetting ***sSet)
+{
+ return 0;
+}
+
+ADDON_STATUS ADDON_SetSetting(const char *settingName, const void *settingValue)
+{
+ string str = settingName;
+ if (str == "host")
+ {
+ string tmp_sHostname;
+ XBMC->Log(LOG_INFO, "%s - Changed Setting 'host' from %s to %s", __FUNCTION__, g_strHostname.c_str(), (const char*) settingValue);
+ tmp_sHostname = g_strHostname;
+ g_strHostname = (const char*) settingValue;
+ if (tmp_sHostname != g_strHostname)
+ return ADDON_STATUS_NEED_RESTART;
+ }
+ else if (str == "user")
+ {
+ string tmp_sUsername = g_strUsername;
+ g_strUsername = (const char*) settingValue;
+ if (tmp_sUsername != g_strUsername)
+ {
+ XBMC->Log(LOG_INFO, "%s - Changed Setting 'user'", __FUNCTION__);
+ return ADDON_STATUS_NEED_RESTART;
+ }
+ }
+ else if (str == "pass")
+ {
+ string tmp_sPassword = g_strPassword;
+ g_strPassword = (const char*) settingValue;
+ if (tmp_sPassword != g_strPassword)
+ {
+ XBMC->Log(LOG_INFO, "%s - Changed Setting 'pass'", __FUNCTION__);
+ return ADDON_STATUS_NEED_RESTART;
+ }
+ }
+ else if (str == "streamport")
+ {
+ int iNewValue = *(int*) settingValue + 1;
+ if (g_iPortStream != iNewValue)
+ {
+ XBMC->Log(LOG_INFO, "%s - Changed Setting 'streamport' from %u to %u", __FUNCTION__, g_iPortStream, iNewValue);
+ g_iPortStream = iNewValue;
+ return ADDON_STATUS_NEED_RESTART;
+ }
+ }
+ else if (str == "webport")
+ {
+ int iNewValue = *(int*) settingValue + 1;
+ if (g_iPortWeb != iNewValue)
+ {
+ XBMC->Log(LOG_INFO, "%s - Changed Setting 'webport' from %u to %u", __FUNCTION__, g_iPortWeb, iNewValue);
+ g_iPortWeb = iNewValue;
+ return ADDON_STATUS_NEED_RESTART;
+ }
+ }
+ else if (str == "recordingport")
+ {
+ int iNewValue = *(int*) settingValue + 1;
+ if (g_iPortRecording != iNewValue)
+ {
+ XBMC->Log(LOG_INFO, "%s - Changed Setting 'recordingport' from %u to %u", __FUNCTION__, g_iPortRecording, iNewValue);
+ g_iPortRecording = iNewValue;
+ return ADDON_STATUS_NEED_RESTART;
+ }
+ }
+ return ADDON_STATUS_OK;
+}
+
+void ADDON_Stop()
+{
+}
+
+void ADDON_FreeSettings()
+{
+}
+
+/***********************************************************
+ * PVR Client AddOn specific public library functions
+ ***********************************************************/
+
+const char* GetPVRAPIVersion(void)
+{
+ static const char *strApiVersion = XBMC_PVR_API_VERSION;
+ return strApiVersion;
+}
+
+const char* GetMininumPVRAPIVersion(void)
+{
+ static const char *strMinApiVersion = XBMC_PVR_MIN_API_VERSION;
+ return strMinApiVersion;
+}
+
+PVR_ERROR GetAddonCapabilities(PVR_ADDON_CAPABILITIES* pCapabilities)
+{
+ //pCapabilities->bSupportsChannelSettings = false;
+ //pCapabilities->bSupportsTimeshift = false;
+ pCapabilities->bSupportsEPG = true;
+ pCapabilities->bSupportsTV = true;
+ pCapabilities->bSupportsRadio = true;
+ pCapabilities->bSupportsRecordings = true;
+ pCapabilities->bSupportsTimers = true;
+ pCapabilities->bSupportsChannelGroups = true;
+ pCapabilities->bSupportsChannelScan = false;
+ pCapabilities->bHandlesInputStream = true;
+ pCapabilities->bHandlesDemuxing = false;
+ pCapabilities->bSupportsLastPlayedPosition = false;
+
+ return PVR_ERROR_NO_ERROR;
+}
+
+const char *GetBackendName(void)
+{
+ static const char *strBackendName = DvbData ? DvbData->GetServerName() : "unknown";
+ return strBackendName;
+}
+
+const char *GetBackendVersion(void)
+{
+ static const char *strBackendVersion = "UNKNOWN";
+ return strBackendVersion;
+}
+
+static CStdString strConnectionString;
+
+const char *GetConnectionString(void)
+{
+ if (DvbData)
+ strConnectionString.Format("%s%s", g_strHostname.c_str(), DvbData->IsConnected() ? "" : " (Not connected!)");
+ else
+ strConnectionString.Format("%s (addon error!)", g_strHostname.c_str());
+ return strConnectionString.c_str();
+}
+
+PVR_ERROR GetDriveSpace(long long *iTotal, long long *iUsed)
+{
+ return PVR_ERROR_SERVER_ERROR;
+}
+
+PVR_ERROR GetEPGForChannel(ADDON_HANDLE handle, const PVR_CHANNEL &channel, time_t iStart, time_t iEnd)
+{
+ if (!DvbData || !DvbData->IsConnected())
+ return PVR_ERROR_SERVER_ERROR;
+
+ return DvbData->GetEPGForChannel(handle, channel, iStart, iEnd);
+}
+
+int GetChannelsAmount(void)
+{
+ if (!DvbData || !DvbData->IsConnected())
+ return 0;
+
+ return DvbData->GetChannelsAmount();
+}
+
+PVR_ERROR GetChannels(ADDON_HANDLE handle, bool bRadio)
+{
+ if (!DvbData || !DvbData->IsConnected())
+ return PVR_ERROR_SERVER_ERROR;
+
+ return DvbData->GetChannels(handle, bRadio);
+}
+
+int GetRecordingsAmount(void)
+{
+ if (!DvbData || !DvbData->IsConnected())
+ return PVR_ERROR_SERVER_ERROR;
+
+ return DvbData->GetRecordingsAmount();
+}
+
+PVR_ERROR GetRecordings(ADDON_HANDLE handle)
+{
+ if (!DvbData || !DvbData->IsConnected())
+ return PVR_ERROR_SERVER_ERROR;
+
+ return DvbData->GetRecordings(handle);
+}
+
+PVR_ERROR DeleteRecording(const PVR_RECORDING &recording)
+{
+ if (!DvbData || !DvbData->IsConnected())
+ return PVR_ERROR_SERVER_ERROR;
+
+ return DvbData->DeleteRecording(recording);
+}
+
+PVR_ERROR RenameRecording(const PVR_RECORDING &recording)
+{
+ return PVR_ERROR_NOT_IMPLEMENTED;
+}
+
+int GetTimersAmount(void)
+{
+ if (!DvbData || !DvbData->IsConnected())
+ return 0;
+
+ return DvbData->GetTimersAmount();
+}
+
+PVR_ERROR GetTimers(ADDON_HANDLE handle)
+{
+ if (!DvbData || !DvbData->IsConnected())
+ return PVR_ERROR_SERVER_ERROR;
+
+ return DvbData->GetTimers(handle);
+}
+
+PVR_ERROR AddTimer(const PVR_TIMER &timer)
+{
+ if (!DvbData || !DvbData->IsConnected())
+ return PVR_ERROR_SERVER_ERROR;
+
+ return DvbData->AddTimer(timer);
+}
+
+PVR_ERROR DeleteTimer(const PVR_TIMER &timer, bool bForceDelete)
+{
+ if (!DvbData || !DvbData->IsConnected())
+ return PVR_ERROR_SERVER_ERROR;
+
+ return DvbData->DeleteTimer(timer);
+}
+
+PVR_ERROR UpdateTimer(const PVR_TIMER &timer)
+{
+ if (!DvbData || !DvbData->IsConnected())
+ return PVR_ERROR_SERVER_ERROR;
+
+ return DvbData->UpdateTimer(timer);
+}
+
+int GetCurrentClientChannel(void)
+{
+ if (!DvbData || !DvbData->IsConnected())
+ return PVR_ERROR_SERVER_ERROR;
+
+ return DvbData->GetCurrentClientChannel();
+}
+
+bool SwitchChannel(const PVR_CHANNEL &channel)
+{
+ if (!DvbData || !DvbData->IsConnected())
+ return false;
+
+ return DvbData->SwitchChannel(channel);
+}
+
+int GetChannelGroupsAmount(void)
+{
+ if (!DvbData || !DvbData->IsConnected())
+ return PVR_ERROR_SERVER_ERROR;
+
+ return DvbData->GetNumChannelGroups();
+}
+
+PVR_ERROR GetChannelGroups(ADDON_HANDLE handle, bool bRadio)
+{
+ if (bRadio)
+ return PVR_ERROR_NO_ERROR;
+
+ if (!DvbData || !DvbData->IsConnected())
+ return PVR_ERROR_SERVER_ERROR;
+
+ return DvbData->GetChannelGroups(handle);
+}
+
+PVR_ERROR GetChannelGroupMembers(ADDON_HANDLE handle, const PVR_CHANNEL_GROUP &group)
+{
+ if (group.bIsRadio)
+ return PVR_ERROR_NO_ERROR;
+
+ if (!DvbData || !DvbData->IsConnected())
+ return PVR_ERROR_SERVER_ERROR;
+
+ return DvbData->GetChannelGroupMembers(handle, group);
+}
+
+void CloseLiveStream(void)
+{
+ DvbData->CloseLiveStream();
+};
+
+bool OpenLiveStream(const PVR_CHANNEL &channel)
+{
+ if (!DvbData || !DvbData->IsConnected())
+ return false;
+
+ return DvbData->OpenLiveStream(channel);
+}
+
+const char * GetLiveStreamURL(const PVR_CHANNEL &channel)
+{
+ if (!DvbData || !DvbData->IsConnected())
+ return "";
+
+ return DvbData->GetLiveStreamURL(channel);
+}
+
+PVR_ERROR SignalStatus(PVR_SIGNAL_STATUS &signalStatus)
+{
+ return DvbData->SignalStatus(signalStatus);
+}
+
+/** UNUSED API FUNCTIONS */
+PVR_ERROR GetStreamProperties(PVR_STREAM_PROPERTIES* pProperties) { return PVR_ERROR_NOT_IMPLEMENTED; }
+void DemuxAbort(void) { return; }
+DemuxPacket* DemuxRead(void) { return NULL; }
+PVR_ERROR DialogChannelScan(void) { return PVR_ERROR_NOT_IMPLEMENTED; }
+PVR_ERROR CallMenuHook(const PVR_MENUHOOK &menuhook) { return PVR_ERROR_NOT_IMPLEMENTED; }
+PVR_ERROR DeleteChannel(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; }
+PVR_ERROR RenameChannel(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; }
+PVR_ERROR MoveChannel(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; }
+PVR_ERROR DialogChannelSettings(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; }
+PVR_ERROR DialogAddChannel(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; }
+bool OpenRecordedStream(const PVR_RECORDING &recording) { return false; }
+void CloseRecordedStream(void) {}
+int ReadRecordedStream(unsigned char *pBuffer, unsigned int iBufferSize) { return 0; }
+long long SeekRecordedStream(long long iPosition, int iWhence /* = SEEK_SET */) { return 0; }
+long long PositionRecordedStream(void) { return -1; }
+long long LengthRecordedStream(void) { return 0; }
+void DemuxReset(void) {}
+void DemuxFlush(void) {}
+int ReadLiveStream(unsigned char *pBuffer, unsigned int iBufferSize) { return 0; }
+long long SeekLiveStream(long long iPosition, int iWhence /* = SEEK_SET */) { return -1; }
+long long PositionLiveStream(void) { return -1; }
+long long LengthLiveStream(void) { return -1; }
+PVR_ERROR SetRecordingPlayCount(const PVR_RECORDING &recording, int count) { return PVR_ERROR_NOT_IMPLEMENTED; }
+PVR_ERROR SetRecordingLastPlayedPosition(const PVR_RECORDING &recording, int lastplayedposition) { return PVR_ERROR_NOT_IMPLEMENTED; }
+int GetRecordingLastPlayedPosition(const PVR_RECORDING &recording) { return -1; }
+unsigned int GetChannelSwitchDelay(void) { return 0; }
+void PauseStream(bool bPaused) {}
+bool CanPauseStream(void) { return false; }
+bool CanSeekStream(void) { return false; }
+}
View
43 addons/pvr.dvbviewer/src/client.h
@@ -0,0 +1,43 @@
+#pragma once
+/*
+ * Copyright (C) 2005-2011 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 "libXBMC_addon.h"
+#include "libXBMC_pvr.h"
+
+#define DEFAULT_HOST "127.0.0.1"
+#define DEFAULT_CONNECT_TIMEOUT 30
+#define DEFAULT_WEB_PORT 8089
+#define DEFAULT_STREAM_PORT 7522
+#define DEFAULT_RECORDING_PORT 8090
+
+extern bool m_bCreated;
+extern std::string g_strHostname;
+extern int g_iPortStream;
+extern int g_iPortWeb;
+extern int g_iPortRecording;
+extern std::string g_strUsername;
+extern std::string g_strPassword;
+extern bool g_bUseFavourites;
+extern std::string g_strFavouritesPath;
+//extern int g_iClientId;
+extern ADDON::CHelper_libXBMC_addon * XBMC;
+extern CHelper_libXBMC_pvr * PVR;
View
2,941 addons/pvr.dvbviewer/src/xmlParser.cpp
2,941 additions, 0 deletions not shown
View
733 addons/pvr.dvbviewer/src/xmlParser.h
@@ -0,0 +1,733 @@
+/****************************************************************************/
+/*! \mainpage XMLParser library
+ * \section intro_sec Introduction
+ *
+ * This is a basic XML parser written in ANSI C++ for portability.
+ * It works by using recursion and a node tree for breaking
+ * down the elements of an XML document.
+ *
+ * @version V2.43
+ * @author Frank Vanden Berghen
+ *
+ * Copyright (c) 2002, Business-Insight
+ * <a href="http://www.Business-Insight.com">Business-Insight</a>
+ * All rights reserved.
+ * See the file <a href="../../AFPL-license.txt">AFPL-license.txt</a> about the licensing terms
+ *
+ * \section tutorial First Tutorial
+ * You can follow a simple <a href="../../xmlParser.html">Tutorial</a> to know the basics...
+ *
+ * \section usage General usage: How to include the XMLParser library inside your project.
+ *
+ * The library is composed of two files: <a href="../../xmlParser.cpp">xmlParser.cpp</a> and
+ * <a href="../../xmlParser.h">xmlParser.h</a>. These are the ONLY 2 files that you need when
+ * using the library inside your own projects.
+ *
+ * All the functions of the library are documented inside the comments of the file
+ * <a href="../../xmlParser.h">xmlParser.h</a>. These comments can be transformed in
+ * full-fledged HTML documentation using the DOXYGEN software: simply type: "doxygen doxy.cfg"
+ *
+ * By default, the XMLParser library uses (char*) for string representation.To use the (wchar_t*)
+ * version of the library, you need to define the "_UNICODE" preprocessor definition variable
+ * (this is usually done inside your project definition file) (This is done automatically for you
+ * when using Visual Studio).
+ *
+ * \section example Advanced Tutorial and Many Examples of usage.
+ *
+ * Some very small introductory examples are described inside the Tutorial file
+ * <a href="../../xmlParser.html">xmlParser.html</a>
+ *
+ * Some additional small examples are also inside the file <a href="../../xmlTest.cpp">xmlTest.cpp</a>
+ * (for the "char*" version of the library) and inside the file
+ * <a href="../../xmlTestUnicode.cpp">xmlTestUnicode.cpp</a> (for the "wchar_t*"
+ * version of the library). If you have a question, please review these additionnal examples
+ * before sending an e-mail to the author.
+ *
+ * To build the examples:
+ * - linux/unix: type "make"
+ * - solaris: type "make -f makefile.solaris"
+ * - windows: Visual Studio: double-click on xmlParser.dsw
+ * (under Visual Studio .NET, the .dsp and .dsw files will be automatically converted to .vcproj and .sln files)
+ *
+ * In order to build the examples you need some additional files:
+ * - linux/unix: makefile
+ * - solaris: makefile.solaris
+ * - windows: Visual Studio: *.dsp, xmlParser.dsw and also xmlParser.lib and xmlParser.dll
+ *
+ * \section debugging Debugging with the XMLParser library
+ *
+ * \subsection debugwin Debugging under WINDOWS
+ *
+ * Inside Visual C++, the "debug versions" of the memory allocation functions are
+ * very slow: Do not forget to compile in "release mode" to get maximum speed.
+ * When I had to debug a software that was using the XMLParser Library, it was usually
+ * a nightmare because the library was sooOOOoooo slow in debug mode (because of the
+ * slow memory allocations in Debug mode). To solve this
+ * problem, during all the debugging session, I am now using a very fast DLL version of the
+ * XMLParser Library (the DLL is compiled in release mode). Using the DLL version of
+ * the XMLParser Library allows me to have lightening XML parsing speed even in debug!
+ * Other than that, the DLL version is useless: In the release version of my tool,
+ * I always use the normal, ".cpp"-based, XMLParser Library (I simply include the
+ * <a href="../../xmlParser.cpp">xmlParser.cpp</a> and
+ * <a href="../../xmlParser.h">xmlParser.h</a> files into the project).
+ *
+ * The file <a href="../../XMLNodeAutoexp.txt">XMLNodeAutoexp.txt</a> contains some
+ * "tweaks" that improve substancially the display of the content of the XMLNode objects
+ * inside the Visual Studio Debugger. Believe me, once you have seen inside the debugger
+ * the "smooth" display of the XMLNode objects, you cannot live without it anymore!
+ *
+ * \subsection debuglinux Debugging under LINUX/UNIX
+ *
+ * The speed of the debug version of the XMLParser library is tolerable so no extra
+ * work.has been done.
+ *
+ ****************************************************************************/
+
+#ifndef __INCLUDE_XML_NODE__
+#define __INCLUDE_XML_NODE__
+
+#include <stdlib.h>
+
+#ifdef _UNICODE
+// If you comment the next "define" line then the library will never "switch to" _UNICODE (wchar_t*) mode (16/32 bits per characters).
+// This is useful when you get error messages like:
+// 'XMLNode::openFileHelper' : cannot convert parameter 2 from 'const char [5]' to 'const wchar_t *'
+// The _XMLWIDECHAR preprocessor variable force the XMLParser library into either utf16/32-mode (the proprocessor variable
+// must be defined) or utf8-mode(the pre-processor variable must be undefined).
+#define _XMLWIDECHAR
+#endif
+
+#if defined(WIN32) || defined(UNDER_CE) || defined(_WIN32) || defined(WIN64) || defined(__BORLANDC__)
+// comment the next line if you are under windows and the compiler is not Microsoft Visual Studio (6.0 or .NET) or Borland
+#define _XMLWINDOWS
+#endif
+
+#ifdef XMLDLLENTRY
+#undef XMLDLLENTRY
+#endif
+#ifdef _USE_XMLPARSER_DLL
+#ifdef _DLL_EXPORTS_
+#define XMLDLLENTRY __declspec(dllexport)
+#else
+#define XMLDLLENTRY __declspec(dllimport)