Update EPG Cache(thanks to open source community)
authorhschang <chang@dev3>
Mon, 27 Aug 2018 06:52:33 +0000 (15:52 +0900)
committerhschang <chang@dev3>
Mon, 27 Aug 2018 06:52:33 +0000 (15:52 +0900)
29 files changed:
data/encoding.conf [changed mode: 0755->0644]
data/menu.xml [changed mode: 0755->0644]
data/setup.xml [changed mode: 0755->0644]
lib/base/Makefile.am
lib/base/encoding.cpp
lib/base/encoding.h
lib/base/estring.cpp
lib/base/estring.h
lib/base/freesatv2.cpp [new file with mode: 0644]
lib/base/freesatv2.h [new file with mode: 0644]
lib/dvb/db.cpp [changed mode: 0755->0644]
lib/dvb/db.h
lib/dvb/dvbtime.cpp
lib/dvb/dvbtime.h
lib/dvb/epgcache.cpp
lib/dvb/epgcache.h
lib/dvb/lowlevel/eit.h
lib/dvb/lowlevel/mhw.h
lib/python/Components/Converter/EventName.py
lib/python/Components/Converter/Makefile.am
lib/python/Components/Converter/genre.py [new file with mode: 0644]
lib/python/Components/UsageConfig.py
lib/python/Screens/InfoBarGenerics.py [changed mode: 0755->0644]
lib/service/event.cpp
lib/service/event.h
lib/service/iservice.h
lib/service/servicedvbrecord.cpp
lib/service/servicemp3.cpp [changed mode: 0755->0644]
lib/service/servicemp3.h

old mode 100755 (executable)
new mode 100644 (file)
index 36f619d..7e900e9
@@ -1,41 +1,56 @@
 #Fallback encoding when in dvb-text no encoding table is given
-#Countycode ISO8859-X or ISO6397
+#Countycode encoding, encoding may be ISO8859-X, ISO6937, UTF8, 
+#                  UTF-8,GB18030(GB2312/GBK/CP936), BIG5(CP950),
+#                  UNICODE(UTF16BE), UTF16LE
 ara ISO8859-6
 tur ISO8859-9
 gre ISO8859-7
+ell ISO8859-7
 pol ISO8859-2
 rus ISO8859-5
 bul ISO8859-5
 tha ISO8859-11
-cze ISO6397
-ces ISO6397
-slo ISO6397
-slk ISO6397
+cze ISO6937
+ces ISO6937
+slo ISO6937
+slk ISO6937
+chi GB18030
+zho GB18030
+chn GB18030
+
+#Fallback encoding when in dvb-text no encoding table is given
+#and no Countrycode  and no transponders configs in this config file
+*   ISO8859-1
+
 #Sorry for that.. in DVB Spec this is the default behavior 
 #when no other encoding is given in dvb-texts..
 #but this breaks too much providers yet.. 
 #so our default is ISO8859-1 without two char byte encoding
 #So all transponders which needs this must be listed here
 #TSID ONID
-0x447 0x1 # Astra 19.2°E 12.304 H - UPC Direct 
-0x427 0x1 # Astra 19.2°E 10.920 H - UPC Direct 
-0x44b 0x1 # Astra 19.2°E 12.382 H - UPC Direct
-0x4ff 0x1 # Astra 19.2°E 11.992 H - UPC Direct 
-0x407 0x1 # Astra 19.2°E 11.671 H - UPC Direct 
+0x2c1 0x600 #Thor 0.8°W
+0x2c2 0x600 #Thor 0.8°W
+0x2c3 0x600 #Thor 0.8°W
+0x2c4 0x600 #Thor 0.8°W
+0x2be 0x600 #Thor 0.8°W
+0x2bf 0x600 #Thor 0.8°W
+0x2bd 0x600 #Thor 0.8°W
+0x2c0 0x600 #Thor 0.8°W
+0x2c2 0x600 #Thor 0.8°W
+0x2c3 0x600 #Thor 0.8°W
+0x2c4 0x600 #Thor 0.8°W
+0x2c5 0x600 #Thor 0.8°W
+0x2c6 0x600 #Thor 0.8°W
+0x2c8 0x600 #Thor 0.8°W
+0xaf2 0xbb #Thor 0.8°W
 0x436 0x1 # Astra 19.2°E 11.973 V - MTV Networks Europe
 0x42a 0x1 # Astra 19.2°E 11.739 V - MTV Networks Europe
-0xc23 0x3 # Astra 23.5°E 10.803 H - CS Link / SkyLink
-0xc85 0x3 # Astra 23.5°E 11.797 H - CS Link / SkyLink
-0xc89 0x3 # Astra 23.5°E 11.876 H - CS Link / SkyLink
-0xc8f 0x3 # Astra 23.5°E 11.992 H - CS Link / SkyLink
-0xc93 0x3 # Astra 23.5°E 12.070 H - CS Link / SkyLink
-0xc95 0x3 # Astra 23.5°E 12.109 H - SkyLink
-0xbc6 0x3 # Astra 23.5°E 12.525 V - CS Link / SkyLink
-0xbc7 0x3 # Astra 23.5°E 12.565 H - SkyLink
 200 318 #Hotbird Eutelsat (Eurosport)
 300 318 #Hotbird Eutelsat (Eurosport, Animal Pl.HD)
 400 318 #Hotbird 13.0 Cyfra+
+700 318 #Hotbird 13.0 Cyfrowy Polsat
 1000 318 #Hotbird 13.0 Grupa ITI
+1100 318 #Hotbird 13.0 Cyfra+
 1500 318 #Hotbird 13.0 Cyfra+
 1600 318 #Hotbird 13.0 Cyfra+
 2800 318 #Hotbird 13.0 MTV Networks (Comedy Central)
@@ -44,6 +59,7 @@ slk ISO6397
 7900 113 #Hotbird 13.0 Cyfrowy Polsat
 8100 113 #Hotbird 13.0 Universal (Cyfra+)
 8100 318 #Hotbird 13.0 Eutelsat (Universal)
+8900 318 #Hotbird 13.0 BBC Brit HD Poland
 11000 318 #Hotbird 13.0 Cyfra+
 11400 318 #Hotbird 13.0 Cyfra+
 11600 318 #Hotbird 13.0 BBC HD, ITI
@@ -54,19 +70,49 @@ slk ISO6397
 13000 318 #Hotbird 13.0 BBC Polska and other
 13100 318 #Hotbird 13.0 Crime and Investigation
 13200 113 #Hotbird 13.0 Cyfrowy Polsat
+15700 318 #Hotbird 13.0 Belgium Satellite Services - TV DISCO
+0x801 0x600 # Ziggo NL
+0x802 0x600 # Ziggo NL
+0x803 0x600 # Ziggo NL
+0x806 0x600 # Ziggo NL
+0x809 0x600 # Ziggo NL
+0x80a 0x600 # Ziggo NL
+0x80d 0x600 # Ziggo NL
+0x80e 0x600 # Ziggo NL
+0x80f 0x600 # Ziggo NL
+0x810 0x600 # Ziggo NL
+0x812 0x600 # Ziggo NL
+0x813 0x600 # Ziggo NL
+0x815 0x600 # Ziggo NL
+0x817 0x600 # Ziggo NL
+0x819 0x600 # Ziggo NL
+0x81a 0x600 # Ziggo NL
+0x81c 0x600 # Ziggo NL
+0x831 0x600 # Ziggo NL
+0x832 0x600 # Ziggo NL
+0x833 0x600 # Ziggo NL
+0x834 0x600 # Ziggo NL
+0x835 0x600 # Ziggo NL
+0x836 0x600 # Ziggo NL
+0x83a 0x600 # Ziggo NL
+0x840 0x600 # Ziggo NL
 #Fallback encoding table for single transponders
-#ISO6397 also enables two byte char encoding
+#ISO6937 also enables two byte char encoding
 #TSID ONID ISO8859-X
-#12800 318 ISO6397 #Viacom ... MTV / VH1 Polska
-21100 126 ISO8859-9 # Digital Platform 7°E 10.928 H 30.000 2/3 8PSK
-41200 126 ISO8859-9 # Digital Platform 7°E 11.451 V 25.066 2/3
-50100 126 ISO8859-9 # Digital Platform 7°E 11.471 H 30.000 3/4
-50200 126 ISO8859-9 # Digital Platform 7°E 11.492 V 30.000 3/4
-50300 126 ISO8859-9 # Digital Platform 7°E 11.639 H 30.000 3/4
-50400 126 ISO8859-9 # Digital Platform 7°E 11.534 V 30.000 3/4
-50500 126 ISO8859-9 # Digital Platform 7°E 11.262 H 27.500 3/4
-50600 126 ISO8859-9 # Digital Platform 7°E 11.575 V and 42°E 11.729 V 15.555 5/6
-50700 126 ISO8859-9 # Digital Platform 7°E 11.596 H 30.000 3/4
-50800 126 ISO8859-9 # Digital Platform 7°E 11.678 H 30.000 3/4
-50900 126 ISO8859-9 # Digital Platform 7°E 11.513 H 27.500 3/4
-51000 126 ISO8859-9 # Digital Platform 7°E 11.617 V 30.000 3/4
+20600 126 ISO8859-9 # Digital Platform 7°E
+20700 126 ISO8859-9 # Digital Platform 7°E
+20800 126 ISO8859-9 # Digital Platform 7°E
+20900 126 ISO8859-9 # Digital Platform 7°E
+21000 126 ISO8859-9 # Digital Platform 7°E
+21100 126 ISO8859-9 # Digital Platform 7°E
+41200 126 ISO8859-9 # Digital Platform 7°E
+50100 126 ISO8859-9 # Digital Platform 7°E
+50200 126 ISO8859-9 # Digital Platform 7°E
+50300 126 ISO8859-9 # Digital Platform 7°E
+50400 126 ISO8859-9 # Digital Platform 7°E
+50500 126 ISO8859-9 # Digital Platform 7°E
+50600 126 ISO8859-9 # Digital Platform 7°E
+50700 126 ISO8859-9 # Digital Platform 7°E
+50800 126 ISO8859-9 # Digital Platform 7°E
+50900 126 ISO8859-9 # Digital Platform 7°E
+51000 126 ISO8859-9 # Digital Platform 7°E
old mode 100755 (executable)
new mode 100644 (file)
index ed0b415..7ab36e7
@@ -54,6 +54,7 @@
                                <item level="0" entryID="av_setup"><setup id="avsetup"/></item>
                                <!--<item level="0" text="Video Setup" entryID="video_setup"><screen module="VideoSetup" /></item>-->
                                <item level="1" entryID="rfmod_setup" requires="RfModulator"><setup id="RFmod"/></item>
+                               <item level="2" entryID="epg_settings"><setup id="epgsettings"/></item>
                                <menu level="0" text="Harddisk" entryID="hardisk_selection" requires="Harddisk">
                                        <id val="harddisk" />
                                        <item level="1" entryID="harddisk_setup"><setup id="harddisk"/></item>
old mode 100755 (executable)
new mode 100644 (file)
index 9fb4a56..aabb58f
                        <item level="2" text="Always include ECM in recordings" requires="ScrambledPlayback" description="Always include ECM messages in recordings. This overrides the individual timer settings globally. It allows recordings to be always decrypted afterwards (sometimes called offline decoding), if supported by your receiver. Default: off.">config.recording.always_ecm</item>
                        <item level="2" text="Never decrypt while recording" requires="ScrambledPlayback" description="Never decrypt the content while recording. This overrides the individual timer settings globally. If enabled, recordings are stored in crypted presentation and must be decrypted afterwards (sometimes called offline decoding). Default: off.">config.recording.never_decrypt</item>
                </setup>
+               <setup key="epgsettings" title="EPG settings">
+                       <item level="1" text="EPG language selection 1" description="Configure the primary EPG language.">config.autolanguage.epglanguage</item>
+                       <item level="1" text="EPG language selection 2" description="Configure the secondary EPG language.">config.autolanguage.epglanguage_alternative</item>
+                       <item level="0" text="Enable EIT EPG" description="Use EIT EPG information when it is available.">config.epg.eit</item>
+                       <item level="2" text="Enable MHW EPG" description="Use MHW EPG information when it is available.">config.epg.mhw</item>
+                       <item level="1" text="Enable freesat EPG" description="Use FreeSat EPG information when it is available.">config.epg.freesat</item>
+                       <item level="1" text="Enable ViaSat EPG" description="Use ViaSat EPG information when it is available.">config.epg.viasat</item>
+                       <item level="1" text="Enable Netmed EPG" description="Use Netmed EPG information when it is available.">config.epg.netmed</item>
+                       <item level="1" text="Enable Virgin EPG" description="Use Virgin EPG information when it is available.">config.epg.virgin</item>
+                       <item level="2" text="Maintain old EPG data for (min)" description="Configure for how many minutes finished events should remain visible in the EPG. Useful when you need information about an event which has just finished, or has been delayed.">config.epg.histminutes</item>
+               </setup>
                <setup key="subtitlesetup" title="Subtitle settings">
                        <item level="2" text="Subtitle font color" description="Configure the color of the subtitles.">config.subtitles.subtitle_fontcolor</item>
                        <item level="2" text="Subtitle font size" description="Configure the font size of the subtitles.">config.subtitles.subtitle_fontsize</item>
index ca9483d..bbd8434 100644 (file)
@@ -26,7 +26,8 @@ libenigma_base_a_SOURCES = \
        smartptr.cpp \
        thread.cpp \
        httpstream.cpp \
-       socketbase.cpp
+       socketbase.cpp \
+       freesatv2.cpp
 
 EXTRA_DIST = \
        eenv.cpp.in
@@ -57,4 +58,5 @@ baseinclude_HEADERS = \
        smartptr.h \
        thread.h \
        httpstream.h \
-       socketbase.h
+       socketbase.h \
+       freesatv2.h 
index 361acce..5609be4 100644 (file)
@@ -5,15 +5,16 @@
 #include <lib/base/eenv.h>
 
 eDVBTextEncodingHandler encodingHandler;  // the one and only instance
+int defaultEncodingTable = 1; // ISO8859-1 / Latin1
 
 inline char toupper(char c)
 {
-       switch (c)
-       {
-               case 'a' ... 'z':
-                       return c-32;
-       }
-       return c;
+       return (c >= 'a' && c <= 'z') ? c - ('a' - 'A') : c;
+}
+
+inline char tolower(char c)
+{
+       return (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c;
 }
 
 eDVBTextEncodingHandler::eDVBTextEncodingHandler()
@@ -41,10 +42,10 @@ eDVBTextEncodingHandler::eDVBTextEncodingHandler()
                                countrycode[2]=toupper(countrycode[2]);
                                m_CountryCodeDefaultMapping[countrycode]=encoding;
                        }
-                       else if ( (sscanf( line, "0x%x 0x%x ISO%d", &tsid, &onid, &encoding ) == 3 && encoding == 6397 )
-                                       ||(sscanf( line, "%d %d ISO%d", &tsid, &onid, &encoding ) == 3 && encoding == 6397 ) )
+                       else if ( (sscanf( line, "0x%x 0x%x ISO%d", &tsid, &onid, &encoding ) == 3 && encoding == 6937 )
+                                       ||(sscanf( line, "%d %d ISO%d", &tsid, &onid, &encoding ) == 3 && encoding == 6937 ) )
                                m_TransponderDefaultMapping[(tsid<<16)|onid]=0;
-                       else if ( sscanf( line, "%s ISO%d", countrycode, &encoding ) == 2 && encoding == 6397 )
+                       else if ( sscanf( line, "%s ISO%d", countrycode, &encoding ) == 2 && encoding == 6937 )
                        {
                                m_CountryCodeDefaultMapping[countrycode]=0;
                                countrycode[0]=toupper(countrycode[0]);
@@ -67,7 +68,7 @@ eDVBTextEncodingHandler::eDVBTextEncodingHandler()
 
 void eDVBTextEncodingHandler::getTransponderDefaultMapping(int tsidonid, int &table)
 {
-       std::map<int, int>::iterator it =
+       std::map<unsigned int, int>::iterator it =
                m_TransponderDefaultMapping.find(tsidonid);
        if ( it != m_TransponderDefaultMapping.end() )
                table = it->second;
@@ -84,5 +85,5 @@ int eDVBTextEncodingHandler::getCountryCodeDefaultMapping( const std::string &co
                m_CountryCodeDefaultMapping.find(country_code);
        if ( it != m_CountryCodeDefaultMapping.end() )
                return it->second;
-       return 1;  // ISO8859-1 / Latin1
+       return defaultEncodingTable;
 }
index 2a58da2..76f9393 100644 (file)
@@ -5,10 +5,30 @@
 #include <set>
 #include <map>
 
+#define ISO8859_5                      0x01    // Latin/Cyrillic
+#define ISO8859_6                      0x02    // Latin/Arabic
+#define ISO8859_7                      0x03    // Latin/Greek
+#define ISO8859_8                      0x04    // Latin/Gebrew
+#define ISO8859_9                      0x05    // Latin 5
+#define ISO8859_10                     0x06    // Latin 6
+#define ISO8859_11                     0x07    // Latin/Thai
+#define ISO8859_12                     0x08    // Reserved
+#define ISO8859_13                     0x09    // Latin 7
+#define ISO8859_14                     0x0A    // Latin 8 (Celtic)
+#define ISO8859_15                     0x0B    // Latin 9
+#define ISO8859_xx                     0x10    // encoded in next two bytes
+#define UNICODE_ENCODING               0x11    // ISO10646 Basic Multilingual Plane
+#define KSX1001_ENCODING               0x12    // KSX1001 Korean
+#define GB18030_ENCODING               0x13    // ISO10646 Simplified Chinese
+#define BIG5_ENCODING                  0x14    // ISO10646 Big5 Traditional Chineese
+#define UTF8_ENCODING                  0x15    // ISO10646 Basic Multilingual Plane in UTF8 encoding
+#define UTF16BE_ENCODING               0x16
+#define UTF16LE_ENCODING               0x17
+
 class eDVBTextEncodingHandler
 {
        std::map<std::string, int> m_CountryCodeDefaultMapping;
-       std::map<int, int> m_TransponderDefaultMapping;
+       std::map<unsigned int, int> m_TransponderDefaultMapping;
        std::set<int> m_TransponderUseTwoCharMapping;
 public:
        eDVBTextEncodingHandler();
@@ -18,5 +38,5 @@ public:
 };
 
 extern eDVBTextEncodingHandler encodingHandler;
-
+extern int defaultEncodingTable;
 #endif // __lib_base_encoding_h__
index f2ac732..10bc89c 100644 (file)
@@ -5,18 +5,19 @@
 #include <lib/base/eerror.h>
 #include <lib/base/encoding.h>
 #include <lib/base/estring.h>
+#include "freesatv2.h"
 
 std::string buildShortName( const std::string &str )
 {
        std::string tmp;
-       static char stropen[3] = { 0xc2, 0x86, 0x00 };
-       static char strclose[3] = { 0xc2, 0x87, 0x00 };
-       size_t open=std::string::npos-1;
-       while ( (open = str.find(stropen, open+2)) != std::string::npos )
+       static char stropen[] = "\xc2\x86";
+       static char strclose[] = "\xc2\x87";
+       size_t open = std::string::npos-1;
+       while ((open = str.find(stropen, open+2)) != std::string::npos)
        {
                size_t close = str.find(strclose, open);
-               if ( close != std::string::npos )
-                       tmp+=str.substr( open+2, close-(open+2) );
+               if (close != std::string::npos)
+                       tmp += str.substr(open+2, close-(open+2));
        }
        return tmp.length() ? tmp : str;
 }
@@ -30,8 +31,8 @@ std::string getNum(int val, int sys)
        if (sys == 10)
                snprintf(buf, 12, "%i", val);
        else if (sys == 16)
-               snprintf(buf, 12, "%X", val);           
-       
+               snprintf(buf, 12, "%X", val);
+
        std::string res;
        res.assign(buf);
        return res;
@@ -151,7 +152,9 @@ static unsigned long c885916[96]={
 0x00E0, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x0107, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
 0x0111, 0x0144, 0x00F2, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x015B, 0x0171, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x0119, 0x021B, 0x00FF};
 
-static unsigned long iso6397[96]={
+static freesatHuffmanDecoder huffmanDecoder;
+
+static unsigned long iso6937[96]={
 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x20AC, 0x00A5, 0x0000, 0x00A7, 0x00A4, 0x2018, 0x201C, 0x00AB, 0x2190, 0x2191, 0x2192, 0x2193,
 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00D7, 0x00B5, 0x00B6, 0x00B7, 0x00F7, 0x2019, 0x201D, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
 0x0000, 0xE002, 0xE003, 0xE004, 0xE005, 0xE006, 0xE007, 0xE008, 0xE009, 0xE00C, 0xE00A, 0xE00B, 0x0000, 0xE00D, 0xE00E, 0xE00F,
@@ -159,7 +162,7 @@ static unsigned long iso6397[96]={
 0x2126, 0x00C6, 0x0110, 0x00AA, 0x0126, 0x0000, 0x0132, 0x013F, 0x0141, 0x00D8, 0x0152, 0x00BA, 0x00DE, 0x0166, 0x014A, 0x0149,
 0x0138, 0x00E6, 0x0111, 0x00F0, 0x0127, 0x0131, 0x0133, 0x0140, 0x0142, 0x00F8, 0x0153, 0x00DF, 0x00FE, 0x0167, 0x014B, 0x00AD};
 
-// Two Char Mapping (aka ISO6397) ( many polish services and UPC Direct/HBO services)
+// Two Char Mapping (aka ISO6937) ( many polish services and UPC Direct/HBO services)
 // get from http://mitglied.lycos.de/buran/charsets/videotex-suppl.html
 static inline unsigned int doVideoTexSuppl(int c1, int c2)
 {
@@ -325,43 +328,49 @@ static inline unsigned int recode(unsigned char d, int cp)
                return d;
        switch (cp)
        {
-       case 0:         // ISO6397
-               return iso6397[d-0xA0];
-       case 1:         // 8859-1 <-> unicode mapping
-               return d;
-       case 2:         // 8859-2 -> unicode mapping
-               return c88592[d-0xA0];
-       case 3:         // 8859-3 -> unicode mapping
-               return c88593[d-0xA0];
-       case 4:         // 8859-2 -> unicode mapping
-               return c88594[d-0xA0];
-       case 5:         // 8859-5 -> unicode mapping
-               return c88595[d-0xA0];
-       case 6:         // 8859-6 -> unicode mapping
-               return c88596[d-0xA0];
-       case 7:         // 8859-7 -> unicode mapping
-               return c88597[d-0xA0];
-       case 8:         // 8859-8 -> unicode mapping
-               return c88598[d-0xA0];
-       case 9:         // 8859-9 -> unicode mapping
-               return c88599[d-0xA0];
-       case 10:// 8859-10 -> unicode mapping
-               return c885910[d-0xA0];
-       case 11:// 8859-11 -> unicode mapping
-               return c885911[d-0xA0];
-/*     case 12:// 8859-12 -> unicode mapping  // reserved for indian use..
-               return c885912[d-0xA0];*/
-       case 13:// 8859-13 -> unicode mapping
-               return c885913[d-0xA0];
-       case 14:// 8859-14 -> unicode mapping
-               return c885914[d-0xA0];
-       case 15:// 8859-15 -> unicode mapping
-               return c885915[d-0xA0];
-       case 16:// 8859-16 -> unicode mapping
-               return c885916[d-0xA0];
-       default:
-               return d;
+       case 0:  return iso6937[d-0xA0]; // ISO6937
+       case 1:  return d;               // 8859-1 -> unicode mapping
+       case 2:  return c88592[d-0xA0];  // 8859-2 -> unicode mapping
+       case 3:  return c88593[d-0xA0];  // 8859-3 -> unicode mapping
+       case 4:  return c88594[d-0xA0];  // 8859-2 -> unicode mapping
+       case 5:  return c88595[d-0xA0];  // 8859-5 -> unicode mapping
+       case 6:  return c88596[d-0xA0];  // 8859-6 -> unicode mapping
+       case 7:  return c88597[d-0xA0];  // 8859-7 -> unicode mapping
+       case 8:  return c88598[d-0xA0];  // 8859-8 -> unicode mapping
+       case 9:  return c88599[d-0xA0];  // 8859-9 -> unicode mapping
+       case 10: return c885910[d-0xA0]; // 8859-10 -> unicode mapping
+       case 11: return c885911[d-0xA0]; // 8859-11 -> unicode mapping
+//     case 12: return c885912[d-0xA0]; // 8859-12 -> unicode mapping  // reserved for indian use..
+       case 13: return c885913[d-0xA0]; // 8859-13 -> unicode mapping
+       case 14: return c885914[d-0xA0]; // 8859-14 -> unicode mapping
+       case 15: return c885915[d-0xA0]; // 8859-15 -> unicode mapping
+       case 16: return c885916[d-0xA0]; // 8859-16 -> unicode mapping
+       default: return d;
+       }
+}
+
+std::string UnicodeToUTF8(long c)
+{
+       if ( c < 0x80 ) {
+               char utf[2] = {static_cast<char>(c), 0};
+               return std::string(utf, 1);
+       }
+       else if ( c < 0x800) {
+               char utf[3] = { static_cast<char>(0xc0 | (c >> 6)), static_cast<char>(0x80 | (c & 0x3f)), 0};
+               return std::string(utf, 2);
+       }
+       else if ( c < 0x10000) {
+               char utf[4] = { static_cast<char>(0xe0 | (c >> 12)), static_cast<char>(0x80 | ((c >> 6) & 0x3f)),
+                               static_cast<char>(0x80 | (c & 0x3f)), 0};
+               return std::string(utf, 3);
+       }
+       else if ( c < 0x200000) {
+               char utf[5] = { static_cast<char>(0xf0 | (c >> 18)), static_cast<char>(0x80 | ((c >> 12) & 0x3f)),
+                               static_cast<char>(0x80 | ((c >> 6) & 0x3f)), static_cast<char>(0x80 | (c & 0x3f)), 0};
+               return std::string(utf, 4);
        }
+       eDebug("[UnicodeToUTF8] invalid unicode character: code=0x%08lx", c); // not a valid unicode
+       return "";
 }
 
 std::string convertDVBUTF8(const unsigned char *data, int len, int table, int tsidonid)
@@ -369,57 +378,82 @@ std::string convertDVBUTF8(const unsigned char *data, int len, int table, int ts
        if (!len)
                return "";
 
-       int i=0, t=0;
+       int i = 0;
+       std::string output = "";
 
-       if ( tsidonid )
+       if (tsidonid)
                encodingHandler.getTransponderDefaultMapping(tsidonid, table);
 
+       // first byte in strings may override general encoding table.
        switch(data[0])
        {
-               case 1 ... 11:
-                       table=data[i++]+4;
-//                     eDebug("(1..11)text encoded in ISO-8859-%d",table);
+               case ISO8859_5 ... ISO8859_15:
+                       // For Thai providers, encoding char is present but faulty.
+                       if (table != 11)
+                               table = data[i] + 4;
+                       ++i;
+//                     eDebug("[convertDVBUTF8] (1..11)text encoded in ISO-8859-%d", table);
                        break;
-               case 0x10:
+               case ISO8859_xx:
                {
-                       int n=(data[++i]<<8);
+                       int n = data[++i] << 8;
                        n |= (data[++i]);
-//                     eDebug("(0x10)text encoded in ISO-8859-%d",n);
+//                     eDebug("[convertDVBUTF8] (0x10)text encoded in ISO-8859-%d",n);
                        ++i;
                        switch(n)
                        {
-                               case 12:
-                                       eDebug("unsup. ISO8859-12 enc.");
+                               case 0x0C: // ETSI EN 300 468 Table A.4: Reserved for future use
+                                       eDebug("[convertDVBUTF8] ISO 8859-12 encoding unsupported");
                                        break;
                                default:
-                                       table=n;
+                                       table = n;
                                        break;
                        }
                        break;
                }
-               case 0x11: //  Basic Multilingual Plane of ISO/IEC 10646-1 enc  (UTF-16... Unicode)
-                       table = 65;
+               case UNICODE_ENCODING: //  Basic Multilingual Plane of ISO/IEC 10646-1 enc  (UTF-16... Unicode)
+                       table = UNICODE_ENCODING;
                        tsidonid = 0;
                        ++i;
                        break;
-               case 0x12:
+               case KSX1001_ENCODING:
+                       ++i;
+                       eDebug("[convertDVBUTF8] KSC 5601 encoding unsupported.");
+                       break;
+               case GB18030_ENCODING:
                        ++i;
-                       eDebug("unsup. KSC 5601 enc.");
+                       eDebug("[convertDVBUTF8] GB-2312-1980 encoding unsupported.");
                        break;
-               case 0x13:
+               case BIG5_ENCODING:
                        ++i;
-                       eDebug("unsup. GB-2312-1980 enc.");
+                       eDebug("[convertDVBUTF8] Big5 subset of ISO/IEC 10646-1 encoding unsupported.");
                        break;
-               case 0x14:
+               case UTF8_ENCODING: // UTF-8 encoding of ISO/IEC 10646-1
+                       ++i;
+                       table = UTF8_ENCODING;
+                       break;
+               case UTF16BE_ENCODING:
+                       ++i;
+                       table = UTF16BE_ENCODING;
+                       break;
+               case UTF16LE_ENCODING:
+                       ++i;
+                       table = UTF16LE_ENCODING;
+                       break;
+               case 0x1F:
+                       {
+                               // Attempt to decode Freesat Huffman encoded string
+                               std::string decoded_string = huffmanDecoder.decode(data, len);
+                               if (!decoded_string.empty())
+                                       return decoded_string;
+                       }
                        ++i;
-                       eDebug("unsup. Big5 subset of ISO/IEC 10646-1 enc.");
+                       eDebug("[convertDVBUTF8] failed to decode bbc freesat huffman");
                        break;
-               case 0x15: // UTF-8 encoding of ISO/IEC 10646-1
-                       return std::string((char*)data+1, len-1);
                case 0x0:
                case 0xC ... 0xF:
-               case 0x16 ... 0x1F:
-                       eDebug("reserved %d", data[0]);
+               case 0x18 ... 0x1E:
+                       eDebug("[convertDVBUTF8] reserved %d", data[0]);
                        ++i;
                        break;
        }
@@ -427,54 +461,75 @@ std::string convertDVBUTF8(const unsigned char *data, int len, int table, int ts
        bool useTwoCharMapping = !table || (tsidonid && encodingHandler.getTransponderUseTwoCharMapping(tsidonid));
 
        if (useTwoCharMapping && table == 5) { // i hope this dont break other transponders which realy use ISO8859-5 and two char byte mapping...
-//             eDebug("Cyfra / Cyfrowy Polsat HACK... override given ISO8859-5 with ISO6397");
+//             eDebug("[convertDVBUTF8] Cyfra / Cyfrowy Polsat HACK... override given ISO8859-5 with ISO6937");
                table = 0;
        }
+       else if ( table == -1 )
+               table = defaultEncodingTable;
 
-       unsigned char res[2048];
-       while (i < len)
+       switch(table)
        {
-               unsigned long code=0;
-               if ( useTwoCharMapping && i+1 < len && (code=doVideoTexSuppl(data[i], data[i+1])) )
-                       i+=2;
-               if (!code) {
-                       if (table == 65) { // unicode
-                               if (i+1 < len) {
-                                       code=(data[i] << 8) | data[i+1];
+               case UTF8_ENCODING:
+                       output = std::string((char*)data + i, len - i);
+                       break;
+               default:
+                       std::string res = "";
+                       while (i < len)
+                       {
+                               unsigned long code = 0;
+                               if (useTwoCharMapping && i+1 < len && (code = doVideoTexSuppl(data[i], data[i+1])))
                                        i += 2;
+                               else if (table == UTF16BE_ENCODING || table == UNICODE_ENCODING) {
+                                       if (i+2 > len)
+                                               break;
+                                       unsigned long w1 = ((unsigned long)(data[i])<<8) | ((unsigned long)(data[i+1]));
+                                       if (w1 < 0xD800UL || w1 > 0xDFFFUL) {
+                                               code = w1;
+                                               i += 2;
+                                       }
+                                       else if (w1 > 0xDBFFUL)
+                                               break;
+                                       else if (i+4 < len) {
+                                               unsigned long w2 = ((unsigned long)(data[i+2]) << 8) | ((unsigned long)(data[i+3]));
+                                               if (w2 < 0xDC00UL || w2 > 0xDFFFUL)
+                                                       return std::string("");
+                                               code = 0x10000UL + (((w1 & 0x03FFUL) << 10 ) | (w2 & 0x03FFUL));
+                                               i += 4;
+                                       }
+                                       else
+                                               break;
                                }
+                               else if (table == UTF16LE_ENCODING) {
+                                       if ((i+2) > len)
+                                               break;
+                                       unsigned long w1 = ((unsigned long)(data[i+1]) << 8) | ((unsigned long)(data[i]));
+                                       if (w1 < 0xD800UL || w1 > 0xDFFFUL) {
+                                               code = w1;
+                                               i += 2;
+                                       }
+                                       else if (w1 > 0xDBFFUL)
+                                               break;
+                                       else if (i+4 < len) {
+                                               unsigned long w2 = ((unsigned long)(data[i+3]) << 8) | ((unsigned long)(data[i+2]));
+                                               if (w2 < 0xDC00UL || w2 > 0xDFFFUL)
+                                                       break;
+                                               code = 0x10000UL + (((w2 & 0x03FFUL) << 10 ) | (w1 & 0x03FFUL));
+                                               i += 4;
+                                       }
+                                       else
+                                               break;
+                               }
+                               if (!code)
+                                       code = recode(data[i++], table);
+
+                               if (!code)
+                                       continue;
+                               res += UnicodeToUTF8(code);
                        }
-                       else
-                               code=recode(data[i++], table);
-               }
-               if (!code)
-                       continue;
-                               // Unicode->UTF8 encoding
-               if (code < 0x80) // identity ascii <-> utf8 mapping
-                       res[t++]=char(code);
-               else if (code < 0x800) // two byte mapping
-               {
-                       res[t++]=(code>>6)|0xC0;
-                       res[t++]=(code&0x3F)|0x80;
-               } else if (code < 0x10000) // three bytes mapping
-               {
-                       res[t++]=(code>>12)|0xE0;
-                       res[t++]=((code>>6)&0x3F)|0x80;
-                       res[t++]=(code&0x3F)|0x80;
-               } else
-               {
-                       res[t++]=(code>>18)|0xF0;
-                       res[t++]=((code>>12)&0x3F)|0x80;
-                       res[t++]=((code>>6)&0x3F)|0x80;
-                       res[t++]=(code&0x3F)|0x80;
-               }
-               if (t+4 > 2047)
-               {
-                       eDebug("convertDVBUTF8 buffer to small.. break now");
+                       output = res;
                        break;
-               }
        }
-       return std::string((char*)res, t);
+       return output;
 }
 
 std::string convertUTF8DVB(const std::string &string, int table)
@@ -485,171 +540,141 @@ std::string convertUTF8DVB(const std::string &string, int table)
 
        unsigned char buf[len];
 
-       for(int i=0;i<len;i++)
+       for (int i = 0; i < len; i++)
        {
-               unsigned char c1=string[i];
+               unsigned char c1 = string[i];
                unsigned int c;
-               if(c1<0x80)
-                       c=c1;
+               if (c1 < 0x80)
+                       c = c1;
                else
                {
-                       i++;
-                       unsigned char c2=string[i];
-                       c=((c1&0x3F)<<6) + (c2&0x3F);
-                       if (table==0||table==1||c1<0xA0)
+                       ++i;
+                       unsigned char c2 = string[i];
+                       c = ((c1&0x3F)<<6) + (c2&0x3F);
+                       if (table == 0 || table == 1 || c1 < 0xA0)
                                ;
                        else
                        {
                                if (!coding_table)
                                {
-                                       switch(table)
+                                       switch (table)
                                        {
-                                               case 2:
-                                                       coding_table = c88592;
-                                                       break;
-                                               case 3:
-                                                       coding_table = c88593;
-                                                       break;
-                                               case 4:
-                                                       coding_table = c88594;
-                                                       break;
-                                               case 5:
-                                                       coding_table = c88595;
-                                                       break;
-                                               case 6:
-                                                       coding_table = c88596;
-                                                       break;
-                                               case 7:
-                                                       coding_table = c88597;
-                                                       break;
-                                               case 8:
-                                                       coding_table = c88598;
-                                                       break;
-                                               case 9:
-                                                       coding_table = c88599;
-                                                       break;
-                                               case 10:
-                                                       coding_table = c885910;
-                                                       break;
-                                               case 11:
-                                                       coding_table = c885911;
-                                                       break;
-/*                                             case 12:   // reserved.. for indian use
-                                                       coding_table = c885912;
-                                               break;*/
-                                               case 13:
-                                                       coding_table = c885913;
-                                                       break;
-                                               case 14:
-                                                       coding_table = c885914;
-                                                       break;
-                                               case 15:
-                                                       coding_table = c885915;
-                                                       break;
-                                               case 16:
-                                                       coding_table = c885916;
-                                                       break;
+                                               case 2: coding_table = c88592; break;
+                                               case 3: coding_table = c88593; break;
+                                               case 4: coding_table = c88594; break;
+                                               case 5: coding_table = c88595; break;
+                                               case 6: coding_table = c88596; break;
+                                               case 7: coding_table = c88597; break;
+                                               case 8: coding_table = c88598; break;
+                                               case 9: coding_table = c88599; break;
+                                               case 10: coding_table = c885910; break;
+                                               case 11: coding_table = c885911; break;
+//                                             case 12: coding_table = c885912; break; // reserved.. for indian use
+                                               case 13: coding_table = c885913; break;
+                                               case 14: coding_table = c885914; break;
+                                               case 15: coding_table = c885915; break;
+                                               case 16: coding_table = c885916; break;
                                                default:
-                                                       eFatal("unknown coding table %d", table);
+                                                       eFatal("[convertUTF8DVB] unknown coding table %d", table);
                                                        break;
                                        }
                                }
-                               for(unsigned int j=0;j<96;j++)
+                               for (unsigned int j = 0; j < 96; j++)
                                {
-                                       if(coding_table[j]==c)
+                                       if (coding_table[j] == c)
                                        {
-                                               c=0xA0+j;
+                                               c = 0xA0 + j;
                                                break;
                                        }
                                }
                        }
                }
-               buf[t++]=(unsigned char)c;
+               buf[t++] = (unsigned char)c;
        }
-       return std::string((char*)buf,t);
+       return std::string((char*)buf, t);
 }
 
 std::string convertLatin1UTF8(const std::string &string)
 {
-       unsigned int t=0, i=0, len=string.size();
+       unsigned int i = 0, len = string.size();
 
-       unsigned char res[2048];
+       std::string res = "";
 
        while (i < len)
        {
-               unsigned long code=(unsigned char)string[i++];
-                               // Unicode->UTF8 encoding
-               if (code < 0x80) // identity latin <-> utf8 mapping
-                       res[t++]=char(code);
-               else if (code < 0x800) // two byte mapping
-               {
-                       res[t++]=(code>>6)|0xC0;
-                       res[t++]=(code&0x3F)|0x80;
-               } else if (code < 0x10000) // three bytes mapping
-               {
-                       res[t++]=(code>>12)|0xE0;
-                       res[t++]=((code>>6)&0x3F)|0x80;
-                       res[t++]=(code&0x3F)|0x80;
-               } else
-               {
-                       res[t++]=(code>>18)|0xF0;
-                       res[t++]=((code>>12)&0x3F)|0x80;
-                       res[t++]=((code>>6)&0x3F)|0x80;
-                       res[t++]=(code&0x3F)|0x80;
-               }
-               if (t+4 > 2047)
-               {
-                       eDebug("convertLatin1UTF8 buffer to small.. break now");
-                       break;
-               }
+               unsigned long code = (unsigned char)string[i++];
+               res += UnicodeToUTF8(code);
        }
-       return std::string((char*)res, t);
+       return res;
 }
 
 int isUTF8(const std::string &string)
 {
-       unsigned int len=string.size();
-
-       for (unsigned int i=0; i < len; ++i)
+       unsigned int len = string.size();
+
+       // Unicode chars: #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
+       // (i.e. any Unicode character, excluding the surrogate blocks, FFFE, and FFFF.
+       // Avoid "compatibility characters", as defined in section 2.3 of The Unicode Standard, Version 5.0.0.
+       // Following characters are also discouraged. They are either control characters or permanently
+       // undefined Unicode characters:
+       //[#x7F-#x84], [#x86-#x9F], [#xFDD0-#xFDEF],
+       //[#x1FFFE-#x1FFFF], [#x2FFFE-#x2FFFF], [#x3FFFE-#x3FFFF],
+       //[#x4FFFE-#x4FFFF], [#x5FFFE-#x5FFFF], [#x6FFFE-#x6FFFF],
+       //[#x7FFFE-#x7FFFF], [#x8FFFE-#x8FFFF], [#x9FFFE-#x9FFFF],
+       //[#xAFFFE-#xAFFFF], [#xBFFFE-#xBFFFF], [#xCFFFE-#xCFFFF],
+       //[#xDFFFE-#xDFFFF], [#xEFFFE-#xEFFFF], [#xFFFFE-#xFFFFF],
+       //[#x10FFFE-#x10FFFF].
+
+       for (unsigned int i = 0; i < len; ++i)
        {
-               if (!(string[i]&0x80)) // normal ASCII
+               if (!(string[i] & 0x80)) // normal ASCII
                        continue;
-               if ((string[i] & 0xE0) == 0xC0) // one char following.
-               {
-                               // first, length check:
-                       if (i+1 >= len)
-                               return 0; // certainly NOT utf-8
-                       i++;
-                       if ((string[i]&0xC0) != 0x80)
-                               return 0; // no, not UTF-8.
-               } else if ((string[i] & 0xF0) == 0xE0)
-               {
-                       if ((i+1) >= len)
-                               return 0;
-                       i++;
-                       if ((string[i]&0xC0) != 0x80)
-                               return 0;
-                       i++;
-                       if ((string[i]&0xC0) != 0x80)
+               int l = 0;
+               if ((string[i] & 0xE0) == 0xC0) // 2-byte
+                       l = 1;
+               else if ((string[i] & 0xF0) == 0xE0)  // 3-byte
+                       l = 2;
+               else if ((string[i] & 0xF8) == 0xF0) // 4-byte
+                       l = 3;
+               if (l == 0 || i + l >= len) // no UTF leader or not enough bytes
+                       return 0;
+
+               while (l-- > 0) {
+                       if ((string[++i] & 0xC0) != 0x80)
                                return 0;
                }
        }
        return 1; // can be UTF8 (or pure ASCII, at least no non-UTF-8 8bit characters)
 }
 
+unsigned int truncateUTF8(std::string &s, unsigned int newsize)
+{
+        unsigned int len = s.size();
+
+        // Assume s is a real UTF8 string!!!
+        while (len > newsize) {
+                while (len-- > 0  && (s[len] & 0xC0) == 0x80)
+                        ; // remove UTF data bytes,  e.g. range 0x80 - 0xBF
+                if (len > 0)   // remove the UTF startbyte, or normal ascii character
+                         --len;
+        }
+        s.resize(len);
+        return len;
+}
+
 std::string removeDVBChars(const std::string &s)
 {
        std::string res;
 
        int len = s.length();
 
-       for(int i = 0; i < len; i++)
+       for (int i = 0; i < len; i++)
        {
                unsigned char c1 = s[i];
                unsigned int c;
 
                        /* UTF8? decode (but only simple) */
-               if((c1 > 0x80) && (i < len-1))
+               if ((c1 > 0x80) && (i < len-1))
                {
                        unsigned char c2 = s[i + 1];
                        c = ((c1&0x3F)<<6) + (c2&0x3F);
@@ -659,10 +684,9 @@ std::string removeDVBChars(const std::string &s)
                                continue;
                        }
                }
-               
                res += s[i];
        }
-       
+
        return res;
 }
 
@@ -671,11 +695,50 @@ void makeUpper(std::string &s)
        std::transform(s.begin(), s.end(), s.begin(), (int(*)(int)) toupper);
 }
 
-std::string replace_all(const std::string &in, const std::string &entity, const std::string &symbol)
+std::string replace_all(const std::string &in, const std::string &entity, const std::string &symbol, int table)
 {
        std::string out = in;
        std::string::size_type loc = 0;
-       while (( loc = out.find(entity, loc)) != std::string::npos )
-       out.replace(loc, entity.length(), symbol);
+       if( table == -1 )
+               table = defaultEncodingTable;
+       switch(table){
+       case UTF8_ENCODING:
+               while (loc < out.length()) {
+                       if ( (entity.length() + loc) <= out.length() && !out.compare(loc, entity.length(), entity)) {
+                               out.replace(loc, entity.length(), symbol);
+                               loc += symbol.length();
+                               continue;
+                       }
+                       if (out.at(loc) < 0x80)
+                               ++loc;
+                       else if ((out.at(loc) & 0xE0) == 0xC0)
+                               loc += 2;
+                       else if ((out.at(loc) & 0xF0) == 0xE0)
+                               loc += 3;
+                       else if ((out.at(loc) & 0xF8) == 0xF0)
+                               loc += 4;
+               }
+               break;
+
+       case UTF16BE_ENCODING:
+       case UTF16LE_ENCODING:
+               while (loc<out.length()) {
+                       if ((entity.length() + loc) <= out.length() && !out.compare(loc, entity.length(), entity)) {
+                               out.replace(loc, entity.length(), symbol);
+                               loc += symbol.length();
+                               continue;
+                       }
+                       loc += 2;
+               }
+               break;
+
+       default:
+               while ((loc = out.find(entity, loc)) != std::string::npos)
+               {
+                       out.replace(loc, entity.length(), symbol);
+                       loc += symbol.length();
+               }
+               break;
+       }
        return out;
 }
index 727d591..589e0a5 100644 (file)
@@ -12,15 +12,16 @@ int strnicmp(const char*, const char*, int);
 
 std::string getNum(int num, int base=10);
 
-std::string convertDVBUTF8(const unsigned char *data, int len, int table=1, int tsidonid=1); // with default ISO8859-1 / Latin1
+std::string convertDVBUTF8(const unsigned char *data, int len, int table=1, int tsidonid=1);
 std::string convertLatin1UTF8(const std::string &string);
 int isUTF8(const std::string &string);
+unsigned int truncateUTF8(std::string &s, unsigned int newsize);
 
 std::string removeDVBChars(const std::string &s);
 void makeUpper(std::string &s);
-std::string replace_all(const std::string &in, const std::string &entity, const std::string &symbol);
+std::string replace_all(const std::string &in, const std::string &entity, const std::string &symbol, int table=-1);
 
-inline std::string convertDVBUTF8(const std::string &string, int table=1, int tsidonid=1) // with default ISO8859-1 / Latin1
+inline std::string convertDVBUTF8(const std::string &string, int table=1, int tsidonid=1)
 {
        return convertDVBUTF8((const unsigned char*)string.c_str(), string.length(), table, tsidonid);
 }
diff --git a/lib/base/freesatv2.cpp b/lib/base/freesatv2.cpp
new file mode 100644 (file)
index 0000000..2e54138
--- /dev/null
@@ -0,0 +1,288 @@
+/*
+FreeSat Huffman decoder for VDR
+Copyright (C) 2008  DOM http://www.rst38.org.uk/vdr/
+Port to C++ / Enigma 2
+Copyright (C) 2008  Martin Croome
+
+This program is free software; you can redistribute 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.
+*/
+#include "freesatv2.h"
+#ifdef FREESATV2_DEBUG
+#      include "eerror.h"
+#endif
+#include <asm/types.h>
+
+#define START   '\0'
+#define STOP    '\0'
+#define ESCAPE  '\1'
+
+#ifndef DATADIR
+#      define DATADIR "/usr/share"
+#endif
+
+#ifndef FREESAT_DATA_DIRECTORY
+#define FREESAT_DATA_DIRECTORY       DATADIR
+#endif
+#define TABLE1_FILENAME FREESAT_DATA_DIRECTORY "/enigma2/freesat.t1"
+#define TABLE2_FILENAME FREESAT_DATA_DIRECTORY "/enigma2/freesat.t2"
+
+static void loadFile(huffTableEntry **table, const char *filename);
+
+
+struct huffTableEntry
+{
+       uint32_t value;
+       uint16_t bits;
+       char next;
+       huffTableEntry * nextEntry;
+
+       huffTableEntry(unsigned int value, short bits, char next) : value(value), bits(bits), next(next), nextEntry(NULL)
+       { }
+};
+
+freesatHuffmanDecoder::freesatHuffmanDecoder()
+{
+       memset(m_tables, 0, sizeof(m_tables));
+       loadFile(&m_tables[0][0], TABLE1_FILENAME);
+       loadFile(&m_tables[1][0], TABLE2_FILENAME);
+}
+
+freesatHuffmanDecoder::~freesatHuffmanDecoder()
+{
+       int     i, j;
+       huffTableEntry *currentEntry, *nextEntry;
+       for ( j = 0 ; j < 2; j++ )
+       {
+               for ( i = 0 ; i < 256; i++ )
+               {
+                       currentEntry = m_tables[j][i];
+                       while ( currentEntry != NULL )
+                       {
+                               nextEntry = currentEntry->nextEntry;
+                               delete currentEntry;
+                               currentEntry = nextEntry;
+                       }
+                       m_tables[j][i] = NULL;
+               }
+       }
+}
+
+
+/** \brief Convert a textual character description into a value
+*
+*  \param str - Encoded (in someway) string
+*
+*  \return Raw character
+*/
+static unsigned char resolveChar(const char *str)
+{
+       const char *p = str;
+       unsigned c0 = *p++, c1 = *p++;
+       if (c1)
+               switch(c0|c1<<8)
+               {
+                       case '0'|'x'<<8:
+                               if ( sscanf(p,"%02x", &c1) == 1 )
+                                       c0 = c1;
+                               break;
+                       case 'E'|'S'<<8:
+                               if ( !strcmp(p,"CAPE") )
+                                       c0 = ESCAPE;
+                               break;
+                       case 'S'|'T'<<8:
+                               if ( !strcmp(p,"OP") )
+                                       c0 = STOP;
+                               else if ( !strcmp(p,"ART") )
+                                       c0 = START;
+                               break;
+               }
+       return c0;
+}
+
+
+/** \brief Decode a binary string into a value
+*
+*  \param binary - Binary string to decode
+*
+*  \return Decoded value
+*/
+static unsigned long decodeBinary(const char *binary)
+{
+       unsigned long mask = 0x80000000;
+       unsigned long val = 0;
+
+       while (*binary)
+       {
+               if ( *binary == '1' )
+               {
+                       val |= mask;
+               }
+               mask >>= 1;
+               ++binary;
+       }
+       return val;
+}
+
+static void loadFile(huffTableEntry **table, const char *filename)
+{
+       char buf[1024];
+       char *from;
+       char *to;
+       char *binary;
+       char *colon;
+
+       FILE *fp = fopen(filename, "r");
+       if ( fp )
+       {
+               while ( fgets(buf,sizeof(buf),fp) != NULL )
+               {
+                       // Tokenize string "in place"
+                       from = buf;
+                       colon = strchr(buf, ':');
+                       if (colon == NULL)
+                               continue;
+                       binary = colon + 1;
+                       *colon = 0;
+                       colon = strchr(binary, ':');
+                       if (colon == NULL)
+                               continue;
+                       *colon = 0;
+                       to = colon + 1;
+                       colon = strchr(to, ':');
+                       if (colon != NULL)
+                               *colon = 0;
+                       {
+                               int bin_len = strlen(binary);
+                               int from_char = resolveChar(from);
+                               char to_char = resolveChar(to);
+                               unsigned long bin = decodeBinary(binary);
+
+                               // Add entry to end of bucket
+                               huffTableEntry **pCurrent = &table[from_char];
+                               while ( *pCurrent != NULL )
+                               {
+                                       pCurrent = &((*pCurrent)->nextEntry);
+                               }
+                               *pCurrent = new huffTableEntry(bin, bin_len, to_char);
+                       }
+               }
+               fclose(fp);
+       }
+#ifdef FREESATV2_DEBUG
+       else
+       {
+               eDebug("[FREESAT] Cannot load '%s'",filename);
+       }
+#endif
+}
+
+
+/** \brief Decode an EPG string as necessary
+*
+*  \param src - Possibly encoded string
+*  \param size - Size of the buffer
+*
+*  \retval NULL - Can't decode
+*  \return A decoded string
+*/
+std::string freesatHuffmanDecoder::decode(const unsigned char *src, size_t size)
+{
+       std::string uncompressed;
+
+       if (src[0] != 0x1f)
+               return uncompressed;
+
+       const unsigned int table_index = src[1] - 1;
+
+       if (table_index <= 1)
+       {
+               huffTableEntry **table = &m_tables[table_index][0];
+               unsigned int value = 0;
+               unsigned int byte = 2;
+               unsigned int bit = 0;
+               int lastch = START;
+
+               while (byte < 6 && byte < size)
+               {
+                       value |= src[byte] << ((5-byte) * 8);
+                       byte++;
+               }
+
+               do
+               {
+                       int found = 0;
+                       unsigned bitShift = 0;
+                       if (lastch == ESCAPE)
+                       {
+                               char nextCh = (value >> 24) & 0xff;
+                               found = 1;
+                               // Encoded in the next 8 bits.
+                               // Terminated by the first ASCII character.
+                               bitShift = 8;
+                               if ((nextCh & 0x80) == 0)
+                                       lastch = nextCh;
+                               uncompressed.append(&nextCh, 1);
+                       }
+                       else
+                       {
+                               huffTableEntry * currentEntry = table[lastch];
+                               while ( currentEntry != NULL )
+                               {
+                                       unsigned mask = 0, maskbit = 0x80000000;
+                                       short kk;
+                                       for ( kk = 0; kk < currentEntry->bits; kk++)
+                                       {
+                                               mask |= maskbit;
+                                               maskbit >>= 1;
+                                       }
+                                       if ((value & mask) == currentEntry->value)
+                                       {
+                                               char nextCh = currentEntry->next;
+                                               bitShift = currentEntry->bits;
+                                               if (nextCh != STOP && nextCh != ESCAPE)
+                                               {
+                                                       uncompressed.append(&nextCh, 1);
+                                               }
+                                               found = 1;
+                                               lastch = nextCh;
+                                               break;
+                                       }
+                                       currentEntry = currentEntry->nextEntry;
+                               }
+                       }
+                       if (found)
+                       {
+                               // Shift up by the number of bits.
+                               unsigned b;
+                               for ( b = 0; b < bitShift; b++)
+                               {
+                                       value = (value << 1) & 0xfffffffe;
+                                       if (byte < size)
+                                               value |= (src[byte] >> (7-bit)) & 1;
+                                       if (bit == 7)
+                                       {
+                                               bit = 0;
+                                               byte++;
+                                       }
+                                       else bit++;
+                               }
+                       }
+                       else
+                       {
+#ifdef FREESATV2_DEBUG
+                               eDebug("[FREESAT] Missing table %d entry: <%s>", table_index + 1, uncompressed.c_str());
+#endif
+                               return uncompressed;
+                       }
+               } while (lastch != STOP && value != 0);
+       }
+       return uncompressed;
+}
diff --git a/lib/base/freesatv2.h b/lib/base/freesatv2.h
new file mode 100644 (file)
index 0000000..6c17a12
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef FREESAT_H
+#define FREESAT_H
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string>
+
+struct huffTableEntry;
+
+class freesatHuffmanDecoder
+{
+private:
+       huffTableEntry *m_tables[2][256];
+public:
+       freesatHuffmanDecoder();
+       ~freesatHuffmanDecoder();
+       std::string decode(const unsigned char *src, size_t size);
+};
+#endif
+
old mode 100755 (executable)
new mode 100644 (file)
index b2d21f4..dadca4d
@@ -1526,6 +1526,21 @@ eServiceReference eDVBDB::searchReference(int tsid, int onid, int sid)
        return eServiceReference();
 }
 
+void eDVBDB::searchAllReferences(std::vector<eServiceReference> &result, int tsid, int onid, int sid)
+{
+       eServiceID Sid(sid);
+       eTransportStreamID Tsid(tsid);
+       eOriginalNetworkID Onid(onid);
+       for (std::map<eServiceReferenceDVB, ePtr<eDVBService> >::iterator sit(m_services.begin());
+               sit != m_services.end(); ++sit)
+       {
+               if (sit->first.getTransportStreamID() == Tsid &&
+                       sit->first.getOriginalNetworkID() == Onid &&
+                       sit->first.getServiceID() == Sid)
+                       result.push_back(sit->first);
+       }
+}
+
 DEFINE_REF(eDVBDBQueryBase);
 
 eDVBDBQueryBase::eDVBDBQueryBase(eDVBDB *db, const eServiceReference &source, eDVBChannelQuery *query)
index 098ee03..dd1585f 100644 (file)
@@ -61,6 +61,7 @@ public:
 //////
        void loadBouquet(const char *path);
        eServiceReference searchReference(int tsid, int onid, int sid);
+       void searchAllReferences(std::vector<eServiceReference> &result, int tsid, int onid, int sid);
        eDVBDB();
        virtual ~eDVBDB();
 #endif
index 26b6767..2db7c8e 100644 (file)
@@ -67,13 +67,8 @@ time_t getRTC()
        return rtc_time != prev_time ? rtc_time : 0;
 }
 
-time_t parseDVBtime(__u8 t1, __u8 t2, __u8 t3, __u8 t4, __u8 t5, __u16 *hash)
+static void parseDVBdate(tm& t, int mjd)
 {
-       tm t;
-       t.tm_sec=fromBCD(t5);
-       t.tm_min=fromBCD(t4);
-       t.tm_hour=fromBCD(t3);
-       int mjd=(t1<<8)|t2;
        int k;
 
        t.tm_year = (int) ((mjd - 15078.2) / 365.25);
@@ -86,12 +81,39 @@ time_t parseDVBtime(__u8 t1, __u8 t2, __u8 t3, __u8 t4, __u8 t5, __u16 *hash)
 
        t.tm_isdst =  0;
        t.tm_gmtoff = 0;
+}
 
-       if (hash) {
-               *hash = t.tm_hour * 60 + t.tm_min;
-               *hash |= t.tm_mday << 11;
-       }
+static inline void parseDVBtime_impl(tm& t, const uint8_t *data)
+{
+       parseDVBdate(t, (data[0] << 8) | data[1]);
+       t.tm_hour = fromBCD(data[2]);
+       t.tm_min = fromBCD(data[3]);
+       t.tm_sec = fromBCD(data[4]);
+}
 
+time_t parseDVBtime(uint16_t mjd, uint32_t stime_bcd)
+{
+       tm t;
+       parseDVBdate(t, mjd);
+       t.tm_hour = fromBCD(stime_bcd >> 16);
+       t.tm_min = fromBCD((stime_bcd >> 8)&0xFF);
+       t.tm_sec = fromBCD(stime_bcd & 0xFF);
+       return timegm(&t);
+}
+
+time_t parseDVBtime(const uint8_t *data)
+{
+       tm t;
+       parseDVBtime_impl(t, data);
+       return timegm(&t);
+}
+
+time_t parseDVBtime(const uint8_t *data, uint16_t *hash)
+{
+       tm t;
+       parseDVBtime_impl(t, data);
+       *hash = t.tm_hour * 60 + t.tm_min;
+       *hash |= t.tm_mday << 11;
        return timegm(&t);
 }
 
@@ -116,7 +138,7 @@ int TDT::createTable(unsigned int nr, const __u8 *data, unsigned int max)
                int length = ((data[1] & 0x0F) << 8) | data[2];
                if ( length >= 5 )
                {
-                       time_t tptime = parseDVBtime(data[3], data[4], data[5], data[6], data[7]);
+                       time_t tptime = parseDVBtime(&data[3]);
                        if (tptime && tptime != -1)
                                eDVBLocalTimeHandler::getInstance()->updateTime(tptime, chan, update_count);
                        error=0;
@@ -264,9 +286,6 @@ void eDVBLocalTimeHandler::updateTime( time_t tp_time, eDVBChannel *chan, int up
        else if (tp_time == -1)
        {
                restart_tdt = true;
-               /*if ( eSystemInfo::getInstance()->getHwType() == eSystemInfo::DM7020 ||
-               ( eSystemInfo::getInstance()->getHwType() == eSystemInfo::DM7000
-                       && eSystemInfo::getInstance()->hasStandbyWakeupTimer() ) )     TODO !!!!!!! */
                {
                        eDebug("[eDVBLocalTimerHandler] no transponder tuned... or no TDT/TOT avail .. try to use RTC :)");
                        time_t rtc_time = getRTC();
index c49e67e..e8f4ae3 100644 (file)
@@ -25,7 +25,9 @@ inline int toBCD(int dec)
        return int(dec/10)*0x10 + dec%10;
 }
 
-time_t parseDVBtime(__u8 t1, __u8 t2, __u8 t3, __u8 t4, __u8 t5, __u16 *hash=0);
+time_t parseDVBtime(uint16_t mjd, uint32_t stime_bcd);
+time_t parseDVBtime(const uint8_t* data);
+time_t parseDVBtime(const uint8_t* data, uint16_t *hash);
 
 class TDT: public eGTable
 {
index 4d32474..0d2545f 100644 (file)
@@ -1,26 +1,78 @@
 #include <lib/dvb/epgcache.h>
 #include <lib/dvb/dvb.h>
+#include <lib/dvb/lowlevel/eit.h>
 
-#undef EPG_DEBUG  
+#undef EPG_DEBUG
 
 #ifdef EPG_DEBUG
 #include <lib/service/event.h>
 #endif
 
+#include <deque>
+#include <string>
 #include <time.h>
 #include <unistd.h>  // for usleep
 #include <sys/vfs.h> // for statfs
-// #include <libmd5sum.h>
 #include <lib/base/eerror.h>
+#include <lib/base/encoding.h>
 #include <lib/base/estring.h>
 #include <lib/dvb/pmt.h>
 #include <lib/dvb/db.h>
 #include <lib/python/python.h>
 #include <dvbsi++/descriptor_tag.h>
 
-int eventData::CacheSize=0;
-descriptorMap eventData::descriptors;
-__u8 eventData::data[4108];
+/* Interval between "garbage collect" cycles */
+#define CLEAN_INTERVAL 60000    //  1 min
+/* Restart EPG data capture */
+#define UPDATE_INTERVAL 3600000  // 60 min
+/* Time to wait after tuning in before EPG data capturing starts */
+#define ZAP_DELAY 2000          // 2 sec
+
+struct DescriptorPair
+{
+       int reference_count;
+       uint8_t* data;
+
+       DescriptorPair() {}
+       DescriptorPair(int c, uint8_t* d): reference_count(c), data(d) {}
+};
+
+typedef std::tr1::unordered_map<uint32_t, DescriptorPair> DescriptorMap;
+
+struct eventData
+{
+       uint8_t rawEITdata[10];
+       uint8_t n_crc;
+       uint8_t type;
+       uint32_t *crc_list;
+       static DescriptorMap descriptors;
+       static uint8_t data[];
+       static unsigned int CacheSize;
+       static bool isCacheCorrupt;
+       eventData(const eit_event_struct* e = NULL, int size = 0, int type = 0, int tsidonid = 0);
+       ~eventData();
+       static void load(FILE *);
+       static void save(FILE *);
+       static void cacheCorrupt(const char* context);
+       const eit_event_struct* get() const;
+       int getEventID() const
+       {
+               return (rawEITdata[0] << 8) | rawEITdata[1];
+       }
+       time_t getStartTime() const
+       {
+               return parseDVBtime(&rawEITdata[2]);
+       }
+       int getDuration() const
+       {
+               return fromBCD(rawEITdata[7])*3600+fromBCD(rawEITdata[8])*60+fromBCD(rawEITdata[9]);
+       }
+};
+
+unsigned int eventData::CacheSize = 0;
+bool eventData::isCacheCorrupt = 0;
+DescriptorMap eventData::descriptors;
+uint8_t eventData::data[2 * 4096 + 12];
 extern const uint32_t crc32_table[256];
 
 const eServiceReference &handleGroup(const eServiceReference &ref)
@@ -33,7 +85,7 @@ const eServiceReference &handleGroup(const eServiceReference &ref)
                        ePtr<iDVBChannelList> db;
                        if (!res->getChannelList(db))
                        {
-                               eBouquet *bouquet=0;
+                               eBouquet *bouquet = NULL;
                                if (!db->getBouquet(ref, bouquet))
                                {
                                        std::list<eServiceReference>::iterator it(bouquet->m_services.begin());
@@ -46,22 +98,30 @@ const eServiceReference &handleGroup(const eServiceReference &ref)
        return ref;
 }
 
-eventData::eventData(const eit_event_struct* e, int size, int type)
-       :ByteSize(size&0xFF), type(type&0xFF)
+static uint32_t calculate_crc_hash(const uint8_t *data, int size)
+{
+       uint32_t crc = 0;
+       for (int i = 0; i < size; ++i)
+               crc = (crc << 8) ^ crc32_table[((crc >> 24) ^ data[i]) & 0xFF];
+       return crc;
+}
+
+eventData::eventData(const eit_event_struct* e, int size, int _type, int tsidonid)
+       :n_crc(0), type(_type & 0xFF), crc_list(NULL)
 {
        if (!e)
-               return;
+               return; /* Used when loading from file */
 
-       __u32 descr[65];
-       __u32 *pdescr=descr;
+       uint32_t descr[65];
+       uint32_t *pdescr=descr;
 
-       __u8 *data = (__u8*)e;
+       uint8_t *data = (uint8_t*)e;
        int ptr=12;
        size -= 12;
 
        while(size > 1)
        {
-               __u8 *descr = data+ptr;
+               uint8_t *descr = data + ptr;
                int descr_len = descr[1];
                descr_len += 2;
                if (size >= descr_len)
@@ -69,69 +129,158 @@ eventData::eventData(const eit_event_struct* e, int size, int type)
                        switch (descr[0])
                        {
                                case EXTENDED_EVENT_DESCRIPTOR:
-                               case SHORT_EVENT_DESCRIPTOR:
                                case LINKAGE_DESCRIPTOR:
                                case COMPONENT_DESCRIPTOR:
+                               case CONTENT_DESCRIPTOR:
+                               case PARENTAL_RATING_DESCRIPTOR:
+                               case PDC_DESCRIPTOR:
                                {
-                                       __u32 crc = 0;
-                                       int cnt=0;
-                                       while(cnt++ < descr_len)
-                                               crc = (crc << 8) ^ crc32_table[((crc >> 24) ^ data[ptr++]) & 0xFF];
-       
-                                       descriptorMap::iterator it =
-                                               descriptors.find(crc);
+                                       uint32_t crc = calculate_crc_hash(descr, descr_len);
+                                       DescriptorMap::iterator it = descriptors.find(crc);
                                        if ( it == descriptors.end() )
                                        {
                                                CacheSize+=descr_len;
-                                               __u8 *d = new __u8[descr_len];
+                                               uint8_t *d = new uint8_t[descr_len];
                                                memcpy(d, descr, descr_len);
-                                               descriptors[crc] = descriptorPair(1, d);
+                                               descriptors[crc] = DescriptorPair(1, d);
                                        }
                                        else
-                                               ++it->second.first;
-                                       *pdescr++=crc;
+                                               ++it->second.reference_count;
+                                       *pdescr++ = crc;
+                                       break;
+                               }
+                               case SHORT_EVENT_DESCRIPTOR:
+                               {
+                                       //parse the data out from the short event descriptor
+                                       //get the country code, which will be used for converting to UTF8
+                                       std::string cc( (const char*)&descr[2], 3);
+                                       std::transform(cc.begin(), cc.end(), cc.begin(), tolower);
+                                       int table = encodingHandler.getCountryCodeDefaultMapping(cc);
+
+                                       int eventNameLen = descr[5];
+                                       int eventTextLen = descr[6 + eventNameLen];
+
+                                       //convert our strings to UTF8
+                                       std::string eventNameUTF8 = convertDVBUTF8((const unsigned char*)&descr[6], eventNameLen, table, tsidonid);
+                                       std::string textUTF8 = convertDVBUTF8((const unsigned char*)&descr[7 + eventNameLen], eventTextLen, table, tsidonid);
+                                       unsigned int eventNameUTF8len = eventNameUTF8.length();
+                                       unsigned int textUTF8len = textUTF8.length();
+
+                                       //Rebuild the short event descriptor with UTF-8 strings
+
+                                       //Save the title first
+                                       if( eventNameUTF8len > 0 ) //only store the data if there is something to store
+                                       {
+                                               /*this will actually cause us to save some memory
+                                                previously some descriptors didnt match because there text was different and titles the same.
+                                                Now that we store them seperatly we can save some space on title data some rough calculation show anywhere from 20 - 40% savings
+                                               */
+                                               eventNameUTF8len = truncateUTF8(eventNameUTF8, 255 - 6);
+                                               int title_len = 6 + eventNameUTF8len;
+                                               uint8_t *title_data = new uint8_t[title_len + 2];
+                                               title_data[0] = SHORT_EVENT_DESCRIPTOR;
+                                               title_data[1] = title_len;
+                                               title_data[2] = descr[2];
+                                               title_data[3] = descr[3];
+                                               title_data[4] = descr[4];
+                                               title_data[5] = eventNameUTF8len + 1;
+                                               title_data[6] = 0x15; //identify event name as UTF-8
+                                               memcpy(&title_data[7], eventNameUTF8.data(), eventNameUTF8len);
+                                               title_data[7 + eventNameUTF8len] = 0;
+
+                                               //Calculate the CRC, based on our new data
+                                               title_len += 2; //add 2 the length to include the 2 bytes in the header
+                                               uint32_t title_crc = calculate_crc_hash(title_data, title_len);
+
+                                               DescriptorMap::iterator it = descriptors.find(title_crc);
+                                               if ( it == descriptors.end() )
+                                               {
+                                                       CacheSize += title_len;
+                                                       descriptors[title_crc] = DescriptorPair(1, title_data);
+                                               }
+                                               else
+                                               {
+                                                       ++it->second.reference_count;
+                                                       delete [] title_data;
+                                               }
+                                               *pdescr++ = title_crc;
+                                       }
+
+                                       //save the text
+                                       if( textUTF8len > 0 ) //only store the data if there is something to store
+                                       {
+                                               textUTF8len = truncateUTF8(textUTF8, 255 - 6);
+                                               int text_len = 6 + textUTF8len;
+                                               uint8_t *text_data = new uint8_t[text_len + 2];
+                                               text_data[0] = SHORT_EVENT_DESCRIPTOR;
+                                               text_data[1] = text_len;
+                                               text_data[2] = descr[2];
+                                               text_data[3] = descr[3];
+                                               text_data[4] = descr[4];
+                                               text_data[5] = 0;
+                                               text_data[6] = textUTF8len + 1; //identify text as UTF-8
+                                               text_data[7] = 0x15; //identify text as UTF-8
+                                               memcpy(&text_data[8], textUTF8.data(), textUTF8len);
+
+                                               text_len += 2; //add 2 the length to include the 2 bytes in the header
+                                               uint32_t text_crc = calculate_crc_hash(text_data, text_len);
+
+                                               DescriptorMap::iterator it = descriptors.find(text_crc);
+                                               if ( it == descriptors.end() )
+                                               {
+                                                       CacheSize += text_len;
+                                                       descriptors[text_crc] = DescriptorPair(1, text_data);
+                                               }
+                                               else
+                                               {
+                                                       ++it->second.reference_count;
+                                                       delete [] text_data;
+                                               }
+                                               *pdescr++ = text_crc;
+                                       }
                                        break;
                                }
                                default: // do not cache all other descriptors
-                                       ptr += descr_len;
                                        break;
                        }
+                       ptr += descr_len;
                        size -= descr_len;
                }
                else
                        break;
        }
+       memcpy(rawEITdata, (uint8_t*)e, 10);
        ASSERT(pdescr <= &descr[65]);
-       ByteSize = 10+((pdescr-descr)*4);
-       EITdata = new __u8[ByteSize];
-       CacheSize+=ByteSize;
-       memcpy(EITdata, (__u8*) e, 10);
-       memcpy(EITdata+10, descr, ByteSize-10);
+       n_crc = pdescr - descr;
+       if (n_crc)
+       {
+               crc_list = new uint32_t[n_crc];
+               memcpy(crc_list, descr, n_crc * sizeof(uint32_t));
+       }
+       CacheSize += sizeof(*this) + n_crc * sizeof(uint32_t);
 }
 
 const eit_event_struct* eventData::get() const
 {
-       int pos = 12;
-       int tmp = ByteSize-10;
-       memcpy(data, EITdata, 10);
-       int descriptors_length=0;
-       __u32 *p = (__u32*)(EITdata+10);
-       while(tmp>3)
+       unsigned int pos = 12;
+       memcpy(data, rawEITdata, 10);
+       unsigned int descriptors_length = 0;
+       for (uint8_t i = 0; i < n_crc; ++i)
        {
-               descriptorMap::iterator it =
-                       descriptors.find(*p++);
-               if ( it != descriptors.end() )
+               DescriptorMap::iterator it = descriptors.find(crc_list[i]);
+               if (it != descriptors.end())
                {
-                       int b = it->second.second[1]+2;
-                       memcpy(data+pos, it->second.second, b );
-                       pos += b;
-                       descriptors_length += b;
+                       unsigned int b = it->second.data[1] + 2;
+                       if (pos + b < sizeof(data))
+                       {
+                               memcpy(data + pos, it->second.data, b);
+                               pos += b;
+                               descriptors_length += b;
+                       }
                }
                else
-                       eFatal("LINE %d descriptor not found in descriptor cache %08x!!!!!!", __LINE__, *(p-1));
-               tmp-=4;
+                       cacheCorrupt("eventData::get");
        }
-       ASSERT(pos <= 4108);
        data[10] = (descriptors_length >> 8) & 0x0F;
        data[11] = descriptors_length & 0xFF;
        return (eit_event_struct*)data;
@@ -139,83 +288,95 @@ const eit_event_struct* eventData::get() const
 
 eventData::~eventData()
 {
-       if ( ByteSize )
+       for ( uint8_t i = 0; i < n_crc; ++i )
        {
-               CacheSize -= ByteSize;
-               __u32 *d = (__u32*)(EITdata+10);
-               ByteSize -= 10;
-               while(ByteSize>3)
+               DescriptorMap::iterator it = descriptors.find(crc_list[i]);
+               if ( it != descriptors.end() )
                {
-                       descriptorMap::iterator it =
-                               descriptors.find(*d++);
-                       if ( it != descriptors.end() )
+                       DescriptorPair &p = it->second;
+                       if (!--p.reference_count) // no more used descriptor
                        {
-                               descriptorPair &p = it->second;
-                               if (!--p.first) // no more used descriptor
-                               {
-                                       CacheSize -= it->second.second[1];
-                                       delete [] it->second.second;    // free descriptor memory
-                                       descriptors.erase(it);  // remove entry from descriptor map
-                               }
+                               CacheSize -= it->second.data[1];
+                               delete [] it->second.data;      // free descriptor memory
+                               descriptors.erase(it);  // remove entry from descriptor map
                        }
-                       else
-                               eFatal("LINE %d descriptor not found in descriptor cache %08x!!!!!!", __LINE__, *(d-1));
-                       ByteSize -= 4;
                }
-               delete [] EITdata;
+               else
+               {
+                       cacheCorrupt("eventData::~eventData");
+               }
        }
+       delete [] crc_list;
+       CacheSize -= sizeof(*this) + n_crc * sizeof(uint32_t);
 }
 
 void eventData::load(FILE *f)
 {
-       int size=0;
+       int size = 0;
        int id=0;
-       __u8 header[2];
-       descriptorPair p;
+       DescriptorPair p;
+       uint8_t header[2];
        fread(&size, sizeof(int), 1, f);
+       descriptors.rehash(size);
        while(size)
        {
-               fread(&id, sizeof(__u32), 1, f);
-               fread(&p.first, sizeof(int), 1, f);
+               fread(&id, sizeof(uint32_t), 1, f);
+               fread(&p.reference_count, sizeof(int), 1, f);
                fread(header, 2, 1, f);
                int bytes = header[1]+2;
-               p.second = new __u8[bytes];
-               p.second[0] = header[0];
-               p.second[1] = header[1];
-               fread(p.second+2, bytes-2, 1, f);
-               descriptors[id]=p;
+               p.data = new uint8_t[bytes];
+               p.data[0] = header[0];
+               p.data[1] = header[1];
+               fread(p.data+2, bytes-2, 1, f);
+               descriptors[id] = p;
                --size;
-               CacheSize+=bytes;
        }
 }
 
 void eventData::save(FILE *f)
 {
+       if (isCacheCorrupt)
+               return;
        int size=descriptors.size();
-       descriptorMap::iterator it(descriptors.begin());
+       DescriptorMap::iterator it(descriptors.begin());
        fwrite(&size, sizeof(int), 1, f);
        while(size)
        {
-               fwrite(&it->first, sizeof(__u32), 1, f);
-               fwrite(&it->second.first, sizeof(int), 1, f);
-               fwrite(it->second.second, it->second.second[1]+2, 1, f);
+               fwrite(&it->first, sizeof(uint32_t), 1, f);
+               fwrite(&it->second.reference_count, sizeof(int), 1, f);
+               fwrite(it->second.data, it->second.data[1]+2, 1, f);
                ++it;
                --size;
        }
 }
 
+void eventData::cacheCorrupt(const char* context)
+{
+
+       eDebug("[eventData] EPG Cache is corrupt (%s), you should restart Enigma!", context);
+       if (!isCacheCorrupt)
+       {
+               isCacheCorrupt = true;
+               if (!eEPGCache::instance->m_filename.empty())
+                       unlink(eEPGCache::instance->m_filename.c_str()); // Remove corrupt EPG data
+       }
+}
+
 eEPGCache* eEPGCache::instance;
-pthread_mutex_t eEPGCache::cache_lock=
+static pthread_mutex_t cache_lock =
        PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
-pthread_mutex_t eEPGCache::channel_map_lock=
+static pthread_mutex_t channel_map_lock =
        PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
 
 DEFINE_REF(eEPGCache)
 
 eEPGCache::eEPGCache()
-       :messages(this,1), cleanTimer(eTimer::create(this)), m_running(0)//, paused(0)
+       :messages(this,1), cleanTimer(eTimer::create(this)), m_running(false)
 {
-       eDebug("[EPGC] Initialized EPGCache (wait for setCacheFile call now)");
+       eDebug("[eEPGCache] Initialized EPGCache (wait for setCacheFile call now)");
+
+       enabledSources = 0;
+       historySeconds = 0;
 
        CONNECT(messages.recv_msg, eEPGCache::gotMessage);
        CONNECT(eDVBLocalTimeHandler::getInstance()->m_timeUpdated, eEPGCache::timeUpdated);
@@ -229,16 +390,15 @@ eEPGCache::eEPGCache()
                res_mgr->connectChannelAdded(slot(*this,&eEPGCache::DVBChannelAdded), m_chanAddedConn);
 
        instance=this;
-       memset(m_filename, 0, sizeof(m_filename));
 }
 
 void eEPGCache::setCacheFile(const char *path)
 {
-       bool inited = !!strlen(m_filename);
-       strncpy(m_filename, path, 1024);
+       bool inited = !m_filename.empty();
+       m_filename = path;
        if (!inited)
        {
-               eDebug("[EPGC] setCacheFile read/write epg data from/to '%s'", m_filename);
+               eDebug("[eEPGCache] setCacheFile read/write epg data from/to '%s'", m_filename.c_str());
                if (eDVBLocalTimeHandler::getInstance()->ready())
                        timeUpdated();
        }
@@ -246,15 +406,15 @@ void eEPGCache::setCacheFile(const char *path)
 
 void eEPGCache::timeUpdated()
 {
-       if (strlen(m_filename))
+       if (!m_filename.empty())
        {
-               if (!sync())
+               if (!m_running)
                {
-                       eDebug("[EPGC] time updated.. start EPG Mainloop");
+                       eDebug("[eEPGCache] time updated.. start EPG Mainloop");
                        run();
+                       m_running = true;
                        singleLock s(channel_map_lock);
-                       channelMapIterator it = m_knownChannels.begin();
-                       for (; it != m_knownChannels.end(); ++it)
+                       for (ChannelMap::const_iterator it = m_knownChannels.begin(); it != m_knownChannels.end(); ++it)
                        {
                                if (it->second->state == -1) {
                                        it->second->state=0;
@@ -265,7 +425,7 @@ void eEPGCache::timeUpdated()
                        messages.send(Message(Message::timeChanged));
        }
        else
-               eDebug("[EPGC] time updated.. but cache file not set yet.. dont start epg!!");
+               eDebug("[eEPGCache] time updated.. but cache file not set yet.. dont start epg!!");
 }
 
 void eEPGCache::DVBChannelAdded(eDVBChannel *chan)
@@ -292,8 +452,7 @@ void eEPGCache::DVBChannelAdded(eDVBChannel *chan)
 
 void eEPGCache::DVBChannelRunning(iDVBChannel *chan)
 {
-       channelMapIterator it =
-               m_knownChannels.find(chan);
+       ChannelMap::const_iterator it = m_knownChannels.find(chan);
        if ( it == m_knownChannels.end() )
                eDebug("[eEPGCache] will start non existing channel %p !!!", chan);
        else
@@ -333,6 +492,36 @@ void eEPGCache::DVBChannelRunning(iDVBChannel *chan)
                                        return;
                                }
 
+#ifdef ENABLE_VIRGIN
+                               res = demux->createSectionReader( this, data.m_VirginNowNextReader );
+                               if ( res )
+                               {
+                                       eDebug("[eEPGCache] couldnt initialize virgin nownext reader!!");
+                                       return;
+                               }
+
+                               res = demux->createSectionReader( this, data.m_VirginScheduleReader );
+                               if ( res )
+                               {
+                                       eDebug("[eEPGCache] couldnt initialize virgin schedule reader!!");
+                                       return;
+                               }
+#endif
+#ifdef ENABLE_NETMED
+                               res = demux->createSectionReader( this, data.m_NetmedScheduleReader );
+                               if ( res )
+                               {
+                                       eDebug("[eEPGCache] couldnt initialize netmed schedule reader!!");
+                                       return;
+                               }
+
+                               res = demux->createSectionReader( this, data.m_NetmedScheduleOtherReader );
+                               if ( res )
+                               {
+                                       eDebug("[eEPGCache] couldnt initialize netmed schedule other reader!!");
+                                       return;
+                               }
+#endif
                                res = demux->createSectionReader( this, data.m_ViasatReader );
                                if ( res )
                                {
@@ -361,8 +550,23 @@ void eEPGCache::DVBChannelRunning(iDVBChannel *chan)
                                        return;
                                }
 #endif
-                               if (m_running) {
-                                       data.state=0;
+#if ENABLE_FREESAT
+                               res = demux->createSectionReader( this, data.m_FreeSatScheduleOtherReader );
+                               if ( res )
+                               {
+                                       eDebug("[eEPGCache] couldnt initialize FreeSat reader!!");
+                                       return;
+                               }
+                               res = demux->createSectionReader( this, data.m_FreeSatScheduleOtherReader2 );
+                               if ( res )
+                               {
+                                       eDebug("[eEPGCache] couldnt initialize FreeSat reader 2!!");
+                                       return;
+                               }
+#endif
+                               if (m_running)
+                               {
+                                       data.state = 0;
                                        messages.send(Message(Message::startChannel, chan));
                                        // -> gotMessage -> changedService
                                }
@@ -375,8 +579,7 @@ void eEPGCache::DVBChannelRunning(iDVBChannel *chan)
 
 void eEPGCache::DVBChannelStateChanged(iDVBChannel *chan)
 {
-       channelMapIterator it =
-               m_knownChannels.find(chan);
+       ChannelMap::iterator it = m_knownChannels.find(chan);
        if ( it != m_knownChannels.end() )
        {
                int state=0;
@@ -396,14 +599,16 @@ void eEPGCache::DVBChannelStateChanged(iDVBChannel *chan)
                                        eDebug("[eEPGCache] remove channel %p", chan);
                                        if (it->second->state >= 0)
                                                messages.send(Message(Message::leaveChannel, chan));
-                                       pthread_mutex_lock(&it->second->channel_active);
-                                       singleLock s(channel_map_lock);
-                                       m_knownChannels.erase(it);
-                                       pthread_mutex_unlock(&it->second->channel_active);
-                                       delete it->second;
-                                       it->second=0;
+                                       channel_data* cd = it->second;
+                                       pthread_mutex_lock(&cd->channel_active);
+                                       {
+                                               singleLock s(channel_map_lock);
+                                               m_knownChannels.erase(it);
+                                       }
+                                       pthread_mutex_unlock(&cd->channel_active);
+                                       delete cd;
                                        // -> gotMessage -> abortEPG
-                                       break;
+                                       return;
                                }
                                default: // ignore all other events
                                        return;
@@ -414,46 +619,46 @@ void eEPGCache::DVBChannelStateChanged(iDVBChannel *chan)
        }
 }
 
-bool eEPGCache::FixOverlapping(std::pair<eventMap,timeMap> &servicemap, time_t TM, int duration, const timeMap::iterator &tm_it, const uniqueEPGKey &service)
+bool eEPGCache::FixOverlapping(EventCacheItem &servicemap, time_t TM, int duration, const timeMap::iterator &tm_it, const uniqueEPGKey &service)
 {
        bool ret = false;
        timeMap::iterator tmp = tm_it;
-       while ((tmp->first+tmp->second->getDuration()-300) > TM)
+       while ((tmp->first + tmp->second->getDuration() - 300) > TM)
        {
-               if(tmp->first != TM 
+               if(tmp->first != TM
 #ifdef ENABLE_PRIVATE_EPG
-                       && tmp->second->type != PRIVATE 
+                       && tmp->second->type != PRIVATE
 #endif
-#ifdef ENABLE_MHW
+#ifdef ENABLE_MHW_EPG
                        && tmp->second->type != MHW
 #endif
                        )
                {
-                       __u16 event_id = tmp->second->getEventID();
-                       servicemap.first.erase(event_id);
+                       uint16_t event_id = tmp->second->getEventID();
+                       servicemap.byEvent.erase(event_id);
 #ifdef EPG_DEBUG
                        Event evt((uint8_t*)tmp->second->get());
                        eServiceEvent event;
                        event.parseFrom(&evt, service.sid<<16|service.onid);
-                       eDebug("(1)erase no more used event %04x %d\n%s %s\n%s",
+                       eDebug("[eEPGCache] (1)erase no more used event %04x %d\n%s %s\n%s",
                                service.sid, event_id,
                                event.getBeginTimeString().c_str(),
                                event.getEventName().c_str(),
                                event.getExtendedDescription().c_str());
 #endif
                        delete tmp->second;
-                       if (tmp == servicemap.second.begin())
+                       if (tmp == servicemap.byTime.begin())
                        {
-                               servicemap.second.erase(tmp);
+                               servicemap.byTime.erase(tmp);
                                break;
                        }
                        else
-                               servicemap.second.erase(tmp--);
+                               servicemap.byTime.erase(tmp--);
                        ret = true;
                }
                else
                {
-                       if (tmp == servicemap.second.begin())
+                       if (tmp == servicemap.byTime.begin())
                                break;
                        --tmp;
                }
@@ -464,97 +669,107 @@ bool eEPGCache::FixOverlapping(std::pair<eventMap,timeMap> &servicemap, time_t T
        {
                if (tmp->first != TM && tmp->second->type != PRIVATE)
                {
-                       __u16 event_id = tmp->second->getEventID();
-                       servicemap.first.erase(event_id);
-#ifdef EPG_DEBUG  
+                       uint16_t event_id = tmp->second->getEventID();
+                       servicemap.byEvent.erase(event_id);
+#ifdef EPG_DEBUG
                        Event evt((uint8_t*)tmp->second->get());
                        eServiceEvent event;
                        event.parseFrom(&evt, service.sid<<16|service.onid);
-                       eDebug("(2)erase no more used event %04x %d\n%s %s\n%s",
+                       eDebug("[eEPGCache] (2)erase no more used event %04x %d\n%s %s\n%s",
                                service.sid, event_id,
                                event.getBeginTimeString().c_str(),
                                event.getEventName().c_str(),
                                event.getExtendedDescription().c_str());
 #endif
                        delete tmp->second;
-                       servicemap.second.erase(tmp++);
+                       servicemap.byTime.erase(tmp++);
                        ret = true;
                }
                else
                        ++tmp;
-               if (tmp == servicemap.second.end())
+               if (tmp == servicemap.byTime.end())
                        break;
        }
        return ret;
 }
 
-void eEPGCache::sectionRead(const __u8 *data, int source, channel_data *channel)
+void eEPGCache::sectionRead(const uint8_t *data, int source, channel_data *channel)
 {
-       eit_t *eit = (eit_t*) data;
+       const eit_t *eit = (const eit_t*) data;
 
-       int len=HILO(eit->section_length)-1;//+3-4;
-       int ptr=EIT_SIZE;
+       int len = eit->getSectionLength()-1;//+3-4;
+       int ptr = EIT_SIZE;
        if ( ptr >= len )
                return;
 
+#if 0
+               /*
+                * disable for now, as this hack breaks EIT parsing for
+                * services with a low segment_last_table_id
+                *
+                * Multichoice should be the exception, not the rule...
+                */
+
        // This fixed the EPG on the Multichoice irdeto systems
        // the EIT packet is non-compliant.. their EIT packet stinks
        if ( data[ptr-1] < 0x40 )
                --ptr;
+#endif
+
+       int onid = eit->getOriginalNetworkId();
+       int tsid = eit->getTransportStreamId();
 
        // Cablecom HACK .. tsid / onid in eit data are incorrect.. so we use
        // it from running channel (just for current transport stream eit data)
-       bool use_transponder_chid = source == SCHEDULE || (source == NOWNEXT && data[0] == 0x4E);
-       eDVBChannelID chid = channel->channel->getChannelID();
-       uniqueEPGKey service( HILO(eit->service_id),
-               use_transponder_chid ? chid.original_network_id.get() : HILO(eit->original_network_id),
-               use_transponder_chid ? chid.transport_stream_id.get() : HILO(eit->transport_stream_id));
+       /*
+        * Make an exception for BEV (onid 0x100, 0x101), which doesn't use
+        * SCHEDULE_OTHER. As a result SCHEDULE will contain data for different tsid's,
+        * so we should not replace it with the current tsid.
+        */
+       bool use_transponder_chid = onid != 0x101 && onid != 0x100 && (source == SCHEDULE || (source == NOWNEXT && data[0] == 0x4E));
+
+       if (use_transponder_chid && channel)
+       {
+               eDVBChannelID chid = channel->channel->getChannelID();
+
+               onid = chid.original_network_id.get();
+               tsid = chid.transport_stream_id.get();
+       }
+
+       uniqueEPGKey service( eit->getServiceID(), onid, tsid);
 
        eit_event_struct* eit_event = (eit_event_struct*) (data+ptr);
        int eit_event_size;
        int duration;
 
-       time_t TM = parseDVBtime(
-                       eit_event->start_time_1,
-                       eit_event->start_time_2,
-                       eit_event->start_time_3,
-                       eit_event->start_time_4,
-                       eit_event->start_time_5);
+       time_t TM = parseDVBtime((const uint8_t*)eit_event + 2);
        time_t now = ::time(0);
 
-       if ( TM != 3599 && TM > -1)
+       if ( TM != 3599 && TM > -1 && channel)
                channel->haveData |= source;
 
        singleLock s(cache_lock);
-       // hier wird immer eine eventMap zurück gegeben.. entweder eine vorhandene..
+       // hier wird immer eine eventMap zurck gegeben.. entweder eine vorhandene..
        // oder eine durch [] erzeugte
-       std::pair<eventMap,timeMap> &servicemap = eventDB[service];
-       eventMap::iterator prevEventIt = servicemap.first.end();
-       timeMap::iterator prevTimeIt = servicemap.second.end();
+       EventCacheItem &servicemap = eventDB[service];
+       eventMap::iterator prevEventIt = servicemap.byEvent.end();
+       timeMap::iterator prevTimeIt = servicemap.byTime.end();
 
        while (ptr<len)
        {
-               __u16 event_hash;
-               eit_event_size = HILO(eit_event->descriptors_loop_length)+EIT_LOOP_SIZE;
+               uint16_t event_hash;
+               eit_event_size = eit_event->getDescriptorsLoopLength()+EIT_LOOP_SIZE;
 
                duration = fromBCD(eit_event->duration_1)*3600+fromBCD(eit_event->duration_2)*60+fromBCD(eit_event->duration_3);
-               TM = parseDVBtime(
-                       eit_event->start_time_1,
-                       eit_event->start_time_2,
-                       eit_event->start_time_3,
-                       eit_event->start_time_4,
-                       eit_event->start_time_5,
-                       &event_hash);
-
-               if ( TM == 3599 )
-                       goto next;
+               TM = parseDVBtime((const uint8_t*)eit_event + 2, &event_hash);
 
-               if ( TM != 3599 && (TM+duration < now || TM > now+14*24*60*60) )
-                       goto next;
-
-               if ( now <= (TM+duration) || TM == 3599 /*NVOD Service*/ )  // old events should not be cached
+               if ( (TM != 3599) &&            // NVOD Service
+                    (now <= (TM+duration)) &&  // skip old events
+                    (TM < (now+28*24*60*60)) &&        // no more than 4 weeks in future
+                    ( (onid != 1714) || (duration != (24*3600-1)) )    // PlatformaHD invalid event
+                  )
                {
-                       __u16 event_id = HILO(eit_event->event_id);
+                       uint16_t event_id = eit_event->getEventId();
                        eventData *evt = 0;
                        int ev_erase_count = 0;
                        int tm_erase_count = 0;
@@ -569,32 +784,31 @@ void eEPGCache::sectionRead(const __u8 *data, int source, channel_data *channel)
 
                        // search in eventmap
                        eventMap::iterator ev_it =
-                               servicemap.first.find(event_id);
+                               servicemap.byEvent.find(event_id);
 
-//                     eDebug("event_id is %d sid is %04x", event_id, service.sid);
+                       //eDebug("[eEPGCache] event_id : %d, sid : %04x", event_id, service.sid);
 
                        // entry with this event_id is already exist ?
-                       if ( ev_it != servicemap.first.end() )
+                       if ( ev_it != servicemap.byEvent.end() )
                        {
                                if ( source > ev_it->second->type )  // update needed ?
                                        goto next; // when not.. then skip this entry
 
                                // search this event in timemap
                                timeMap::iterator tm_it_tmp =
-                                       servicemap.second.find(ev_it->second->getStartTime());
+                                       servicemap.byTime.find(ev_it->second->getStartTime());
 
-                               if ( tm_it_tmp != servicemap.second.end() )
+                               if ( tm_it_tmp != servicemap.byTime.end() )
                                {
                                        if ( tm_it_tmp->first == TM ) // just update eventdata
                                        {
                                                // exempt memory
                                                eventData *tmp = ev_it->second;
-                                               ev_it->second = tm_it_tmp->second =
-                                                       new eventData(eit_event, eit_event_size, source);
+                                               ev_it->second = tm_it_tmp->second = new eventData(eit_event, eit_event_size, source, (tsid<<16)|onid);
                                                if (FixOverlapping(servicemap, TM, duration, tm_it_tmp, service))
                                                {
-                                                       prevEventIt = servicemap.first.end();
-                                                       prevTimeIt = servicemap.second.end();
+                                                       prevEventIt = servicemap.byEvent.end();
+                                                       prevTimeIt = servicemap.byTime.end();
                                                }
                                                delete tmp;
                                                goto next;
@@ -603,8 +817,8 @@ void eEPGCache::sectionRead(const __u8 *data, int source, channel_data *channel)
                                        {
                                                tm_erase_count++;
                                                // delete the found record from timemap
-                                               servicemap.second.erase(tm_it_tmp);
-                                               prevTimeIt=servicemap.second.end();
+                                               servicemap.byTime.erase(tm_it_tmp);
+                                               prevTimeIt = servicemap.byTime.end();
                                        }
                                }
                        }
@@ -612,28 +826,27 @@ void eEPGCache::sectionRead(const __u8 *data, int source, channel_data *channel)
                        // search in timemap, for check of a case if new time has coincided with time of other event
                        // or event was is not found in eventmap
                        timeMap::iterator tm_it =
-                               servicemap.second.find(TM);
+                               servicemap.byTime.find(TM);
 
-                       if ( tm_it != servicemap.second.end() )
+                       if ( tm_it != servicemap.byTime.end() )
                        {
                                // event with same start time but another event_id...
                                if ( source > tm_it->second->type &&
-                                       ev_it == servicemap.first.end() )
+                                       ev_it == servicemap.byEvent.end() )
                                        goto next; // when not.. then skip this entry
 
                                // search this time in eventmap
-                               eventMap::iterator ev_it_tmp =
-                                       servicemap.first.find(tm_it->second->getEventID());
+                               eventMap::iterator ev_it_tmp = servicemap.byEvent.find(tm_it->second->getEventID());
 
-                               if ( ev_it_tmp != servicemap.first.end() )
+                               if ( ev_it_tmp != servicemap.byEvent.end() )
                                {
                                        ev_erase_count++;
                                        // delete the found record from eventmap
-                                       servicemap.first.erase(ev_it_tmp);
-                                       prevEventIt=servicemap.first.end();
+                                       servicemap.byEvent.erase(ev_it_tmp);
+                                       prevEventIt = servicemap.byEvent.end();
                                }
                        }
-                       evt = new eventData(eit_event, eit_event_size, source);
+                       evt = new eventData(eit_event, eit_event_size, source, (tsid<<16)|onid);
 #ifdef EPG_DEBUG
                        bool consistencyCheck=true;
 #endif
@@ -649,90 +862,90 @@ void eEPGCache::sectionRead(const __u8 *data, int source, channel_data *channel)
                        {
                                // exempt memory
                                delete ev_it->second;
-                               tm_it=prevTimeIt=servicemap.second.insert( prevTimeIt, std::pair<const time_t, eventData*>( TM, evt ) );
-                               ev_it->second=evt;
+                               tm_it = prevTimeIt = servicemap.byTime.insert( prevTimeIt, std::pair<const time_t, eventData*>( TM, evt ) );
+                               ev_it->second = evt;
                        }
                        else if (ev_erase_count > 0 && tm_erase_count == 0)
                        {
                                // exempt memory
                                delete tm_it->second;
-                               ev_it=prevEventIt=servicemap.first.insert( prevEventIt, std::pair<const __u16, eventData*>( event_id, evt) );
-                               tm_it->second=evt;
+                               ev_it = prevEventIt = servicemap.byEvent.insert( prevEventIt, std::pair<const uint16_t, eventData*>( event_id, evt) );
+                               tm_it->second = evt;
                        }
                        else // added new eventData
                        {
 #ifdef EPG_DEBUG
                                consistencyCheck=false;
 #endif
-                               ev_it=prevEventIt=servicemap.first.insert( prevEventIt, std::pair<const __u16, eventData*>( event_id, evt) );
-                               tm_it=prevTimeIt=servicemap.second.insert( prevTimeIt, std::pair<const time_t, eventData*>( TM, evt ) );
+                               ev_it = prevEventIt = servicemap.byEvent.insert( prevEventIt, std::pair<const uint16_t, eventData*>( event_id, evt) );
+                               tm_it = prevTimeIt = servicemap.byTime.insert( prevTimeIt, std::pair<const time_t, eventData*>( TM, evt ) );
                        }
 
 #ifdef EPG_DEBUG
                        if ( consistencyCheck )
                        {
                                if ( tm_it->second != evt || ev_it->second != evt )
-                                       eFatal("tm_it->second != ev_it->second");
+                                       eFatal("[eEPGCache] tm_it->second != ev_it->second");
                                else if ( tm_it->second->getStartTime() != tm_it->first )
-                                       eFatal("event start_time(%d) non equal timemap key(%d)",
-                                               tm_it->second->getStartTime(), tm_it->first );
+                                       eFatal("[eEPGCache] event start_time(%d) non equal timemap key(%d)",
+                                               (int)tm_it->second->getStartTime(), (int)tm_it->first );
                                else if ( tm_it->first != TM )
-                                       eFatal("timemap key(%d) non equal TM(%d)",
-                                               tm_it->first, TM);
+                                       eFatal("[eEPGCache] timemap key(%d) non equal TM(%d)",
+                                               (int)tm_it->first, (int)TM);
                                else if ( ev_it->second->getEventID() != ev_it->first )
-                                       eFatal("event_id (%d) non equal event_map key(%d)",
-                                               ev_it->second->getEventID(), ev_it->first);
+                                       eFatal("[eEPGCache] event_id (%d) non equal event_map key(%d)",
+                                               (int)ev_it->second->getEventID(), (int)ev_it->first);
                                else if ( ev_it->first != event_id )
-                                       eFatal("eventmap key(%d) non equal event_id(%d)",
-                                               ev_it->first, event_id );
+                                       eFatal("[eEPGCache] eventmap key(%d) non equal event_id(%d)",
+                                               (int)ev_it->first, (int)event_id );
                        }
 #endif
                        if (FixOverlapping(servicemap, TM, duration, tm_it, service))
                        {
-                               prevEventIt = servicemap.first.end();
-                               prevTimeIt = servicemap.second.end();
+                               prevEventIt = servicemap.byEvent.end();
+                               prevTimeIt = servicemap.byTime.end();
                        }
                }
 next:
 #ifdef EPG_DEBUG
-               if ( servicemap.first.size() != servicemap.second.size() )
+               if ( servicemap.byEvent.size() != servicemap.byTime.size() )
                {
                        FILE *f = fopen("/hdd/event_map.txt", "w+");
                        int i=0;
-                       for (eventMap::iterator it(servicemap.first.begin())
-                               ; it != servicemap.first.end(); ++it )
+                       for (eventMap::iterator it(servicemap.byEvent.begin())
+                               ; it != servicemap.byEvent.end(); ++it )
                                fprintf(f, "%d(key %d) -> time %d, event_id %d, data %p\n", 
                                        i++, (int)it->first, (int)it->second->getStartTime(), (int)it->second->getEventID(), it->second );
                        fclose(f);
                        f = fopen("/hdd/time_map.txt", "w+");
                        i=0;
-                       for (timeMap::iterator it(servicemap.second.begin())
-                               ; it != servicemap.second.end(); ++it )
+                       for (timeMap::iterator it(servicemap.byTime.begin())
+                               ; it != servicemap.byTime.end(); ++it )
                                        fprintf(f, "%d(key %d) -> time %d, event_id %d, data %p\n", 
                                                i++, (int)it->first, (int)it->second->getStartTime(), (int)it->second->getEventID(), it->second );
                        fclose(f);
 
-                       eFatal("(1)map sizes not equal :( sid %04x tsid %04x onid %04x size %d size2 %d", 
-                               service.sid, service.tsid, service.onid, 
-                               servicemap.first.size(), servicemap.second.size() );
+                       eFatal("[eEPGCache] (1)map sizes not equal :( sid %04x tsid %04x onid %04x size %d size2 %d",
+                               service.sid, service.tsid, service.onid,
+                               servicemap.byEvent.size(), servicemap.byTime.size() );
                }
 #endif
                ptr += eit_event_size;
-               eit_event=(eit_event_struct*)(((__u8*)eit_event)+eit_event_size);
+               eit_event = (eit_event_struct*)(((uint8_t*)eit_event) + eit_event_size);
        }
 }
 
 void eEPGCache::flushEPG(const uniqueEPGKey & s)
 {
-       eDebug("[EPGC] flushEPG %d", (int)(bool)s);
+       eDebug("[eEPGCache] flushEPG %d", (int)(bool)s);
        singleLock l(cache_lock);
        if (s)  // clear only this service
        {
                eventCache::iterator it = eventDB.find(s);
                if ( it != eventDB.end() )
                {
-                       eventMap &evMap = it->second.first;
-                       timeMap &tmMap = it->second.second;
+                       eventMap &evMap = it->second.byEvent;
+                       timeMap &tmMap = it->second.byTime;
                        tmMap.clear();
                        for (eventMap::iterator i = evMap.begin(); i != evMap.end(); ++i)
                                delete i->second;
@@ -756,8 +969,8 @@ void eEPGCache::flushEPG(const uniqueEPGKey & s)
                for (eventCache::iterator it(eventDB.begin());
                        it != eventDB.end(); ++it)
                {
-                       eventMap &evMap = it->second.first;
-                       timeMap &tmMap = it->second.second;
+                       eventMap &evMap = it->second.byEvent;
+                       timeMap &tmMap = it->second.byTime;
                        for (eventMap::iterator i = evMap.begin(); i != evMap.end(); ++i)
                                delete i->second;
                        evMap.clear();
@@ -769,42 +982,38 @@ void eEPGCache::flushEPG(const uniqueEPGKey & s)
 #endif
                channelLastUpdated.clear();
                singleLock m(channel_map_lock);
-               for (channelMapIterator it(m_knownChannels.begin()); it != m_knownChannels.end(); ++it)
+               for (ChannelMap::const_iterator it(m_knownChannels.begin()); it != m_knownChannels.end(); ++it)
                        it->second->startEPG();
        }
-       eDebug("[EPGC] %i bytes for cache used", eventData::CacheSize);
 }
 
 void eEPGCache::cleanLoop()
 {
-       singleLock s(cache_lock);
-       if (!eventDB.empty())
-       {
-               eDebug("[EPGC] start cleanloop");
-
-               time_t now = ::time(0);
+       { /* scope for cache lock */
+               time_t now = ::time(0) - historySeconds;
+               singleLock s(cache_lock);
 
                for (eventCache::iterator DBIt = eventDB.begin(); DBIt != eventDB.end(); DBIt++)
                {
                        bool updated = false;
-                       for (timeMap::iterator It = DBIt->second.second.begin(); It != DBIt->second.second.end() && It->first < now;)
+                       for (timeMap::iterator It = DBIt->second.byTime.begin(); It != DBIt->second.byTime.end() && It->first < now;)
                        {
                                if ( now > (It->first+It->second->getDuration()) )  // outdated normal entry (nvod references to)
                                {
                                        // remove entry from eventMap
-                                       eventMap::iterator b(DBIt->second.first.find(It->second->getEventID()));
-                                       if ( b != DBIt->second.first.end() )
+                                       eventMap::iterator b(DBIt->second.byEvent.find(It->second->getEventID()));
+                                       if ( b != DBIt->second.byEvent.end() )
                                        {
                                                // release Heap Memory for this entry   (new ....)
-//                                             eDebug("[EPGC] delete old event (evmap)");
-                                               DBIt->second.first.erase(b);
+                                               //eDebug("[eEPGCache] delete old event (evmap)");
+                                               DBIt->second.byEvent.erase(b);
                                        }
 
                                        // remove entry from timeMap
-//                                     eDebug("[EPGC] release heap mem");
+                                       //eDebug("[eEPGCache] release heap mem");
                                        delete It->second;
-                                       DBIt->second.second.erase(It++);
-//                                     eDebug("[EPGC] delete old event (timeMap)");
+                                       DBIt->second.byTime.erase(It++);
+                                       //eDebug("[eEPGCache] delete old event (timeMap)");
                                        updated = true;
                                }
                                else
@@ -817,7 +1026,7 @@ void eEPGCache::cleanLoop()
                                        content_time_tables.find( DBIt->first );
                                if ( x != content_time_tables.end() )
                                {
-                                       timeMap &tmMap = DBIt->second.second;
+                                       timeMap &tmMap = DBIt->second.byTime;
                                        for ( contentMap::iterator i = x->second.begin(); i != x->second.end(); )
                                        {
                                                for ( contentTimeMap::iterator it(i->second.begin());
@@ -837,19 +1046,18 @@ void eEPGCache::cleanLoop()
                        }
 #endif
                }
-               eDebug("[EPGC] stop cleanloop");
-               eDebug("[EPGC] %i bytes for cache used", eventData::CacheSize);
-       }
+       } /* release lock */
        cleanTimer->start(CLEAN_INTERVAL,true);
 }
 
 eEPGCache::~eEPGCache()
 {
+       m_running = false;
        messages.send(Message::quit);
        kill(); // waiting for thread shutdown
        singleLock s(cache_lock);
        for (eventCache::iterator evIt = eventDB.begin(); evIt != eventDB.end(); evIt++)
-               for (eventMap::iterator It = evIt->second.first.begin(); It != evIt->second.first.end(); It++)
+               for (eventMap::iterator It = evIt->second.byEvent.begin(); It != evIt->second.byEvent.end(); It++)
                        delete It->second;
 }
 
@@ -863,8 +1071,7 @@ void eEPGCache::gotMessage( const Message &msg )
                case Message::startChannel:
                {
                        singleLock s(channel_map_lock);
-                       channelMapIterator channel =
-                               m_knownChannels.find(msg.channel);
+                       ChannelMap::const_iterator channel = m_knownChannels.find(msg.channel);
                        if ( channel != m_knownChannels.end() )
                                channel->second->startChannel();
                        break;
@@ -872,8 +1079,7 @@ void eEPGCache::gotMessage( const Message &msg )
                case Message::leaveChannel:
                {
                        singleLock s(channel_map_lock);
-                       channelMapIterator channel =
-                               m_knownChannels.find(msg.channel);
+                       ChannelMap::const_iterator channel = m_knownChannels.find(msg.channel);
                        if ( channel != m_knownChannels.end() )
                                channel->second->abortEPG();
                        break;
@@ -885,7 +1091,7 @@ void eEPGCache::gotMessage( const Message &msg )
                case Message::got_private_pid:
                {
                        singleLock s(channel_map_lock);
-                       for (channelMapIterator it(m_knownChannels.begin()); it != m_knownChannels.end(); ++it)
+                       for (ChannelMap::const_iterator it(m_knownChannels.begin()); it != m_knownChannels.end(); ++it)
                        {
                                eDVBChannel *channel = (eDVBChannel*) it->first;
                                channel_data *data = it->second;
@@ -906,9 +1112,9 @@ void eEPGCache::gotMessage( const Message &msg )
                                                update = ZAP_DELAY;
                                        data->startPrivateTimer->start(update, 1);
                                        if (update >= 60000)
-                                               eDebug("[EPGC] next private update in %i min", update/60000);
+                                               eDebug("[eEPGCache] next private update in %i min", update/60000);
                                        else if (update >= 1000)
-                                               eDebug("[EPGC] next private update in %i sec", update/1000);
+                                               eDebug("[eEPGCache] next private update in %i sec", update/1000);
                                        break;
                                }
                        }
@@ -919,7 +1125,7 @@ void eEPGCache::gotMessage( const Message &msg )
                case Message::got_mhw2_channel_pid:
                {
                        singleLock s(channel_map_lock);
-                       for (channelMapIterator it(m_knownChannels.begin()); it != m_knownChannels.end(); ++it)
+                       for (ChannelMap::const_iterator it(m_knownChannels.begin()); it != m_knownChannels.end(); ++it)
                        {
                                eDVBChannel *channel = (eDVBChannel*) it->first;
                                channel_data *data = it->second;
@@ -928,7 +1134,7 @@ void eEPGCache::gotMessage( const Message &msg )
                                        chid.original_network_id.get() == msg.service.onid )
                                {
                                        data->m_mhw2_channel_pid = msg.pid;
-                                       eDebug("[EPGC] got mhw2 channel pid %04x", msg.pid);
+                                       eDebug("[eEPGCache] got mhw2 channel pid %04x", msg.pid);
                                        break;
                                }
                        }
@@ -937,7 +1143,7 @@ void eEPGCache::gotMessage( const Message &msg )
                case Message::got_mhw2_title_pid:
                {
                        singleLock s(channel_map_lock);
-                       for (channelMapIterator it(m_knownChannels.begin()); it != m_knownChannels.end(); ++it)
+                       for (ChannelMap::const_iterator it(m_knownChannels.begin()); it != m_knownChannels.end(); ++it)
                        {
                                eDVBChannel *channel = (eDVBChannel*) it->first;
                                channel_data *data = it->second;
@@ -946,7 +1152,7 @@ void eEPGCache::gotMessage( const Message &msg )
                                        chid.original_network_id.get() == msg.service.onid )
                                {
                                        data->m_mhw2_title_pid = msg.pid;
-                                       eDebug("[EPGC] got mhw2 title pid %04x", msg.pid);
+                                       eDebug("[eEPGCache] got mhw2 title pid %04x", msg.pid);
                                        break;
                                }
                        }
@@ -955,7 +1161,7 @@ void eEPGCache::gotMessage( const Message &msg )
                case Message::got_mhw2_summary_pid:
                {
                        singleLock s(channel_map_lock);
-                       for (channelMapIterator it(m_knownChannels.begin()); it != m_knownChannels.end(); ++it)
+                       for (ChannelMap::const_iterator it(m_knownChannels.begin()); it != m_knownChannels.end(); ++it)
                        {
                                eDVBChannel *channel = (eDVBChannel*) it->first;
                                channel_data *data = it->second;
@@ -964,7 +1170,7 @@ void eEPGCache::gotMessage( const Message &msg )
                                        chid.original_network_id.get() == msg.service.onid )
                                {
                                        data->m_mhw2_summary_pid = msg.pid;
-                                       eDebug("[EPGC] got mhw2 summary pid %04x", msg.pid);
+                                       eDebug("[eEPGCache] got mhw2 summary pid %04x", msg.pid);
                                        break;
                                }
                        }
@@ -975,7 +1181,7 @@ void eEPGCache::gotMessage( const Message &msg )
                        cleanLoop();
                        break;
                default:
-                       eDebug("unhandled EPGCache Message!!");
+                       eDebug("[eEPGCache] unhandled EPGCache Message!!");
                        break;
        }
 }
@@ -983,137 +1189,177 @@ void eEPGCache::gotMessage( const Message &msg )
 void eEPGCache::thread()
 {
        hasStarted();
-       m_running=1;
        nice(4);
        load();
        cleanLoop();
        runLoop();
        save();
-       m_running=0;
 }
 
+static const char* EPGDAT_IN_FLASH = "/epg.dat";
+
 void eEPGCache::load()
 {
-       FILE *f = fopen(m_filename, "r");
-       if (f)
+       if (m_filename.empty())
+               m_filename = "/hdd/epg.dat";
+       const char* EPGDAT = m_filename.c_str();
+       std::string filenamex = m_filename + ".loading";
+       const char* EPGDATX = filenamex.c_str();
+       FILE *f = fopen(EPGDAT, "rb");
+       int renameResult;
+       if (f == NULL)
+       {
+               /* No EPG on harddisk, so try internal flash */
+               eDebug("[eEPGCache] %s not found, try %s", EPGDAT, EPGDAT_IN_FLASH);
+               EPGDAT = EPGDAT_IN_FLASH;
+               f = fopen(EPGDAT, "rb");
+               if (f == NULL)
+                       return;
+               renameResult = -1;
+       }
+       else
+       {
+               unlink(EPGDATX);
+               renameResult = rename(EPGDAT, EPGDATX);
+               if (renameResult) eDebug("[eEPGCache] failed to rename %s", EPGDAT);
+       }
        {
-               unlink(m_filename);
                int size=0;
                int cnt=0;
-
+               unsigned int magic=0;
+               unlink(EPGDAT_IN_FLASH);/* Don't keep it around when in flash */
+               fread( &magic, sizeof(int), 1, f);
+               if (magic != 0x98765432)
                {
-                       unsigned int magic=0;
-                       fread( &magic, sizeof(int), 1, f);
-                       if (magic != 0x98765432)
-                       {
-                               eDebug("[EPGC] epg file has incorrect byte order.. dont read it");
-                               fclose(f);
-                               return;
-                       }
-                       char text1[13];
-                       fread( text1, 13, 1, f);
-                       if ( !strncmp( text1, "ENIGMA_EPG_V7", 13) )
+                       eDebug("[eEPGCache] epg file has incorrect byte order.. dont read it");
+                       fclose(f);
+                       return;
+               }
+               char text1[13];
+               fread( text1, 13, 1, f);
+               if ( !memcmp( text1, "ENIGMA_EPG_V7", 13) )
+               {
+                       singleLock s(cache_lock);
+                       fread( &size, sizeof(int), 1, f);
+                       eventDB.rehash(size); /* Reserve buckets in advance */
+                       while(size--)
                        {
-                               singleLock s(cache_lock);
+                               uniqueEPGKey key;
+                               int size=0;
+                               fread( &key, sizeof(uniqueEPGKey), 1, f);
                                fread( &size, sizeof(int), 1, f);
+                               EventCacheItem& item = eventDB[key]; /* Constructs new entry */
                                while(size--)
                                {
-                                       uniqueEPGKey key;
-                                       eventMap evMap;
-                                       timeMap tmMap;
-                                       int size=0;
-                                       fread( &key, sizeof(uniqueEPGKey), 1, f);
-                                       fread( &size, sizeof(int), 1, f);
-                                       while(size--)
+                                       uint8_t len=0;
+                                       uint8_t type=0;
+                                       eventData *event=0;
+                                       fread( &type, sizeof(uint8_t), 1, f);
+                                       fread( &len, sizeof(uint8_t), 1, f);
+                                       event = new eventData(0, len, type);
+                                       event->n_crc = (len-10) / sizeof(uint32_t);
+                                       fread( event->rawEITdata, 10, 1, f);
+                                       if (event->n_crc)
                                        {
-                                               __u8 len=0;
-                                               __u8 type=0;
-                                               eventData *event=0;
-                                               fread( &type, sizeof(__u8), 1, f);
-                                               fread( &len, sizeof(__u8), 1, f);
-                                               event = new eventData(0, len, type);
-                                               event->EITdata = new __u8[len];
-                                               eventData::CacheSize+=len;
-                                               fread( event->EITdata, len, 1, f);
-                                               evMap[ event->getEventID() ]=event;
-                                               tmMap[ event->getStartTime() ]=event;
-                                               ++cnt;
+                                               event->crc_list = new uint32_t[event->n_crc];
+                                               fread( event->crc_list, sizeof(uint32_t), event->n_crc, f);
                                        }
-                                       eventDB[key]=std::pair<eventMap,timeMap>(evMap,tmMap);
+                                       eventData::CacheSize += sizeof(eventData) + event->n_crc * sizeof(uint32_t);
+                                       item.byEvent[event->getEventID()] = event;
+                                       item.byTime[event->getStartTime()] = event;
+                                       ++cnt;
                                }
-                               eventData::load(f);
-                               eDebug("[EPGC] %d events read from %s", cnt, m_filename);
+                       }
+                       eventData::load(f);
+                       eDebug("[eEPGCache] %d events read from %s", cnt, EPGDAT);
 #ifdef ENABLE_PRIVATE_EPG
-                               char text2[11];
-                               fread( text2, 11, 1, f);
-                               if ( !strncmp( text2, "PRIVATE_EPG", 11) )
+                       char text2[11];
+                       fread( text2, 11, 1, f);
+                       if ( !memcmp( text2, "PRIVATE_EPG", 11) )
+                       {
+                               size=0;
+                               fread( &size, sizeof(int), 1, f);
+                               eventDB.rehash(size); /* Reserve buckets in advance */
+                               while(size--)
                                {
-                                       size=0;
+                                       int size=0;
+                                       uniqueEPGKey key;
+                                       fread( &key, sizeof(uniqueEPGKey), 1, f);
+                                       eventMap &evMap = eventDB[key].byEvent;
                                        fread( &size, sizeof(int), 1, f);
                                        while(size--)
                                        {
-                                               int size=0;
-                                               uniqueEPGKey key;
-                                               fread( &key, sizeof(uniqueEPGKey), 1, f);
-                                               eventMap &evMap=eventDB[key].first;
+                                               int size;
+                                               int content_id;
+                                               fread( &content_id, sizeof(int), 1, f);
                                                fread( &size, sizeof(int), 1, f);
                                                while(size--)
                                                {
-                                                       int size;
-                                                       int content_id;
-                                                       fread( &content_id, sizeof(int), 1, f);
-                                                       fread( &size, sizeof(int), 1, f);
-                                                       while(size--)
-                                                       {
-                                                               time_t time1, time2;
-                                                               __u16 event_id;
-                                                               fread( &time1, sizeof(time_t), 1, f);
-                                                               fread( &time2, sizeof(time_t), 1, f);
-                                                               fread( &event_id, sizeof(__u16), 1, f);
-                                                               content_time_tables[key][content_id][time1]=std::pair<time_t, __u16>(time2, event_id);
-                                                               eventMap::iterator it =
-                                                                       evMap.find(event_id);
-                                                               if (it != evMap.end())
-                                                                       it->second->type = PRIVATE;
-                                                       }
+                                                       time_t time1, time2;
+                                                       uint16_t event_id;
+                                                       fread( &time1, sizeof(time_t), 1, f);
+                                                       fread( &time2, sizeof(time_t), 1, f);
+                                                       fread( &event_id, sizeof(uint16_t), 1, f);
+                                                       content_time_tables[key][content_id][time1]=std::pair<time_t, uint16_t>(time2, event_id);
+                                                       eventMap::iterator it =
+                                                               evMap.find(event_id);
+                                                       if (it != evMap.end())
+                                                               it->second->type = PRIVATE;
                                                }
                                        }
                                }
-#endif // ENABLE_PRIVATE_EPG
                        }
-                       else
-                               eDebug("[EPGC] don't read old epg database");
-                       fclose(f);
+#endif // ENABLE_PRIVATE_EPG
+               }
+               else
+                       eDebug("[eEPGCache] don't read old epg database");
+               posix_fadvise(fileno(f), 0, 0, POSIX_FADV_DONTNEED);
+               fclose(f);
+               // We got this far, so the EPG file is okay.
+               if (renameResult == 0)
+               {
+                       renameResult = rename(EPGDATX, EPGDAT);
+                       if (renameResult) eDebug("[eEPGCache] failed to rename epg.dat back");
                }
        }
 }
 
 void eEPGCache::save()
 {
-       /* create empty file */
-       FILE *f = fopen(m_filename, "w");
+       const char* EPGDAT = m_filename.c_str();
+       if (eventData::isCacheCorrupt)
+               return;
+       // only save epg.dat if it's worth the trouble...
+       if (eventData::CacheSize < 10240)
+               return;
 
+       /* create empty file */
+       FILE *f = fopen(EPGDAT, "wb");
        if (!f)
        {
-               eDebug("[EPGC] couldn't save epg data to '%s'(%m)", m_filename);
-               return;
+               eDebug("[eEPGCache] couldn't save epg data to '%s'(%m)", EPGDAT);
+               EPGDAT = EPGDAT_IN_FLASH;
+               f = fopen(EPGDAT, "wb");
+               if (!f)
+                       return;
        }
 
-       char *buf = realpath(m_filename, NULL);
+       char *buf = realpath(EPGDAT, NULL);
        if (!buf)
        {
-               eDebug("[EPGC] realpath to '%s' failed in save (%m)", m_filename);
+               eDebug("[eEPGCache] realpath to '%s' failed in save (%m)", EPGDAT);
                fclose(f);
                return;
        }
 
-       eDebug("[EPGC] store epg to realpath '%s'", buf);
+       eDebug("[eEPGCache] store epg to realpath '%s'", buf);
 
        struct statfs s;
        off64_t tmp;
        if (statfs(buf, &s) < 0) {
-               eDebug("[EPGC] statfs '%s' failed in save (%m)", buf);
+               eDebug("[eEPGCache] statfs '%s' failed in save (%m)", buf);
                fclose(f);
+               free(buf);
                return;
        }
 
@@ -1124,7 +1370,7 @@ void eEPGCache::save()
        tmp*=s.f_bsize;
        if ( tmp < (eventData::CacheSize*12)/10 ) // 20% overhead
        {
-               eDebug("[EPGC] not enough free space at path '%s' %lld bytes availd but %d needed", buf, tmp, (eventData::CacheSize*12)/10);
+               eDebug("[eEPGCache] not enough free space at '%s' %lld bytes available but %u needed", buf, tmp, (eventData::CacheSize*12)/10);
                fclose(f);
                return;
        }
@@ -1138,20 +1384,21 @@ void eEPGCache::save()
        fwrite( &size, sizeof(int), 1, f );
        for (eventCache::iterator service_it(eventDB.begin()); service_it != eventDB.end(); ++service_it)
        {
-               timeMap &timemap = service_it->second.second;
+               timeMap &timemap = service_it->second.byTime;
                fwrite( &service_it->first, sizeof(uniqueEPGKey), 1, f);
                size = timemap.size();
                fwrite( &size, sizeof(int), 1, f);
                for (timeMap::iterator time_it(timemap.begin()); time_it != timemap.end(); ++time_it)
                {
-                       __u8 len = time_it->second->ByteSize;
-                       fwrite( &time_it->second->type, sizeof(__u8), 1, f );
-                       fwrite( &len, sizeof(__u8), 1, f);
-                       fwrite( time_it->second->EITdata, len, 1, f);
+                       uint8_t len = time_it->second->n_crc * sizeof(uint32_t) + 10;
+                       fwrite( &time_it->second->type, sizeof(uint8_t), 1, f );
+                       fwrite( &len, sizeof(uint8_t), 1, f);
+                       fwrite( time_it->second->rawEITdata, 10, 1, f);
+                       fwrite( time_it->second->crc_list, sizeof(uint32_t), time_it->second->n_crc, f);
                        ++cnt;
                }
        }
-       eDebug("[EPGC] %d events written to %s", cnt, m_filename);
+       eDebug("[eEPGCache] %d events written to %s", cnt, EPGDAT);
        eventData::save(f);
 #ifdef ENABLE_PRIVATE_EPG
        const char* text3 = "PRIVATE_EPG";
@@ -1174,7 +1421,7 @@ void eEPGCache::save()
                        {
                                fwrite( &it->first, sizeof(time_t), 1, f);
                                fwrite( &it->second.first, sizeof(time_t), 1, f);
-                               fwrite( &it->second.second, sizeof(__u16), 1, f);
+                               fwrite( &it->second.second, sizeof(uint16_t), 1, f);
                        }
                }
        }
@@ -1209,31 +1456,32 @@ eEPGCache::channel_data::channel_data(eEPGCache *ml)
        pthread_mutex_init(&channel_active, 0);
 }
 
-bool eEPGCache::channel_data::finishEPG()
+void eEPGCache::channel_data::finishEPG()
 {
        if (!isRunning)  // epg ready
        {
-               eDebug("[EPGC] stop caching events(%ld)", ::time(0));
+               eDebug("[eEPGCache] stop caching events(%ld)", ::time(0));
                zapTimer->start(UPDATE_INTERVAL, 1);
-               eDebug("[EPGC] next update in %i min", UPDATE_INTERVAL / 60000);
+               eDebug("[eEPGCache] next update in %i min", UPDATE_INTERVAL / 60000);
                for (unsigned int i=0; i < sizeof(seenSections)/sizeof(tidMap); ++i)
                {
                        seenSections[i].clear();
                        calcedSections[i].clear();
                }
-               singleLock l(cache->cache_lock);
-               cache->channelLastUpdated[channel->getChannelID()] = ::time(0);
 #ifdef ENABLE_MHW_EPG
-               cleanup();
+               cleanupMHW();
 #endif
-               return true;
+#ifdef ENABLE_FREESAT
+               cleanupFreeSat();
+#endif
+               singleLock l(cache_lock);
+               cache->channelLastUpdated[channel->getChannelID()] = ::time(0);
        }
-       return false;
 }
 
 void eEPGCache::channel_data::startEPG()
 {
-       eDebug("[EPGC] start caching events(%ld)", ::time(0));
+       eDebug("[eEPGCache] start caching events(%ld)", ::time(0));
        state=0;
        haveData=0;
        for (unsigned int i=0; i < sizeof(seenSections)/sizeof(tidMap); ++i)
@@ -1241,60 +1489,145 @@ void eEPGCache::channel_data::startEPG()
                seenSections[i].clear();
                calcedSections[i].clear();
        }
+#ifdef ENABLE_MHW_EPG
+               cleanupMHW();
+#endif
+#ifdef ENABLE_FREESAT
+               cleanupFreeSat();
+#endif
 
        eDVBSectionFilterMask mask;
        memset(&mask, 0, sizeof(mask));
 
 #ifdef ENABLE_MHW_EPG
-       mask.pid = 0xD3;
-       mask.data[0] = 0x91;
-       mask.mask[0] = 0xFF;
-       m_MHWReader->connectRead(slot(*this, &eEPGCache::channel_data::readMHWData), m_MHWConn);
-       m_MHWReader->start(mask);
-       isRunning |= MHW;
-       memcpy(&m_MHWFilterMask, &mask, sizeof(eDVBSectionFilterMask));
-
-       mask.pid = m_mhw2_channel_pid;
-       mask.data[0] = 0xC8;
-       mask.mask[0] = 0xFF;
-       mask.data[1] = 0;
-       mask.mask[1] = 0xFF;
-       m_MHWReader2->connectRead(slot(*this, &eEPGCache::channel_data::readMHWData2), m_MHWConn2);
-       m_MHWReader2->start(mask);
-       isRunning |= MHW;
-       memcpy(&m_MHWFilterMask2, &mask, sizeof(eDVBSectionFilterMask));
-       mask.data[1] = 0;
-       mask.mask[1] = 0;
-       m_MHWTimeoutet=false;
+       if (eEPGCache::getInstance()->getEpgSources() & eEPGCache::MHW)
+       {
+               mask.pid = 0xD3;
+               mask.data[0] = 0x91;
+               mask.mask[0] = 0xFF;
+               m_MHWReader->connectRead(slot(*this, &eEPGCache::channel_data::readMHWData), m_MHWConn);
+               m_MHWReader->start(mask);
+               isRunning |= MHW;
+               memcpy(&m_MHWFilterMask, &mask, sizeof(eDVBSectionFilterMask));
+
+               mask.pid = m_mhw2_channel_pid;
+               mask.data[0] = 0xC8;
+               mask.mask[0] = 0xFF;
+               mask.data[1] = 0;
+               mask.mask[1] = 0xFF;
+               m_MHWReader2->connectRead(slot(*this, &eEPGCache::channel_data::readMHWData2), m_MHWConn2);
+               m_MHWReader2->start(mask);
+               isRunning |= MHW;
+               memcpy(&m_MHWFilterMask2, &mask, sizeof(eDVBSectionFilterMask));
+               mask.data[1] = 0;
+               mask.mask[1] = 0;
+               m_MHWTimeoutet=false;
+       }
+#endif
+#ifdef ENABLE_FREESAT
+       if (eEPGCache::getInstance()->getEpgSources() & eEPGCache::FREESAT_SCHEDULE_OTHER)
+       {
+               mask.pid = 3842;
+               mask.flags = eDVBSectionFilterMask::rfCRC;
+               mask.data[0] = 0x60;
+               mask.mask[0] = 0xFE;
+               m_FreeSatScheduleOtherReader->connectRead(slot(*this, &eEPGCache::channel_data::readFreeSatScheduleOtherData), m_FreeSatScheduleOtherConn);
+               m_FreeSatScheduleOtherReader->start(mask);
+
+               /*
+                * faster pid, available on ITV HD transponder.
+                * We rely on the fact that we have either of the two,
+                * never both. (both readers share the same data callback
+                * and status maps)
+                */
+               mask.pid = 3003;
+               m_FreeSatScheduleOtherReader2->connectRead(slot(*this, &eEPGCache::channel_data::readFreeSatScheduleOtherData), m_FreeSatScheduleOtherConn2);
+               m_FreeSatScheduleOtherReader2->start(mask);
+               isRunning |= FREESAT_SCHEDULE_OTHER;
+       }
 #endif
-
        mask.pid = 0x12;
        mask.flags = eDVBSectionFilterMask::rfCRC;
 
-       mask.data[0] = 0x4E;
-       mask.mask[0] = 0xFE;
-       m_NowNextReader->connectRead(slot(*this, &eEPGCache::channel_data::readData), m_NowNextConn);
-       m_NowNextReader->start(mask);
-       isRunning |= NOWNEXT;
+       if (eEPGCache::getInstance()->getEpgSources() & eEPGCache::NOWNEXT)
+       {
+               mask.data[0] = 0x4E;
+               mask.mask[0] = 0xFE;
+               m_NowNextReader->connectRead(bind(slot(*this, &eEPGCache::channel_data::readData), (int)eEPGCache::NOWNEXT), m_NowNextConn);
+               m_NowNextReader->start(mask);
+               isRunning |= NOWNEXT;
+       }
+
+       if (eEPGCache::getInstance()->getEpgSources() & eEPGCache::SCHEDULE)
+       {
+               mask.data[0] = 0x50;
+               mask.mask[0] = 0xF0;
+               m_ScheduleReader->connectRead(bind(slot(*this, &eEPGCache::channel_data::readData), (int)eEPGCache::SCHEDULE), m_ScheduleConn);
+               m_ScheduleReader->start(mask);
+               isRunning |= SCHEDULE;
+       }
+
+       if (eEPGCache::getInstance()->getEpgSources() & eEPGCache::SCHEDULE_OTHER)
+       {
+               mask.data[0] = 0x60;
+               mask.mask[0] = 0xF0;
+               m_ScheduleOtherReader->connectRead(bind(slot(*this, &eEPGCache::channel_data::readData), (int)eEPGCache::SCHEDULE_OTHER), m_ScheduleOtherConn);
+               m_ScheduleOtherReader->start(mask);
+               isRunning |= SCHEDULE_OTHER;
+       }
 
-       mask.data[0] = 0x50;
-       mask.mask[0] = 0xF0;
-       m_ScheduleReader->connectRead(slot(*this, &eEPGCache::channel_data::readData), m_ScheduleConn);
-       m_ScheduleReader->start(mask);
-       isRunning |= SCHEDULE;
+#ifdef ENABLE_VIRGIN
+       if (eEPGCache::getInstance()->getEpgSources() & eEPGCache::VIRGIN_NOWNEXT)
+       {
+               mask.pid = 0x2bc;
+               mask.data[0] = 0x4E;
+               mask.mask[0] = 0xFE;
+               m_VirginNowNextReader->connectRead(bind(slot(*this, &eEPGCache::channel_data::readData), (int)eEPGCache::VIRGIN_NOWNEXT), m_VirginNowNextConn);
+               m_VirginNowNextReader->start(mask);
+               isRunning |= VIRGIN_NOWNEXT;
+       }
 
-       mask.data[0] = 0x60;
-       m_ScheduleOtherReader->connectRead(slot(*this, &eEPGCache::channel_data::readData), m_ScheduleOtherConn);
-       m_ScheduleOtherReader->start(mask);
-       isRunning |= SCHEDULE_OTHER;
+       if (eEPGCache::getInstance()->getEpgSources() & eEPGCache::VIRGIN_SCHEDULE)
+       {
+               mask.pid = 0x2bc;
+               mask.data[0] = 0x50;
+               mask.mask[0] = 0xFE;
+               m_VirginScheduleReader->connectRead(bind(slot(*this, &eEPGCache::channel_data::readData), (int)eEPGCache::VIRGIN_SCHEDULE), m_VirginScheduleConn);
+               m_VirginScheduleReader->start(mask);
+               isRunning |= VIRGIN_SCHEDULE;
+       }
+#endif
+#ifdef ENABLE_NETMED
+       if (eEPGCache::getInstance()->getEpgSources() & eEPGCache::NETMED_SCHEDULE)
+       {
+               mask.pid = 0x1388;
+               mask.data[0] = 0x50;
+               mask.mask[0] = 0xF0;
+               m_NetmedScheduleReader->connectRead(bind(slot(*this, &eEPGCache::channel_data::readData), (int)eEPGCache::NETMED_SCHEDULE), m_NetmedScheduleConn);
+               m_NetmedScheduleReader->start(mask);
+               isRunning |= NETMED_SCHEDULE;
+       }
 
-       mask.pid = 0x39;
+       if (eEPGCache::getInstance()->getEpgSources() & eEPGCache::NETMED_SCHEDULE_OTHER)
+       {
+               mask.pid = 0x1388;
+               mask.data[0] = 0x60;
+               mask.mask[0] = 0xF0;
+               m_NetmedScheduleOtherReader->connectRead(bind(slot(*this, &eEPGCache::channel_data::readData), (int)eEPGCache::NETMED_SCHEDULE_OTHER), m_NetmedScheduleOtherConn);
+               m_NetmedScheduleOtherReader->start(mask);
+               isRunning |= NETMED_SCHEDULE_OTHER;
+       }
+#endif
+       if (eEPGCache::getInstance()->getEpgSources() & eEPGCache::VIASAT)
+       {
+               mask.pid = 0x39;
 
-       mask.data[0] = 0x40;
-       mask.mask[0] = 0x40;
-       m_ViasatReader->connectRead(slot(*this, &eEPGCache::channel_data::readDataViasat), m_ViasatConn);
-       m_ViasatReader->start(mask);
-       isRunning |= VIASAT;
+               mask.data[0] = 0x40;
+               mask.mask[0] = 0x40;
+               m_ViasatReader->connectRead(bind(slot(*this, &eEPGCache::channel_data::readData), (int)eEPGCache::VIASAT), m_ViasatConn);
+               m_ViasatReader->start(mask);
+               isRunning |= VIASAT;
+       }
 
        abortTimer->start(7000,true);
 }
@@ -1305,28 +1638,72 @@ void eEPGCache::channel_data::abortNonAvail()
        {
                if ( !(haveData&NOWNEXT) && (isRunning&NOWNEXT) )
                {
-                       eDebug("[EPGC] abort non avail nownext reading");
+                       eDebug("[eEPGCache] abort non avail nownext reading");
                        isRunning &= ~NOWNEXT;
                        m_NowNextReader->stop();
                        m_NowNextConn=0;
                }
                if ( !(haveData&SCHEDULE) && (isRunning&SCHEDULE) )
                {
-                       eDebug("[EPGC] abort non avail schedule reading");
+                       eDebug("[eEPGCache] abort non avail schedule reading");
                        isRunning &= ~SCHEDULE;
                        m_ScheduleReader->stop();
                        m_ScheduleConn=0;
                }
                if ( !(haveData&SCHEDULE_OTHER) && (isRunning&SCHEDULE_OTHER) )
                {
-                       eDebug("[EPGC] abort non avail schedule other reading");
+                       eDebug("[eEPGCache] abort non avail schedule other reading");
                        isRunning &= ~SCHEDULE_OTHER;
                        m_ScheduleOtherReader->stop();
                        m_ScheduleOtherConn=0;
                }
+#ifdef ENABLE_VIRGIN
+               if ( !(haveData&VIRGIN_NOWNEXT) && (isRunning&VIRGIN_NOWNEXT) )
+               {
+                       eDebug("[eEPGCache] abort non avail virgin nownext reading");
+                       isRunning &= ~VIRGIN_NOWNEXT;
+                       m_VirginNowNextReader->stop();
+                       m_VirginNowNextConn=0;
+               }
+               if ( !(haveData&VIRGIN_SCHEDULE) && (isRunning&VIRGIN_SCHEDULE) )
+               {
+                       eDebug("[eEPGCache] abort non avail virgin schedule reading");
+                       isRunning &= ~VIRGIN_SCHEDULE;
+                       m_VirginScheduleReader->stop();
+                       m_VirginScheduleConn=0;
+               }
+#endif
+#ifdef ENABLE_NETMED
+               if ( !(haveData&NETMED_SCHEDULE) && (isRunning&NETMED_SCHEDULE) )
+               {
+                       eDebug("[eEPGCache] abort non avail netmed schedule reading");
+                       isRunning &= ~NETMED_SCHEDULE;
+                       m_NetmedScheduleReader->stop();
+                       m_NetmedScheduleConn=0;
+               }
+               if ( !(haveData&NETMED_SCHEDULE_OTHER) && (isRunning&NETMED_SCHEDULE_OTHER) )
+               {
+                       eDebug("[eEPGCache] abort non avail netmed schedule other reading");
+                       isRunning &= ~NETMED_SCHEDULE_OTHER;
+                       m_NetmedScheduleOtherReader->stop();
+                       m_NetmedScheduleOtherConn=0;
+               }
+#endif
+#ifdef ENABLE_FREESAT
+               if ( !(haveData&FREESAT_SCHEDULE_OTHER) && (isRunning&FREESAT_SCHEDULE_OTHER) )
+               {
+                       eDebug("[eEPGCache] abort non avail FreeSat schedule_other reading");
+                       isRunning &= ~FREESAT_SCHEDULE_OTHER;
+                       m_FreeSatScheduleOtherReader->stop();
+                       m_FreeSatScheduleOtherReader2->stop();
+                       m_FreeSatScheduleOtherConn=0;
+                       m_FreeSatScheduleOtherConn2=0;
+                       cleanupFreeSat();
+               }
+#endif
                if ( !(haveData&VIASAT) && (isRunning&VIASAT) )
                {
-                       eDebug("[EPGC] abort non avail viasat reading");
+                       eDebug("[eEPGCache] abort non avail viasat reading");
                        isRunning &= ~VIASAT;
                        m_ViasatReader->stop();
                        m_ViasatConn=0;
@@ -1334,7 +1711,7 @@ void eEPGCache::channel_data::abortNonAvail()
 #ifdef ENABLE_MHW_EPG
                if ( !(haveData&MHW) && (isRunning&MHW) )
                {
-                       eDebug("[EPGC] abort non avail mhw reading");
+                       eDebug("[eEPGCache] abort non avail mhw reading");
                        isRunning &= ~MHW;
                        m_MHWReader->stop();
                        m_MHWConn=0;
@@ -1354,6 +1731,12 @@ void eEPGCache::channel_data::abortNonAvail()
                                seenSections[i].clear();
                                calcedSections[i].clear();
                        }
+#ifdef ENABLE_MHW_EPG
+                       cleanupMHW();
+#endif
+#ifdef ENABLE_FREESAT
+                       cleanupFreeSat();
+#endif
                }
        }
        ++state;
@@ -1371,9 +1754,9 @@ void eEPGCache::channel_data::startChannel()
 
        zapTimer->start(update, 1);
        if (update >= 60000)
-               eDebug("[EPGC] next update in %i min", update/60000);
+               eDebug("[eEPGCache] next update in %i min", update/60000);
        else if (update >= 1000)
-               eDebug("[EPGC] next update in %i sec", update/1000);
+               eDebug("[eEPGCache] next update in %i sec", update/1000);
 }
 
 void eEPGCache::channel_data::abortEPG()
@@ -1383,11 +1766,17 @@ void eEPGCache::channel_data::abortEPG()
                seenSections[i].clear();
                calcedSections[i].clear();
        }
+#ifdef ENABLE_MHW_EPG
+       cleanupMHW();
+#endif
+#ifdef ENABLE_FREESAT
+       cleanupFreeSat();
+#endif
        abortTimer->stop();
        zapTimer->stop();
        if (isRunning)
        {
-               eDebug("[EPGC] abort caching events !!");
+               eDebug("[eEPGCache] abort caching events !!");
                if (isRunning & SCHEDULE)
                {
                        isRunning &= ~SCHEDULE;
@@ -1406,6 +1795,44 @@ void eEPGCache::channel_data::abortEPG()
                        m_ScheduleOtherReader->stop();
                        m_ScheduleOtherConn=0;
                }
+#ifdef ENABLE_VIRGIN
+               if (isRunning & VIRGIN_NOWNEXT)
+               {
+                       isRunning &= ~VIRGIN_NOWNEXT;
+                       m_VirginNowNextReader->stop();
+                       m_VirginNowNextConn=0;
+               }
+               if (isRunning & VIRGIN_SCHEDULE)
+               {
+                       isRunning &= ~VIRGIN_SCHEDULE;
+                       m_VirginScheduleReader->stop();
+                       m_VirginScheduleConn=0;
+               }
+#endif
+#ifdef ENABLE_NETMED
+               if (isRunning & NETMED_SCHEDULE)
+               {
+                       isRunning &= ~NETMED_SCHEDULE;
+                       m_NetmedScheduleReader->stop();
+                       m_NetmedScheduleConn=0;
+               }
+               if (isRunning & NETMED_SCHEDULE_OTHER)
+               {
+                       isRunning &= ~NETMED_SCHEDULE_OTHER;
+                       m_NetmedScheduleOtherReader->stop();
+                       m_NetmedScheduleOtherConn=0;
+               }
+#endif
+#ifdef ENABLE_FREESAT
+               if (isRunning & FREESAT_SCHEDULE_OTHER)
+               {
+                       isRunning &= ~FREESAT_SCHEDULE_OTHER;
+                       m_FreeSatScheduleOtherReader->stop();
+                       m_FreeSatScheduleOtherReader2->stop();
+                       m_FreeSatScheduleOtherConn=0;
+                       m_FreeSatScheduleOtherConn2=0;
+               }
+#endif
                if (isRunning & VIASAT)
                {
                        isRunning &= ~VIASAT;
@@ -1432,52 +1859,57 @@ void eEPGCache::channel_data::abortEPG()
        pthread_mutex_unlock(&channel_active);
 }
 
-
-void eEPGCache::channel_data::readDataViasat( const __u8 *data)
-{
-       __u8 *d=0;
-       memcpy(&d, &data, sizeof(__u8*));
-       d[0] |= 0x80;
-       readData(data);
-}
-
-void eEPGCache::channel_data::readData( const __u8 *data)
+void eEPGCache::channel_data::readData( const uint8_t *data, int source)
 {
-       int source;
        int map;
-       iDVBSectionReader *reader=NULL;
-       switch(data[0])
+       iDVBSectionReader *reader = NULL;
+       switch (source)
        {
-               case 0x4E ... 0x4F:
-                       reader=m_NowNextReader;
-                       source=NOWNEXT;
-                       map=0;
+               case NOWNEXT:
+                       reader = m_NowNextReader;
+                       map = 0;
+                       break;
+               case SCHEDULE:
+                       reader = m_ScheduleReader;
+                       map = 1;
+                       break;
+               case SCHEDULE_OTHER:
+                       reader = m_ScheduleOtherReader;
+                       map = 2;
+                       break;
+               case VIASAT:
+                       reader = m_ViasatReader;
+                       map = 3;
                        break;
-               case 0x50 ... 0x5F:
-                       reader=m_ScheduleReader;
-                       source=SCHEDULE;
-                       map=1;
+#ifdef ENABLE_NETMED
+               case NETMED_SCHEDULE:
+                       reader = m_NetmedScheduleReader;
+                       map = 1;
                        break;
-               case 0x60 ... 0x6F:
-                       reader=m_ScheduleOtherReader;
-                       source=SCHEDULE_OTHER;
-                       map=2;
+               case NETMED_SCHEDULE_OTHER:
+                       reader = m_NetmedScheduleOtherReader;
+                       map = 2;
                        break;
-               case 0xD0 ... 0xDF:
-               case 0xE0 ... 0xEF:
-                       reader=m_ViasatReader;
-                       source=VIASAT;
-                       map=3;
+#endif
+#ifdef ENABLE_VIRGIN
+               case VIRGIN_NOWNEXT:
+                       reader = m_VirginNowNextReader;
+                       map = 0;
+                       break;
+               case VIRGIN_SCHEDULE:
+                       reader = m_VirginScheduleReader;
+                       map = 1;
                        break;
+#endif
                default:
-                       eDebug("[EPGC] unknown table_id !!!");
+                       eDebug("[eEPGCache] unknown source");
                        return;
        }
        tidMap &seenSections = this->seenSections[map];
        tidMap &calcedSections = this->calcedSections[map];
        if ( (state == 1 && calcedSections == seenSections) || state > 1 )
        {
-               eDebugNoNewLine("[EPGC] ");
+               eDebugNoNewLine("[eEPGCache] ");
                switch (source)
                {
                        case NOWNEXT:
@@ -1496,9 +1928,29 @@ void eEPGCache::channel_data::readData( const __u8 *data)
                                m_ViasatConn=0;
                                eDebugNoNewLine("viasat");
                                break;
+#ifdef ENABLE_NETMED
+                       case NETMED_SCHEDULE:
+                               m_NetmedScheduleConn=0;
+                               eDebugNoNewLine("netmed schedule");
+                               break;
+                       case NETMED_SCHEDULE_OTHER:
+                               m_NetmedScheduleOtherConn=0;
+                               eDebugNoNewLine("netmed schedule other");
+                               break;
+#endif
+#ifdef ENABLE_VIRGIN
+                       case VIRGIN_NOWNEXT:
+                               m_VirginNowNextConn=0;
+                               eDebugNoNewLine("virgin nownext");
+                               break;
+                       case VIRGIN_SCHEDULE:
+                               m_VirginScheduleConn=0;
+                               eDebugNoNewLine("virgin schedule");
+                               break;
+#endif
                        default: eDebugNoNewLine("unknown");break;
                }
-               eDebug(" finished(%ld)", ::time(0));
+               eDebugNoNewLine(" finished(%ld)\n", ::time(0));
                if ( reader )
                        reader->stop();
                isRunning &= ~source;
@@ -1508,7 +1960,7 @@ void eEPGCache::channel_data::readData( const __u8 *data)
        else
        {
                eit_t *eit = (eit_t*) data;
-               __u32 sectionNo = data[0] << 24;
+               uint32_t sectionNo = data[0] << 24;
                sectionNo |= data[3] << 16;
                sectionNo |= data[4] << 8;
                sectionNo |= eit->section_number;
@@ -1520,44 +1972,165 @@ void eEPGCache::channel_data::readData( const __u8 *data)
                {
                        seenSections.insert(sectionNo);
                        calcedSections.insert(sectionNo);
-                       __u32 tmpval = sectionNo & 0xFFFFFF00;
-                       __u8 incr = source == NOWNEXT ? 1 : 8;
+                       uint32_t tmpval = sectionNo & 0xFFFFFF00;
+                       uint8_t incr = source == NOWNEXT ? 1 : 8;
                        for ( int i = 0; i <= eit->last_section_number; i+=incr )
                        {
                                if ( i == eit->section_number )
                                {
                                        for (int x=i; x <= eit->segment_last_section_number; ++x)
+                                       {
                                                calcedSections.insert(tmpval|(x&0xFF));
+                                       }
                                }
                                else
+                               {
                                        calcedSections.insert(tmpval|(i&0xFF));
+                               }
                        }
                        cache->sectionRead(data, source, this);
                }
        }
 }
 
-RESULT eEPGCache::lookupEventTime(const eServiceReference &service, time_t t, const eventData *&result, int direction)
-// if t == -1 we search the current event...
+#if ENABLE_FREESAT
+
+freesatEITSubtableStatus::freesatEITSubtableStatus(u_char version, uint8_t maxSection) : version(version)
 {
-       singleLock s(cache_lock);
-       uniqueEPGKey key(handleGroup(service));
+       initMap(maxSection);
+}
 
-       // check if EPG for this service is ready...
+void freesatEITSubtableStatus::initMap(uint8_t maxSection)
+{
+       int i, maxSectionIdx = maxSection / 8;
+       for (i = 0; i < 32; i++)
+       {
+               sectionMap[i] = (i <= maxSectionIdx ? 0x0100 : 0x0000 );
+       }
+}
+
+bool freesatEITSubtableStatus::isSectionPresent(uint8_t sectionNo)
+{
+       uint8_t sectionIdx = sectionNo / 8;
+       uint8_t bitOffset = sectionNo % 8;
+
+       return ((sectionMap[sectionIdx] & (1 << bitOffset)) != 0);
+}
+
+bool freesatEITSubtableStatus::isCompleted()
+{
+       uint32_t i = 0;
+       uint8_t calc;
+
+       while ( i < 32 )
+       {
+               calc = sectionMap[i] >> 8;
+               if (! calc) return true; // Last segment passed
+               if (calc ^ ( sectionMap[i] & 0xFF ) ) // Segment not fully found
+                       return false;
+               i++;
+       }
+       return true; // All segments ok
+}
+
+void freesatEITSubtableStatus::seen(uint8_t sectionNo, uint8_t maxSegmentSection)
+{
+       uint8_t sectionIdx = sectionNo / 8;
+       uint8_t bitOffset = sectionNo % 8;
+       uint8_t maxBitOffset = maxSegmentSection % 8;
+
+       sectionMap[sectionIdx] &= 0x00FF; // Clear calc map
+       sectionMap[sectionIdx] |= ((0x01FF << maxBitOffset) & 0xFF00); // Set calc map
+       sectionMap[sectionIdx] |= (1 << bitOffset); // Set seen map
+}
+
+bool freesatEITSubtableStatus::isVersionChanged(u_char testVersion)
+{
+       return version != testVersion;
+}
+
+void freesatEITSubtableStatus::updateVersion(u_char newVersion, uint8_t maxSection)
+{
+       version = newVersion;
+       initMap(maxSection);
+}
+
+void eEPGCache::channel_data::cleanupFreeSat()
+{
+       m_FreeSatSubTableStatus.clear();
+       m_FreesatTablesToComplete = 0;
+}
+
+void eEPGCache::channel_data::readFreeSatScheduleOtherData( const uint8_t *data)
+{
+       eit_t *eit = (eit_t*) data;
+       uint32_t subtableNo = data[0] << 24; // Table ID
+       subtableNo |= data[3] << 16; // Service ID Hi
+       subtableNo |= data[4] << 8; // Service ID Lo
+
+       // Check for sub-table version in map
+       std::map<uint32_t, freesatEITSubtableStatus> &freeSatSubTableStatus = this->m_FreeSatSubTableStatus;
+       std::map<uint32_t, freesatEITSubtableStatus>::iterator itmap = freeSatSubTableStatus.find(subtableNo);
+
+       freesatEITSubtableStatus *fsstatus;
+       if ( itmap == freeSatSubTableStatus.end() )
+       {
+               // New sub table. Store version.
+               //eDebug("[eEPGCache] New subtable (%x) version (%d) now/next (%d) tsid (%x/%x) onid (%x/%x)", subtableNo, eit->version_number, eit->current_next_indicator, eit->transport_stream_id_hi, eit->transport_stream_id_lo, eit->original_network_id_hi, eit->original_network_id_lo);
+               fsstatus = new freesatEITSubtableStatus(eit->version_number, eit->last_section_number);
+               m_FreesatTablesToComplete++;
+               freeSatSubTableStatus.insert(std::pair<uint32_t,freesatEITSubtableStatus>(subtableNo, *fsstatus));
+       }
+       else
+       {
+               fsstatus = &itmap->second;
+               // Existing subtable. Check version. Should check current / next as well? Seems to always be current for Freesat
+               if ( fsstatus->isVersionChanged(eit->version_number) )
+               {
+                       eDebug("[eEPGCache] FS subtable (%x) version changed (%d) now/next (%d)", subtableNo, eit->version_number, eit->current_next_indicator);
+                       m_FreesatTablesToComplete++;
+                       fsstatus->updateVersion(eit->version_number, eit->last_section_number);
+               }
+               else
+               {
+                       if ( fsstatus->isSectionPresent(eit->section_number) )
+                       {
+//                             eDebug("[eEPGCache] DUP FS sub/sec/ver (%x/%d/%d)", subtableNo, eit->section_number, eit->version_number);
+                               return;
+                       }
+               }
+       }
+
+//     eDebug("[eEPGCache] New FS sub/sec/ls/lss/ver (%x/%d/%d/%d/%d)", subtableNo, eit->section_number, eit->last_section_number, eit->segment_last_section_number, eit->version_number);
+       fsstatus->seen(eit->section_number, eit->segment_last_section_number);
+       if (fsstatus->isCompleted())
+       {
+               m_FreesatTablesToComplete--;
+       }
+       cache->sectionRead(data, FREESAT_SCHEDULE_OTHER, this);
+}
+#endif
+
+RESULT eEPGCache::lookupEventTime(const eServiceReference &service, time_t t, const eventData *&result, int direction)
+// if t == -1 we search the current event...
+{
+       uniqueEPGKey key(handleGroup(service));
+
+       // check if EPG for this service is ready...
        eventCache::iterator It = eventDB.find( key );
-       if ( It != eventDB.end() && !It->second.first.empty() ) // entrys cached ?
+       if ( It != eventDB.end() && !It->second.byEvent.empty() ) // entrys cached ?
        {
                if (t==-1)
                        t = ::time(0);
-               timeMap::iterator i = direction <= 0 ? It->second.second.lower_bound(t) :  // find > or equal
-                       It->second.second.upper_bound(t); // just >
-               if ( i != It->second.second.end() )
+               timeMap::iterator i = direction <= 0 ? It->second.byTime.lower_bound(t) :  // find > or equal
+                       It->second.byTime.upper_bound(t); // just >
+               if ( i != It->second.byTime.end() )
                {
                        if ( direction < 0 || (direction == 0 && i->first > t) )
                        {
                                timeMap::iterator x = i;
                                --x;
-                               if ( x != It->second.second.end() )
+                               if ( x != It->second.byTime.end() )
                                {
                                        time_t start_time = x->first;
                                        if (direction >= 0)
@@ -1579,16 +2152,6 @@ RESULT eEPGCache::lookupEventTime(const eServiceReference &service, time_t t, co
        return -1;
 }
 
-RESULT eEPGCache::lookupEventTime(const eServiceReference &service, time_t t, const eit_event_struct *&result, int direction)
-{
-       singleLock s(cache_lock);
-       const eventData *data=0;
-       RESULT ret = lookupEventTime(service, t, data, direction);
-       if ( !ret && data )
-               result = data->get();
-       return ret;
-}
-
 RESULT eEPGCache::lookupEventTime(const eServiceReference &service, time_t t, Event *& result, int direction)
 {
        singleLock s(cache_lock);
@@ -1604,6 +2167,7 @@ RESULT eEPGCache::lookupEventTime(const eServiceReference &service, time_t t, eP
        singleLock s(cache_lock);
        const eventData *data=0;
        RESULT ret = lookupEventTime(service, t, data, direction);
+       result = NULL;
        if ( !ret && data )
        {
                Event ev((uint8_t*)data->get());
@@ -1616,14 +2180,13 @@ RESULT eEPGCache::lookupEventTime(const eServiceReference &service, time_t t, eP
 
 RESULT eEPGCache::lookupEventId(const eServiceReference &service, int event_id, const eventData *&result )
 {
-       singleLock s(cache_lock);
        uniqueEPGKey key(handleGroup(service));
 
-       eventCache::iterator It = eventDB.find( key );
-       if ( It != eventDB.end() && !It->second.first.empty() ) // entrys cached?
+       eventCache::iterator It = eventDB.find(key);
+       if (It != eventDB.end())
        {
-               eventMap::iterator i( It->second.first.find( event_id ));
-               if ( i != It->second.first.end() )
+               eventMap::iterator i = It->second.byEvent.find(event_id);
+               if ( i != It->second.byEvent.end() )
                {
                        result = i->second;
                        return 0;
@@ -1631,19 +2194,48 @@ RESULT eEPGCache::lookupEventId(const eServiceReference &service, int event_id,
                else
                {
                        result = 0;
-                       eDebug("[EPGC] event %04x not found in epgcache", event_id);
+                       eDebug("[eEPGCache] event %04x not found in epgcache", event_id);
                }
        }
        return -1;
 }
 
-RESULT eEPGCache::lookupEventId(const eServiceReference &service, int event_id, const eit_event_struct *&result)
+RESULT eEPGCache::saveEventToFile(const char* filename, const eServiceReference &service, int eit_event_id, time_t begTime, time_t endTime)
 {
+       RESULT ret = -1;
        singleLock s(cache_lock);
-       const eventData *data=0;
-       RESULT ret = lookupEventId(service, event_id, data);
-       if ( !ret && data )
-               result = data->get();
+       const eventData *data = NULL;
+       if ( eit_event_id != -1 )
+       {
+               eDebug("[eEPGCache] %s epg event id %x", __func__, eit_event_id);
+               ret = lookupEventId(service, eit_event_id, data);
+       }
+       if ( (ret != 0) && (begTime != -1) )
+       {
+               time_t queryTime = begTime;
+               if (endTime != -1)
+                       queryTime += (endTime - begTime) / 2;
+               ret = lookupEventTime(service, queryTime, data);
+       }
+       if (ret == 0)
+       {
+               int fd = open(filename, O_CREAT|O_WRONLY, 0666);
+               if (fd < 0)
+               {
+                       eDebug("[eEPGCache] Failed to create file: %s", filename);
+                       return fd;
+               }
+               const eit_event_struct *event = data->get();
+               int evLen = event->getDescriptorsLoopLength() + 12/*EIT_LOOP_SIZE*/;
+               int wr = ::write( fd, event, evLen );
+               ::close(fd);
+               if ( wr != evLen )
+               {
+                       ::unlink(filename); /* Remove faulty file */
+                       eDebug("[eEPGCache] eit write error on %s: %m", filename);
+                       ret = (wr < 0) ? wr : -1;
+               }
+       }
        return ret;
 }
 
@@ -1662,6 +2254,7 @@ RESULT eEPGCache::lookupEventId(const eServiceReference &service, int event_id,
        singleLock s(cache_lock);
        const eventData *data=0;
        RESULT ret = lookupEventId(service, event_id, data);
+       result = NULL;
        if ( !ret && data )
        {
                Event ev((uint8_t*)data->get());
@@ -1679,16 +2272,16 @@ RESULT eEPGCache::startTimeQuery(const eServiceReference &service, time_t begin,
        if (begin == -1)
                begin = ::time(0);
        eventCache::iterator It = eventDB.find(ref);
-       if ( It != eventDB.end() && It->second.second.size() )
+       if ( It != eventDB.end() && !It->second.byTime.empty() )
        {
-               m_timemap_cursor = It->second.second.lower_bound(begin);
-               if ( m_timemap_cursor != It->second.second.end() )
+               m_timemap_cursor = It->second.byTime.lower_bound(begin);
+               if ( m_timemap_cursor != It->second.byTime.end() )
                {
                        if ( m_timemap_cursor->first != begin )
                        {
                                timeMap::iterator x = m_timemap_cursor;
                                --x;
-                               if ( x != It->second.second.end() )
+                               if ( x != It->second.byTime.end() )
                                {
                                        time_t start_time = x->first;
                                        if ( begin > start_time && begin < (start_time+x->second->getDuration()))
@@ -1698,9 +2291,9 @@ RESULT eEPGCache::startTimeQuery(const eServiceReference &service, time_t begin,
                }
 
                if (minutes != -1)
-                       m_timemap_end = It->second.second.lower_bound(begin+minutes*60);
+                       m_timemap_end = It->second.byTime.lower_bound(begin+minutes*60);
                else
-                       m_timemap_end = It->second.second.end();
+                       m_timemap_end = It->second.byTime.end();
 
                currentQueryTsidOnid = (ref.getTransportStreamID().get()<<16) | ref.getOriginalNetworkID().get();
                return m_timemap_cursor == m_timemap_end ? -1 : 0;
@@ -1708,26 +2301,6 @@ RESULT eEPGCache::startTimeQuery(const eServiceReference &service, time_t begin,
        return -1;
 }
 
-RESULT eEPGCache::getNextTimeEntry(const eventData *& result)
-{
-       if ( m_timemap_cursor != m_timemap_end )
-       {
-               result = m_timemap_cursor++->second;
-               return 0;
-       }
-       return -1;
-}
-
-RESULT eEPGCache::getNextTimeEntry(const eit_event_struct *&result)
-{
-       if ( m_timemap_cursor != m_timemap_end )
-       {
-               result = m_timemap_cursor++->second->get();
-               return 0;
-       }
-       return -1;
-}
-
 RESULT eEPGCache::getNextTimeEntry(Event *&result)
 {
        if ( m_timemap_cursor != m_timemap_end )
@@ -1749,8 +2322,9 @@ RESULT eEPGCache::getNextTimeEntry(ePtr<eServiceEvent> &result)
        return -1;
 }
 
-void fillTuple(ePyObject tuple, const char *argstring, int argcount, ePyObject service, eServiceEvent *ptr, ePyObject nowTime, ePyObject service_name )
+void fillTuple(ePyObject tuple, const char *argstring, int argcount, ePyObject service_reference, eServiceEvent *ptr, ePyObject service_name, ePyObject nowTime, eventData *evData )
 {
+       //eDebug("[eEPGCache] fillTuple arg=%s argcnt=%d, ptr=%d evData=%d", argstring, argcount, ptr ? 1 : 0, evData ? 1 : 0);
        ePyObject tmp;
        int spos=0, tpos=0;
        char c;
@@ -1763,13 +2337,13 @@ void fillTuple(ePyObject tuple, const char *argstring, int argcount, ePyObject s
                                tmp = PyLong_FromLong(0);
                                break;
                        case 'I': // Event Id
-                               tmp = ptr ? PyLong_FromLong(ptr->getEventId()) : ePyObject();
+                               tmp = evData ? PyLong_FromLong(evData->getEventID()) : (ptr ? PyLong_FromLong(ptr->getEventId()) : ePyObject());
                                break;
                        case 'B': // Event Begin Time
-                               tmp = ptr ? PyLong_FromLong(ptr->getBeginTime()) : ePyObject();
+                               tmp = ptr ? PyLong_FromLong(ptr->getBeginTime()) : (evData ? PyLong_FromLong(evData->getStartTime()) : ePyObject());
                                break;
                        case 'D': // Event Duration
-                               tmp = ptr ? PyLong_FromLong(ptr->getDuration()) : ePyObject();
+                               tmp = ptr ? PyLong_FromLong(ptr->getDuration()) : (evData ? PyLong_FromLong(evData->getDuration()) : ePyObject());
                                break;
                        case 'T': // Event Title
                                tmp = ptr ? PyString_FromString(ptr->getEventName().c_str()) : ePyObject();
@@ -1780,12 +2354,18 @@ void fillTuple(ePyObject tuple, const char *argstring, int argcount, ePyObject s
                        case 'E': // Event Extended Description
                                tmp = ptr ? PyString_FromString(ptr->getExtendedDescription().c_str()) : ePyObject();
                                break;
+                       case 'P': // Event Parental Rating
+                               tmp = ptr ? ePyObject(ptr->getParentalData()) : ePyObject();
+                               break;
+                       case 'W': // Event Content Description
+                               tmp = ptr ? ePyObject(ptr->getGenreData()) : ePyObject();
+                               break;
                        case 'C': // Current Time
                                tmp = nowTime;
                                inc_refcount = true;
                                break;
                        case 'R': // service reference string
-                               tmp = service;
+                               tmp = service_reference;
                                inc_refcount = true;
                                break;
                        case 'n': // short service name
@@ -1798,7 +2378,7 @@ void fillTuple(ePyObject tuple, const char *argstring, int argcount, ePyObject s
                                continue;
                        default:  // ignore unknown
                                tmp = ePyObject();
-                               eDebug("fillTuple unknown '%c'... insert 'None' in result", c);
+                               eDebug("[eEPGCache] fillTuple unknown '%c'... insert 'None' in result", c);
                }
                if (!tmp)
                {
@@ -1815,7 +2395,7 @@ int handleEvent(eServiceEvent *ptr, ePyObject dest_list, const char* argstring,
 {
        if (convertFunc)
        {
-               fillTuple(convertFuncArgs, argstring, argcount, service, ptr, nowTime, service_name);
+               fillTuple(convertFuncArgs, argstring, argcount, service, ptr, service_name, nowTime, 0);
                ePyObject result = PyObject_CallObject(convertFunc, convertFuncArgs);
                if (!result)
                {
@@ -1827,7 +2407,7 @@ int handleEvent(eServiceEvent *ptr, ePyObject dest_list, const char* argstring,
                        Py_DECREF(dest_list);
                        PyErr_SetString(PyExc_StandardError,
                                "error in convertFunc execute");
-                       eDebug("error in convertFunc execute");
+                       eDebug("[eEPGCache] handleEvent: error in convertFunc execute");
                        return -1;
                }
                PyList_Append(dest_list, result);
@@ -1836,7 +2416,7 @@ int handleEvent(eServiceEvent *ptr, ePyObject dest_list, const char* argstring,
        else
        {
                ePyObject tuple = PyTuple_New(argcount);
-               fillTuple(tuple, argstring, argcount, service, ptr, nowTime, service_name);
+               fillTuple(tuple, argstring, argcount, service, ptr, service_name, nowTime, 0);
                PyList_Append(dest_list, tuple);
                Py_DECREF(tuple);
        }
@@ -1852,6 +2432,8 @@ int handleEvent(eServiceEvent *ptr, ePyObject dest_list, const char* argstring,
 //   T = Event Title
 //   S = Event Short Description
 //   E = Event Extended Description
+//   P = Event Parental Rating
+//   W = Event Content Description ('W'hat)
 //   C = Current Time
 //   R = Service Reference
 //   N = Service Name
@@ -1880,7 +2462,7 @@ PyObject *eEPGCache::lookupEvent(ePyObject list, ePyObject convertFunc)
        {
                PyErr_SetString(PyExc_StandardError,
                        "type error");
-               eDebug("no list");
+               eDebug("[eEPGCache] no list");
                return NULL;
        }
        int listIt=0;
@@ -1888,11 +2470,11 @@ PyObject *eEPGCache::lookupEvent(ePyObject list, ePyObject convertFunc)
        if (!listSize)
        {
                PyErr_SetString(PyExc_StandardError,
-                       "not params given");
-               eDebug("not params given");
+                       "no params given");
+               eDebug("[eEPGCache] no params given");
                return NULL;
        }
-       else 
+       else
        {
                ePyObject argv=PyList_GET_ITEM(list, 0); // borrowed reference!
                if (PyString_Check(argv))
@@ -1903,7 +2485,7 @@ PyObject *eEPGCache::lookupEvent(ePyObject list, ePyObject convertFunc)
                else
                        argstring = "I"; // just event id as default
                argcount = strlen(argstring);
-//             eDebug("have %d args('%s')", argcount, argstring);
+//             eDebug("[eEPGCache] have %d args('%s')", argcount, argstring);
        }
 
        bool forceReturnOne = strchr(argstring, 'X') ? true : false;
@@ -1916,7 +2498,7 @@ PyObject *eEPGCache::lookupEvent(ePyObject list, ePyObject convertFunc)
                {
                        PyErr_SetString(PyExc_StandardError,
                                "convertFunc must be callable");
-                       eDebug("convertFunc is not callable");
+                       eDebug("[eEPGCache] convertFunc is not callable");
                        return NULL;
                }
                convertFuncArgs = PyTuple_New(argcount);
@@ -1952,7 +2534,7 @@ PyObject *eEPGCache::lookupEvent(ePyObject list, ePyObject convertFunc)
                                        {
                                                if (!PyString_Check(entry))
                                                {
-                                                       eDebug("tuple entry 0 is no a string");
+                                                       eDebug("[eEPGCache] tuple entry 0 is no a string");
                                                        goto skip_entry;
                                                }
                                                service = entry;
@@ -1962,7 +2544,7 @@ PyObject *eEPGCache::lookupEvent(ePyObject list, ePyObject convertFunc)
                                                type=PyInt_AsLong(entry);
                                                if (type < -1 || type > 2)
                                                {
-                                                       eDebug("unknown type %d", type);
+                                                       eDebug("[eEPGCache] unknown type %d", type);
                                                        goto skip_entry;
                                                }
                                                break;
@@ -1973,7 +2555,7 @@ PyObject *eEPGCache::lookupEvent(ePyObject list, ePyObject convertFunc)
                                                minutes=PyInt_AsLong(entry);
                                                break;
                                        default:
-                                               eDebug("unneeded extra argument");
+                                               eDebug("[eEPGCache] unneeded extra argument");
                                                break;
                                }
                        }
@@ -1982,12 +2564,6 @@ PyObject *eEPGCache::lookupEvent(ePyObject list, ePyObject convertFunc)
                                stime = ::time(0);
 
                        eServiceReference ref(handleGroup(eServiceReference(PyString_AS_STRING(service))));
-                       if (ref.type != eServiceReference::idDVB)
-                       {
-                               eDebug("service reference for epg query is not valid");
-                               continue;
-                       }
-
                        // redirect subservice querys to parent service
                        eServiceReferenceDVB &dvb_ref = (eServiceReferenceDVB&)ref;
                        if (dvb_ref.getParentTransportStreamID().get()) // linkage subservice
@@ -2096,63 +2672,302 @@ skip_entry:
        return dest_list;
 }
 
-void fillTuple2(ePyObject tuple, const char *argstring, int argcount, eventData *evData, eServiceEvent *ptr, ePyObject service_name, ePyObject service_reference)
+static void fill_eit_start(eit_event_struct *evt, time_t t)
 {
-       ePyObject tmp;
-       int pos=0;
-       while(pos < argcount)
+    tm *time = gmtime(&t);
+
+    int l = 0;
+    int month = time->tm_mon + 1;
+    if (month == 1 || month == 2)
+        l = 1;
+    int mjd = 14956 + time->tm_mday + (int)((time->tm_year - l) * 365.25) + (int)((month + 1 + l*12) * 30.6001);
+    evt->start_time_1 = mjd >> 8;
+    evt->start_time_2 = mjd & 0xFF;
+
+    evt->start_time_3 = toBCD(time->tm_hour);
+    evt->start_time_4 = toBCD(time->tm_min);
+    evt->start_time_5 = toBCD(time->tm_sec);
+
+}
+
+static void fill_eit_duration(eit_event_struct *evt, int time)
+{
+    //time is given in second
+    //convert to hour, minutes, seconds
+    evt->duration_1 = toBCD(time / 3600);
+    evt->duration_2 = toBCD((time % 3600) / 60);
+    evt->duration_3 = toBCD((time % 3600) % 60);
+}
+
+// convert from set of strings to DVB format (EIT)
+void eEPGCache::submitEventData(const std::vector<eServiceReferenceDVB>& serviceRefs, long start,
+       long duration, const char* title, const char* short_summary,
+       const char* long_description, char event_type)
+{
+       std::vector<int> sids;
+       std::vector<eDVBChannelID> chids;
+       for (std::vector<eServiceReferenceDVB>::const_iterator serviceRef = serviceRefs.begin();
+               serviceRef != serviceRefs.end();
+               ++serviceRef)
        {
-               bool inc_refcount=false;
-               switch(argstring[pos])
+               eDVBChannelID chid;
+               serviceRef->getChannelID(chid);
+               chids.push_back(chid);
+               sids.push_back(serviceRef->getServiceID().get());
+       }
+       submitEventData(sids, chids, start, duration, title, short_summary, long_description, event_type, EPG_IMPORT);
+}
+
+void eEPGCache::submitEventData(const std::vector<int>& sids, const std::vector<eDVBChannelID>& chids, long start,
+       long duration, const char* title, const char* short_summary,
+       const char* long_description, char event_type, int source)
+{
+       if (!title)
+               return;
+       if (sids.size() != chids.size())
+               return;
+       static const int EIT_LENGTH = 4108;
+       static const uint8_t codePage = 0x15; // UTF-8 encoding
+       uint8_t data[EIT_LENGTH];
+
+       eit_t *packet = (eit_t *) data;
+       packet->table_id = 0x50;
+       packet->section_syntax_indicator = 1;
+
+       packet->version_number = 0;     // eEPGCache::sectionRead() will dig this for the moment
+       packet->current_next_indicator = 0;
+       packet->section_number = 0;     // eEPGCache::sectionRead() will dig this for the moment
+       packet->last_section_number = 0;        // eEPGCache::sectionRead() will dig this for the moment
+
+       packet->segment_last_section_number = 0; // eEPGCache::sectionRead() will dig this for the moment
+       packet->segment_last_table_id = 0x50;
+
+       eit_event_t *evt_struct = (eit_event_t*) (data + EIT_SIZE);
+
+       uint16_t eventId = start & 0xFFFF;
+       evt_struct->setEventId(eventId);
+
+       //6 bytes start time, 3 bytes duration
+       fill_eit_start(evt_struct, start);
+       fill_eit_duration(evt_struct, duration);
+
+       evt_struct->running_status = 0;
+       evt_struct->free_CA_mode = 0;
+
+       //no support for different code pages, only DVB's latin1 character set
+       //TODO: convert text to correct character set (data is probably passed in as UTF-8)
+       uint8_t *x = (uint8_t *) evt_struct;
+       x += EIT_LOOP_SIZE;
+       int nameLength = strnlen(title, 246);
+       int descLength = short_summary ? strnlen(short_summary, 246 - nameLength) : 0;
+
+       eit_short_event_descriptor_struct *short_evt = (eit_short_event_descriptor_struct*) x;
+       short_evt->descriptor_tag = SHORT_EVENT_DESCRIPTOR;
+       short_evt->descriptor_length = EIT_SHORT_EVENT_DESCRIPTOR_SIZE + nameLength + descLength + 1 - 2; //+1 for length of short description, -2 for tag and length
+       if (nameLength) ++short_evt->descriptor_length; // +1 for codepage byte
+       if (descLength) ++short_evt->descriptor_length;
+       short_evt->language_code_1 = 'e';
+       short_evt->language_code_2 = 'n';
+       short_evt->language_code_3 = 'g';
+       short_evt->event_name_length = nameLength ? nameLength + 1 : 0;
+       x = (uint8_t *) short_evt;
+       x += EIT_SHORT_EVENT_DESCRIPTOR_SIZE;
+       *x = codePage;
+       ++x;
+       memcpy(x, title, nameLength);
+       x += nameLength;
+       if (descLength)
+       {
+               *x = descLength + 1;
+               ++x;
+               *x = codePage;
+               ++x;
+               memcpy(x, short_summary, descLength);
+               x += descLength;
+       }
+       else
+       {
+               *x = 0;
+               ++x;
+       }
+
+       //Content type
+       if (event_type != 0)
+       {
+               x[0] = 0x54;
+               x[1] = 2;
+               x[2] = event_type;
+               x[3] = 0;
+               x += 4;
+       }
+
+       //Long description
+       int currentLoopLength = x - (uint8_t*)short_evt;
+       static const int overheadPerDescriptor = 9; //increase if codepages are added!!!
+       static const int MAX_LEN = 256 - overheadPerDescriptor;
+
+       int textLength = long_description ? strnlen(long_description, EIT_LENGTH) : 0;//EIT_LENGTH is a bit too much, but it's only here as a reasonable end point
+       int lastDescriptorNumber = (textLength + MAX_LEN-1) / MAX_LEN - 1;
+       int remainingTextLength = textLength - lastDescriptorNumber * MAX_LEN;
+
+       //if long description is too long, just try to fill as many descriptors as possible
+       while ( (lastDescriptorNumber+1) * 256 + currentLoopLength > EIT_LENGTH - EIT_LOOP_SIZE)
+       {
+               lastDescriptorNumber--;
+               remainingTextLength = MAX_LEN;
+       }
+
+       for (int descrIndex = 0; descrIndex <= lastDescriptorNumber; ++descrIndex)
+       {
+               eit_extended_descriptor_struct *ext_evt = (eit_extended_descriptor_struct*) x;
+               ext_evt->descriptor_tag = EIT_EXTENDED_EVENT_DESCRIPOR;
+               //descriptor header length is 6, including the 2 tag and length bytes
+               //so the length field must be: stringlength + 1 (2 4-bits numbers) + 3 (lang code) + 2 bytes for item info length field and text length field
+               int currentTextLength = descrIndex < lastDescriptorNumber ? MAX_LEN : remainingTextLength;
+               ext_evt->descriptor_length = 6 + currentTextLength + 1;
+
+               ext_evt->descriptor_number = descrIndex;
+               ext_evt->last_descriptor_number = lastDescriptorNumber;
+               ext_evt->iso_639_2_language_code_1 = 'e';
+               ext_evt->iso_639_2_language_code_2 = 'n';
+               ext_evt->iso_639_2_language_code_3 = 'g';
+
+               x[6] = 0; //item information (car, year, director, etc. Unsupported for now)
+               x[7] = currentTextLength + 1; //length of description string (part in this message)
+               x[8] = codePage;
+               memcpy(x + 9, &long_description[descrIndex*MAX_LEN], currentTextLength);
+
+               x += 2 + ext_evt->descriptor_length;
+       }
+
+       //TODO: add age and more
+       int desc_loop_length = x - ((uint8_t*)evt_struct + EIT_LOOP_SIZE);
+       evt_struct->setDescriptorsLoopLength(desc_loop_length);
+
+       int packet_length = (x - data) - 3; //should add 1 for crc....
+       packet->setSectionLength(packet_length);
+       // Add channelrefs and submit data.
+       for (unsigned int i = 0; i < chids.size(); i++)
+       {
+               packet->setServiceId(sids[i]);
+               packet->setTransportStreamId(chids[i].transport_stream_id.get());
+               packet->setOriginalNetworkId(chids[i].original_network_id.get());
+               sectionRead(data, source, 0);
+       }
+}
+
+void eEPGCache::setEpgHistorySeconds(time_t seconds)
+{
+       historySeconds = seconds;
+}
+
+void eEPGCache::setEpgSources(unsigned int mask)
+{
+       enabledSources = mask;
+}
+
+unsigned int eEPGCache::getEpgSources()
+{
+       return enabledSources;
+}
+
+static const char* getStringFromPython(ePyObject obj)
+{
+       char *result = 0;
+       if (PyString_Check(obj))
+       {
+               result = PyString_AS_STRING(obj);
+       }
+       return result;
+}
+
+void eEPGCache::importEvent(ePyObject serviceReference, ePyObject list)
+{
+       importEvents(serviceReference, list);
+}
+
+//here we get a python tuple of tuples ;)
+// consider it an array of objects with the following data
+// 1. start time (long)
+// 2. duration (int)
+// 3. event title (string)
+// 4. short description (string)
+// 5. extended description (string)
+// 6. event type (byte)
+void eEPGCache::importEvents(ePyObject serviceReferences, ePyObject list)
+{
+       std::vector<eServiceReferenceDVB> refs;
+
+       if (PyString_Check(serviceReferences))
+       {
+               char *refstr;
+               refstr = PyString_AS_STRING(serviceReferences);
+               if (!refstr)
                {
-                       case '0': // PyLong 0
-                               tmp = PyLong_FromLong(0);
-                               break;
-                       case 'I': // Event Id
-                               tmp = PyLong_FromLong(evData->getEventID());
-                               break;
-                       case 'B': // Event Begin Time
-                               if (ptr)
-                                       tmp = ptr ? PyLong_FromLong(ptr->getBeginTime()) : ePyObject();
-                               else
-                                       tmp = PyLong_FromLong(evData->getStartTime());
-                               break;
-                       case 'D': // Event Duration
-                               if (ptr)
-                                       tmp = ptr ? PyLong_FromLong(ptr->getDuration()) : ePyObject();
-                               else
-                                       tmp = PyLong_FromLong(evData->getDuration());
-                               break;
-                       case 'T': // Event Title
-                               tmp = ptr ? PyString_FromString(ptr->getEventName().c_str()) : ePyObject();
-                               break;
-                       case 'S': // Event Short Description
-                               tmp = ptr ? PyString_FromString(ptr->getShortDescription().c_str()) : ePyObject();
-                               break;
-                       case 'E': // Event Extended Description
-                               tmp = ptr ? PyString_FromString(ptr->getExtendedDescription().c_str()) : ePyObject();
-                               break;
-                       case 'R': // service reference string
-                               tmp = service_reference;
-                               inc_refcount = true;
-                               break;
-                       case 'n': // short service name
-                       case 'N': // service name
-                               tmp = service_name;
-                               inc_refcount = true;
-                               break;
-                       default:  // ignore unknown
-                               tmp = ePyObject();
-                               eDebug("fillTuple2 unknown '%c'... insert None in Result", argstring[pos]);
+                       eDebug("[eEPGCache:import] serviceReference string is 0, aborting");
+                       return;
                }
-               if (!tmp)
+               refs.push_back(eServiceReferenceDVB(refstr));
+       }
+       else if (PyList_Check(serviceReferences))
+       {
+               int nRefs = PyList_Size(serviceReferences);
+               for (int i = 0; i < nRefs; ++i)
                {
-                       tmp = Py_None;
-                       inc_refcount = true;
+                       PyObject* item = PyList_GET_ITEM(serviceReferences, i);
+                       char *refstr;
+                       refstr = PyString_AS_STRING(item);
+                       if (!refstr)
+                       {
+                               eDebug("[eEPGCache:import] a serviceref item is not a string");
+                       }
+                       else
+                       {
+                               refs.push_back(eServiceReferenceDVB(refstr));
+                       }
                }
-               if (inc_refcount)
-                       Py_INCREF(tmp);
-               PyTuple_SET_ITEM(tuple, pos++, tmp);
+       }
+       else
+       {
+               eDebug("[eEPGCache:import] serviceReference string is neither string nor list, aborting");
+               return;
+       }
+
+       bool isTuple = PyTuple_Check(list);
+       if (!isTuple && !PyList_Check(list))
+       {
+
+               eDebug("[eEPGCache:import] argument 'list' is neither list nor tuple.");
+               return;
+       }
+
+       int numberOfEvents = isTuple ? PyTuple_Size(list) : PyList_Size(list);
+
+       for (int i = 0; i < numberOfEvents;  ++i)
+       {
+               ePyObject singleEvent = isTuple ? PyTuple_GET_ITEM(list, i) : PyList_GET_ITEM(list, i);
+               if (!PyTuple_Check(singleEvent))
+               {
+                       eDebug("[eEPGCache:import] eventdata tuple does not pass PyTuple_Check, aborting");
+                       return;
+               }
+               int tupleSize = PyTuple_Size(singleEvent);
+               if (tupleSize < 5)
+               {
+                       eDebug("[eEPGCache:import] eventdata tuple does not contain enough fields, aborting");
+                       return;
+               }
+
+               long start = PyLong_AsLong(PyTuple_GET_ITEM(singleEvent, 0));
+               long duration = PyInt_AsLong(PyTuple_GET_ITEM(singleEvent, 1));
+               const char *title = getStringFromPython(PyTuple_GET_ITEM(singleEvent, 2));
+               const char *short_summary = getStringFromPython(PyTuple_GET_ITEM(singleEvent, 3));
+               const char *long_description = getStringFromPython(PyTuple_GET_ITEM(singleEvent, 4));
+               char event_type = (char) PyInt_AsLong(PyTuple_GET_ITEM(singleEvent, 5));
+
+               Py_BEGIN_ALLOW_THREADS;
+               submitEventData(refs, start, duration, title, short_summary, long_description, event_type);
+               Py_END_ALLOW_THREADS;
        }
 }
 
@@ -2163,6 +2978,8 @@ void fillTuple2(ePyObject tuple, const char *argstring, int argcount, eventData
 //   D = Event Duration
 //   T = Event Title
 //   S = Event Short Description
+//   P = Event Parental Rating
+//   W = Event Content Description
 //   E = Event Extended Description
 //   R = Service Reference
 //   N = Service Name
@@ -2170,12 +2987,13 @@ void fillTuple2(ePyObject tuple, const char *argstring, int argcount, eventData
 //  the second tuple entry is the MAX matches value
 //  the third tuple entry is the type of query
 //     0 = search for similar broadcastings (SIMILAR_BROADCASTINGS_SEARCH)
-//     1 = search events with exactly title name (EXAKT_TITLE_SEARCH)
+//     1 = search events with exactly title name (EXACT_TITLE_SEARCH)
 //     2 = search events with text in title name (PARTIAL_TITLE_SEARCH)
+//     3 = search events starting with title name (START_TITLE_SEARCH)
 //  when type is 0 (SIMILAR_BROADCASTINGS_SEARCH)
 //   the fourth is the servicereference string
 //   the fifth is the eventid
-//  when type is 1 or 2 (EXAKT_TITLE_SEARCH or PARTIAL_TITLE_SEARCH)
+//  when type > 0 (*_TITLE_SEARCH)
 //   the fourth is the search text
 //   the fifth is
 //     0 = case sensitive (CASE_CHECK)
@@ -2184,8 +3002,7 @@ void fillTuple2(ePyObject tuple, const char *argstring, int argcount, eventData
 PyObject *eEPGCache::search(ePyObject arg)
 {
        ePyObject ret;
-       int descridx = -1;
-       __u32 descr[512];
+       std::deque<uint32_t> descr;
        int eventid = -1;
        const char *argstring=0;
        char *refstr=0;
@@ -2211,11 +3028,14 @@ PyObject *eEPGCache::search(ePyObject arg)
 #endif
                                argstring = PyString_AS_STRING(obj);
                                for (int i=0; i < argcount; ++i)
+                               {
                                        switch(argstring[i])
                                        {
                                        case 'S':
                                        case 'E':
                                        case 'T':
+                                       case 'P':
+                                       case 'W':
                                                needServiceEvent=true;
                                                break;
                                        case 'N':
@@ -2230,20 +3050,24 @@ PyObject *eEPGCache::search(ePyObject arg)
                                        default:
                                                break;
                                        }
+                               }
                        }
                        else
                        {
                                PyErr_SetString(PyExc_StandardError,
                                        "type error");
-                               eDebug("tuple arg 0 is not a string");
+                               eDebug("[eEPGCache] tuple arg 0 is not a string");
                                return NULL;
                        }
                }
                if (tuplesize > 1)
+               {
                        maxmatches = PyLong_AsLong(PyTuple_GET_ITEM(arg, 1));
+               }
                if (tuplesize > 2)
                {
                        querytype = PyLong_AsLong(PyTuple_GET_ITEM(arg, 2));
+
                        if (tuplesize > 4 && querytype == 0)
                        {
                                ePyObject obj = PyTuple_GET_ITEM(arg, 3);
@@ -2259,47 +3083,44 @@ PyObject *eEPGCache::search(ePyObject arg)
                                                lookupEventId(ref, eventid, evData);
                                                if (evData)
                                                {
-                                                       __u8 *data = evData->EITdata;
-                                                       int tmp = evData->ByteSize-10;
-                                                       __u32 *p = (__u32*)(data+10);
-                                                               // search short and extended event descriptors
-                                                       while(tmp>3)
+                                                       // search short and extended event descriptors
+                                                       for (uint8_t i = 0; i < evData->n_crc; ++i)
                                                        {
-                                                               __u32 crc = *p++;
-                                                               descriptorMap::iterator it =
+                                                               uint32_t crc = evData->crc_list[i];
+                                                               DescriptorMap::iterator it =
                                                                        eventData::descriptors.find(crc);
                                                                if (it != eventData::descriptors.end())
                                                                {
-                                                                       __u8 *descr_data = it->second.second;
+                                                                       uint8_t *descr_data = it->second.data;
                                                                        switch(descr_data[0])
                                                                        {
                                                                        case 0x4D ... 0x4E:
-                                                                               descr[++descridx]=crc;
+                                                                               descr.push_back(crc);
+                                                                               break;
                                                                        default:
                                                                                break;
                                                                        }
                                                                }
-                                                               tmp-=4;
                                                        }
                                                }
-                                               if (descridx<0)
-                                                       eDebug("event not found");
+                                               if (descr.empty())
+                                                       eDebug("[eEPGCache] event not found");
                                        }
                                        else
                                        {
                                                PyErr_SetString(PyExc_StandardError, "type error");
-                                               eDebug("tuple arg 4 is not a valid service reference string");
+                                               eDebug("[eEPGCache] tuple arg 4 is not a valid service reference string");
                                                return NULL;
                                        }
                                }
                                else
                                {
                                        PyErr_SetString(PyExc_StandardError, "type error");
-                                       eDebug("tuple arg 4 is not a string");
+                                       eDebug("[eEPGCache] tuple arg 4 is not a string");
                                        return NULL;
                                }
                        }
-                       else if (tuplesize > 4 && (querytype == 1 || querytype == 2) )
+                       else if (tuplesize > 4 && (querytype > 0) )
                        {
                                ePyObject obj = PyTuple_GET_ITEM(arg, 3);
                                if (PyString_Check(obj))
@@ -2311,83 +3132,85 @@ PyObject *eEPGCache::search(ePyObject arg)
 #else
                                        int textlen = PyString_Size(obj);
 #endif
-                                       if (querytype == 1)
-                                               eDebug("lookup for events with '%s' as title(%s)", str, casetype?"ignore case":"case sensitive");
-                                       else
-                                               eDebug("lookup for events with '%s' in title(%s)", str, casetype?"ignore case":"case sensitive");
+                                       switch (querytype)
+                                       {
+                                               case 1:
+                                                       eDebug("[eEPGCache] lookup events with '%s' as title (%s)", str, casetype?"ignore case":"case sensitive");
+                                                       break;
+                                               case 2:
+                                                       eDebug("[eEPGCache] lookup events with '%s' in title (%s)", str, casetype?"ignore case":"case sensitive");
+                                                       break;
+                                               case 3:
+                                                       eDebug("[eEPGCache] lookup events, title starting with '%s' (%s)", str, casetype?"ignore case":"case sensitive");
+                                                       break;
+                                       }
+                                       Py_BEGIN_ALLOW_THREADS; /* No Python code in this section, so other threads can run */
                                        singleLock s(cache_lock);
-                                       for (descriptorMap::iterator it(eventData::descriptors.begin());
-                                               it != eventData::descriptors.end() && descridx < 511; ++it)
+                                       std::string title;
+                                       for (DescriptorMap::iterator it(eventData::descriptors.begin());
+                                               it != eventData::descriptors.end(); ++it)
                                        {
-                                               __u8 *data = it->second.second;
-                                               if ( data[0] == 0x4D ) // short event descriptor
+                                               uint8_t *data = it->second.data;
+                                               if ( data[0] == SHORT_EVENT_DESCRIPTOR )
                                                {
+                                                       const char *titleptr = (const char*)&data[6];
                                                        int title_len = data[5];
-                                                       if ( querytype == 1 )
+                                                       if (data[6] < 0x20)
                                                        {
-                                                               int offs = 6;
-                                                               // skip DVB-Text Encoding!
-                                                               if (data[6] == 0x10)
-                                                               {
-                                                                       offs+=3;
-                                                                       title_len-=3;
-                                                               }
-                                                               else if(data[6] > 0 && data[6] < 0x20)
-                                                               {
-                                                                       offs+=1;
-                                                                       title_len-=1;
-                                                               }
+                                                               /* custom encoding */
+                                                               title = convertDVBUTF8((unsigned char*)titleptr, title_len, 0x40, 0);
+                                                               titleptr = title.data();
+                                                               title_len = title.length();
+                                                       }
+                                                       if (title_len < textlen)
+                                                               /*Doesn't fit, so cannot match anything */
+                                                               continue;
+                                                       if (querytype == 1)
+                                                       {
+                                                               /* require exact title match */
                                                                if (title_len != textlen)
                                                                        continue;
-                                                               if ( casetype )
+                                                       }
+                                                       else if (querytype == 3)
+                                                       {
+                                                               /* Do a "startswith" match by pretending the text isn't that long */
+                                                               title_len = textlen;
+                                                       }
+                                                       if (casetype)
+                                                       {
+                                                               while (title_len >= textlen)
                                                                {
-                                                                       if ( !strncasecmp((const char*)data+offs, str, title_len) )
+                                                                       if (!strncasecmp(titleptr, str, textlen))
                                                                        {
-//                                                                             std::string s((const char*)data+offs, title_len);
-//                                                                             eDebug("match1 %s %s", str, s.c_str() );
-                                                                               descr[++descridx] = it->first;
+                                                                               descr.push_back(it->first);
+                                                                               break;
                                                                        }
-                                                               }
-                                                               else if ( !strncmp((const char*)data+offs, str, title_len) )
-                                                               {
-//                                                                     std::string s((const char*)data+offs, title_len);
-//                                                                     eDebug("match2 %s %s", str, s.c_str() );
-                                                                       descr[++descridx] = it->first;
+                                                                       title_len--;
+                                                                       titleptr++;
                                                                }
                                                        }
                                                        else
                                                        {
-                                                               int idx=0;
-                                                               while((title_len-idx) >= textlen)
+                                                               while (title_len >= textlen)
                                                                {
-                                                                       if (casetype)
-                                                                       {
-                                                                               if (!strncasecmp((const char*)data+6+idx, str, textlen) )
-                                                                               {
-                                                                                       descr[++descridx] = it->first;
-//                                                                                     std::string s((const char*)data+6, title_len);
-//                                                                                     eDebug("match 3 %s %s", str, s.c_str() );
-                                                                                       break;
-                                                                               }
-                                                                       }
-                                                                       else if (!strncmp((const char*)data+6+idx, str, textlen) )
+                                                                       if (!memcmp(titleptr, str, textlen))
                                                                        {
-                                                                               descr[++descridx] = it->first;
-//                                                                             std::string s((const char*)data+6, title_len);
-//                                                                             eDebug("match 4 %s %s", str, s.c_str() );
+                                                                               descr.push_back(it->first);
                                                                                break;
                                                                        }
-                                                                       ++idx;
+                                                                       title_len--;
+                                                                       titleptr++;
                                                                }
                                                        }
                                                }
                                        }
+                                       Py_END_ALLOW_THREADS;
                                }
                                else
                                {
                                        PyErr_SetString(PyExc_StandardError,
                                                "type error");
-                                       eDebug("tuple arg 4 is not a string");
+                                       eDebug("[eEPGCache] tuple arg 4 is not a string");
                                        return NULL;
                                }
                        }
@@ -2395,7 +3218,7 @@ PyObject *eEPGCache::search(ePyObject arg)
                        {
                                PyErr_SetString(PyExc_StandardError,
                                        "type error");
-                               eDebug("tuple arg 3(%d) is not a known querytype(0, 1, 2)", querytype);
+                               eDebug("[eEPGCache] tuple arg 3(%d) is not a known querytype(0..3)", querytype);
                                return NULL;
                        }
                }
@@ -2403,7 +3226,7 @@ PyObject *eEPGCache::search(ePyObject arg)
                {
                        PyErr_SetString(PyExc_StandardError,
                                "type error");
-                       eDebug("not enough args in tuple");
+                       eDebug("[eEPGCache] not enough args in tuple");
                        return NULL;
                }
        }
@@ -2411,11 +3234,11 @@ PyObject *eEPGCache::search(ePyObject arg)
        {
                PyErr_SetString(PyExc_StandardError,
                        "type error");
-               eDebug("arg 0 is not a tuple");
+               eDebug("[eEPGCache] arg 0 is not a tuple");
                return NULL;
        }
 
-       if (descridx > -1)
+       if (!descr.empty())
        {
                int maxcount=maxmatches;
                eServiceReferenceDVB ref(refstr?(const eServiceReferenceDVB&)handleGroup(eServiceReference(refstr)):eServiceReferenceDVB(""));
@@ -2432,105 +3255,118 @@ PyObject *eEPGCache::search(ePyObject arg)
                                ++cit;
                                continue;
                        }
-                       ePyObject service_name;
-                       ePyObject service_reference;
-                       timeMap &evmap = cit->second.second;
+                       timeMap &evmap = cit->second.byTime;
                        // check all events
                        for (timeMap::iterator evit(evmap.begin()); evit != evmap.end() && maxcount; ++evit)
                        {
-                               int evid = evit->second->getEventID();
-                               if ( evid == eventid)
-                                       continue;
-                               __u8 *data = evit->second->EITdata;
-                               int tmp = evit->second->ByteSize-10;
-                               __u32 *p = (__u32*)(data+10);
+                               if (querytype == 0)
+                               {
+                                       /* ignore the current event, when looking for similar events */
+                                       if (evit->second->getEventID() == eventid)
+                                               continue;
+                               }
                                // check if any of our descriptor used by this event
-                               int cnt=-1;
-                               while(tmp>3)
+                               unsigned int cnt = 0;
+                               for (uint8_t i = 0; i < evit->second->n_crc; ++i)
                                {
-                                       __u32 crc32 = *p++;
-                                       for ( int i=0; i <= descridx; ++i)
+                                       uint32_t crc32 = evit->second->crc_list[i];
+                                       for (std::deque<uint32_t>::const_iterator it = descr.begin();
+                                               it != descr.end(); ++it)
                                        {
-                                               if (descr[i] == crc32)  // found...
+                                               if (*it == crc32)  // found...
+                                               {
                                                        ++cnt;
+                                                       if (querytype)
+                                                       {
+                                                               /* we need only one match, when we're not looking for similar broadcasting events */
+                                                               i = evit->second->n_crc;
+                                                               break;
+                                                       }
+                                               }
                                        }
-                                       tmp-=4;
                                }
-                               if ( (querytype == 0 && cnt == descridx) ||
-                                        ((querytype == 1 || querytype == 2) && cnt != -1) )
+                               if ( (querytype == 0 && cnt == descr.size()) ||
+                                        ((querytype > 0) && cnt != 0) )
                                {
                                        const uniqueEPGKey &service = cit->first;
-                                       eServiceReference ref =
-                                               eDVBDB::getInstance()->searchReference(service.tsid, service.onid, service.sid);
-                                       if (ref.valid())
+                                       std::vector<eServiceReference> refs;
+                                       eDVBDB::getInstance()->searchAllReferences(refs, service.tsid, service.onid, service.sid);
+                                       for (unsigned int i = 0; i < refs.size(); i++)
                                        {
-                                       // create servive event
-                                               eServiceEvent ptr;
-                                               const eventData *ev_data=0;
-                                               if (needServiceEvent)
+                                               eServiceReference ref = refs[i];
+                                               if (ref.valid())
                                                {
-                                                       if (lookupEventId(ref, evid, ev_data))
-                                                               eDebug("event not found !!!!!!!!!!!");
-                                                       else
+                                                       ePyObject service_name;
+                                                       ePyObject service_reference;
+                                               // create servive event
+                                                       eServiceEvent ptr;
+                                                       const eventData *ev_data=0;
+                                                       if (needServiceEvent)
                                                        {
-                                                               const eServiceReferenceDVB &dref = (const eServiceReferenceDVB&)ref;
-                                                               Event ev((uint8_t*)ev_data->get());
-                                                               ptr.parseFrom(&ev, (dref.getTransportStreamID().get()<<16)|dref.getOriginalNetworkID().get());
+                                                               if (lookupEventId(ref, evit->second->getEventID(), ev_data))
+                                                                       eDebug("[eEPGCache] event not found !!!!!!!!!!!");
+                                                               else
+                                                               {
+                                                                       const eServiceReferenceDVB &dref = (const eServiceReferenceDVB&)ref;
+                                                                       Event ev((uint8_t*)ev_data->get());
+                                                                       ptr.parseFrom(&ev, (dref.getTransportStreamID().get()<<16)|dref.getOriginalNetworkID().get());
+                                                               }
                                                        }
-                                               }
-                                       // create service name
-                                               if (must_get_service_name && !service_name)
-                                               {
-                                                       ePtr<iStaticServiceInformation> sptr;
-                                                       eServiceCenterPtr service_center;
-                                                       eServiceCenter::getPrivInstance(service_center);
-                                                       if (service_center)
+                                               // create service name
+                                                       if (must_get_service_name && !service_name)
                                                        {
-                                                               service_center->info(ref, sptr);
-                                                               if (sptr)
+                                                               ePtr<iStaticServiceInformation> sptr;
+                                                               eServiceCenterPtr service_center;
+                                                               eServiceCenter::getPrivInstance(service_center);
+                                                               if (service_center)
                                                                {
-                                                                       std::string name;
-                                                                       sptr->getName(ref, name);
-
-                                                                       if (must_get_service_name == 1)
+                                                                       service_center->info(ref, sptr);
+                                                                       if (sptr)
                                                                        {
-                                                                               size_t pos;
-                                                                               // filter short name brakets
-                                                                               while((pos = name.find("\xc2\x86")) != std::string::npos)
-                                                                                       name.erase(pos,2);
-                                                                               while((pos = name.find("\xc2\x87")) != std::string::npos)
-                                                                                       name.erase(pos,2);
-                                                                       }
-                                                                       else
-                                                                               name = buildShortName(name);
+                                                                               std::string name;
+                                                                               sptr->getName(ref, name);
 
-                                                                       if (name.length())
-                                                                               service_name = PyString_FromString(name.c_str());
+                                                                               if (must_get_service_name == 1)
+                                                                               {
+                                                                                       size_t pos;
+                                                                                       // filter short name brakets
+                                                                                       while((pos = name.find("\xc2\x86")) != std::string::npos)
+                                                                                               name.erase(pos,2);
+                                                                                       while((pos = name.find("\xc2\x87")) != std::string::npos)
+                                                                                               name.erase(pos,2);
+                                                                               }
+                                                                               else
+                                                                                       name = buildShortName(name);
+
+                                                                               if (name.length())
+                                                                                       service_name = PyString_FromString(name.c_str());
+                                                                       }
                                                                }
+                                                               if (!service_name)
+                                                                       service_name = PyString_FromString("<n/a>");
                                                        }
-                                                       if (!service_name)
-                                                               service_name = PyString_FromString("<n/a>");
+                                               // create servicereference string
+                                                       if (must_get_service_reference && !service_reference)
+                                                               service_reference = PyString_FromString(ref.toString().c_str());
+                                               // create list
+                                                       if (!ret)
+                                                               ret = PyList_New(0);
+                                               // create tuple
+                                                       ePyObject tuple = PyTuple_New(argcount);
+                                               // fill tuple
+                                                       ePyObject tmp = ePyObject();
+                                                       fillTuple(tuple, argstring, argcount, service_reference, ev_data ? &ptr : 0, service_name, tmp, evit->second);
+                                                       PyList_Append(ret, tuple);
+                                                       Py_DECREF(tuple);
+                                                       if (service_name)
+                                                               Py_DECREF(service_name);
+                                                       if (service_reference)
+                                                               Py_DECREF(service_reference);
+                                                       --maxcount;
                                                }
-                                       // create servicereference string
-                                               if (must_get_service_reference && !service_reference)
-                                                       service_reference = PyString_FromString(ref.toString().c_str());
-                                       // create list
-                                               if (!ret)
-                                                       ret = PyList_New(0);
-                                       // create tuple
-                                               ePyObject tuple = PyTuple_New(argcount);
-                                       // fill tuple
-                                               fillTuple2(tuple, argstring, argcount, evit->second, ev_data ? &ptr : 0, service_name, service_reference);
-                                               PyList_Append(ret, tuple);
-                                               Py_DECREF(tuple);
-                                               --maxcount;
                                        }
                                }
                        }
-                       if (service_name)
-                               Py_DECREF(service_name);
-                       if (service_reference)
-                               Py_DECREF(service_reference);
                        if (first)
                        {
                                // now start at first service in epgcache database ( only in SIMILAR BROADCASTING SEARCH )
@@ -2576,11 +3412,11 @@ void eEPGCache::PMTready(eDVBServicePMTHandler *pmthandler)
                                                switch ((*desc)->getTag())
                                                {
                                                        case 0xC2: // user defined
-                                                               if ((*desc)->getLength() == 8) 
+                                                               if ((*desc)->getLength() == 8)
                                                                {
-                                                                       __u8 buffer[10];
+                                                                       uint8_t buffer[10];
                                                                        (*desc)->writeToBuffer(buffer);
-                                                                       if (!strncmp((const char *)buffer+2, "EPGDATA", 7))
+                                                                       if (!memcmp((const char *)buffer+2, "EPGDATA", 7))
                                                                        {
                                                                                eServiceReferenceDVB ref;
                                                                                if (!pmthandler->getServiceReference(ref))
@@ -2589,7 +3425,7 @@ void eEPGCache::PMTready(eDVBServicePMTHandler *pmthandler)
                                                                                        messages.send(Message(Message::got_mhw2_channel_pid, ref, pid));
                                                                                }
                                                                        }
-                                                                       else if(!strncmp((const char *)buffer+2, "FICHAS", 6))
+                                                                       else if(!memcmp((const char *)buffer+2, "FICHAS", 6))
                                                                        {
                                                                                eServiceReferenceDVB ref;
                                                                                if (!pmthandler->getServiceReference(ref))
@@ -2598,7 +3434,7 @@ void eEPGCache::PMTready(eDVBServicePMTHandler *pmthandler)
                                                                                        messages.send(Message(Message::got_mhw2_summary_pid, ref, pid));
                                                                                }
                                                                        }
-                                                                       else if(!strncmp((const char *)buffer+2, "GENEROS", 7))
+                                                                       else if(!memcmp((const char *)buffer+2, "GENEROS", 7))
                                                                        {
                                                                                eServiceReferenceDVB ref;
                                                                                if (!pmthandler->getServiceReference(ref))
@@ -2625,7 +3461,7 @@ void eEPGCache::PMTready(eDVBServicePMTHandler *pmthandler)
                                                                break;
                                                        case 0x90:
                                                        {
-                                                               UnknownDescriptor *descr = (UnknownDescriptor*)*desc;
+                                                               Descriptor *descr = (Descriptor*)*desc;
                                                                int descr_len = descr->getLength();
                                                                if (descr_len == 4)
                                                                {
@@ -2657,27 +3493,27 @@ void eEPGCache::PMTready(eDVBServicePMTHandler *pmthandler)
                }
        }
        else
-               eDebug("PMTready but no pmt!!");
+               eDebug("[eEPGCache] PMTready but no pmt!!");
 }
 
 struct date_time
 {
-       __u8 data[5];
+       uint8_t data[5];
        time_t tm;
        date_time( const date_time &a )
        {
                memcpy(data, a.data, 5);
                tm = a.tm;
        }
-       date_time( const __u8 data[5])
+       date_time( const uint8_t data[5])
        {
                memcpy(this->data, data, 5);
-               tm = parseDVBtime(data[0], data[1], data[2], data[3], data[4]);
+               tm = parseDVBtime(data);
        }
        date_time()
        {
        }
-       const __u8& operator[](int pos) const
+       const uint8_t& operator[](int pos) const
        {
                return data[pos];
        }
@@ -2691,42 +3527,44 @@ struct less_datetime
        }
 };
 
-void eEPGCache::privateSectionRead(const uniqueEPGKey &current_service, const __u8 *data)
+void eEPGCache::privateSectionRead(const uniqueEPGKey &current_service, const uint8_t *data)
 {
        contentMap &content_time_table = content_time_tables[current_service];
        singleLock s(cache_lock);
        std::map< date_time, std::list<uniqueEPGKey>, less_datetime > start_times;
-       eventMap &evMap = eventDB[current_service].first;
-       timeMap &tmMap = eventDB[current_service].second;
-       int ptr=8;
+       EventCacheItem &eventDBitem = eventDB[current_service];
+       eventMap &evMap = eventDBitem.byEvent;
+       timeMap &tmMap = eventDBitem.byTime;
+       int ptr = 8;
        int content_id = data[ptr++] << 24;
        content_id |= data[ptr++] << 16;
        content_id |= data[ptr++] << 8;
        content_id |= data[ptr++];
 
-       contentTimeMap &time_event_map =
-               content_time_table[content_id];
+       contentTimeMap &time_event_map = content_time_table[content_id];
        for ( contentTimeMap::iterator it( time_event_map.begin() );
                it != time_event_map.end(); ++it )
        {
                eventMap::iterator evIt( evMap.find(it->second.second) );
                if ( evIt != evMap.end() )
                {
+                       // time_event_map can have other timestamp -> get timestamp from eventData
+                       time_t ev_time = evIt->second->getStartTime();
                        delete evIt->second;
                        evMap.erase(evIt);
+                       tmMap.erase(ev_time);
                }
-               tmMap.erase(it->second.first);
        }
        time_event_map.clear();
 
-       __u8 duration[3];
+       uint8_t duration[3];
        memcpy(duration, data+ptr, 3);
        ptr+=3;
  &nbs