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 }