/***************************************************************************
                          dvbsection.cpp  -  description
                             -------------------
    begin                : Tue Dec 16 2003
    copyright            : (C) 2003-2005 by Christophe Thommeret
    email                : hftom@free.fr
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute 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.                                   *
 *                                                                         *
 ***************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <time.h>

#include <qtextcodec.h>

#include <klocale.h>

#include "dvbsection.h"
#include "gdvb.h"



DVBsection::DVBsection( bool *ok, int anum, int tnum, const QString &charset )
{
	defaultCharset = charset.ascii();
	demuxer = "";
	*ok = true;
	adapter = anum;
	tuner = tnum;
	QString s = QString("/dev/dvb/adapter%1/demux%2").arg( anum ).arg( tnum );

	if ((fdDemux = open( s.ascii(), O_RDWR | O_NONBLOCK )) < 0) {
		perror ("open failed");
		*ok = false;
	}

	isRunning = false;
}



DVBsection::DVBsection( bool *ok, int anum, int tnum, bool /*openDemux*/, const QString &charset )
{
	defaultCharset = charset.ascii();
	*ok = true;
	demuxer = QString("/dev/dvb/adapter%1/demux%2").arg( anum ).arg( tnum );
	isRunning = false;
}




DVBsection::~DVBsection()
{
	if ( demuxer.isEmpty() ) {
		close( fdDemux );
	}
}



bool DVBsection::openFilter( int pid, int tid, int timeout, bool checkcrc )
{
	if ((fdDemux = open( demuxer.ascii(), O_RDWR | O_NONBLOCK )) < 0) {
		perror ("open failed");
		return false;
	}
	if ( !setFilter( pid, tid, timeout, checkcrc ) )
		return false;
	return true;
}



bool DVBsection::setFilter( int pid, int tid, int timeout, bool checkcrc )
{
	struct dmx_sct_filter_params sctfilter;

	memset( &sctfilter, 0, sizeof( sctfilter ) );

	sctfilter.pid = pid;
	if ( tid<256 && tid>0 ) {
		sctfilter.filter.filter[0] = tid;
		sctfilter.filter.mask[0] = 0xff;
	}
	sctfilter.flags = DMX_IMMEDIATE_START;
	if ( checkcrc )
		sctfilter.flags|= DMX_CHECK_CRC;
	sctfilter.timeout = timeout;

	if ( ioctl( fdDemux, DMX_SET_FILTER, &sctfilter ) < 0 ) {
		perror ( "ioctl DMX_SET_FILTER failed" );
		return false;
	}
	return true;
}



void DVBsection::closeFilter()
{
	ioctl( fdDemux, DMX_STOP );
	close( fdDemux );
}



void DVBsection::stopFilter()
{
	ioctl( fdDemux, DMX_STOP );
}



unsigned int DVBsection::getBits( unsigned char *b, int offbits, int nbits )
{
	int i, nbytes;
	unsigned int ret = 0;
	unsigned char *buf;

	buf = b+(offbits/8);
	offbits %=8;
	nbytes = (offbits+nbits)/8;
	if ( ((offbits+nbits)%8)>0 )
		nbytes++;
	for ( i=0; i<nbytes; i++ )
		ret += buf[i]<<((nbytes-i-1)*8);
	i = (4-nbytes)*8+offbits;
	ret = ((ret<<i)>>i)>>((nbytes*8)-nbits-offbits);

	return ret;
}



QString DVBsection::getText( unsigned char *buf, int length )
{
	unsigned int index=0;
	QCString s;
	QString ret="";
	int i, val;
	QCString table;

	if ( length==0 )
		return "";

	switch ( buf[0] ) {
		case 0x01: table = "ISO8859-5"; ++index; break;
		case 0x02: table = "ISO8859-6"; ++index; break;
		case 0x03: table = "ISO8859-7"; ++index; break;
		case 0x04: table = "ISO8859-8"; ++index; break;
		case 0x05: table = "ISO8859-9"; ++index; break;
		case 0x06: table = "ISO8859-10"; ++index; break;
		case 0x09: table = "ISO8859-13"; ++index; break;
		case 0x0A: table = "ISO8859-14"; ++index; break;
		case 0x0B: table = "ISO8859-15"; ++index; break;
		case 0x13: table = "GB2312"; ++index; break;
		case 0x14: table = "Big5"; ++index; break;
		case 0x10: {
			if ( length<3 )
				return "";
			switch ( buf[2] ) {
				case 0x01: table = "ISO8859-1"; break;
				case 0x02: table = "ISO8859-2"; break;
				case 0x03: table = "ISO8859-3"; break;
				case 0x04: table = "ISO8859-4"; break;
				case 0x05: table = "ISO8859-5"; break;
				case 0x06: table = "ISO8859-6"; break;
				case 0x07: table = "ISO8859-7"; break;
				case 0x08: table = "ISO8859-8"; break;
				case 0x09: table = "ISO8859-9"; break;
				case 0x0A: table = "ISO8859-10"; break;
				case 0x0D: table = "ISO8859-13"; break;
				case 0x0E: table = "ISO8859-14"; break;
				case 0x0F: table = "ISO8859-15"; break;
				default: table = defaultCharset;
			}
			index = 3;
			break;
		}
		default: {
			if ( buf[0]>=0x20 )
				table = defaultCharset;
			else
				++index;
		}
	}

	if ( table=="ISO6937" ) {
		for ( i=index; i<length; i++ ) {
			if ( buf[i]<0x20 || (buf[i]>=0x80 && buf[i]<=0x9f) )
				continue; // control codes
			if ( buf[i]>=0xC0 && buf[i]<=0xCF ) { // next char has accent
				if ( i==length-1 ) // Accent char not followed by char
					continue;
				val = ( buf[i]<<8 ) + buf[++i];
				switch (val) {
					// zgrep ' /xc' /usr/share/i18n/charmaps/ISO_6937.gz | grep -v 'not a real' | sed 's@<U\(.*\)> */x\([0-9a-f]*\)/x\([0-9a-f]*\) *\([^ ].*\)@case 0x\2\3: ret += QChar(0x\1); break; //\4@'
					case 0xc141: ret += QChar(0x00C0); break; //LATIN CAPITAL LETTER A WITH GRAVE
					case 0xc145: ret += QChar(0x00C8); break; //LATIN CAPITAL LETTER E WITH GRAVE
					case 0xc149: ret += QChar(0x00CC); break; //LATIN CAPITAL LETTER I WITH GRAVE
					case 0xc14f: ret += QChar(0x00D2); break; //LATIN CAPITAL LETTER O WITH GRAVE
					case 0xc155: ret += QChar(0x00D9); break; //LATIN CAPITAL LETTER U WITH GRAVE
					case 0xc161: ret += QChar(0x00E0); break; //LATIN SMALL LETTER A WITH GRAVE
					case 0xc165: ret += QChar(0x00E8); break; //LATIN SMALL LETTER E WITH GRAVE
					case 0xc169: ret += QChar(0x00EC); break; //LATIN SMALL LETTER I WITH GRAVE
					case 0xc16f: ret += QChar(0x00F2); break; //LATIN SMALL LETTER O WITH GRAVE
					case 0xc175: ret += QChar(0x00F9); break; //LATIN SMALL LETTER U WITH GRAVE
					case 0xc220: ret += QChar(0x00B4); break; //ACUTE ACCENT
					case 0xc241: ret += QChar(0x00C1); break; //LATIN CAPITAL LETTER A WITH ACUTE
					case 0xc243: ret += QChar(0x0106); break; //LATIN CAPITAL LETTER C WITH ACUTE
					case 0xc245: ret += QChar(0x00C9); break; //LATIN CAPITAL LETTER E WITH ACUTE
					case 0xc249: ret += QChar(0x00CD); break; //LATIN CAPITAL LETTER I WITH ACUTE
					case 0xc24c: ret += QChar(0x0139); break; //LATIN CAPITAL LETTER L WITH ACUTE
					case 0xc24e: ret += QChar(0x0143); break; //LATIN CAPITAL LETTER N WITH ACUTE
					case 0xc24f: ret += QChar(0x00D3); break; //LATIN CAPITAL LETTER O WITH ACUTE
					case 0xc252: ret += QChar(0x0154); break; //LATIN CAPITAL LETTER R WITH ACUTE
					case 0xc253: ret += QChar(0x015A); break; //LATIN CAPITAL LETTER S WITH ACUTE
					case 0xc255: ret += QChar(0x00DA); break; //LATIN CAPITAL LETTER U WITH ACUTE
					case 0xc259: ret += QChar(0x00DD); break; //LATIN CAPITAL LETTER Y WITH ACUTE
					case 0xc25a: ret += QChar(0x0179); break; //LATIN CAPITAL LETTER Z WITH ACUTE
					case 0xc261: ret += QChar(0x00E1); break; //LATIN SMALL LETTER A WITH ACUTE
					case 0xc263: ret += QChar(0x0107); break; //LATIN SMALL LETTER C WITH ACUTE
					case 0xc265: ret += QChar(0x00E9); break; //LATIN SMALL LETTER E WITH ACUTE
					case 0xc269: ret += QChar(0x00ED); break; //LATIN SMALL LETTER I WITH ACUTE
					case 0xc26c: ret += QChar(0x013A); break; //LATIN SMALL LETTER L WITH ACUTE
					case 0xc26e: ret += QChar(0x0144); break; //LATIN SMALL LETTER N WITH ACUTE
					case 0xc26f: ret += QChar(0x00F3); break; //LATIN SMALL LETTER O WITH ACUTE
					case 0xc272: ret += QChar(0x0155); break; //LATIN SMALL LETTER R WITH ACUTE
					case 0xc273: ret += QChar(0x015B); break; //LATIN SMALL LETTER S WITH ACUTE
					case 0xc275: ret += QChar(0x00FA); break; //LATIN SMALL LETTER U WITH ACUTE
					case 0xc279: ret += QChar(0x00FD); break; //LATIN SMALL LETTER Y WITH ACUTE
					case 0xc27a: ret += QChar(0x017A); break; //LATIN SMALL LETTER Z WITH ACUTE
					case 0xc341: ret += QChar(0x00C2); break; //LATIN CAPITAL LETTER A WITH CIRCUMFLEX
					case 0xc343: ret += QChar(0x0108); break; //LATIN CAPITAL LETTER C WITH CIRCUMFLEX
					case 0xc345: ret += QChar(0x00CA); break; //LATIN CAPITAL LETTER E WITH CIRCUMFLEX
					case 0xc347: ret += QChar(0x011C); break; //LATIN CAPITAL LETTER G WITH CIRCUMFLEX
					case 0xc348: ret += QChar(0x0124); break; //LATIN CAPITAL LETTER H WITH CIRCUMFLEX
					case 0xc349: ret += QChar(0x00CE); break; //LATIN CAPITAL LETTER I WITH CIRCUMFLEX
					case 0xc34a: ret += QChar(0x0134); break; //LATIN CAPITAL LETTER J WITH CIRCUMFLEX
					case 0xc34f: ret += QChar(0x00D4); break; //LATIN CAPITAL LETTER O WITH CIRCUMFLEX
					case 0xc353: ret += QChar(0x015C); break; //LATIN CAPITAL LETTER S WITH CIRCUMFLEX
					case 0xc355: ret += QChar(0x00DB); break; //LATIN CAPITAL LETTER U WITH CIRCUMFLEX
					case 0xc357: ret += QChar(0x0174); break; //LATIN CAPITAL LETTER W WITH CIRCUMFLEX
					case 0xc359: ret += QChar(0x0176); break; //LATIN CAPITAL LETTER Y WITH CIRCUMFLEX
					case 0xc361: ret += QChar(0x00E2); break; //LATIN SMALL LETTER A WITH CIRCUMFLEX
					case 0xc363: ret += QChar(0x0109); break; //LATIN SMALL LETTER C WITH CIRCUMFLEX
					case 0xc365: ret += QChar(0x00EA); break; //LATIN SMALL LETTER E WITH CIRCUMFLEX
					case 0xc367: ret += QChar(0x011D); break; //LATIN SMALL LETTER G WITH CIRCUMFLEX
					case 0xc368: ret += QChar(0x0125); break; //LATIN SMALL LETTER H WITH CIRCUMFLEX
					case 0xc369: ret += QChar(0x00EE); break; //LATIN SMALL LETTER I WITH CIRCUMFLEX
					case 0xc36a: ret += QChar(0x0135); break; //LATIN SMALL LETTER J WITH CIRCUMFLEX
					case 0xc36f: ret += QChar(0x00F4); break; //LATIN SMALL LETTER O WITH CIRCUMFLEX
					case 0xc373: ret += QChar(0x015D); break; //LATIN SMALL LETTER S WITH CIRCUMFLEX
					case 0xc375: ret += QChar(0x00FB); break; //LATIN SMALL LETTER U WITH CIRCUMFLEX
					case 0xc377: ret += QChar(0x0175); break; //LATIN SMALL LETTER W WITH CIRCUMFLEX
					case 0xc379: ret += QChar(0x0177); break; //LATIN SMALL LETTER Y WITH CIRCUMFLEX
					case 0xc441: ret += QChar(0x00C3); break; //LATIN CAPITAL LETTER A WITH TILDE
					case 0xc449: ret += QChar(0x0128); break; //LATIN CAPITAL LETTER I WITH TILDE
					case 0xc44e: ret += QChar(0x00D1); break; //LATIN CAPITAL LETTER N WITH TILDE
					case 0xc44f: ret += QChar(0x00D5); break; //LATIN CAPITAL LETTER O WITH TILDE
					case 0xc455: ret += QChar(0x0168); break; //LATIN CAPITAL LETTER U WITH TILDE
					case 0xc461: ret += QChar(0x00E3); break; //LATIN SMALL LETTER A WITH TILDE
					case 0xc469: ret += QChar(0x0129); break; //LATIN SMALL LETTER I WITH TILDE
					case 0xc46e: ret += QChar(0x00F1); break; //LATIN SMALL LETTER N WITH TILDE
					case 0xc46f: ret += QChar(0x00F5); break; //LATIN SMALL LETTER O WITH TILDE
					case 0xc475: ret += QChar(0x0169); break; //LATIN SMALL LETTER U WITH TILDE
					case 0xc520: ret += QChar(0x00AF); break; //MACRON
					case 0xc541: ret += QChar(0x0100); break; //LATIN CAPITAL LETTER A WITH MACRON
					case 0xc545: ret += QChar(0x0112); break; //LATIN CAPITAL LETTER E WITH MACRON
					case 0xc549: ret += QChar(0x012A); break; //LATIN CAPITAL LETTER I WITH MACRON
					case 0xc54f: ret += QChar(0x014C); break; //LATIN CAPITAL LETTER O WITH MACRON
					case 0xc555: ret += QChar(0x016A); break; //LATIN CAPITAL LETTER U WITH MACRON
					case 0xc561: ret += QChar(0x0101); break; //LATIN SMALL LETTER A WITH MACRON
					case 0xc565: ret += QChar(0x0113); break; //LATIN SMALL LETTER E WITH MACRON
					case 0xc569: ret += QChar(0x012B); break; //LATIN SMALL LETTER I WITH MACRON
					case 0xc56f: ret += QChar(0x014D); break; //LATIN SMALL LETTER O WITH MACRON
					case 0xc575: ret += QChar(0x016B); break; //LATIN SMALL LETTER U WITH MACRON
					case 0xc620: ret += QChar(0x02D8); break; //BREVE
					case 0xc641: ret += QChar(0x0102); break; //LATIN CAPITAL LETTER A WITH BREVE
					case 0xc647: ret += QChar(0x011E); break; //LATIN CAPITAL LETTER G WITH BREVE
					case 0xc655: ret += QChar(0x016C); break; //LATIN CAPITAL LETTER U WITH BREVE
					case 0xc661: ret += QChar(0x0103); break; //LATIN SMALL LETTER A WITH BREVE
					case 0xc667: ret += QChar(0x011F); break; //LATIN SMALL LETTER G WITH BREVE
					case 0xc675: ret += QChar(0x016D); break; //LATIN SMALL LETTER U WITH BREVE
					case 0xc720: ret += QChar(0x02D9); break; //DOT ABOVE (Mandarin Chinese light tone)
					case 0xc743: ret += QChar(0x010A); break; //LATIN CAPITAL LETTER C WITH DOT ABOVE
					case 0xc745: ret += QChar(0x0116); break; //LATIN CAPITAL LETTER E WITH DOT ABOVE
					case 0xc747: ret += QChar(0x0120); break; //LATIN CAPITAL LETTER G WITH DOT ABOVE
					case 0xc749: ret += QChar(0x0130); break; //LATIN CAPITAL LETTER I WITH DOT ABOVE
					case 0xc75a: ret += QChar(0x017B); break; //LATIN CAPITAL LETTER Z WITH DOT ABOVE
					case 0xc763: ret += QChar(0x010B); break; //LATIN SMALL LETTER C WITH DOT ABOVE
					case 0xc765: ret += QChar(0x0117); break; //LATIN SMALL LETTER E WITH DOT ABOVE
					case 0xc767: ret += QChar(0x0121); break; //LATIN SMALL LETTER G WITH DOT ABOVE
					case 0xc77a: ret += QChar(0x017C); break; //LATIN SMALL LETTER Z WITH DOT ABOVE
					case 0xc820: ret += QChar(0x00A8); break; //DIAERESIS
					case 0xc841: ret += QChar(0x00C4); break; //LATIN CAPITAL LETTER A WITH DIAERESIS
					case 0xc845: ret += QChar(0x00CB); break; //LATIN CAPITAL LETTER E WITH DIAERESIS
					case 0xc849: ret += QChar(0x00CF); break; //LATIN CAPITAL LETTER I WITH DIAERESIS
					case 0xc84f: ret += QChar(0x00D6); break; //LATIN CAPITAL LETTER O WITH DIAERESIS
					case 0xc855: ret += QChar(0x00DC); break; //LATIN CAPITAL LETTER U WITH DIAERESIS
					case 0xc859: ret += QChar(0x0178); break; //LATIN CAPITAL LETTER Y WITH DIAERESIS
					case 0xc861: ret += QChar(0x00E4); break; //LATIN SMALL LETTER A WITH DIAERESIS
					case 0xc865: ret += QChar(0x00EB); break; //LATIN SMALL LETTER E WITH DIAERESIS
					case 0xc869: ret += QChar(0x00EF); break; //LATIN SMALL LETTER I WITH DIAERESIS
					case 0xc86f: ret += QChar(0x00F6); break; //LATIN SMALL LETTER O WITH DIAERESIS
					case 0xc875: ret += QChar(0x00FC); break; //LATIN SMALL LETTER U WITH DIAERESIS
					case 0xc879: ret += QChar(0x00FF); break; //LATIN SMALL LETTER Y WITH DIAERESIS
					case 0xca20: ret += QChar(0x02DA); break; //RING ABOVE
					case 0xca41: ret += QChar(0x00C5); break; //LATIN CAPITAL LETTER A WITH RING ABOVE
					case 0xca55: ret += QChar(0x016E); break; //LATIN CAPITAL LETTER U WITH RING ABOVE
					case 0xca61: ret += QChar(0x00E5); break; //LATIN SMALL LETTER A WITH RING ABOVE
					case 0xca75: ret += QChar(0x016F); break; //LATIN SMALL LETTER U WITH RING ABOVE
					case 0xcb20: ret += QChar(0x00B8); break; //CEDILLA
					case 0xcb43: ret += QChar(0x00C7); break; //LATIN CAPITAL LETTER C WITH CEDILLA
					case 0xcb47: ret += QChar(0x0122); break; //LATIN CAPITAL LETTER G WITH CEDILLA
					case 0xcb4b: ret += QChar(0x0136); break; //LATIN CAPITAL LETTER K WITH CEDILLA
					case 0xcb4c: ret += QChar(0x013B); break; //LATIN CAPITAL LETTER L WITH CEDILLA
					case 0xcb4e: ret += QChar(0x0145); break; //LATIN CAPITAL LETTER N WITH CEDILLA
					case 0xcb52: ret += QChar(0x0156); break; //LATIN CAPITAL LETTER R WITH CEDILLA
					case 0xcb53: ret += QChar(0x015E); break; //LATIN CAPITAL LETTER S WITH CEDILLA
					case 0xcb54: ret += QChar(0x0162); break; //LATIN CAPITAL LETTER T WITH CEDILLA
					case 0xcb63: ret += QChar(0x00E7); break; //LATIN SMALL LETTER C WITH CEDILLA
					case 0xcb67: ret += QChar(0x0123); break; //LATIN SMALL LETTER G WITH CEDILLA
					case 0xcb6b: ret += QChar(0x0137); break; //LATIN SMALL LETTER K WITH CEDILLA
					case 0xcb6c: ret += QChar(0x013C); break; //LATIN SMALL LETTER L WITH CEDILLA
					case 0xcb6e: ret += QChar(0x0146); break; //LATIN SMALL LETTER N WITH CEDILLA
					case 0xcb72: ret += QChar(0x0157); break; //LATIN SMALL LETTER R WITH CEDILLA
					case 0xcb73: ret += QChar(0x015F); break; //LATIN SMALL LETTER S WITH CEDILLA
					case 0xcb74: ret += QChar(0x0163); break; //LATIN SMALL LETTER T WITH CEDILLA
					case 0xcd20: ret += QChar(0x02DD); break; //DOUBLE ACUTE ACCENT
					case 0xcd4f: ret += QChar(0x0150); break; //LATIN CAPITAL LETTER O WITH DOUBLE ACUTE
					case 0xcd55: ret += QChar(0x0170); break; //LATIN CAPITAL LETTER U WITH DOUBLE ACUTE
					case 0xcd6f: ret += QChar(0x0151); break; //LATIN SMALL LETTER O WITH DOUBLE ACUTE
					case 0xcd75: ret += QChar(0x0171); break; //LATIN SMALL LETTER U WITH DOUBLE ACUTE
					case 0xce20: ret += QChar(0x02DB); break; //OGONEK
					case 0xce41: ret += QChar(0x0104); break; //LATIN CAPITAL LETTER A WITH OGONEK
					case 0xce45: ret += QChar(0x0118); break; //LATIN CAPITAL LETTER E WITH OGONEK
					case 0xce49: ret += QChar(0x012E); break; //LATIN CAPITAL LETTER I WITH OGONEK
					case 0xce55: ret += QChar(0x0172); break; //LATIN CAPITAL LETTER U WITH OGONEK
					case 0xce61: ret += QChar(0x0105); break; //LATIN SMALL LETTER A WITH OGONEK
					case 0xce65: ret += QChar(0x0119); break; //LATIN SMALL LETTER E WITH OGONEK
					case 0xce69: ret += QChar(0x012F); break; //LATIN SMALL LETTER I WITH OGONEK
					case 0xce75: ret += QChar(0x0173); break; //LATIN SMALL LETTER U WITH OGONEK
					case 0xcf20: ret += QChar(0x02C7); break; //CARON (Mandarin Chinese third tone)
					case 0xcf43: ret += QChar(0x010C); break; //LATIN CAPITAL LETTER C WITH CARON
					case 0xcf44: ret += QChar(0x010E); break; //LATIN CAPITAL LETTER D WITH CARON
					case 0xcf45: ret += QChar(0x011A); break; //LATIN CAPITAL LETTER E WITH CARON
					case 0xcf4c: ret += QChar(0x013D); break; //LATIN CAPITAL LETTER L WITH CARON
					case 0xcf4e: ret += QChar(0x0147); break; //LATIN CAPITAL LETTER N WITH CARON
					case 0xcf52: ret += QChar(0x0158); break; //LATIN CAPITAL LETTER R WITH CARON
					case 0xcf53: ret += QChar(0x0160); break; //LATIN CAPITAL LETTER S WITH CARON
					case 0xcf54: ret += QChar(0x0164); break; //LATIN CAPITAL LETTER T WITH CARON
					case 0xcf5a: ret += QChar(0x017D); break; //LATIN CAPITAL LETTER Z WITH CARON
					case 0xcf63: ret += QChar(0x010D); break; //LATIN SMALL LETTER C WITH CARON
					case 0xcf64: ret += QChar(0x010F); break; //LATIN SMALL LETTER D WITH CARON
					case 0xcf65: ret += QChar(0x011B); break; //LATIN SMALL LETTER E WITH CARON
					case 0xcf6c: ret += QChar(0x013E); break; //LATIN SMALL LETTER L WITH CARON
					case 0xcf6e: ret += QChar(0x0148); break; //LATIN SMALL LETTER N WITH CARON
					case 0xcf72: ret += QChar(0x0159); break; //LATIN SMALL LETTER R WITH CARON
					case 0xcf73: ret += QChar(0x0161); break; //LATIN SMALL LETTER S WITH CARON
					case 0xcf74: ret += QChar(0x0165); break; //LATIN SMALL LETTER T WITH CARON
					case 0xcf7a: ret += QChar(0x017E); break; //LATIN SMALL LETTER Z WITH CARON
					default: break; // unknown
				}
			}
			//zgrep ' /x[a-f]' /usr/share/i18n/charmaps/ISO_6937.gz | grep -v 'not a real' | sed 's@<U\(.*\)> */x\([0-9a-f]*\) *\([^ ].*\)@ else if(buf[i]==0x\2)\n  ret += QChar(0x\1); //\3@'
			else {
				switch ( buf[i] ) {
					case 0xa0: ret += QChar(0x00A0); break; //NO-BREAK SPACE
					case 0xa1: ret += QChar(0x00A1); break; //INVERTED EXCLAMATION MARK
					case 0xa2: ret += QChar(0x00A2); break; //CENT SIGN
					case 0xa3: ret += QChar(0x00A3); break; //POUND SIGN
					case 0xa5: ret += QChar(0x00A5); break; //YEN SIGN
					case 0xa7: ret += QChar(0x00A7); break; //SECTION SIGN
					case 0xa8: ret += QChar(0x00A4); break; //CURRENCY SIGN
					case 0xa9: ret += QChar(0x2018); break; //LEFT SINGLE QUOTATION MARK
					case 0xaa: ret += QChar(0x201C); break; //LEFT DOUBLE QUOTATION MARK
					case 0xab: ret += QChar(0x00AB); break; //LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
					case 0xac: ret += QChar(0x2190); break; //LEFTWARDS ARROW
					case 0xad: ret += QChar(0x2191); break; //UPWARDS ARROW
					case 0xae: ret += QChar(0x2192); break; //RIGHTWARDS ARROW
					case 0xaf: ret += QChar(0x2193); break; //DOWNWARDS ARROW
					case 0xb0: ret += QChar(0x00B0); break; //DEGREE SIGN
					case 0xb1: ret += QChar(0x00B1); break; //PLUS-MINUS SIGN
					case 0xb2: ret += QChar(0x00B2); break; //SUPERSCRIPT TWO
					case 0xb3: ret += QChar(0x00B3); break; //SUPERSCRIPT THREE
					case 0xb4: ret += QChar(0x00D7); break; //MULTIPLICATION SIGN
					case 0xb5: ret += QChar(0x00B5); break; //MICRO SIGN
					case 0xb6: ret += QChar(0x00B6); break; //PILCROW SIGN
					case 0xb7: ret += QChar(0x00B7); break; //MIDDLE DOT
					case 0xb8: ret += QChar(0x00F7); break; //DIVISION SIGN
					case 0xb9: ret += QChar(0x2019); break; //RIGHT SINGLE QUOTATION MARK
					case 0xba: ret += QChar(0x201D); break; //RIGHT DOUBLE QUOTATION MARK
					case 0xbb: ret += QChar(0x00BB); break; //RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
					case 0xbc: ret += QChar(0x00BC); break; //VULGAR FRACTION ONE QUARTER
					case 0xbd: ret += QChar(0x00BD); break; //VULGAR FRACTION ONE HALF
					case 0xbe: ret += QChar(0x00BE); break; //VULGAR FRACTION THREE QUARTERS
					case 0xbf: ret += QChar(0x00BF); break; //INVERTED QUESTION MARK
					case 0xd0: ret += QChar(0x2014); break; //EM DASH
					case 0xd1: ret += QChar(0x00B9); break; //SUPERSCRIPT ONE
					case 0xd2: ret += QChar(0x00AE); break; //REGISTERED SIGN
					case 0xd3: ret += QChar(0x00A9); break; //COPYRIGHT SIGN
					case 0xd4: ret += QChar(0x2122); break; //TRADE MARK SIGN
					case 0xd5: ret += QChar(0x266A); break; //EIGHTH NOTE
					case 0xd6: ret += QChar(0x00AC); break; //NOT SIGN
					case 0xd7: ret += QChar(0x00A6); break; //BROKEN BAR
					case 0xdc: ret += QChar(0x215B); break; //VULGAR FRACTION ONE EIGHTH
					case 0xdd: ret += QChar(0x215C); break; //VULGAR FRACTION THREE EIGHTHS
					case 0xde: ret += QChar(0x215D); break; //VULGAR FRACTION FIVE EIGHTHS
					case 0xdf: ret += QChar(0x215E); break; //VULGAR FRACTION SEVEN EIGHTHS
					case 0xe0: ret += QChar(0x2126); break; //OHM SIGN
					case 0xe1: ret += QChar(0x00C6); break; //LATIN CAPITAL LETTER AE
					case 0xe2: ret += QChar(0x00D0); break; //LATIN CAPITAL LETTER ETH (Icelandic)
					case 0xe3: ret += QChar(0x00AA); break; //FEMININE ORDINAL INDICATOR
					case 0xe4: ret += QChar(0x0126); break; //LATIN CAPITAL LETTER H WITH STROKE
					case 0xe6: ret += QChar(0x0132); break; //LATIN CAPITAL LIGATURE IJ
					case 0xe7: ret += QChar(0x013F); break; //LATIN CAPITAL LETTER L WITH MIDDLE DOT
					case 0xe8: ret += QChar(0x0141); break; //LATIN CAPITAL LETTER L WITH STROKE
					case 0xe9: ret += QChar(0x00D8); break; //LATIN CAPITAL LETTER O WITH STROKE
					case 0xea: ret += QChar(0x0152); break; //LATIN CAPITAL LIGATURE OE
					case 0xeb: ret += QChar(0x00BA); break; //MASCULINE ORDINAL INDICATOR
					case 0xec: ret += QChar(0x00DE); break; //LATIN CAPITAL LETTER THORN (Icelandic)
					case 0xed: ret += QChar(0x0166); break; //LATIN CAPITAL LETTER T WITH STROKE
					case 0xee: ret += QChar(0x014A); break; //LATIN CAPITAL LETTER ENG (Sami)
					case 0xef: ret += QChar(0x0149); break; //LATIN SMALL LETTER N PRECEDED BY APOSTROPHE
					case 0xf0:	ret += QChar(0x0138); break; //LATIN SMALL LETTER KRA (Greenlandic)
					case 0xf1:	ret += QChar(0x00E6); break; //LATIN SMALL LETTER AE
					case 0xf2:	ret += QChar(0x0111); break; //LATIN SMALL LETTER D WITH STROKE
					case 0xf3:	ret += QChar(0x00F0); break; //LATIN SMALL LETTER ETH (Icelandic)
					case 0xf4:	ret += QChar(0x0127); break; //LATIN SMALL LETTER H WITH STROKE
					case 0xf5:	ret += QChar(0x0131); break; //LATIN SMALL LETTER DOTLESS I
					case 0xf6:	ret += QChar(0x0133); break; //LATIN SMALL LIGATURE IJ
					case 0xf7:	ret += QChar(0x0140); break; //LATIN SMALL LETTER L WITH MIDDLE DOT
					case 0xf8:	ret += QChar(0x0142); break; //LATIN SMALL LETTER L WITH STROKE
					case 0xf9:	ret += QChar(0x00F8); break; //LATIN SMALL LETTER O WITH STROKE
					case 0xfa:	ret += QChar(0x0153); break; //LATIN SMALL LIGATURE OE
					case 0xfb:	ret += QChar(0x00DF); break; //LATIN SMALL LETTER SHARP S (German)
					case 0xfc:	ret += QChar(0x00FE); break; //LATIN SMALL LETTER THORN (Icelandic)
					case 0xfd:	ret += QChar(0x0167); break; //LATIN SMALL LETTER T WITH STROKE
					case 0xfe:	ret += QChar(0x014B); break; //LATIN SMALL LETTER ENG (Sami)
					case 0xff: ret += QChar(0x00AD); break; //SOFT HYPHEN
					default: ret += buf[i];
				}
			}
		}
		return ret;
	}
	else {
		for ( i=index; i<length; i++ ) {
			if ( buf[i]<0x20 || (buf[i]>=0x80 && buf[i]<=0x9f) )
				continue; // control codes
			s += buf[i];
		}
		QTextCodec *codec = QTextCodec::codecForName( table );
		return codec->toUnicode( s );
	}
}



QString DVBsection::langDesc( unsigned char* buf )
{
	char c[4];
	QString s;

	memset( mempcpy( c, buf+2, 3 ), 0, 1 );
	s = c;
	return s;
}



QTime DVBsection::getTime( unsigned char *buf )
{
	int h, m, s;

	h = ((getBits(buf,0,4)*10)+getBits(buf,4,4))%24;
	m = ((getBits(buf,8,4)*10)+getBits(buf,12,4))%60;
	s = ((getBits(buf,16,4)*10)+getBits(buf,20,4))%60;
	return QTime( h, m, s );
}



QDate DVBsection::getDate( unsigned char *buf )
{
	int i, j, m, D, Y, M, k, mjd;

	mjd = getBits(buf,0,16);
	i = (int)((mjd-15078.2)/365.25);
	j = (int)(i*365.25);
	m = (int)((mjd-14956.1-j)/30.6001);
	D = mjd-14956-j-(int)(m*30.6001);
	if ( m==14 || m==15 )
		k = 1;
	else
		k = 0;
	Y = i+k+1900;
	M = m-1-k*12;

	return QDate( (Y>=1970)?Y:1970, (M>0 && M<13)?M:1, (D>0 && D<32)?D:1 );
}



QDateTime DVBsection::getDateTime( unsigned char *buf )
{
	/*int hh, mm, ss;
	int i, j, m, D, Y, M, k, mjd;
	int sec;

	mjd = getBits(buf,0,16);
	i = (int)((mjd-15078.2)/365.25);
	j = (int)(i*365.25);
	m = (int)((mjd-14956.1-j)/30.6001);
	D = mjd-14956-j-(int)(m*30.6001);
	if ( m==14 || m==15 ) k = 1;
	else k = 0;
	Y = i+k+1900;
	M = m-1-k*12;

	hh = ((getBits(buf+2,0,4)*10)+getBits(buf+2,4,4))%24;
	mm = ((getBits(buf+2,8,4)*10)+getBits(buf+2,12,4))%60;
	ss = ((getBits(buf+2,16,4)*10)+getBits(buf+2,20,4))%60;

	QDateTime dt( QDate( (Y>=1970)?Y:1970, (M>0 && M<13)?M:1, (D>0 && D<32)?D:1 ), QTime( hh, mm, ss ) );
	QDateTime u( QDate( 1970, 1, 1 ), QTime( 0, 0, 0 ) );
	sec = u.secsTo( dt );
	u.setTime_t( sec ); // UTC to local
	return u;*/

	int i, j, m, k, mjd;
	struct tm tt;
	struct tm *t=&tt;

	mjd = getBits(buf,0,16);
	i = (int)((mjd-15078.2)/365.25);
	j = (int)(i*365.25);
	m = (int)((mjd-14956.1-j)/30.6001);
	t->tm_mday = mjd-14956-j-(int)(m*30.6001);
	if ( m==14 || m==15 )
		k = 1;
	else
		k = 0;
	t->tm_year = i+k;
	t->tm_mon = m-1-k*12-1;
	t->tm_sec = ((getBits(buf+2,16,4)*10)+getBits(buf+2,20,4))%60;
	t->tm_min = ((getBits(buf+2,8,4)*10)+getBits(buf+2,12,4))%60;
	t->tm_hour = ((getBits(buf+2,0,4)*10)+getBits(buf+2,4,4))%24;
	t->tm_isdst = -1;
	t->tm_gmtoff = 0;

	time_t p=timegm(t);
	if ( p>0 ) {
		t = localtime(&p);
		return QDateTime( QDate( t->tm_year+1900, t->tm_mon+1, t->tm_mday ), QTime( t->tm_hour, t->tm_min, t->tm_sec ) );
	}
	else
		return QDateTime( QDate( 1970, 1, 1 ), QTime( 0, 0, 0 ) );
}

#include "dvbsection.moc"
