File indexing completed on 2024-05-05 04:52:23

0001 /*
0002  * updatedvbsi.cpp
0003  *
0004  * Copyright (C) 2008-2011 Christoph Pfister <christophpfister@gmail.com>
0005  *
0006  * This program is free software; you can redistribute it and/or modify
0007  * it under the terms of the GNU General Public License as published by
0008  * the Free Software Foundation; either version 2 of the License, or
0009  * (at your option) any later version.
0010  *
0011  * This program is distributed in the hope that it will be useful,
0012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014  * GNU General Public License for more details.
0015  *
0016  * You should have received a copy of the GNU General Public License along
0017  * with this program; if not, write to the Free Software Foundation, Inc.,
0018  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
0019  */
0020 
0021 #include <QDebug>
0022 #if QT_VERSION < 0x050500
0023 # define qInfo qDebug
0024 #endif
0025 
0026 #include <QDomDocument>
0027 #include <QFile>
0028 #include <QRegExp>
0029 
0030 class Element
0031 {
0032 public:
0033     enum Type
0034     {
0035         Bool,
0036         Int,
0037         List
0038     };
0039 
0040     Element() { }
0041     ~Element() { }
0042 
0043     QString toString() const;
0044 
0045     Type type;
0046     QString name;
0047     int bits;
0048     int bitIndex;
0049     QString offsetString;
0050     QString lengthFunc;
0051     QString listType;
0052 };
0053 
0054 QString Element::toString() const
0055 {
0056     if (type == Bool) {
0057         int bitsLeft = 8 - (bitIndex % 8);
0058         int andValue = (1 << (bitsLeft - 1));
0059         return (QString("(at(") + QString::number(bitIndex / 8) + offsetString + ") & 0x" + QString::number(andValue, 16) + ") != 0");
0060     }
0061 
0062     if (type == List) {
0063         return (QString::number(bitIndex / 8) + offsetString);
0064     }
0065 
0066     QString result;
0067     int currentBitIndex = bitIndex;
0068     int currentBits = bits;
0069 
0070     while (true) {
0071         int bitsLeft = 8 - (currentBitIndex % 8);
0072         int andValue = (1 << (bitsLeft)) - 1;
0073         int leftShift = currentBits - bitsLeft;
0074 
0075         if (leftShift != 0) {
0076             result += '(';
0077         }
0078 
0079         if (andValue != 255) {
0080             result += '(';
0081         }
0082 
0083         result += "at(" + QString::number(currentBitIndex / 8) + offsetString + ')';
0084 
0085         if (andValue != 255) {
0086             result += " & 0x" + QString::number(andValue, 16) + ')';
0087         }
0088 
0089         if (leftShift != 0) {
0090             if (leftShift > 0) {
0091                 result += " << " + QString::number(leftShift) + ')';
0092             } else {
0093                 result += " >> " + QString::number(-leftShift) + ')';
0094             }
0095         }
0096 
0097         if (currentBits <= bitsLeft) {
0098             break;
0099         }
0100 
0101         result += " | ";
0102         currentBitIndex += bitsLeft;
0103         currentBits -= bitsLeft;
0104     }
0105 
0106     return result;
0107 }
0108 
0109 QTextStream &operator<<(QTextStream &stream, const Element &element)
0110 {
0111     stream << element.toString();
0112     return stream;
0113 }
0114 
0115 class SiXmlParser
0116 {
0117 public:
0118     enum Type
0119     {
0120         Descriptor,
0121         Entry,
0122         Section
0123     };
0124 
0125     static void parseEntry(QDomNode node, Type type, QTextStream &headerStream, QTextStream &cppStream);
0126 };
0127 
0128 void SiXmlParser::parseEntry(QDomNode node, Type type, QTextStream &headerStream, QTextStream &cppStream)
0129 {
0130     QList<Element> elements;
0131     int bitIndex = 0;
0132     int minBits = 0;
0133     QString offsetString;
0134 
0135     switch (type) {
0136     case Descriptor:
0137         bitIndex = 2 * 8;
0138         minBits = 2 * 8;
0139         break;
0140 
0141     case Entry:
0142         bitIndex = 0;
0143         minBits = 0;
0144         break;
0145 
0146     case Section:
0147         bitIndex = 8 * 8;
0148         minBits = 12 * 8;
0149         break;
0150     }
0151 
0152     for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) {
0153         QDomNamedNodeMap attributes = child.attributes();
0154 
0155         Element element;
0156         element.name = child.nodeName();
0157         element.bits = attributes.namedItem("bits").nodeValue().toInt();
0158         element.bitIndex = bitIndex;
0159         element.offsetString = offsetString;
0160 
0161         QString type = attributes.namedItem("type").nodeValue();
0162 
0163         if ((element.name == "unused") || (type == "int")) {
0164             if (element.bits <= 0) {
0165                 qCritical() << "invalid number of bits";
0166                 return;
0167             }
0168 
0169             element.type = Element::Int;
0170         } else if (type == "bool") {
0171             element.type = Element::Bool;
0172 
0173             if (element.bits != 1) {
0174                 qCritical() << "invalid number of bits";
0175                 return;
0176             }
0177         } else if (type == "list") {
0178             element.type = Element::List;
0179             element.bits = 0;
0180             element.lengthFunc = attributes.namedItem("lengthFunc").nodeValue();
0181             element.listType = attributes.namedItem("listType").nodeValue();
0182 
0183             if ((element.bitIndex % 8) != 0) {
0184                 qCritical() << "list doesn't start at a byte boundary";
0185                 return;
0186             }
0187 
0188             offsetString += " + " + element.name + "Length";
0189         } else {
0190             qCritical() << "unknown type:" << type;
0191             return;
0192         }
0193 
0194         bitIndex += element.bits;
0195         minBits += element.bits;
0196 
0197         if (element.name != "unused") {
0198             elements.append(element);
0199         }
0200     }
0201 
0202     if ((minBits % 8) != 0) {
0203         qCritical() << "minBits isn't a multiple of 8";
0204         return;
0205     }
0206 
0207     QString entryName = node.nodeName();
0208     QString initFunctionName = QString(entryName).replace(QRegExp("^Dvb|^Atsc"), "init");
0209     bool ignoreFirstNewLine = false;
0210 
0211     QString funcName = entryName + "::";
0212 
0213     switch (type) {
0214     case Descriptor:
0215         funcName += entryName;
0216         cppStream << "\n";
0217         cppStream << funcName << "(const DvbDescriptor &descriptor) : DvbDescriptor(descriptor)\n";
0218         cppStream << "{\n";
0219         cppStream << "\tif (getLength() < " << (minBits / 8) << ") {\n";
0220         cppStream << "\t\tqCWarning(logDvbSi, \"Invalid descriptor\");\n";
0221         cppStream << "\t\tinitSectionData();\n";
0222         cppStream << "\t\treturn;\n";
0223         cppStream << "\t}\n";
0224         break;
0225 
0226     case Entry: {
0227         funcName += initFunctionName;
0228         cppStream << "\n";
0229         cppStream << "void " << funcName << "(const char *data, int size)\n";
0230         cppStream << "{\n";
0231         cppStream << "\tif (size < " << (minBits / 8) << ") {\n";
0232         cppStream << "\t\tif (size != 0) {\n";
0233         cppStream << "\t\tqCWarning(logDvbSi, \"Invalid entry at descriptor\");\n";
0234         cppStream << "\t\t}\n";
0235         cppStream << "\n";
0236         cppStream << "\t\tinitSectionData();\n";
0237         cppStream << "\t\treturn;\n";
0238         cppStream << "\t}\n";
0239         cppStream << "\n";
0240 
0241         bool fixedLength = true;
0242 
0243         for (int i = 0; i < elements.size(); ++i) {
0244             const Element &element = elements.at(i);
0245 
0246             if (element.name != "entryLength") {
0247                 continue;
0248             }
0249 
0250             if (((element.bitIndex + element.bits) % 8) != 0) {
0251                 qCritical() << "entry length doesn't start at a byte boundary";
0252                 return;
0253             }
0254 
0255             QString entryLengthCalculation = element.toString();
0256 
0257             while (true) {
0258                 int oldSize = entryLengthCalculation.size();
0259                 entryLengthCalculation.replace(QRegExp("at\\(([0-9]*)\\)"), "quint8(data[\\1])");
0260 
0261                 if (entryLengthCalculation.size() == oldSize) {
0262                     break;
0263                 }
0264             }
0265 
0266             cppStream << "\tint entryLength = ((" << entryLengthCalculation << ") + " << ((element.bitIndex + element.bits) / 8) << ");\n";
0267             cppStream << "\n";
0268             cppStream << "\tif (entryLength > size) {\n";
0269             cppStream << "\t\tqCWarning(logDvbSi, \"Adjusting length on descriptor\");\n";
0270             cppStream << "\t\tentryLength = size;\n";
0271             cppStream << "\t}\n";
0272             cppStream << "\n";
0273             cppStream << "\tinitSectionData(data, entryLength, size);\n";
0274             ignoreFirstNewLine = true;
0275 
0276             elements.removeAt(i);
0277             fixedLength = false;
0278             break;
0279         }
0280 
0281         if (fixedLength) {
0282             cppStream << "\tinitSectionData(data, " << (minBits / 8) << ", size);\n";
0283             ignoreFirstNewLine = true;
0284         }
0285 
0286         break;
0287         }
0288 
0289     case Section:
0290         funcName += initFunctionName;
0291         cppStream << "\n";
0292         cppStream << "void " << entryName << "::" << initFunctionName << "(const char *data, int size)\n";
0293         cppStream << "{\n";
0294         cppStream << "\tif (size < " << (minBits / 8) << ") {\n";
0295         cppStream << "\t\tinitSectionData();\n";
0296         cppStream << "\t\treturn;\n";
0297         cppStream << "\t}\n";
0298         cppStream << "\n";
0299         cppStream << "\tinitStandardSection(data, size);\n";
0300         ignoreFirstNewLine = true;
0301         break;
0302     }
0303 
0304     QList<QString> privateVars;
0305 
0306     for (int i = 0; i < elements.size(); ++i) {
0307         const Element &element = elements.at(i);
0308 
0309         if ((element.type != Element::List) || element.lengthFunc.isEmpty()) {
0310             continue;
0311         }
0312 
0313         if ((i <= 0) || (elements.at(i - 1).name != element.lengthFunc)) {
0314             qCritical() << "lengthFunc isn't a valid back reference";
0315             return;
0316         }
0317 
0318         if (ignoreFirstNewLine) {
0319             ignoreFirstNewLine = false;
0320         } else {
0321             cppStream << "\n";
0322         }
0323 
0324         cppStream << "\t" << element.name << "Length = " << elements.at(i - 1) << ";\n";
0325         cppStream << "\n";
0326 
0327         if (element.offsetString.isEmpty()) {
0328             cppStream << "\tif (" << element.name << "Length > (getLength() - " << (minBits / 8) << ")) {\n";
0329             cppStream << "\t\tqCWarning(logDvbSi, \"Adjusting length on descriptor\");\n";
0330             cppStream << "\t\t" << element.name << "Length = (getLength() - " << (minBits / 8) << ");\n";
0331         } else {
0332             cppStream << "\tif (" << element.name << "Length > (getLength() - (" << (minBits / 8) << element.offsetString << "))) {\n";
0333             cppStream << "\t\tqCWarning(logDvbSi, \"Adjusting length on descriptor\");\n";
0334             cppStream << "\t\t" << element.name << "Length = (getLength() - (" << (minBits / 8) << element.offsetString << "));\n";
0335         }
0336 
0337         cppStream << "\t}\n";
0338 
0339         elements.removeAt(i - 1);
0340         --i;
0341 
0342         privateVars.append(element.name + "Length");
0343     }
0344 
0345     cppStream << "}\n";
0346 
0347     switch (type) {
0348     case Descriptor:
0349         headerStream << "\n";
0350         headerStream << "class " << entryName << " : public DvbDescriptor\n";
0351         headerStream << "{\n";
0352         headerStream << "public:\n";
0353         headerStream << "\texplicit " << entryName << "(const DvbDescriptor &descriptor);\n";
0354         break;
0355 
0356     case Entry:
0357         headerStream << "\n";
0358         headerStream << "class " << entryName << " : public DvbSectionData\n";
0359         headerStream << "{\n";
0360         headerStream << "public:\n";
0361         headerStream << "\t" << entryName << "(const char *data, int size)\n";
0362         headerStream << "\t{\n";
0363         headerStream << "\t\t" << initFunctionName << "(data, size);\n";
0364         headerStream << "\t}\n";
0365         headerStream << "\n";
0366         break;
0367 
0368     case Section:
0369         headerStream << "\n";
0370         headerStream << "class " << entryName << " : public DvbStandardSection\n";
0371         headerStream << "{\n";
0372         headerStream << "public:\n";
0373         headerStream << "\t" << entryName << "(const char *data, int size)\n";
0374         headerStream << "\t{\n";
0375         headerStream << "\t\t" << initFunctionName << "(data, size);\n";
0376         headerStream << "\t}\n";
0377         headerStream << "\n";
0378         headerStream << "\texplicit " << entryName << "(const QByteArray &byteArray)\n";
0379         headerStream << "\t{\n";
0380         headerStream << "\t\t" << initFunctionName << "(byteArray.constData(), byteArray.size());\n";
0381         headerStream << "\t}\n";
0382         headerStream << "\n";
0383         break;
0384     }
0385 
0386     headerStream << "\t~" << entryName << "() { }\n";
0387 
0388     if (type == Entry) {
0389         headerStream << "\n";
0390         headerStream << "\tvoid advance()\n";
0391         headerStream << "\t{\n";
0392         headerStream << "\t\t" << initFunctionName << "(getData() + getLength(), getSize() - getLength());\n";
0393         headerStream << "\t}\n";
0394     }
0395 
0396     if (type == Section) {
0397         QString extension = node.attributes().namedItem("extension").nodeValue();
0398 
0399         if (!extension.isEmpty()) {
0400             headerStream << "\n";
0401             headerStream << "\tint " << extension << "() const\n";
0402             headerStream << "\t{\n";
0403             headerStream << "\t\treturn (at(3) << 8) | at(4);\n";
0404             headerStream << "\t}\n";
0405         }
0406     }
0407 
0408     foreach (const Element &element, elements) {
0409         switch (element.type) {
0410         case Element::Bool:
0411             headerStream << "\n";
0412             headerStream << "\tbool " << element.name << "() const\n";
0413             headerStream << "\t{\n";
0414             headerStream << "\t\treturn (" << element << ");\n";
0415             headerStream << "\t}\n";
0416             break;
0417 
0418         case Element::Int:
0419             headerStream << "\n";
0420             headerStream << "\tint " << element.name << "() const\n";
0421             headerStream << "\t{\n";
0422             headerStream << "\t\treturn " << element << ";\n";
0423             headerStream << "\t}\n";
0424             break;
0425 
0426         case Element::List:
0427             if (element.listType == "unused") {
0428                 break;
0429             }
0430 
0431             if (element.listType == "DvbString") {
0432                 headerStream << "\n";
0433                 headerStream << "\tQString " << element.name << "() const\n";
0434                 headerStream << "\t{\n";
0435                 headerStream << "\t\treturn DvbSiText::convertText(getData() + " << element << ", ";
0436             } else if (element.listType == "DvbInt") {
0437                 headerStream << "\n";
0438 
0439                 headerStream << "\tint " << element.name << "Length() const\n\t{\n";
0440                 headerStream << "\t\treturn (getLength() - 4) / 2;\n\t}\n\n";
0441                 headerStream << "\tint " << element.name << "(int idx) const\n";
0442                 headerStream << "\t{\n";
0443                 headerStream << "\t\tint pos = (idx * 2) + 4;\n";
0444                 headerStream << "\t\treturn (at(pos) << 8) | at(pos + 1);\n";
0445             } else if (element.listType == "AtscString") {
0446                 headerStream << "\n";
0447                 headerStream << "\tQString " << element.name << "() const\n";
0448                 headerStream << "\t{\n";
0449                 headerStream << "\t\treturn AtscPsipText::convertText(getData() + " << element << ", ";
0450             } else {
0451                 headerStream << "\n";
0452                 headerStream << "\t" << element.listType << " " << element.name << "() const\n";
0453                 headerStream << "\t{\n";
0454                 headerStream << "\t\treturn " << element.listType << "(getData() + " << element << ", ";
0455             }
0456 
0457             if (element.listType != "DvbInt") {
0458                 if (element.lengthFunc.isEmpty()) {
0459                     if (element.offsetString.isEmpty()) {
0460                         headerStream << "getLength() - " << (minBits / 8) << ");\n";
0461                     } else {
0462                         headerStream << "getLength() - (" << (minBits / 8) << element.offsetString << "));\n";
0463                     }
0464                 } else {
0465                     headerStream << element.name << "Length);\n";
0466                 }
0467             }
0468 
0469             headerStream << "\t}\n";
0470             break;
0471         }
0472     }
0473 
0474     headerStream << "\n";
0475     headerStream << "private:\n";
0476 
0477     if ((type == Descriptor) || (type == Section)) {
0478         headerStream << "\tQ_DISABLE_COPY(" << entryName << ")\n";
0479     }
0480 
0481     if ((type == Entry) || (type == Section)) {
0482         headerStream << "\tvoid " << initFunctionName << "(const char *data, int size);\n";
0483     }
0484 
0485     if (!privateVars.isEmpty()) {
0486         headerStream << '\n';
0487 
0488         foreach (const QString &privateVar, privateVars) {
0489             headerStream << "\tint " << privateVar << ";\n";
0490         }
0491     }
0492 
0493     headerStream << "};\n";
0494 }
0495 
0496 class FileHelper
0497 {
0498 public:
0499     explicit FileHelper(const QString &name);
0500 
0501     bool isValid() const
0502     {
0503         return valid;
0504     }
0505 
0506     QTextStream &getStream()
0507     {
0508         return outStream;
0509     }
0510 
0511     void removeOrig()
0512     {
0513         if (!QFile::remove(origName)) {
0514             qCritical() << "can't remove" << origName;
0515         }
0516     }
0517 
0518 private:
0519     bool valid;
0520     QString origName;
0521     QFile outFile;
0522     QTextStream outStream;
0523 };
0524 
0525 FileHelper::FileHelper(const QString &name) : valid(false)
0526 {
0527     origName = name + ".orig";
0528 
0529     if (!QFile::rename(name, origName)) {
0530         qCritical() << "can't rename" << name << "to" << origName;
0531         return;
0532     }
0533 
0534     QFile inFile(origName);
0535 
0536     if (!inFile.open(QIODevice::ReadOnly)) {
0537         qCritical() << "can't open file" << inFile.fileName();
0538         return;
0539     }
0540 
0541     outFile.setFileName(name);
0542 
0543     if (!outFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
0544         qCritical() << "can't open file" << outFile.fileName();
0545     }
0546 
0547     QTextStream inStream(&inFile);
0548     inStream.setCodec("UTF-8");
0549 
0550     outStream.setDevice(&outFile);
0551     outStream.setCodec("UTF-8");
0552 
0553     while (true) {
0554         if (inStream.atEnd()) {
0555             qCritical() << "can't find autogeneration boundary in" << inFile.fileName();
0556             return;
0557         }
0558 
0559         QString line = inStream.readLine();
0560         outStream << line << "\n";
0561 
0562         if (line == "// everything below this line is automatically generated") {
0563             break;
0564         }
0565     }
0566 
0567     valid = true;
0568 }
0569 
0570 int main()
0571 {
0572     QFile xmlFile("dvbsi.xml");
0573 
0574     if (!xmlFile.open(QIODevice::ReadOnly)) {
0575         qCritical() << "can't open file" << xmlFile.fileName();
0576         return 1;
0577     }
0578 
0579     QDomDocument document;
0580 
0581     if (!document.setContent(&xmlFile)) {
0582         qCritical() << "can't read file" << xmlFile.fileName();
0583         return 1;
0584     }
0585 
0586     QDomElement root = document.documentElement();
0587 
0588     if (root.nodeName() != "dvbsi") {
0589         qCritical() << "invalid root name";
0590         return 1;
0591     }
0592 
0593     FileHelper headerFile("../src/dvb/dvbsi.h");
0594 
0595     if (!headerFile.isValid()) {
0596         return 1;
0597     }
0598 
0599     FileHelper cppFile("../src/dvb/dvbsi.cpp");
0600 
0601     if (!cppFile.isValid()) {
0602         return 1;
0603     }
0604 
0605     QTextStream &headerStream = headerFile.getStream();
0606     QTextStream &cppStream = cppFile.getStream();
0607 
0608     for (QDomNode child = root.firstChild(); !child.isNull(); child = child.nextSibling()) {
0609         if (child.nodeName() == "descriptors") {
0610             for (QDomNode grandChild = child.firstChild(); !grandChild.isNull();
0611                  grandChild = grandChild.nextSibling()) {
0612                 SiXmlParser::parseEntry(grandChild, SiXmlParser::Descriptor, headerStream, cppStream);
0613             }
0614         } else if (child.nodeName() == "entries") {
0615             for (QDomNode grandChild = child.firstChild(); !grandChild.isNull();
0616                  grandChild = grandChild.nextSibling()) {
0617                 SiXmlParser::parseEntry(grandChild, SiXmlParser::Entry, headerStream, cppStream);
0618             }
0619         } else if (child.nodeName() == "sections") {
0620             for (QDomNode grandChild = child.firstChild(); !grandChild.isNull();
0621                  grandChild = grandChild.nextSibling()) {
0622                 SiXmlParser::parseEntry(grandChild, SiXmlParser::Section, headerStream, cppStream);
0623             }
0624         } else {
0625             qCritical() << "unknown entry:" << child.nodeName();
0626         }
0627     }
0628 
0629     headerStream << "\n";
0630     headerStream << "#endif /* DVBSI_H */\n";
0631 
0632     headerFile.removeOrig();
0633     cppFile.removeOrig();
0634 
0635     xmlFile.close();
0636 
0637 #if 0
0638     // Don't rewrite the xml file
0639 
0640     if (xmlFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
0641         xmlFile.write(document.toByteArray(2));
0642     }
0643 #endif
0644     return 0;
0645 }