/*
 * dvbsi.cpp
 *
 * Copyright (C) 2008-2011 Christoph Pfister <christophpfister@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "../log.h"

#include <QTextCodec>

#include "dvbsi.h"

void DvbSection::initSection(const char *data, int size)
{
	if (size < 3) {
		initSectionData();
		return;
	}

	int sectionLength = ((((quint8(data[1]) & 0xf) << 8) | quint8(data[2])) + 3);

	if (sectionLength > size) {
		qCInfo(logDvbSi, "Adjusting length");
		sectionLength = size;
	}

	initSectionData(data, sectionLength, size);
}

int DvbStandardSection::verifyCrc32(const char *data, int size)
{
	unsigned int crc32 = 0xffffffff;

	for (int i = 0; i < size; ++i) {
		crc32 = ((crc32 << 8) ^ crc32Table[(crc32 >> 24) ^ quint8(data[i])]);
	}

	return crc32;
}

/*
 * unsigned int crc32TableValue(int index)
 * {
 * 	unsigned int value = 0;
 *
 * 	for (int i = 8; i >= 0; --i) {
 * 		if (((value & 0x80000000) != 0) ^ ((index & (1 << i)) != 0)) {
 * 			value = (value << 1) ^ 0x04c11db7;
 * 		} else {
 * 			value <<= 1;
 * 		}
 * 	}
 *
 * 	return value;
 * }
 */

const unsigned int DvbStandardSection::crc32Table[] =
{
	0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9,
	0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
	0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
	0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
	0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9,
	0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
	0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011,
	0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd,
	0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
	0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,
	0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81,
	0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
	0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49,
	0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
	0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
	0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d,
	0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae,
	0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
	0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
	0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca,
	0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
	0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02,
	0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066,
	0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
	0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e,
	0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692,
	0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
	0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,
	0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
	0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
	0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686,
	0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a,
	0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
	0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
	0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f,
	0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
	0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47,
	0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b,
	0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
	0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,
	0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7,
	0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
	0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f,
	0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
	0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
	0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b,
	0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f,
	0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
	0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
	0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
	0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
	0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24,
	0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30,
	0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
	0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088,
	0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,
	0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
	0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c,
	0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
	0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
	0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0,
	0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c,
	0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
	0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
};

void DvbStandardSection::initStandardSection(const char *data, int size)
{
	if (size < 12) {
		initSectionData();
		return;
	}

	initSection(data, size);
}

class Iso6937Codec : public QTextCodec
{
public:
	Iso6937Codec() { }
	~Iso6937Codec() { }

	QByteArray name() const override
	{
		return "ISO 6937"; // actually a superset of ISO 6937
	}

	int mibEnum() const override
	{
		return 14;
	}

	QByteArray convertFromUnicode(const QChar *, int, QTextCodec::ConverterState *) const override
	{
		return QByteArray();
	}

	QString convertToUnicode(const char *input, int size, QTextCodec::ConverterState *) const override
	{
		QString result;
		unsigned short diacriticalMark = 0;

		for (; size > 0; ++input, --size) {
			unsigned short value = table[quint8(*input)];

			if (value == 0xffff) {
				continue;
			}

			if ((value & 0xff00) == 0x0300) {
				// diacritical mark
				diacriticalMark = value;
				continue;
			}

			result.append(value);

			if (diacriticalMark != 0) {
				result.append(diacriticalMark);
				diacriticalMark = 0;
			}
		}

		return result.normalized(QString::NormalizationForm_C);
	}

private:
	static const unsigned short table[];
};

const unsigned short Iso6937Codec::table[] = {
	0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
	0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
	0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
	0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,
	0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
	0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
	0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
	0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
	0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
	0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
	0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
	0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
	0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
	0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
	0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
	0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,
	0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
	0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
	0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
	0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
	0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x20ac, 0x00a5, 0xffff, 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,
	0xffff, 0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0306, 0x0307,
	0x0308, 0xffff, 0x030a, 0x0327, 0xffff, 0x030b, 0x0328, 0x030c,
	0x2015, 0x00b9, 0x00ae, 0x00a9, 0x2122, 0x266a, 0x00ac, 0x00a6,
	0xffff, 0xffff, 0xffff, 0xffff, 0x215b, 0x215c, 0x215d, 0x215e,
	0x2126, 0x00c6, 0x0110, 0x00aa, 0x0126, 0xffff, 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
};

QString DvbSiText::convertText(const char *data, int size)
{
	TextEncoding encoding = Iso6937;

	if (size < 1) {
		return QString();
	}

	if (size >2 && data[0] == 0x0e && data[size - 1] == 0x0f ) {
		// Remove LS0 and LS1 codes found on some ISDB-T streams
		data++;
		size -= 2;

		encoding = Iso8859_1;
	}

	if (override6937)
		encoding = Iso8859_1;

	if (quint8(data[0]) < 0x20) {
		switch (data[0]) {
		case 0x00: encoding = Iso6937; break;
		case 0x01: encoding = Iso8859_5; break;
		case 0x02: encoding = Iso8859_6; break;
		case 0x03: encoding = Iso8859_7; break;
		case 0x04: encoding = Iso8859_8; break;
		case 0x05: encoding = Iso8859_9; break;
		case 0x06: encoding = Iso8859_10; break;
		case 0x07: encoding = Iso8859_11; break;
		case 0x09: encoding = Iso8859_13; break;
		case 0x0a: encoding = Iso8859_14; break;
		case 0x0b: encoding = Iso8859_15; break;
		case 0x11: encoding = Iso10646_ucs2; break;
		case 0x12: encoding = Iso2022_kr; break;
		case 0x13: encoding = Gb2312; break;
		case 0x14: encoding = Utf_16be; break;
		case 0x15: encoding = Utf_8; break;
		case 0x10: {
			if (size < 3) {
				return QString();
			}

			if (data[1] != 0) {
				return QString();
			}

			switch (data[2]) {
			case 0x01: encoding = Iso8859_1; break;
			case 0x02: encoding = Iso8859_2; break;
			case 0x03: encoding = Iso8859_3; break;
			case 0x04: encoding = Iso8859_4; break;
			case 0x05: encoding = Iso8859_5; break;
			case 0x06: encoding = Iso8859_6; break;
			case 0x07: encoding = Iso8859_7; break;
			case 0x08: encoding = Iso8859_8; break;
			case 0x09: encoding = Iso8859_9; break;
			case 0x0a: encoding = Iso8859_10; break;
			case 0x0b: encoding = Iso8859_11; break;
			case 0x0d: encoding = Iso8859_13; break;
			case 0x0e: encoding = Iso8859_14; break;
			case 0x0f: encoding = Iso8859_15; break;
			default:
				return QString();
			}

			data += 2;
			size -= 2;
			break;
		    }
		}

		data++;
		size--;
	}

	if (codecTable[encoding] == NULL) {
		QTextCodec *codec = NULL;

		switch (encoding) {
		case Iso6937: codec = new Iso6937Codec(); break;
		case Iso8859_1: codec = QTextCodec::codecForName("ISO 8859-1"); break;
		case Iso8859_2: codec = QTextCodec::codecForName("ISO 8859-2"); break;
		case Iso8859_3: codec = QTextCodec::codecForName("ISO 8859-3"); break;
		case Iso8859_4: codec = QTextCodec::codecForName("ISO 8859-4"); break;
		case Iso8859_5: codec = QTextCodec::codecForName("ISO 8859-5"); break;
		case Iso8859_6: codec = QTextCodec::codecForName("ISO 8859-6"); break;
		case Iso8859_7: codec = QTextCodec::codecForName("ISO 8859-7"); break;
		case Iso8859_8: codec = QTextCodec::codecForName("ISO 8859-8"); break;
		case Iso8859_9: codec = QTextCodec::codecForName("ISO 8859-9"); break;
		case Iso8859_10: codec = QTextCodec::codecForName("ISO 8859-10"); break;
		case Iso8859_11: codec = QTextCodec::codecForName("ISO 8859-11"); break;
		case Iso8859_13: codec = QTextCodec::codecForName("ISO 8859-13"); break;
		case Iso8859_14: codec = QTextCodec::codecForName("ISO 8859-14"); break;
		case Iso8859_15: codec = QTextCodec::codecForName("ISO 8859-15"); break;
		case Iso10646_ucs2: codec = QTextCodec::codecForName("UTF-16"); break;
		case Iso2022_kr: codec = QTextCodec::codecForName("ISO 2022-KR"); break;
		case Gb2312: codec = QTextCodec::codecForName("GB2312"); break;
		case Utf_16be: codec = QTextCodec::codecForName("UTF-16BE"); break;
		case Utf_8: codec = QTextCodec::codecForName("UTF-8"); break;
		}

		Q_ASSERT(codec != NULL);
		codecTable[encoding] = codec;
	}

	if (encoding <= Iso8859_15) {
		// only strip control codes for one-byte character tables

		char *dest = new char[size];
		char *destIt = dest;

		for (const char *it = data; it != (data + size); ++it) {
			unsigned char value = *it;

			if ((value < 0x80) || (value > 0x9f)) {
				*(destIt++) = value;
			}
		}

		QString result = codecTable[encoding]->toUnicode(dest, int(destIt - dest));
		delete[] dest;

		return result;
	}

	return codecTable[encoding]->toUnicode(data, size);
}

void DvbSiText::setOverride6937(bool override)
{
	override6937 = override;
}

QTextCodec *DvbSiText::codecTable[EncodingTypeMax + 1] = { NULL };
bool DvbSiText::override6937 = false;

void DvbDescriptor::initDescriptor(const char *data, int size)
{
	if (size < 2) {
		if (size != 0) {
			qCInfo(logDvbSi, "Invalid descriptor");
		}

		initSectionData();
		return;
	}

	int descriptorLength = (quint8(data[1]) + 2);

	if (descriptorLength > size) {
		qCInfo(logDvbSi, "Adjusting length");
		descriptorLength = size;
	}

	initSectionData(data, descriptorLength, size);
}

QString AtscPsipText::interpretTextData(const char *data, unsigned int len,
					unsigned int mode)
{
	// Based on the "mode" values in A/65C Table 6.41, convert
	// the data into a string
	QString result;

	if ((mode <= 0x06) ||
	    (mode >= 0x09 && mode <= 0x10) ||
	    (mode >= 0x20 && mode <= 0x27) ||
	    (mode >= 0x30 && mode <= 0x33)) {
		// Select UNICODE Code range based on mode as leading octet
		for (unsigned int i = 0; i < len; i++) {
			QChar val = (data[i] | (mode << 8));
			result += val;
		}
	} else if (mode == 0x3f) {
		// UTF-16
		for (unsigned int i = 0; i < len; i += 2) {
			QChar val = ((unsigned char)data[i] * 256) + (unsigned char)data[i+1];
			result += val;
		}
	} else {
		// We currently don't support the following:
		// 0x3e Standard Compression Scheme for UNICODE (SCSU)
		// 0x40/0x41 Taiwan
		// 0x48 South Korea
		qCInfo(logDvbSi, "Unsupported ATSC Text mode %d", mode);
	}
	return result;
}

QString AtscPsipText::convertText(const char *data, int size)
{
	QString result;

	if (size < 1) {
		return QString();
	}

	unsigned int num_strings = quint8(data[0]);

	if (num_strings == 0) {
		return result;
	}

	// Note that for now we are only supporting the first string sent.  If
	// multiple languages are sent, we only grab the first one provided
	int offset = 1;

	// First three bytes are the ISO 639 language code
	offset += 3;

	if (offset > size) {
		qCInfo(logDvbSi, "Adjusting length");
		return result;
	}

	int num_segments = quint8(data[offset++]);

	for (int j = 0; j < num_segments; j++) {
		if ((offset + 3) > size) {
			qCInfo(logDvbSi, "Adjusting length");
			return result;
		}

		int comp_type = quint8(data[offset++]);
		int mode = quint8(data[offset++]);
		int num_bytes = quint8(data[offset++]);

		if ((offset + num_bytes) > size) {
			qCInfo(logDvbSi, "Adjusting length");
			return result;
		}

		const char *comp_string = (data + offset);

		if (comp_type == 0x00) {
			// Uncompressed
			result += interpretTextData(comp_string,
						    num_bytes, mode);
		} else if ((comp_type == 0x01 || comp_type == 0x02) &&
			   mode == 0x00) {
			// Huffman Compression
			// As per the spec, only mode 0x00 supports Huffman coding
			result +=
				AtscHuffmanString::convertText(comp_string, num_bytes, comp_type);
		} else {
			qCInfo(logDvbSi, "Unsupported compression / mode %d %d", comp_type, mode);
		}

		offset += num_bytes;
	}

	return result;
}

QString AtscHuffmanString::convertText(const char *data_, int length, int table)
{
	AtscHuffmanString huffmanstring(data_, length, table);
	huffmanstring.decompress();
	return huffmanstring.result;
}

AtscHuffmanString::AtscHuffmanString(const char *data_, int length, int table) : data(data_),
	bitsLeft(8 * length)
{
	if (table == 1) {
		offsets = Huffman1Offsets;
		tableBase = Huffman1Tables;
	} else {
		offsets = Huffman2Offsets;
		tableBase = Huffman2Tables;
	}
}

AtscHuffmanString::~AtscHuffmanString() { }

bool AtscHuffmanString::hasBits()
{
	return bitsLeft > 0;
}

unsigned char AtscHuffmanString::getBit()
{
	if (bitsLeft >= 1) {
		int shift = (--bitsLeft % 8);
		unsigned char value = (data[0] >> shift) & 0x1;

		if (shift == 0) {
			data++;
		}
		return value;
	}

	return 0;
}

unsigned char AtscHuffmanString::getByte()
{
	if (bitsLeft >= 8) {
		int shift = ((bitsLeft - 1) % 8);
		// Note this takes advantage of the fact that there is always
		// at least one byte remaining (to avoid a read-past end
		// condition)
		unsigned char value = ((((data[0] << 8) | quint8(data[1])) >> (shift + 1)) & 0xff);

		bitsLeft -= 8;
		data++;
		return value;
	}

	return 0;
}

void AtscHuffmanString::decompress()
{
	const unsigned char *table = tableBase;

	while (hasBits()) {
		int index = 0;

		do {
			index = table[2 * index + getBit()];
		} while (index < 128);

		index &= 0x7f;

		if (index == 27) {
			// escape --> uncompressed character(s)
			while (true) {
				index = getByte();

				if (index < 128) {
					break;
				}

				result += QChar(index);
			}
		}

		if (index == 0) {
			// end
			break;
		}

		result += QChar(index);
		table = tableBase + offsets[index];
	}
}

const unsigned short AtscHuffmanString::Huffman1Offsets[128] = {
	0x0000, 0x003a, 0x003c, 0x003e, 0x0040, 0x0042, 0x0044, 0x0046,
	0x0048, 0x004a, 0x004c, 0x004e, 0x0050, 0x0052, 0x0054, 0x0056,
	0x0058, 0x005a, 0x005c, 0x005e, 0x0060, 0x0062, 0x0064, 0x0066,
	0x0068, 0x006a, 0x006c, 0x006e, 0x0070, 0x0072, 0x0074, 0x0076,
	0x0078, 0x00ce, 0x00d2, 0x00d4, 0x00d6, 0x00d8, 0x00da, 0x00dc,
	0x00e6, 0x00e8, 0x00ea, 0x00f0, 0x00f2, 0x00f4, 0x0106, 0x0112,
	0x0114, 0x011c, 0x0128, 0x0130, 0x0134, 0x0136, 0x0138, 0x013a,
	0x013c, 0x013e, 0x0146, 0x0148, 0x014a, 0x014c, 0x014e, 0x0150,
	0x0152, 0x0154, 0x017e, 0x0192, 0x01ac, 0x01ba, 0x01d2, 0x01e4,
	0x01fa, 0x0206, 0x021e, 0x0226, 0x0232, 0x023e, 0x0252, 0x0264,
	0x027a, 0x0294, 0x0298, 0x02a4, 0x02c8, 0x02de, 0x02e6, 0x02f4,
	0x0304, 0x0306, 0x030c, 0x0310, 0x0312, 0x0314, 0x0316, 0x0318,
	0x031a, 0x031c, 0x0352, 0x036a, 0x038e, 0x03ae, 0x03ee, 0x0406,
	0x0428, 0x0444, 0x0472, 0x0476, 0x0490, 0x04be, 0x04d6, 0x050a,
	0x0544, 0x0564, 0x0566, 0x059a, 0x05d0, 0x05fc, 0x0622, 0x062c,
	0x0646, 0x0654, 0x067c, 0x068a, 0x068c, 0x068e, 0x0690, 0x0692 };

const unsigned char AtscHuffmanString::Huffman1Tables[] = {
	0x1b, 0x1c, 0xb4, 0xa4, 0xb2, 0xb7, 0xda, 0x01,
	0xd1, 0x02, 0x03, 0x9b, 0x04, 0xd5, 0xd9, 0x05,
	0xcb, 0xd6, 0x06, 0xcf, 0x07, 0x08, 0xca, 0x09,
	0xc9, 0xc5, 0xc6, 0x0a, 0xd2, 0xc4, 0xc7, 0xcc,
	0xd0, 0xc8, 0xd7, 0xce, 0x0b, 0xc1, 0x0c, 0xc2,
	0xcd, 0xc3, 0x0d, 0x0e, 0x0f, 0x10, 0xd3, 0x11,
	0xd4, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
	0x19, 0x1a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
	0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
	0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
	0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
	0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
	0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
	0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
	0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
	0x29, 0x2a, 0xd8, 0xe5, 0xb9, 0x01, 0xa7, 0xb1,
	0xec, 0xd1, 0x02, 0xad, 0xb2, 0xda, 0xe3, 0xb3,
	0x03, 0xe4, 0xe6, 0x04, 0x9b, 0xe2, 0x05, 0x06,
	0x07, 0x08, 0x09, 0xd5, 0x0a, 0xd6, 0x0b, 0xd9,
	0x0c, 0xa6, 0xe9, 0xcb, 0xc5, 0xcf, 0x0d, 0x0e,
	0xca, 0xc9, 0x0f, 0xc7, 0x10, 0x11, 0xe1, 0x12,
	0x13, 0xc6, 0xd2, 0xc8, 0xce, 0xc1, 0xc4, 0xd0,
	0xcc, 0x14, 0x15, 0xef, 0xc2, 0xd7, 0x16, 0xcd,
	0x17, 0xf4, 0xd4, 0x18, 0x19, 0x1a, 0xc3, 0xd3,
	0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22,
	0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x01, 0x80,
	0xa0, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0xb1, 0x9b,
	0x9b, 0x9b, 0x9b, 0xa0, 0x04, 0xf3, 0xe4, 0xb9,
	0x01, 0xf4, 0xa0, 0x9b, 0x02, 0x03, 0x9b, 0x9b,
	0x9b, 0x9b, 0x01, 0x02, 0x9b, 0xc1, 0xc8, 0xd3,
	0x9b, 0x9b, 0x9b, 0xa0, 0x07, 0x08, 0xb1, 0xd2,
	0xd3, 0xd4, 0xd5, 0xad, 0xcd, 0xc1, 0x01, 0x02,
	0x03, 0xa0, 0x04, 0x9b, 0x05, 0x06, 0xa0, 0x05,
	0xc9, 0xd7, 0xd3, 0x01, 0x02, 0x9b, 0xae, 0x80,
	0x03, 0x04, 0x9b, 0x9b, 0x02, 0x03, 0xad, 0x9b,
	0x01, 0x80, 0xa0, 0xb0, 0x04, 0x05, 0x80, 0x9b,
	0xb1, 0xb2, 0xa0, 0xb0, 0xb9, 0x01, 0x02, 0x03,
	0x02, 0x03, 0xb1, 0xba, 0x01, 0xb0, 0x9b, 0x80,
	0x80, 0x01, 0xb0, 0x9b, 0x9b, 0xb8, 0x9b, 0x9b,
	0x9b, 0x9b, 0x9b, 0xb0, 0x9b, 0xa0, 0x02, 0x03,
	0xb1, 0xb3, 0xb9, 0xb0, 0x01, 0x9b, 0x9b, 0xa0,
	0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
	0x9b, 0x80, 0x9b, 0x9b, 0x13, 0x14, 0xaa, 0xad,
	0xae, 0xf6, 0xe7, 0xf4, 0xe2, 0xe9, 0x01, 0x02,
	0xc2, 0xf0, 0x9b, 0xf3, 0xe3, 0xe6, 0xf7, 0x03,
	0xf5, 0x04, 0x05, 0x06, 0xf2, 0x07, 0x08, 0x09,
	0x0a, 0x0b, 0x0c, 0xe4, 0xa0, 0x0d, 0xec, 0xee,
	0x0e, 0xed, 0x0f, 0x10, 0x11, 0x12, 0x08, 0x09,
	0xc1, 0xd3, 0x9b, 0x01, 0xc3, 0x02, 0xe9, 0xec,
	0x03, 0xf2, 0xf5, 0x04, 0xef, 0xe1, 0x05, 0xe5,
	0x06, 0x07, 0x0b, 0x0c, 0xc1, 0xf9, 0x01, 0xc2,
	0xcf, 0xe5, 0xf5, 0x9b, 0xe9, 0x02, 0xa0, 0x03,
	0x04, 0x05, 0xf2, 0x06, 0xec, 0x07, 0xe1, 0x08,
	0x09, 0xe8, 0x0a, 0xef, 0x05, 0x06, 0xf9, 0x9b,
	0x01, 0xf5, 0x02, 0xf2, 0xe9, 0xe5, 0xef, 0x03,
	0xe1, 0x04, 0x0a, 0x0b, 0xf1, 0xf5, 0xf3, 0x01,
	0xed, 0xf9, 0xc3, 0x02, 0xec, 0xee, 0xe4, 0xf8,
	0x03, 0x9b, 0xf6, 0x04, 0x05, 0xe1, 0x06, 0x07,
	0x08, 0x09, 0x07, 0x08, 0xa0, 0x9b, 0xcc, 0x01,
	0xe5, 0x02, 0xec, 0xf5, 0xef, 0x03, 0xe9, 0xf2,
	0x04, 0x05, 0xe1, 0x06, 0x09, 0x0a, 0xae, 0xec,
	0xf9, 0xc1, 0xe8, 0x01, 0x9b, 0x02, 0x03, 0x04,
	0xe1, 0xf5, 0xe9, 0x05, 0xe5, 0x06, 0xf2, 0xef,
	0x07, 0x08, 0xef, 0x05, 0x80, 0x9b, 0xf5, 0x01,
	0x02, 0xe9, 0xe1, 0x03, 0xe5, 0x04, 0xee, 0x0b,
	0xba, 0xd4, 0xae, 0xf2, 0xe3, 0x01, 0xa0, 0x02,
	0x80, 0x9b, 0xed, 0x03, 0xc9, 0xf3, 0xf4, 0x04,
	0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x02, 0x03,
	0x9b, 0xf5, 0x01, 0xe1, 0xef, 0xe5, 0x05, 0xe9,
	0xe1, 0xef, 0xf5, 0xee, 0x9b, 0xe5, 0x01, 0x02,
	0x03, 0x04, 0x04, 0x05, 0xa0, 0x9b, 0x01, 0xf5,
	0x02, 0xe5, 0xef, 0x03, 0xe1, 0xe9, 0x08, 0x09,
	0xaa, 0xd4, 0x01, 0x9b, 0xe3, 0x02, 0xf2, 0x03,
	0xe5, 0x04, 0xf5, 0xf9, 0xe9, 0x05, 0xef, 0x06,
	0x07, 0xe1, 0xe5, 0x08, 0xce, 0xa0, 0xc6, 0xf5,
	0x01, 0x02, 0x9b, 0xc2, 0x03, 0xe1, 0x04, 0xef,
	0x05, 0xe9, 0x06, 0x07, 0x09, 0x0a, 0xe4, 0xf3,
	0xe6, 0xf6, 0xf7, 0xf0, 0xf2, 0x01, 0xec, 0x02,
	0x03, 0xa0, 0x9b, 0x04, 0x05, 0xf5, 0x06, 0x07,
	0xee, 0x08, 0x0b, 0x0c, 0xa0, 0xf3, 0xf9, 0xae,
	0xd2, 0xc7, 0x01, 0x9b, 0x02, 0xf5, 0x03, 0x04,
	0x05, 0xe9, 0xec, 0x06, 0xe5, 0x07, 0xef, 0x08,
	0xe1, 0x09, 0xf2, 0x0a, 0x01, 0xf5, 0x9b, 0xd6,
	0x04, 0x05, 0xe8, 0x9b, 0x01, 0xf5, 0x02, 0xe1,
	0xe9, 0xef, 0x03, 0xe5, 0x10, 0x11, 0xaa, 0xec,
	0xf1, 0xae, 0xa0, 0xf7, 0xed, 0xee, 0x01, 0x02,
	0x9b, 0xeb, 0x03, 0x04, 0x05, 0x06, 0xe3, 0x07,
	0xef, 0x08, 0xe9, 0xf5, 0x09, 0xe1, 0xe5, 0xf0,
	0xe8, 0x0a, 0x0b, 0x0c, 0x0d, 0xf4, 0x0e, 0x0f,
	0xe8, 0x0a, 0xad, 0xce, 0x9b, 0x01, 0xd6, 0x02,
	0xf5, 0xf7, 0x03, 0x04, 0xe1, 0xe5, 0xe9, 0x05,
	0xf2, 0x06, 0xef, 0x07, 0x08, 0x09, 0xee, 0x03,
	0xec, 0xae, 0x01, 0x9b, 0x02, 0xf0, 0x06, 0xe9,
	0xa0, 0xc3, 0xef, 0x9b, 0xe5, 0x01, 0x80, 0x02,
	0x03, 0xe1, 0x04, 0x05, 0x06, 0x07, 0xc6, 0xd7,
	0x01, 0x9b, 0xf2, 0x02, 0x03, 0xe8, 0xe5, 0xe1,
	0x04, 0xe9, 0xef, 0x05, 0x9b, 0x9b, 0x02, 0xef,
	0xe1, 0x9b, 0x01, 0xe5, 0x01, 0xef, 0x9b, 0xe1,
	0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
	0x9b, 0x9b, 0x9b, 0x9b, 0x19, 0x1a, 0x9b, 0xba,
	0xe5, 0xea, 0xf8, 0x01, 0x02, 0xe6, 0xa7, 0x03,
	0xfa, 0xe8, 0x04, 0xf7, 0x05, 0xf5, 0xe2, 0x06,
	0xeb, 0x07, 0xf0, 0x08, 0x80, 0xf6, 0xe7, 0x09,
	0xe4, 0x0a, 0xa0, 0xe9, 0x0b, 0xe3, 0xf9, 0x0c,
	0x0d, 0xed, 0x0e, 0x0f, 0xf3, 0x10, 0x11, 0xec,
	0x12, 0xf4, 0xf2, 0x13, 0xee, 0x14, 0x15, 0x16,
	0x17, 0x18, 0x0a, 0x0b, 0xf3, 0x9b, 0xf5, 0xe2,
	0x01, 0x80, 0xa0, 0x02, 0xe5, 0xf2, 0xe9, 0x03,
	0xec, 0x04, 0xf9, 0x05, 0xef, 0x06, 0xe1, 0x07,
	0x08, 0x09, 0x10, 0x11, 0xc3, 0xcc, 0xc7, 0x9b,
	0xe3, 0x01, 0x80, 0xec, 0xf9, 0x02, 0xf3, 0x03,
	0xf5, 0x04, 0x05, 0xf2, 0x06, 0xe9, 0xa0, 0x07,
	0x08, 0xef, 0xf4, 0x09, 0x0a, 0xe1, 0x0b, 0xe8,
	0xeb, 0xe5, 0x0c, 0x0d, 0x0e, 0x0f, 0x0e, 0x0f,
	0xae, 0xf5, 0xf7, 0x01, 0xec, 0x02, 0xe4, 0xe7,
	0xf2, 0x03, 0x9b, 0xef, 0x04, 0xf6, 0x05, 0x06,
	0xf9, 0xf3, 0x07, 0xe9, 0xe1, 0x08, 0x09, 0x80,
	0x0a, 0x0b, 0xe5, 0x0c, 0x0d, 0xa0, 0x1e, 0x1f,
	0x9b, 0xa1, 0xad, 0xe8, 0xea, 0xf1, 0xf5, 0xfa,
	0x01, 0x02, 0x03, 0x04, 0xba, 0xf8, 0xa7, 0xe2,
	0xe9, 0x05, 0x06, 0x07, 0xe6, 0xed, 0xe7, 0xeb,
	0x08, 0x09, 0xf6, 0xf0, 0x0a, 0xef, 0x0b, 0xe3,
	0x0c, 0x0d, 0x0e, 0xf9, 0x0f, 0xe4, 0xec, 0x10,
	0xe5, 0x11, 0xf4, 0xf7, 0x12, 0x13, 0xe1, 0x14,
	0x15, 0x16, 0xee, 0xf3, 0x17, 0x80, 0x18, 0x19,
	0xf2, 0x1a, 0x1b, 0xa0, 0x1c, 0x1d, 0xa0, 0x0b,
	0xf5, 0x9b, 0x01, 0xec, 0xf3, 0xf2, 0x80, 0xe1,
	0x02, 0x03, 0xf4, 0xe9, 0xef, 0xe6, 0x04, 0x05,
	0x06, 0x07, 0xe5, 0x08, 0x09, 0x0a, 0x0f, 0x10,
	0xba, 0xf9, 0xa7, 0xf4, 0x9b, 0x01, 0xe7, 0xec,
	0x02, 0xee, 0x03, 0xef, 0xf5, 0x04, 0xf2, 0x05,
	0x06, 0xe9, 0x07, 0xf3, 0xe1, 0x08, 0x09, 0x0a,
	0x0b, 0xe5, 0x80, 0x0c, 0xe8, 0xa0, 0x0d, 0x0e,
	0xe5, 0x0d, 0xe2, 0xf5, 0xf7, 0x9b, 0xec, 0x01,
	0xf9, 0xee, 0x02, 0x03, 0x04, 0xf2, 0x05, 0x80,
	0x06, 0xa0, 0xe1, 0xef, 0x07, 0xf4, 0xe9, 0x08,
	0x09, 0x0a, 0x0b, 0x0c, 0x15, 0x16, 0xa1, 0xf8,
	0xe9, 0xeb, 0x01, 0x80, 0x9b, 0xfa, 0xe2, 0x02,
	0x03, 0x04, 0xa0, 0xf0, 0x05, 0x06, 0x07, 0xe1,
	0x08, 0xe6, 0xf2, 0xed, 0xf6, 0x09, 0xe4, 0x0a,
	0xef, 0xf4, 0xec, 0xf3, 0xe7, 0xe5, 0x0b, 0xe3,
	0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13,
	0xee, 0x14, 0xef, 0x01, 0x9b, 0xe1, 0x0b, 0x0c,
	0xd4, 0xef, 0xe6, 0xec, 0xf7, 0xe1, 0x01, 0xba,
	0x02, 0x9b, 0xf9, 0x03, 0x04, 0x05, 0xf3, 0x06,
	0x07, 0x08, 0xe9, 0xa0, 0x09, 0x80, 0xe5, 0x0a,
	0x15, 0x16, 0xa7, 0xba, 0xe3, 0xf7, 0xf2, 0xad,
	0xe2, 0x01, 0x02, 0x9b, 0xe6, 0x03, 0xed, 0xf6,
	0x04, 0xeb, 0x05, 0xf4, 0x06, 0x07, 0x08, 0xf3,
	0x09, 0xf5, 0x0a, 0xef, 0x0b, 0x0c, 0x80, 0xf9,
	0xe1, 0x0d, 0xe4, 0xe9, 0xa0, 0x0e, 0x0f, 0xec,
	0xe5, 0x10, 0x11, 0x12, 0x13, 0x14, 0x0a, 0x0b,
	0xf9, 0x9b, 0xf5, 0xf3, 0x01, 0x02, 0xe2, 0xed,
	0x80, 0x03, 0xf0, 0xef, 0x04, 0xa0, 0x05, 0xe9,
	0x06, 0xe1, 0x07, 0x08, 0x09, 0xe5, 0x18, 0x19,
	0xe2, 0xea, 0xf2, 0xe8, 0xec, 0xed, 0xfa, 0x9b,
	0x01, 0xf5, 0x02, 0x03, 0xf6, 0x04, 0xba, 0xe6,
	0x05, 0x06, 0xeb, 0xef, 0x07, 0xa7, 0xf9, 0x08,
	0x09, 0x0a, 0x0b, 0xe3, 0x0c, 0xee, 0xe1, 0x0d,
	0xf3, 0x0e, 0xe9, 0x0f, 0x10, 0xf4, 0x80, 0xe4,
	0xe5, 0x11, 0x12, 0xe7, 0xa0, 0x13, 0x14, 0x15,
	0x16, 0x17, 0x1b, 0x1c, 0xae, 0xfa, 0xbf, 0x01,
	0xa7, 0x9b, 0x02, 0xe9, 0xf8, 0xf9, 0x03, 0xe5,
	0xe8, 0x04, 0xe1, 0xeb, 0x05, 0xe2, 0x06, 0x07,
	0xe3, 0x08, 0xe7, 0xf4, 0x09, 0x80, 0xf6, 0xf0,
	0x0a, 0xe4, 0x0b, 0xf3, 0xf7, 0x0c, 0x0d, 0xef,
	0xec, 0xa0, 0x0e, 0x0f, 0xed, 0xe6, 0x10, 0xf5,
	0x11, 0x12, 0x13, 0x14, 0x15, 0xf2, 0x16, 0xee,
	0x17, 0x18, 0x19, 0x1a, 0x0e, 0x0f, 0xed, 0xa7,
	0x9b, 0xe4, 0x01, 0xf9, 0xf3, 0xf2, 0xf4, 0x02,
	0xe8, 0x03, 0xec, 0xf0, 0x04, 0xe1, 0xe9, 0x05,
	0x06, 0x80, 0xa0, 0x07, 0x08, 0x09, 0x0a, 0xe5,
	0xef, 0x0b, 0x0c, 0x0d, 0x9b, 0xf5, 0x18, 0x19,
	0xba, 0xac, 0xf6, 0x9b, 0xf0, 0xe2, 0x01, 0xe6,
	0x02, 0xa7, 0xae, 0xe7, 0x03, 0xe3, 0xf5, 0x04,
	0xed, 0x05, 0x06, 0x07, 0xeb, 0x08, 0x09, 0xee,
	0xf2, 0x0a, 0xe4, 0x0b, 0xf9, 0xec, 0x0c, 0x0d,
	0xf4, 0x80, 0x0e, 0xef, 0xf3, 0xa0, 0xe1, 0x0f,
	0xe9, 0x10, 0x11, 0xe5, 0x12, 0x13, 0x14, 0x15,
	0x16, 0x17, 0x19, 0x1a, 0xa7, 0xac, 0xbf, 0xc3,
	0xc8, 0xe4, 0xe6, 0xed, 0xf2, 0xae, 0xec, 0xee,
	0xf9, 0x01, 0x02, 0x03, 0x04, 0xba, 0x05, 0x9b,
	0xf5, 0x06, 0x07, 0x08, 0x09, 0xeb, 0xf0, 0x0a,
	0x0b, 0x0c, 0xe1, 0xe3, 0x0d, 0xe8, 0x0e, 0x0f,
	0xef, 0x10, 0x11, 0xf3, 0x12, 0xe9, 0x13, 0xe5,
	0x14, 0x15, 0xf4, 0x16, 0x17, 0xa0, 0x18, 0x80,
	0x14, 0x15, 0xba, 0xbf, 0xe4, 0xf7, 0x9b, 0xa7,
	0x01, 0xee, 0x02, 0x03, 0x04, 0xe3, 0xe2, 0xed,
	0x05, 0xf9, 0x06, 0xf4, 0x07, 0xec, 0x08, 0xf5,
	0xf2, 0x09, 0xe1, 0xf3, 0x0a, 0xef, 0x0b, 0x0c,
	0x0d, 0xe9, 0x80, 0xe5, 0x0e, 0xa0, 0x0f, 0xe8,
	0x10, 0x11, 0x12, 0x13, 0x11, 0x12, 0xeb, 0xfa,
	0x80, 0xe6, 0x9b, 0x01, 0xa0, 0x02, 0x03, 0xe9,
	0xe1, 0x04, 0xe4, 0xf0, 0xed, 0xe2, 0xe3, 0xe7,
	0xec, 0x05, 0xe5, 0x06, 0x07, 0x08, 0x09, 0xf4,
	0x0a, 0x0b, 0x0c, 0xf3, 0xee, 0x0d, 0x0e, 0xf2,
	0x0f, 0x10, 0x04, 0xe5, 0xf3, 0xef, 0x9b, 0x01,
	0xe1, 0x02, 0x03, 0xe9, 0x0b, 0x0c, 0xa7, 0xe2,
	0xec, 0xe3, 0xf2, 0x01, 0x9b, 0x02, 0x03, 0x04,
	0xe9, 0xef, 0xee, 0xe5, 0xe1, 0x80, 0x05, 0xa0,
	0x06, 0x07, 0x08, 0x09, 0xf3, 0x0a, 0x05, 0x06,
	0x9b, 0xa0, 0xe1, 0xe5, 0xe9, 0x01, 0x80, 0xf0,
	0x02, 0xf4, 0x03, 0x04, 0xa0, 0x13, 0xe3, 0xad,
	0xe4, 0xe9, 0xee, 0xef, 0xf0, 0xf4, 0xf6, 0xa1,
	0xe1, 0xed, 0x01, 0xe2, 0x02, 0x03, 0x04, 0xa7,
	0x05, 0x06, 0xf7, 0x07, 0x9b, 0xec, 0x08, 0xe5,
	0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0xf3, 0x0f,
	0x10, 0x11, 0x80, 0x12, 0x05, 0x06, 0xe5, 0xfa,
	0xa0, 0xf9, 0x9b, 0x01, 0x80, 0xe9, 0x02, 0xe1,
	0x03, 0x04, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
	0x9b, 0x9b, 0x9b, 0x9b };

const unsigned short AtscHuffmanString::Huffman2Offsets[128] = {
	0x0000, 0x002c, 0x002e, 0x0030, 0x0032, 0x0034, 0x0036, 0x0038,
	0x003a, 0x003c, 0x003e, 0x0040, 0x0042, 0x0044, 0x0046, 0x0048,
	0x004a, 0x004c, 0x004e, 0x0050, 0x0052, 0x0054, 0x0056, 0x0058,
	0x005a, 0x005c, 0x005e, 0x0060, 0x0062, 0x0064, 0x0066, 0x0068,
	0x006a, 0x00de, 0x00e0, 0x00ea, 0x00ec, 0x00ee, 0x00f0, 0x00f2,
	0x00f8, 0x00fa, 0x00fc, 0x00fe, 0x0100, 0x0104, 0x0116, 0x0120,
	0x0122, 0x012c, 0x0132, 0x0138, 0x013c, 0x0140, 0x0144, 0x0146,
	0x014a, 0x014c, 0x0154, 0x0156, 0x0158, 0x015a, 0x015c, 0x015e,
	0x0160, 0x0162, 0x0176, 0x0184, 0x0194, 0x01a2, 0x01b2, 0x01ba,
	0x01c8, 0x01d2, 0x01de, 0x01ea, 0x01f2, 0x01fc, 0x0208, 0x0210,
	0x021a, 0x0228, 0x022a, 0x0234, 0x024a, 0x025a, 0x025e, 0x0264,
	0x026e, 0x0270, 0x0272, 0x0274, 0x0276, 0x0278, 0x027a, 0x027c,
	0x027e, 0x0280, 0x02b4, 0x02ce, 0x02f0, 0x031a, 0x0358, 0x036e,
	0x038e, 0x03ac, 0x03d8, 0x03e0, 0x03f4, 0x0424, 0x0440, 0x0476,
	0x04ae, 0x04ce, 0x04d0, 0x0506, 0x0534, 0x0560, 0x0586, 0x0592,
	0x05aa, 0x05b8, 0x05dc, 0x05ec, 0x05ee, 0x05f0, 0x05f2, 0x05f4 };

const unsigned char AtscHuffmanString::Huffman2Tables[] = {
	0x14, 0x15, 0x9b, 0xd6, 0xc9, 0xcf, 0xd7, 0xc7,
	0x01, 0xa2, 0xce, 0xcb, 0x02, 0x03, 0xc5, 0xcc,
	0xc6, 0xc8, 0x04, 0xc4, 0x05, 0xc2, 0x06, 0xc3,
	0xd2, 0x07, 0xd3, 0x08, 0xca, 0xd4, 0x09, 0xcd,
	0xd0, 0x0a, 0xc1, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
	0x10, 0x11, 0x12, 0x13, 0x9b, 0x9b, 0x9b, 0x9b,
	0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
	0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
	0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
	0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
	0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
	0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
	0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
	0x9b, 0x9b, 0x38, 0x39, 0xad, 0xaf, 0xb7, 0xda,
	0xa8, 0xb3, 0xb5, 0x01, 0x02, 0x9b, 0xb4, 0xf1,
	0xa2, 0xd5, 0xd6, 0xd9, 0x03, 0x04, 0x05, 0xcf,
	0x06, 0xc9, 0xf9, 0xea, 0xeb, 0xf5, 0xf6, 0x07,
	0x08, 0x09, 0xb2, 0xc5, 0xc6, 0xb1, 0x0a, 0xee,
	0xcb, 0x0b, 0xd4, 0x0c, 0xc4, 0xc8, 0xd2, 0x0d,
	0x0e, 0x0f, 0xc7, 0xca, 0xce, 0xd0, 0xd7, 0x10,
	0xc2, 0x11, 0xcc, 0xec, 0xe5, 0xe7, 0x12, 0xcd,
	0x13, 0x14, 0xc3, 0x15, 0x16, 0x17, 0xed, 0x18,
	0x19, 0xf2, 0x1a, 0xd3, 0x1b, 0x1c, 0xe4, 0x1d,
	0xc1, 0xe3, 0x1e, 0xe9, 0xf0, 0xe2, 0xf7, 0x1f,
	0xf3, 0xe6, 0x20, 0x21, 0x22, 0xe8, 0xef, 0x23,
	0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0xf4,
	0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0xe1, 0x30, 0x31,
	0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x9b, 0x9b,
	0x03, 0x04, 0x80, 0xae, 0xc8, 0xd4, 0x01, 0x02,
	0x9b, 0xa0, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
	0x9b, 0x9b, 0x02, 0xf3, 0xa0, 0xf4, 0x9b, 0x01,
	0x9b, 0x9b, 0xac, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
	0x01, 0xa0, 0x9b, 0xa2, 0x07, 0x08, 0xe2, 0xe4,
	0xe5, 0xe6, 0xa0, 0xf2, 0xe1, 0x01, 0x02, 0xf3,
	0xe3, 0x03, 0x04, 0x05, 0x9b, 0x06, 0x04, 0x80,
	0xca, 0xd3, 0xa2, 0x01, 0x9b, 0x02, 0x03, 0xa0,
	0x9b, 0xa0, 0x03, 0x04, 0x9b, 0xb7, 0xf4, 0xa0,
	0xb0, 0xf3, 0x01, 0x02, 0xb9, 0x02, 0xb8, 0x9b,
	0xa0, 0x01, 0xae, 0x02, 0xb6, 0x9b, 0x01, 0xa0,
	0xa0, 0x01, 0x9b, 0xb0, 0xae, 0x01, 0x9b, 0xa0,
	0xae, 0x01, 0xa0, 0x9b, 0x9b, 0x9b, 0x9b, 0x01,
	0xac, 0xae, 0x9b, 0x9b, 0x02, 0x03, 0x9b, 0xa0,
	0xb5, 0xb6, 0xb8, 0x01, 0x9b, 0xa0, 0x9b, 0xa0,
	0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0xa0,
	0x9b, 0x9b, 0x08, 0x09, 0xe6, 0xf5, 0xf3, 0xf4,
	0x9b, 0xe4, 0x01, 0xed, 0x02, 0x03, 0x04, 0xf2,
	0x05, 0x06, 0xec, 0xee, 0x07, 0xa0, 0x05, 0x06,
	0x9b, 0xec, 0xf5, 0x01, 0x02, 0xe1, 0xef, 0xe5,
	0xe9, 0xf2, 0x03, 0x04, 0x06, 0x07, 0x9b, 0xe9,
	0xf9, 0xf2, 0xf5, 0x01, 0x02, 0x03, 0xec, 0xef,
	0xe1, 0x04, 0xe8, 0x05, 0x05, 0x06, 0xf9, 0xf2,
	0xf5, 0x9b, 0xe5, 0xef, 0x01, 0x02, 0xe9, 0xe1,
	0x03, 0x04, 0x06, 0x07, 0xe1, 0xe9, 0xee, 0xf6,
	0xe4, 0xec, 0xf3, 0x01, 0x02, 0xf2, 0x03, 0x04,
	0x9b, 0x05, 0x02, 0x03, 0xe5, 0xec, 0x9b, 0xef,
	0x01, 0xf2, 0x05, 0x06, 0xf5, 0xef, 0x9b, 0xec,
	0xe9, 0x01, 0xe1, 0xf2, 0x02, 0xe5, 0x03, 0x04,
	0x03, 0x04, 0x9b, 0xe5, 0xe9, 0xf5, 0xe1, 0x01,
	0xef, 0x02, 0x04, 0x05, 0xa0, 0xc9, 0xf3, 0x9b,
	0xae, 0xf2, 0x01, 0x02, 0x03, 0xee, 0xef, 0x05,
	0x9b, 0xae, 0xe9, 0xe5, 0x01, 0xf5, 0x02, 0xe1,
	0x03, 0x04, 0xe5, 0x03, 0xe1, 0xe9, 0xf2, 0x9b,
	0x01, 0x02, 0x03, 0x04, 0x9b, 0xe9, 0xf5, 0x01,
	0xe5, 0x02, 0xef, 0xe1, 0xe1, 0x05, 0x9b, 0xe3,
	0xef, 0x01, 0xf5, 0xe5, 0x02, 0x03, 0xe9, 0x04,
	0xe5, 0x03, 0x9b, 0xe9, 0x01, 0xe1, 0xef, 0x02,
	0x03, 0x04, 0xa7, 0xee, 0xec, 0xf2, 0xf3, 0x01,
	0x9b, 0x02, 0xe1, 0x06, 0x9b, 0xe8, 0xe9, 0x01,
	0xf2, 0xec, 0x02, 0xef, 0x03, 0xe5, 0x04, 0x05,
	0x9b, 0x9b, 0x03, 0x04, 0x9b, 0xae, 0x01, 0xe9,
	0x02, 0xe1, 0xe5, 0xef, 0x09, 0x0a, 0xf6, 0xf9,
	0x01, 0xae, 0xe3, 0xe9, 0xf5, 0x9b, 0xe5, 0xef,
	0x02, 0x03, 0xe1, 0x04, 0xe8, 0x05, 0x06, 0xf4,
	0x07, 0x08, 0xe8, 0x07, 0xe5, 0xf7, 0xd6, 0xe1,
	0x9b, 0xe9, 0xf2, 0x01, 0x02, 0x03, 0x04, 0xef,
	0x05, 0x06, 0xae, 0x01, 0x9b, 0xee, 0xe9, 0x02,
	0xe5, 0x9b, 0xa0, 0x01, 0x03, 0x04, 0x9b, 0xe8,
	0xe5, 0xe1, 0xef, 0x01, 0xe9, 0x02, 0x9b, 0x9b,
	0x9b, 0xef, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
	0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
	0x18, 0x19, 0xe8, 0xef, 0xf8, 0x9b, 0xa7, 0xf7,
	0xfa, 0x01, 0x02, 0x03, 0x04, 0xe5, 0xae, 0x05,
	0xe6, 0xe2, 0x06, 0xf6, 0xeb, 0xf5, 0xe9, 0x07,
	0xf0, 0xf9, 0xe7, 0x08, 0x09, 0xe4, 0x0a, 0xe3,
	0x0b, 0xed, 0x0c, 0xf3, 0x0d, 0x0e, 0x0f, 0xec,
	0x10, 0xf4, 0x11, 0x12, 0xf2, 0xa0, 0x13, 0x14,
	0x15, 0xee, 0x16, 0x17, 0x0b, 0x0c, 0xe4, 0xf3,
	0x9b, 0xae, 0xe2, 0x01, 0x02, 0x03, 0xec, 0xa0,
	0x04, 0xe9, 0xf2, 0xf5, 0x05, 0xf9, 0xe1, 0x06,
	0xef, 0x07, 0xe5, 0x08, 0x09, 0x0a, 0x0f, 0x10,
	0xf1, 0xae, 0xc4, 0xf9, 0xac, 0x01, 0xe3, 0x02,
	0x9b, 0xf2, 0x03, 0x04, 0xa0, 0xec, 0xf5, 0x05,
	0x06, 0xe9, 0x07, 0xeb, 0x08, 0xf4, 0x09, 0xe5,
	0x0a, 0xef, 0xe1, 0xe8, 0x0b, 0x0c, 0x0d, 0x0e,
	0x13, 0x14, 0xa7, 0xbb, 0xe6, 0xed, 0xf7, 0xe7,
	0xf6, 0x01, 0x02, 0x9b, 0xee, 0x03, 0x04, 0xec,
	0x05, 0xf5, 0x06, 0xac, 0xe4, 0xf9, 0xf2, 0x07,
	0x08, 0x09, 0xae, 0x0a, 0xef, 0x0b, 0xe1, 0xf3,
	0x0c, 0xe9, 0x0d, 0x0e, 0x0f, 0x10, 0xe5, 0x11,
	0x12, 0xa0, 0x1d, 0x1e, 0xa9, 0xe8, 0xf5, 0x9b,
	0x01, 0xad, 0xbb, 0xeb, 0xfa, 0x02, 0xa7, 0xe6,
	0xe2, 0xe7, 0x03, 0x04, 0x05, 0x06, 0xe9, 0xf8,
	0x07, 0xac, 0xef, 0xf0, 0x08, 0xed, 0xf6, 0xf9,
	0x09, 0xf7, 0x0a, 0x0b, 0xae, 0x0c, 0xe3, 0x0d,
	0xe5, 0xf4, 0x0e, 0x0f, 0xe4, 0x10, 0xec, 0x11,
	0xe1, 0x12, 0x13, 0x14, 0x15, 0x16, 0xee, 0xf3,
	0x17, 0x18, 0xf2, 0xa0, 0x19, 0x1a, 0x1b, 0x1c,
	0x09, 0x0a, 0xae, 0x9b, 0xec, 0x01, 0xf5, 0x02,
	0xf4, 0xe6, 0x03, 0xe1, 0xe5, 0xe9, 0x04, 0xf2,
	0xef, 0x05, 0x06, 0x07, 0xa0, 0x08, 0x0e, 0x0f,
	0xad, 0xe7, 0x9b, 0xa7, 0xf9, 0x01, 0xec, 0x02,
	0xac, 0xf2, 0x03, 0xae, 0xf3, 0xf5, 0x04, 0x05,
	0xef, 0x06, 0x07, 0xe9, 0xe1, 0x08, 0x09, 0xe8,
	0x0a, 0x0b, 0xe5, 0x0c, 0xa0, 0x0d, 0x0d, 0x0e,
	0xa7, 0xac, 0xf3, 0xad, 0x01, 0x02, 0x9b, 0xf9,
	0xf5, 0xae, 0x03, 0xee, 0x04, 0xf2, 0x05, 0x06,
	0xf4, 0x07, 0x08, 0x09, 0xef, 0xe1, 0xa0, 0x0a,
	0xe9, 0x0b, 0x0c, 0xe5, 0x14, 0x15, 0xac, 0xe2,
	0xf8, 0x9b, 0xae, 0xfa, 0x01, 0xeb, 0x02, 0xa0,
	0x03, 0x04, 0xf0, 0x05, 0x06, 0xe6, 0xf6, 0x07,
	0xe4, 0xed, 0xe7, 0x08, 0xe1, 0xef, 0xf2, 0x09,
	0x0a, 0x0b, 0xec, 0x0c, 0xe5, 0xe3, 0x0d, 0xf4,
	0x0e, 0xf3, 0x0f, 0x10, 0x11, 0xee, 0x12, 0x13,
	0x03, 0xef, 0x9b, 0xe1, 0xe5, 0xf5, 0x01, 0x02,
	0x08, 0x09, 0xec, 0xf9, 0xa7, 0xee, 0x01, 0xac,
	0x9b, 0xae, 0x02, 0x03, 0x04, 0xf3, 0x05, 0xe9,
	0x06, 0xa0, 0x07, 0xe5, 0x16, 0x17, 0xa7, 0xad,
	0xee, 0xe3, 0xeb, 0xf2, 0x9b, 0xe2, 0x01, 0x02,
	0xf5, 0x03, 0xf4, 0xac, 0x04, 0x05, 0xe6, 0xed,
	0xf6, 0x06, 0xae, 0xf0, 0x07, 0x08, 0xf3, 0x09,
	0x0a, 0xe4, 0x0b, 0x0c, 0xf9, 0x0d, 0xef, 0x0e,
	0xe1, 0x0f, 0x10, 0xe9, 0xec, 0x11, 0xa0, 0xe5,
	0x12, 0x13, 0x14, 0x15, 0x0c, 0x0d, 0xa7, 0xbb,
	0x9b, 0x01, 0xf9, 0xae, 0xe2, 0x02, 0xed, 0xf3,
	0x03, 0xf5, 0xef, 0xf0, 0x04, 0x05, 0xe9, 0x06,
	0x07, 0x08, 0x09, 0xa0, 0xe1, 0xe5, 0x0a, 0x0b,
	0x19, 0x1a, 0xad, 0xbb, 0xe2, 0xea, 0xed, 0xf2,
	0xfa, 0xe6, 0xec, 0x01, 0x02, 0x03, 0x9b, 0xf5,
	0x04, 0xa7, 0xf6, 0xf9, 0x05, 0x06, 0xeb, 0xef,
	0x07, 0x08, 0x09, 0x0a, 0xac, 0x0b, 0x0c, 0xe3,
	0xae, 0x0d, 0xee, 0xe9, 0x0e, 0xe1, 0x0f, 0xf3,
	0x10, 0x11, 0xf4, 0x12, 0xe7, 0xe5, 0x13, 0x14,
	0xe4, 0x15, 0x16, 0x17, 0xa0, 0x18, 0x1a, 0x1b,
	0xc2, 0x9b, 0xad, 0xac, 0xf8, 0x01, 0xae, 0x02,
	0x03, 0xe5, 0xe7, 0xe8, 0xf9, 0xe9, 0xeb, 0x04,
	0xe3, 0xe1, 0x05, 0xf6, 0x06, 0xe4, 0x07, 0xe2,
	0xf0, 0x08, 0x09, 0xf3, 0xf4, 0xf7, 0xef, 0x0a,
	0x0b, 0x0c, 0x0d, 0xec, 0x0e, 0x0f, 0x10, 0xf5,
	0xed, 0x11, 0xe6, 0xa0, 0x12, 0xf2, 0x13, 0x14,
	0x15, 0xee, 0x16, 0x17, 0x18, 0x19, 0x0e, 0x0f,
	0xad, 0xed, 0xf9, 0x9b, 0xae, 0x01, 0xf3, 0x02,
	0x03, 0xf5, 0xf4, 0xf0, 0x04, 0xef, 0x05, 0xe9,
	0x06, 0xe8, 0xa0, 0xe1, 0xec, 0x07, 0xf2, 0x08,
	0xe5, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x9b, 0xf5,
	0x19, 0x1a, 0xa9, 0xbb, 0xf6, 0xe6, 0x01, 0x9b,
	0xad, 0xe2, 0xf0, 0x02, 0xa7, 0x03, 0x04, 0x05,
	0xf5, 0xe3, 0xac, 0xe7, 0xf2, 0x06, 0xeb, 0x07,
	0xec, 0xed, 0xee, 0xf9, 0x08, 0xae, 0x09, 0x0a,
	0xe4, 0x0b, 0x0c, 0xf4, 0x0d, 0xf3, 0x0e, 0x0f,
	0x10, 0xe1, 0xef, 0x11, 0xe9, 0x12, 0x13, 0xe5,
	0x14, 0xa0, 0x15, 0x16, 0x17, 0x18, 0xa0, 0x16,
	0xa2, 0xa7, 0xe2, 0xeb, 0xed, 0xee, 0x9b, 0xf7,
	0x01, 0x02, 0x03, 0xbb, 0xf9, 0xf0, 0x04, 0x05,
	0xec, 0x06, 0x07, 0x08, 0xf5, 0xe1, 0x09, 0xac,
	0xe3, 0x0a, 0xe8, 0x0b, 0xe9, 0x0c, 0xef, 0xf3,
	0xae, 0x0d, 0x0e, 0xe5, 0x0f, 0x10, 0x11, 0xf4,
	0x12, 0x13, 0x14, 0x15, 0x14, 0x15, 0xbb, 0xe2,
	0xad, 0xed, 0x01, 0x9b, 0xa7, 0xe3, 0xac, 0xec,
	0xee, 0x02, 0xf7, 0x03, 0x04, 0xf9, 0x05, 0x06,
	0x07, 0x08, 0xf4, 0xae, 0xf5, 0x09, 0x0a, 0xf2,
	0xe1, 0xf3, 0x0b, 0x0c, 0x0d, 0xe9, 0x0e, 0x0f,
	0xef, 0xe5, 0x10, 0xa0, 0xe8, 0x11, 0x12, 0x13,
	0x11, 0x12, 0xef, 0xf6, 0x9b, 0xeb, 0xf9, 0x01,
	0xa0, 0xe2, 0x02, 0xe1, 0x03, 0xed, 0x04, 0xe3,
	0xe9, 0x05, 0xe4, 0xe5, 0xe7, 0x06, 0xec, 0xf0,
	0x07, 0x08, 0x09, 0x0a, 0x0b, 0xf3, 0x0c, 0xf4,
	0xee, 0x0d, 0xf2, 0x0e, 0x0f, 0x10, 0x05, 0xe5,
	0xf3, 0xf9, 0x9b, 0x01, 0xef, 0x02, 0x03, 0xe1,
	0x04, 0xe9, 0x0a, 0x0b, 0xae, 0x9b, 0xec, 0xed,
	0x01, 0x02, 0xf3, 0xee, 0xf2, 0x03, 0xe5, 0x04,
	0xe8, 0xa0, 0xe1, 0x05, 0xef, 0x06, 0x07, 0x08,
	0xe9, 0x09, 0x05, 0x06, 0xa0, 0xac, 0xad, 0xf4,
	0xe9, 0x01, 0x02, 0xe1, 0xe5, 0x03, 0x9b, 0x04,
	0x11, 0xa0, 0xbf, 0xe1, 0xe2, 0xe6, 0xed, 0xe4,
	0xe9, 0xf7, 0xa7, 0x01, 0x02, 0xbb, 0x03, 0x04,
	0xec, 0x05, 0x9b, 0xee, 0x06, 0xef, 0x07, 0xac,
	0xe5, 0xf3, 0x08, 0x09, 0x0a, 0xae, 0x0b, 0x0c,
	0x0d, 0x0e, 0x0f, 0x10, 0x06, 0x07, 0xa0, 0xae,
	0xe1, 0xe5, 0xec, 0xfa, 0x9b, 0xef, 0xe9, 0x01,
	0x02, 0x03, 0x04, 0x05, 0x9b, 0x9b, 0x9b, 0x9b,
	0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b };

void DvbPmtFilter::processSection(const char *data, int size)
{
	unsigned char tableId = data[0];

	if (tableId != 0x02) {
		return;
	}

	DvbPmtSection pmtSection(data, size);

	if (!pmtSection.isValid() || (pmtSection.programNumber() != programNumber)) {
		return;
	}

	if ((size == lastPmtSectionData.size()) &&
	    (memcmp(data, lastPmtSectionData.constData(), size) == 0)) {
		return;
	}

	lastPmtSectionData = pmtSection.toByteArray();
	emit pmtSectionChanged(lastPmtSectionData);
}

void DvbSectionGenerator::initPat(int transportStreamId, int programNumber, int pmtPid)
{
	Q_ASSERT((pmtPid >= 0) && (pmtPid <= 0x1fff));

	char *data = startSection(16);
	data[0] = 0x00;
	data[3] = char(transportStreamId >> 8);
	data[4] = char(transportStreamId);
	data[8] = char(programNumber >> 8);
	data[9] = char(programNumber);
	data[10] = 0xe0 | char(pmtPid >> 8);
	data[11] = char(pmtPid);
	endSection(16, 0x00);
}

void DvbSectionGenerator::initPmt(int pmtPid, const DvbPmtSection &section, const QList<int> &pids)
{
	Q_ASSERT(section.isValid());

	char *data = startSection(section.getSectionLength());

	DvbPmtSectionEntry entry = section.entries();
	memcpy(data, section.getData(), entry.getData() - section.getData());
	int size = int(entry.getData() - section.getData());

	while (entry.isValid()) {
		if (pids.contains(entry.pid())) {
			memcpy(data + size, entry.getData(), entry.getLength());
			size += entry.getLength();
		}

		entry.advance();
	}

	endSection(size + 4, pmtPid);
}

QByteArray DvbSectionGenerator::generatePackets()
{
	char *data = packets.data();

	for (int i = 3; i < packets.size(); i += 188) {
		data[i] = (data[i] & 0xf0) | char(continuityCounter);
		continuityCounter = (continuityCounter + 1) & 0x0f;
	}

	return packets;
}

char *DvbSectionGenerator::startSection(int sectionLength)
{
	Q_ASSERT((sectionLength >= 4) && (sectionLength <= 0x1002));
	packets.resize(((sectionLength / 184) + 1) * 188);
	return packets.data() + 5;
}

void DvbSectionGenerator::endSection(int sectionLength, int pid)
{
	Q_ASSERT((sectionLength >= 4) && (sectionLength <= 0x1002));
	Q_ASSERT((pid >= 0) && (pid <= 0x1fff));

	packets.resize(((sectionLength / 184) + 1) * 188);
	char *data = packets.data();

	data[0] = 0x47;
	data[1] = 0x40 | char(pid >> 8);
	data[2] = char(pid);
	data[3] = 0x10;
	data[4] = 0x00;
	data[6] = 0xb0 | char((sectionLength - 3) >> 8);
	data[7] = char(sectionLength - 3);
	data[10] = 0xc1 | char(versionNumber << 1);
	data[11] = 0x00;
	data[12] = 0x00;

	int size = sectionLength + 5;
	unsigned int crc32 = 0xffffffff;

	for (int i = 5; i < (size - 4); ++i) {
		unsigned char byte = data[i];
		crc32 = (crc32 << 8) ^ DvbStandardSection::crc32Table[(crc32 >> 24) ^ byte];
	}

	data[size - 4] = char(crc32 >> 24);
	data[size - 3] = char(crc32 >> 16);
	data[size - 2] = char(crc32 >> 8);
	data[size - 1] = char(crc32);

	for (int i = 188; i < size; i += 188) {
		// split the section into multiple packets if necessary
		memmove(data + i + 4, data + i, size - i);
		data[i] = 0x47;
		data[i + 1] = char(pid >> 8);
		data[i + 2] = char(pid);
		data[i + 3] = 0x10; // continuity counter is filled out in generatePackets()
		size += 4;
	}

	// pad the unused bytes
	memset(data + size, 0xff, packets.size() - size);

	// increment version number
	versionNumber = (versionNumber + 1) & 0x1f;
}

DvbPmtParser::DvbPmtParser(const DvbPmtSection &section) : videoPid(-1), teletextPid(-1)
{
	for (DvbPmtSectionEntry entry = section.entries(); entry.isValid(); entry.advance()) {
		QString streamLanguage;
		QString subtitleLanguage;
		QString fourcc;
		bool teletextPresent = false;
		bool audioPresent = false;
		bool videoPresent = false;

		for (DvbDescriptor descriptor = entry.descriptors(); descriptor.isValid();
		     descriptor.advance()) {
			switch (descriptor.descriptorTag()) {
			case 0x05: {
				DvbRegistrationDescriptor registrationDescriptor(descriptor);
				fourcc.clear();
				fourcc.append(QChar(registrationDescriptor.formatIdentifier0()));
				fourcc.append(QChar(registrationDescriptor.formatIdentifier1()));
				fourcc.append(QChar(registrationDescriptor.formatIdentifier2()));
				fourcc.append(QChar(registrationDescriptor.formatIdentifier3()));

				/* Handle audio and video fourcc's */
				if (entry.streamType() == 0x06 &&
				    (fourcc == "EAC3" ||
				     fourcc == "AC-3" ||
				     fourcc == "DTS1" ||
				     fourcc == "DTS2" ||
				     fourcc == "DTS3" ||
				     fourcc == "BSSD"))
					audioPresent = true;
				else if ((entry.streamType() == 0x06 && fourcc == "HEVC") ||
					 (entry.streamType() == 0xea && fourcc == "VC-1"))
					videoPresent = true;
				break;
			    }

			case 0x0a: {
				DvbLanguageDescriptor languageDescriptor(descriptor);

				if (!languageDescriptor.isValid()) {
					break;
				}

				// ISO 8859-1 equals to unicode range 0x0000 - 0x00ff
				streamLanguage.clear();
				streamLanguage.append(QChar(languageDescriptor.languageCode1()));
				streamLanguage.append(QChar(languageDescriptor.languageCode2()));
				streamLanguage.append(QChar(languageDescriptor.languageCode3()));
				break;
			    }

			case 0x56:
				teletextPresent = true;
				break;

			case 0x59: {
				DvbSubtitleDescriptor subtitleDescriptor(descriptor);

				if (!subtitleDescriptor.isValid()) {
					break;
				}

				if ((subtitleDescriptor.subtitleType() >= 0x01) &&
				    (subtitleDescriptor.subtitleType() <= 0x03)) {
					// FIXME how to deal with vbi and teletext subtitles?
					qCInfo(logDvbSi, "Unsupported subtitle found: VBI/teletext (%d)", subtitleDescriptor.subtitleType());
				}

				// ISO 8859-1 equals to unicode range 0x0000 - 0x00ff
				subtitleLanguage.clear();
				subtitleLanguage.append(QChar(subtitleDescriptor.languageCode1()));
				subtitleLanguage.append(QChar(subtitleDescriptor.languageCode2()));
				subtitleLanguage.append(QChar(subtitleDescriptor.languageCode3()));
				break;
			    }

			case 0x6a:
			case 0x7a:
				audioPresent = true;
				break;
			}
		}

		/*
		 * The code above already checks for streamType. So,
		 * once we add a video channel here, just rely on it,
		 * without parsing the streamType() again.
		 */
		if (videoPresent) {
			if (videoPid < 0) {
				videoPid = entry.pid();
			} else {
				qCInfo(logDvbSi, "More than one video PID");
			}
			continue;
		}

		// Updated with Table 2-34 of ISO/IEC 13818-1:2018 / ITU-T H.222.0 2017
		// and with https://www.wikiwand.com/en/Program-specific_information
		// (as private IDs are not at ITU/ISO/IEC specs

		switch (entry.streamType()) {
		case 0x01: // ISO/IEC 11172-2 (MPEG-1) video
		case 0x02: // ITU-T H.262 (MPEG2) video or ISO/IEC 11172-2
		case 0x10: // ISO/IEC 14496-2 (MPEG-4) video
		case 0x1b: // ITU-T H.264 video
		case 0x1e: // ISO/IEC 23002-3 (MPEG-4 auxiliary video)
		case 0x1f: // ITU-T H.264 Annex G (MPEG-4 SVC sub-bitstream on AVC)
		case 0x20: // ITU-T H.264 Annex H (MPEG-4 MVC sub-bitstream on AVC)
		case 0x21: // ITU-T T.800 (JPEG 2000 video)
		case 0x22: // ITU-T H.262 additional view for 3D
		case 0x23: // ITU-T H.264 additional view for 3D
		case 0x24: // ITU-T H.265 or HEVC sub-bitstream
		case 0x25: // ITU-T H.265 Annex A - HEVC video stream or subset
		case 0x26: // ITU-T H.264 Annex I - MVCD substream on HEVC
		case 0x28: // ITU-T H.265 Annex G - HEVC enhancemeng sub-partition
		case 0x29: // ITU-T H.265 Annex G - HEVC temporal enhancement
		case 0x2a: // ITU-T H.265 Annex H - HEVC enhancement
		case 0x2b: // ITU-T H.265 Annex H - HEVC temporal enhancement
		case 0x42: // CAVS video
		// User-private video streams
		case 0x80: // MPEG-2 MOTO video
		case 0xd1: // Dirac (Ultra HD video)
			if (videoPid < 0) {
				videoPid = entry.pid();
			} else {
				qCInfo(logDvbSi, "More than one video PID");
			}

			break;

		case 0x03: // ISO/IEC 11172-3 (MPEG1) audio
		case 0x04: // ISO/IEC 13818-3 (MPEG2) audio
		case 0x07: // ISO/IEC 13522 MHEG - DTS and DTS-HD Audio
		case 0x0f: // ISO/IEC 13818-7 Audio with ADTS transport syntax
		case 0x11: // ISO/IEC 14496-3 Audio (AAC / LATM)
		case 0x1c: // ISO/IEC 14496-3 Audio, without additional transport syntax
		case 0x2d: // ISO/IEC 23008-3 Audio with MHAS – main stream
		case 0x2e: // ISO/IEC 23008-3 Audio with MHAS – auxiliary stream
		// User-private audio streams
		case 0x81: // AC-3 audio (ATSC specific)
		case 0x83: // TrueHD lossless audio
		case 0x84: // SDDS
		case 0x85: // DTS on HDMV
		case 0x86: // DTS 8 channel
		case 0x87: // enhanced AC-3 audio (ATSC specific)
		case 0x8a: // DTS
		case 0x91: // A52 VLS
		case 0x94: // SDDS
			audioPids.append(qMakePair(entry.pid(), streamLanguage));
			break;

		case 0x06: // private data - can be teletext, subtitle, ac3 or something else
			if (teletextPresent) {
				if (teletextPid < 0) {
					teletextPid = entry.pid();
				} else {
					qCInfo(logDvbSi, "More than one teletext PID");
				}
			}

			if (!subtitleLanguage.isEmpty()) {
				subtitlePids.append(qMakePair(entry.pid(), subtitleLanguage));

				if (teletextPresent) {
					qCInfo(logDvbSi, "Subtitle and teletext on the same PID");
				}
			}

			if (audioPresent) {
				audioPids.append(qMakePair(entry.pid(), streamLanguage));
			}

			break;

		default:
			if (!subtitleLanguage.isEmpty()) {
				qCInfo(logDvbSi, "Subtitle with unexpected stream type found");
			}

			if (teletextPresent) {
				qCInfo(logDvbSi, "Teletext with unexpected stream type found");
			}

			break;
		}
	}
}

void AtscEitSectionEntry::initEitSectionEntry(const char *data, int size)
{
	if (size < 12) {
		if (size != 0) {
			qCInfo(logDvbSi, "Invalid entry");
		}

		initSectionData();
		return;
	}

	titleLength = quint8(data[9]);

	if (titleLength > (size - 12)) {
		qCInfo(logDvbSi, "Adjusting length");
		titleLength = (size - 12);
	}

	// too ugly to be automatically generated
	int entryLength = ((((quint8(data[10 + titleLength]) & 0xf) << 8) |
		quint8(data[11 + titleLength])) + 12 + titleLength);

	if (entryLength > size) {
		qCInfo(logDvbSi, "Adjusting length");
		entryLength = size;
	}

	initSectionData(data, entryLength, size);
}

// everything below this line is automatically generated

void DvbPatSectionEntry::initPatSectionEntry(const char *data, int size)
{
	if (size < 4) {
		if (size != 0) {
		qCWarning(logDvbSi, "Invalid entry at descriptor");
		}

		initSectionData();
		return;
	}

	initSectionData(data, 4, size);
}

void DvbPmtSectionEntry::initPmtSectionEntry(const char *data, int size)
{
	if (size < 5) {
		if (size != 0) {
		qCWarning(logDvbSi, "Invalid entry at descriptor");
		}

		initSectionData();
		return;
	}

	int entryLength = ((((quint8(data[3]) & 0xf) << 8) | quint8(data[4])) + 5);

	if (entryLength > size) {
		qCWarning(logDvbSi, "Adjusting length on descriptor");
		entryLength = size;
	}

	initSectionData(data, entryLength, size);
}

void DvbSdtSectionEntry::initSdtSectionEntry(const char *data, int size)
{
	if (size < 5) {
		if (size != 0) {
		qCWarning(logDvbSi, "Invalid entry at descriptor");
		}

		initSectionData();
		return;
	}

	int entryLength = ((((quint8(data[3]) & 0xf) << 8) | quint8(data[4])) + 5);

	if (entryLength > size) {
		qCWarning(logDvbSi, "Adjusting length on descriptor");
		entryLength = size;
	}

	initSectionData(data, entryLength, size);
}

void DvbEitSectionEntry::initEitSectionEntry(const char *data, int size)
{
	if (size < 12) {
		if (size != 0) {
		qCWarning(logDvbSi, "Invalid entry at descriptor");
		}

		initSectionData();
		return;
	}

	int entryLength = ((((quint8(data[10]) & 0xf) << 8) | quint8(data[11])) + 12);

	if (entryLength > size) {
		qCWarning(logDvbSi, "Adjusting length on descriptor");
		entryLength = size;
	}

	initSectionData(data, entryLength, size);
}

void DvbEitContentEntry::initEitContentEntry(const char *data, int size)
{
	if (size < 2) {
		if (size != 0) {
		qCWarning(logDvbSi, "Invalid entry at descriptor");
		}

		initSectionData();
		return;
	}

	initSectionData(data, 2, size);
}

void DvbParentalRatingEntry::initParentalRatingEntry(const char *data, int size)
{
	if (size < 4) {
		if (size != 0) {
		qCWarning(logDvbSi, "Invalid entry at descriptor");
		}

		initSectionData();
		return;
	}

	initSectionData(data, 4, size);
}

void DvbNitSectionEntry::initNitSectionEntry(const char *data, int size)
{
	if (size < 6) {
		if (size != 0) {
		qCWarning(logDvbSi, "Invalid entry at descriptor");
		}

		initSectionData();
		return;
	}

	int entryLength = ((((quint8(data[4]) & 0xf) << 8) | quint8(data[5])) + 6);

	if (entryLength > size) {
		qCWarning(logDvbSi, "Adjusting length on descriptor");
		entryLength = size;
	}

	initSectionData(data, entryLength, size);
}

void AtscMgtSectionEntry::initMgtSectionEntry(const char *data, int size)
{
	if (size < 11) {
		if (size != 0) {
		qCWarning(logDvbSi, "Invalid entry at descriptor");
		}

		initSectionData();
		return;
	}

	int entryLength = ((((quint8(data[9]) & 0xf) << 8) | quint8(data[10])) + 11);

	if (entryLength > size) {
		qCWarning(logDvbSi, "Adjusting length on descriptor");
		entryLength = size;
	}

	initSectionData(data, entryLength, size);
}

void AtscVctSectionEntry::initVctSectionEntry(const char *data, int size)
{
	if (size < 32) {
		if (size != 0) {
		qCWarning(logDvbSi, "Invalid entry at descriptor");
		}

		initSectionData();
		return;
	}

	int entryLength = ((((quint8(data[30]) & 0x3) << 8) | quint8(data[31])) + 32);

	if (entryLength > size) {
		qCWarning(logDvbSi, "Adjusting length on descriptor");
		entryLength = size;
	}

	initSectionData(data, entryLength, size);
}

DvbRegistrationDescriptor::DvbRegistrationDescriptor(const DvbDescriptor &descriptor) : DvbDescriptor(descriptor)
{
	if (getLength() < 6) {
		qCWarning(logDvbSi, "Invalid descriptor");
		initSectionData();
		return;
	}
}

DvbLanguageDescriptor::DvbLanguageDescriptor(const DvbDescriptor &descriptor) : DvbDescriptor(descriptor)
{
	if (getLength() < 6) {
		qCWarning(logDvbSi, "Invalid descriptor");
		initSectionData();
		return;
	}
}

DvbSubtitleDescriptor::DvbSubtitleDescriptor(const DvbDescriptor &descriptor) : DvbDescriptor(descriptor)
{
	if (getLength() < 10) {
		qCWarning(logDvbSi, "Invalid descriptor");
		initSectionData();
		return;
	}
}

DvbServiceDescriptor::DvbServiceDescriptor(const DvbDescriptor &descriptor) : DvbDescriptor(descriptor)
{
	if (getLength() < 5) {
		qCWarning(logDvbSi, "Invalid descriptor");
		initSectionData();
		return;
	}

	providerNameLength = at(3);

	if (providerNameLength > (getLength() - 5)) {
		qCWarning(logDvbSi, "Adjusting length on descriptor");
		providerNameLength = (getLength() - 5);
	}

	serviceNameLength = at(4 + providerNameLength);

	if (serviceNameLength > (getLength() - (5 + providerNameLength))) {
		qCWarning(logDvbSi, "Adjusting length on descriptor");
		serviceNameLength = (getLength() - (5 + providerNameLength));
	}
}

DvbShortEventDescriptor::DvbShortEventDescriptor(const DvbDescriptor &descriptor) : DvbDescriptor(descriptor)
{
	if (getLength() < 7) {
		qCWarning(logDvbSi, "Invalid descriptor");
		initSectionData();
		return;
	}

	eventNameLength = at(5);

	if (eventNameLength > (getLength() - 7)) {
		qCWarning(logDvbSi, "Adjusting length on descriptor");
		eventNameLength = (getLength() - 7);
	}

	textLength = at(6 + eventNameLength);

	if (textLength > (getLength() - (7 + eventNameLength))) {
		qCWarning(logDvbSi, "Adjusting length on descriptor");
		textLength = (getLength() - (7 + eventNameLength));
	}
}

DvbExtendedEventDescriptor::DvbExtendedEventDescriptor(const DvbDescriptor &descriptor) : DvbDescriptor(descriptor)
{
	if (getLength() < 8) {
		qCWarning(logDvbSi, "Invalid descriptor");
		initSectionData();
		return;
	}

	itemsLength = at(6);

	if (itemsLength > (getLength() - 8)) {
		qCWarning(logDvbSi, "Adjusting length on descriptor");
		itemsLength = (getLength() - 8);
	}

	textLength = at(7 + itemsLength);

	if (textLength > (getLength() - (8 + itemsLength))) {
		qCWarning(logDvbSi, "Adjusting length on descriptor");
		textLength = (getLength() - (8 + itemsLength));
	}
}

DvbContentDescriptor::DvbContentDescriptor(const DvbDescriptor &descriptor) : DvbDescriptor(descriptor)
{
	if (getLength() < 2) {
		qCWarning(logDvbSi, "Invalid descriptor");
		initSectionData();
		return;
	}
}

DvbParentalRatingDescriptor::DvbParentalRatingDescriptor(const DvbDescriptor &descriptor) : DvbDescriptor(descriptor)
{
	if (getLength() < 2) {
		qCWarning(logDvbSi, "Invalid descriptor");
		initSectionData();
		return;
	}
}

DvbCableDescriptor::DvbCableDescriptor(const DvbDescriptor &descriptor) : DvbDescriptor(descriptor)
{
	if (getLength() < 13) {
		qCWarning(logDvbSi, "Invalid descriptor");
		initSectionData();
		return;
	}
}

DvbSatelliteDescriptor::DvbSatelliteDescriptor(const DvbDescriptor &descriptor) : DvbDescriptor(descriptor)
{
	if (getLength() < 13) {
		qCWarning(logDvbSi, "Invalid descriptor");
		initSectionData();
		return;
	}
}

DvbTerrestrialDescriptor::DvbTerrestrialDescriptor(const DvbDescriptor &descriptor) : DvbDescriptor(descriptor)
{
	if (getLength() < 13) {
		qCWarning(logDvbSi, "Invalid descriptor");
		initSectionData();
		return;
	}
}

IsdbTerrestrialDescriptor::IsdbTerrestrialDescriptor(const DvbDescriptor &descriptor) : DvbDescriptor(descriptor)
{
	if (getLength() < 4) {
		qCWarning(logDvbSi, "Invalid descriptor");
		initSectionData();
		return;
	}
}

AtscChannelNameDescriptor::AtscChannelNameDescriptor(const DvbDescriptor &descriptor) : DvbDescriptor(descriptor)
{
	if (getLength() < 2) {
		qCWarning(logDvbSi, "Invalid descriptor");
		initSectionData();
		return;
	}
}

void DvbPatSection::initPatSection(const char *data, int size)
{
	if (size < 12) {
		initSectionData();
		return;
	}

	initStandardSection(data, size);
}

void DvbPmtSection::initPmtSection(const char *data, int size)
{
	if (size < 16) {
		initSectionData();
		return;
	}

	initStandardSection(data, size);
	descriptorsLength = ((at(10) & 0xf) << 8) | at(11);

	if (descriptorsLength > (getLength() - 16)) {
		qCWarning(logDvbSi, "Adjusting length on descriptor");
		descriptorsLength = (getLength() - 16);
	}
}

void DvbSdtSection::initSdtSection(const char *data, int size)
{
	if (size < 15) {
		initSectionData();
		return;
	}

	initStandardSection(data, size);
}

void DvbEitSection::initEitSection(const char *data, int size)
{
	if (size < 18) {
		initSectionData();
		return;
	}

	initStandardSection(data, size);
}

void DvbNitSection::initNitSection(const char *data, int size)
{
	if (size < 16) {
		initSectionData();
		return;
	}

	initStandardSection(data, size);
	descriptorsLength = ((at(8) & 0xf) << 8) | at(9);

	if (descriptorsLength > (getLength() - 16)) {
		qCWarning(logDvbSi, "Adjusting length on descriptor");
		descriptorsLength = (getLength() - 16);
	}

	entriesLength = ((at(10 + descriptorsLength) & 0xf) << 8) | at(11 + descriptorsLength);

	if (entriesLength > (getLength() - (16 + descriptorsLength))) {
		qCWarning(logDvbSi, "Adjusting length on descriptor");
		entriesLength = (getLength() - (16 + descriptorsLength));
	}
}

void AtscMgtSection::initMgtSection(const char *data, int size)
{
	if (size < 17) {
		initSectionData();
		return;
	}

	initStandardSection(data, size);
}

void AtscVctSection::initVctSection(const char *data, int size)
{
	if (size < 14) {
		initSectionData();
		return;
	}

	initStandardSection(data, size);
}

void AtscEitSection::initEitSection(const char *data, int size)
{
	if (size < 14) {
		initSectionData();
		return;
	}

	initStandardSection(data, size);
}

void AtscEttSection::initEttSection(const char *data, int size)
{
	if (size < 17) {
		initSectionData();
		return;
	}

	initStandardSection(data, size);
}
