/* * updatedvbsi.cpp * * Copyright (C) 2008-2011 Christoph Pfister * * 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 #if QT_VERSION < 0x050500 # define qInfo qDebug #endif #include #include #include class Element { public: enum Type { Bool, Int, List }; Element() { } ~Element() { } QString toString() const; Type type; QString name; int bits; int bitIndex; QString offsetString; QString lengthFunc; QString listType; }; QString Element::toString() const { if (type == Bool) { int bitsLeft = 8 - (bitIndex % 8); int andValue = (1 << (bitsLeft - 1)); return (QString("(at(") + QString::number(bitIndex / 8) + offsetString + ") & 0x" + QString::number(andValue, 16) + ") != 0"); } if (type == List) { return (QString::number(bitIndex / 8) + offsetString); } QString result; int currentBitIndex = bitIndex; int currentBits = bits; while (true) { int bitsLeft = 8 - (currentBitIndex % 8); int andValue = (1 << (bitsLeft)) - 1; int leftShift = currentBits - bitsLeft; if (leftShift != 0) { result += '('; } if (andValue != 255) { result += '('; } result += "at(" + QString::number(currentBitIndex / 8) + offsetString + ')'; if (andValue != 255) { result += " & 0x" + QString::number(andValue, 16) + ')'; } if (leftShift != 0) { if (leftShift > 0) { result += " << " + QString::number(leftShift) + ')'; } else { result += " >> " + QString::number(-leftShift) + ')'; } } if (currentBits <= bitsLeft) { break; } result += " | "; currentBitIndex += bitsLeft; currentBits -= bitsLeft; } return result; } QTextStream &operator<<(QTextStream &stream, const Element &element) { stream << element.toString(); return stream; } class SiXmlParser { public: enum Type { Descriptor, Entry, Section }; static void parseEntry(QDomNode node, Type type, QTextStream &headerStream, QTextStream &cppStream); }; void SiXmlParser::parseEntry(QDomNode node, Type type, QTextStream &headerStream, QTextStream &cppStream) { QList elements; int bitIndex = 0; int minBits = 0; QString offsetString; switch (type) { case Descriptor: bitIndex = 2 * 8; minBits = 2 * 8; break; case Entry: bitIndex = 0; minBits = 0; break; case Section: bitIndex = 8 * 8; minBits = 12 * 8; break; } for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { QDomNamedNodeMap attributes = child.attributes(); Element element; element.name = child.nodeName(); element.bits = attributes.namedItem("bits").nodeValue().toInt(); element.bitIndex = bitIndex; element.offsetString = offsetString; QString type = attributes.namedItem("type").nodeValue(); if ((element.name == "unused") || (type == "int")) { if (element.bits <= 0) { qCritical() << "invalid number of bits"; return; } element.type = Element::Int; } else if (type == "bool") { element.type = Element::Bool; if (element.bits != 1) { qCritical() << "invalid number of bits"; return; } } else if (type == "list") { element.type = Element::List; element.bits = 0; element.lengthFunc = attributes.namedItem("lengthFunc").nodeValue(); element.listType = attributes.namedItem("listType").nodeValue(); if ((element.bitIndex % 8) != 0) { qCritical() << "list doesn't start at a byte boundary"; return; } offsetString += " + " + element.name + "Length"; } else { qCritical() << "unknown type:" << type; return; } bitIndex += element.bits; minBits += element.bits; if (element.name != "unused") { elements.append(element); } } if ((minBits % 8) != 0) { qCritical() << "minBits isn't a multiple of 8"; return; } QString entryName = node.nodeName(); QString initFunctionName = QString(entryName).replace(QRegExp("^Dvb|^Atsc"), "init"); bool ignoreFirstNewLine = false; QString funcName = entryName + "::"; switch (type) { case Descriptor: funcName += entryName; cppStream << "\n"; cppStream << funcName << "(const DvbDescriptor &descriptor) : DvbDescriptor(descriptor)\n"; cppStream << "{\n"; cppStream << "\tif (getLength() < " << (minBits / 8) << ") {\n"; cppStream << "\t\tqCWarning(logDvbSi, \"Invalid descriptor\");\n"; cppStream << "\t\tinitSectionData();\n"; cppStream << "\t\treturn;\n"; cppStream << "\t}\n"; break; case Entry: { funcName += initFunctionName; cppStream << "\n"; cppStream << "void " << funcName << "(const char *data, int size)\n"; cppStream << "{\n"; cppStream << "\tif (size < " << (minBits / 8) << ") {\n"; cppStream << "\t\tif (size != 0) {\n"; cppStream << "\t\tqCWarning(logDvbSi, \"Invalid entry at descriptor\");\n"; cppStream << "\t\t}\n"; cppStream << "\n"; cppStream << "\t\tinitSectionData();\n"; cppStream << "\t\treturn;\n"; cppStream << "\t}\n"; cppStream << "\n"; bool fixedLength = true; for (int i = 0; i < elements.size(); ++i) { const Element &element = elements.at(i); if (element.name != "entryLength") { continue; } if (((element.bitIndex + element.bits) % 8) != 0) { qCritical() << "entry length doesn't start at a byte boundary"; return; } QString entryLengthCalculation = element.toString(); while (true) { int oldSize = entryLengthCalculation.size(); entryLengthCalculation.replace(QRegExp("at\\(([0-9]*)\\)"), "quint8(data[\\1])"); if (entryLengthCalculation.size() == oldSize) { break; } } cppStream << "\tint entryLength = ((" << entryLengthCalculation << ") + " << ((element.bitIndex + element.bits) / 8) << ");\n"; cppStream << "\n"; cppStream << "\tif (entryLength > size) {\n"; cppStream << "\t\tqCWarning(logDvbSi, \"Adjusting length on descriptor\");\n"; cppStream << "\t\tentryLength = size;\n"; cppStream << "\t}\n"; cppStream << "\n"; cppStream << "\tinitSectionData(data, entryLength, size);\n"; ignoreFirstNewLine = true; elements.removeAt(i); fixedLength = false; break; } if (fixedLength) { cppStream << "\tinitSectionData(data, " << (minBits / 8) << ", size);\n"; ignoreFirstNewLine = true; } break; } case Section: funcName += initFunctionName; cppStream << "\n"; cppStream << "void " << entryName << "::" << initFunctionName << "(const char *data, int size)\n"; cppStream << "{\n"; cppStream << "\tif (size < " << (minBits / 8) << ") {\n"; cppStream << "\t\tinitSectionData();\n"; cppStream << "\t\treturn;\n"; cppStream << "\t}\n"; cppStream << "\n"; cppStream << "\tinitStandardSection(data, size);\n"; ignoreFirstNewLine = true; break; } QList privateVars; for (int i = 0; i < elements.size(); ++i) { const Element &element = elements.at(i); if ((element.type != Element::List) || element.lengthFunc.isEmpty()) { continue; } if ((i <= 0) || (elements.at(i - 1).name != element.lengthFunc)) { qCritical() << "lengthFunc isn't a valid back reference"; return; } if (ignoreFirstNewLine) { ignoreFirstNewLine = false; } else { cppStream << "\n"; } cppStream << "\t" << element.name << "Length = " << elements.at(i - 1) << ";\n"; cppStream << "\n"; if (element.offsetString.isEmpty()) { cppStream << "\tif (" << element.name << "Length > (getLength() - " << (minBits / 8) << ")) {\n"; cppStream << "\t\tqCWarning(logDvbSi, \"Adjusting length on descriptor\");\n"; cppStream << "\t\t" << element.name << "Length = (getLength() - " << (minBits / 8) << ");\n"; } else { cppStream << "\tif (" << element.name << "Length > (getLength() - (" << (minBits / 8) << element.offsetString << "))) {\n"; cppStream << "\t\tqCWarning(logDvbSi, \"Adjusting length on descriptor\");\n"; cppStream << "\t\t" << element.name << "Length = (getLength() - (" << (minBits / 8) << element.offsetString << "));\n"; } cppStream << "\t}\n"; elements.removeAt(i - 1); --i; privateVars.append(element.name + "Length"); } cppStream << "}\n"; switch (type) { case Descriptor: headerStream << "\n"; headerStream << "class " << entryName << " : public DvbDescriptor\n"; headerStream << "{\n"; headerStream << "public:\n"; headerStream << "\texplicit " << entryName << "(const DvbDescriptor &descriptor);\n"; break; case Entry: headerStream << "\n"; headerStream << "class " << entryName << " : public DvbSectionData\n"; headerStream << "{\n"; headerStream << "public:\n"; headerStream << "\t" << entryName << "(const char *data, int size)\n"; headerStream << "\t{\n"; headerStream << "\t\t" << initFunctionName << "(data, size);\n"; headerStream << "\t}\n"; headerStream << "\n"; break; case Section: headerStream << "\n"; headerStream << "class " << entryName << " : public DvbStandardSection\n"; headerStream << "{\n"; headerStream << "public:\n"; headerStream << "\t" << entryName << "(const char *data, int size)\n"; headerStream << "\t{\n"; headerStream << "\t\t" << initFunctionName << "(data, size);\n"; headerStream << "\t}\n"; headerStream << "\n"; headerStream << "\texplicit " << entryName << "(const QByteArray &byteArray)\n"; headerStream << "\t{\n"; headerStream << "\t\t" << initFunctionName << "(byteArray.constData(), byteArray.size());\n"; headerStream << "\t}\n"; headerStream << "\n"; break; } headerStream << "\t~" << entryName << "() { }\n"; if (type == Entry) { headerStream << "\n"; headerStream << "\tvoid advance()\n"; headerStream << "\t{\n"; headerStream << "\t\t" << initFunctionName << "(getData() + getLength(), getSize() - getLength());\n"; headerStream << "\t}\n"; } if (type == Section) { QString extension = node.attributes().namedItem("extension").nodeValue(); if (!extension.isEmpty()) { headerStream << "\n"; headerStream << "\tint " << extension << "() const\n"; headerStream << "\t{\n"; headerStream << "\t\treturn (at(3) << 8) | at(4);\n"; headerStream << "\t}\n"; } } foreach (const Element &element, elements) { switch (element.type) { case Element::Bool: headerStream << "\n"; headerStream << "\tbool " << element.name << "() const\n"; headerStream << "\t{\n"; headerStream << "\t\treturn (" << element << ");\n"; headerStream << "\t}\n"; break; case Element::Int: headerStream << "\n"; headerStream << "\tint " << element.name << "() const\n"; headerStream << "\t{\n"; headerStream << "\t\treturn " << element << ";\n"; headerStream << "\t}\n"; break; case Element::List: if (element.listType == "unused") { break; } if (element.listType == "DvbString") { headerStream << "\n"; headerStream << "\tQString " << element.name << "() const\n"; headerStream << "\t{\n"; headerStream << "\t\treturn DvbSiText::convertText(getData() + " << element << ", "; } else if (element.listType == "DvbInt") { headerStream << "\n"; headerStream << "\tint " << element.name << "Length() const\n\t{\n"; headerStream << "\t\treturn (getLength() - 4) / 2;\n\t}\n\n"; headerStream << "\tint " << element.name << "(int idx) const\n"; headerStream << "\t{\n"; headerStream << "\t\tint pos = (idx * 2) + 4;\n"; headerStream << "\t\treturn (at(pos) << 8) | at(pos + 1);\n"; } else if (element.listType == "AtscString") { headerStream << "\n"; headerStream << "\tQString " << element.name << "() const\n"; headerStream << "\t{\n"; headerStream << "\t\treturn AtscPsipText::convertText(getData() + " << element << ", "; } else { headerStream << "\n"; headerStream << "\t" << element.listType << " " << element.name << "() const\n"; headerStream << "\t{\n"; headerStream << "\t\treturn " << element.listType << "(getData() + " << element << ", "; } if (element.listType != "DvbInt") { if (element.lengthFunc.isEmpty()) { if (element.offsetString.isEmpty()) { headerStream << "getLength() - " << (minBits / 8) << ");\n"; } else { headerStream << "getLength() - (" << (minBits / 8) << element.offsetString << "));\n"; } } else { headerStream << element.name << "Length);\n"; } } headerStream << "\t}\n"; break; } } headerStream << "\n"; headerStream << "private:\n"; if ((type == Descriptor) || (type == Section)) { headerStream << "\tQ_DISABLE_COPY(" << entryName << ")\n"; } if ((type == Entry) || (type == Section)) { headerStream << "\tvoid " << initFunctionName << "(const char *data, int size);\n"; } if (!privateVars.isEmpty()) { headerStream << '\n'; foreach (const QString &privateVar, privateVars) { headerStream << "\tint " << privateVar << ";\n"; } } headerStream << "};\n"; } class FileHelper { public: explicit FileHelper(const QString &name); bool isValid() const { return valid; } QTextStream &getStream() { return outStream; } void removeOrig() { if (!QFile::remove(origName)) { qCritical() << "can't remove" << origName; } } private: bool valid; QString origName; QFile outFile; QTextStream outStream; }; FileHelper::FileHelper(const QString &name) : valid(false) { origName = name + ".orig"; if (!QFile::rename(name, origName)) { qCritical() << "can't rename" << name << "to" << origName; return; } QFile inFile(origName); if (!inFile.open(QIODevice::ReadOnly)) { qCritical() << "can't open file" << inFile.fileName(); return; } outFile.setFileName(name); if (!outFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { qCritical() << "can't open file" << outFile.fileName(); } QTextStream inStream(&inFile); inStream.setCodec("UTF-8"); outStream.setDevice(&outFile); outStream.setCodec("UTF-8"); while (true) { if (inStream.atEnd()) { qCritical() << "can't find autogeneration boundary in" << inFile.fileName(); return; } QString line = inStream.readLine(); outStream << line << "\n"; if (line == "// everything below this line is automatically generated") { break; } } valid = true; } int main() { QFile xmlFile("dvbsi.xml"); if (!xmlFile.open(QIODevice::ReadOnly)) { qCritical() << "can't open file" << xmlFile.fileName(); return 1; } QDomDocument document; if (!document.setContent(&xmlFile)) { qCritical() << "can't read file" << xmlFile.fileName(); return 1; } QDomElement root = document.documentElement(); if (root.nodeName() != "dvbsi") { qCritical() << "invalid root name"; return 1; } FileHelper headerFile("../src/dvb/dvbsi.h"); if (!headerFile.isValid()) { return 1; } FileHelper cppFile("../src/dvb/dvbsi.cpp"); if (!cppFile.isValid()) { return 1; } QTextStream &headerStream = headerFile.getStream(); QTextStream &cppStream = cppFile.getStream(); for (QDomNode child = root.firstChild(); !child.isNull(); child = child.nextSibling()) { if (child.nodeName() == "descriptors") { for (QDomNode grandChild = child.firstChild(); !grandChild.isNull(); grandChild = grandChild.nextSibling()) { SiXmlParser::parseEntry(grandChild, SiXmlParser::Descriptor, headerStream, cppStream); } } else if (child.nodeName() == "entries") { for (QDomNode grandChild = child.firstChild(); !grandChild.isNull(); grandChild = grandChild.nextSibling()) { SiXmlParser::parseEntry(grandChild, SiXmlParser::Entry, headerStream, cppStream); } } else if (child.nodeName() == "sections") { for (QDomNode grandChild = child.firstChild(); !grandChild.isNull(); grandChild = grandChild.nextSibling()) { SiXmlParser::parseEntry(grandChild, SiXmlParser::Section, headerStream, cppStream); } } else { qCritical() << "unknown entry:" << child.nodeName(); } } headerStream << "\n"; headerStream << "#endif /* DVBSI_H */\n"; headerFile.removeOrig(); cppFile.removeOrig(); xmlFile.close(); #if 0 // Don't rewrite the xml file if (xmlFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { xmlFile.write(document.toByteArray(2)); } #endif return 0; }