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

0001 /*
0002  * convertscanfiles.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 <QCoreApplication>
0027 #include <QDate>
0028 #include <QDir>
0029 #include <QRegularExpression>
0030 
0031 #include "../src/dvb/dvbtransponder.h"
0032 
0033 // Mark fallthoughs to shut up gcc 7 warnings
0034 #ifndef __GNUC__
0035     #define FALLTHROUGH
0036 #else
0037 #  if __GNUC__ <= 6
0038     #define FALLTHROUGH
0039 #  else
0040     #define FALLTHROUGH __attribute__ ((fallthrough));
0041 #  endif
0042 #endif
0043 
0044 class NumericalLessThan
0045 {
0046 public:
0047     bool operator()(const QString &x, const QString &y)
0048     {
0049         int i = 0;
0050 
0051         while (true) {
0052             if ((i == x.length()) || (i == y.length())) {
0053                 return x.length() < y.length();
0054             }
0055 
0056             if (x.at(i) != y.at(i)) {
0057                 break;
0058             }
0059 
0060             ++i;
0061         }
0062 
0063         int xIndex = x.indexOf(' ', i);
0064 
0065         if (xIndex == -1) {
0066             xIndex = x.length();
0067         }
0068 
0069         int yIndex = y.indexOf(' ', i);
0070 
0071         if (yIndex == -1) {
0072             yIndex = y.length();
0073         }
0074 
0075         if (xIndex != yIndex) {
0076             return xIndex < yIndex;
0077         } else {
0078             return x.at(i) < y.at(i);
0079         }
0080     }
0081 };
0082 
0083 class parseDvbv5
0084 {
0085 public:
0086     bool parseInputLine(QString line);
0087     void resetParser();
0088     QString outputLine();
0089     parseDvbv5(QString name);
0090     int getPos();
0091     bool hasTransponder;
0092     DvbTransponderBase::TransmissionType type;
0093 
0094 private:
0095     QString name;
0096 
0097     int lineno;
0098 
0099     QString delsys = "";
0100     QString frq = "";
0101     QString modulation = "";
0102     QString symbolRate = "";
0103     QString fec = "";
0104     QString polar = "";
0105     QString inversion = "";
0106     QString rollOff = "";
0107     QString plscode = "";
0108     QString plsmode = "";
0109     QString bandwidth = "";
0110     QString fec_hi = "";
0111     QString fec_lo = "";
0112     QString t_mode = "";
0113     QString g_interval = "";
0114     QString hierarchy = "";
0115 
0116     // ISDB-T specific fields
0117 
0118     QString isdbtLayerEnabled = "";
0119     QString isdbtPartialReception = "";
0120     QString isdbtSb = "";
0121     QString isdbtSbSubchId = "";
0122     QString isdbtSbSegIdx = "";
0123     QString isdbtSbSegCount = "";
0124     QString isdbtLayerAFec = "";
0125     QString isdbtLayerAModulation = "";
0126     QString isdbtLayerASegCount = "";
0127     QString isdbtLayerAInterleaving = "";
0128     QString isdbtLayerBFec = "";
0129     QString isdbtLayerBModulation = "";
0130     QString isdbtLayerBSegCount = "";
0131     QString isdbtLayerBInterleaving = "";
0132     QString isdbtLayerCFec = "";
0133     QString isdbtLayerCModulation = "";
0134     QString isdbtLayerCSegCount = "";
0135     QString isdbtLayerCInterleaving = "";
0136     int isdbtLayers = 0;
0137     int streamid = 0;
0138 };
0139 
0140 int parseDvbv5::getPos()
0141 {
0142     return lineno;
0143 };
0144 
0145 parseDvbv5::parseDvbv5(QString name)
0146 {
0147     type = DvbTransponderBase::Invalid;
0148     hasTransponder = false;
0149 
0150     this->name = name;
0151 
0152     lineno = 0;
0153 };
0154 
0155 void parseDvbv5::resetParser()
0156 {
0157     delsys = "";
0158     frq = "";
0159     modulation = "";
0160     symbolRate = "";
0161     fec = "";
0162     polar = "";
0163     inversion = "";
0164     rollOff = "";
0165     plscode = "";
0166     plsmode = "";
0167     bandwidth = "";
0168     fec_hi = "";
0169     fec_lo = "";
0170     t_mode = "";
0171     g_interval = "";
0172     hierarchy = "";
0173 
0174     isdbtLayerEnabled = "";
0175     isdbtPartialReception = "";
0176     isdbtSb = "";
0177     isdbtSbSubchId = "";
0178     isdbtSbSegIdx = "";
0179     isdbtSbSegCount = "";
0180     isdbtLayerAFec = "";
0181     isdbtLayerAModulation = "";
0182     isdbtLayerASegCount = "";
0183     isdbtLayerAInterleaving = "";
0184     isdbtLayerBFec = "";
0185     isdbtLayerBModulation = "";
0186     isdbtLayerBSegCount = "";
0187     isdbtLayerBInterleaving = "";
0188     isdbtLayerCFec = "";
0189     isdbtLayerCModulation = "";
0190     isdbtLayerCSegCount = "";
0191     isdbtLayerCInterleaving = "";
0192     isdbtLayers = 0;
0193     streamid = 0;
0194 }
0195 
0196 bool parseDvbv5::parseInputLine(QString line)
0197 {
0198     lineno++;
0199 
0200     QRegularExpression rejex = QRegularExpression("^\\s*\\[(.*)]");
0201     if (line.contains(rejex)) {
0202         bool oldHasTransponder = hasTransponder;
0203         hasTransponder = true;
0204         return oldHasTransponder;
0205     }
0206 
0207     int pos = line.indexOf('#');
0208 
0209     if (pos != -1) {
0210         while ((pos > 0) && (line[pos - 1] == ' ')) {
0211             --pos;
0212         }
0213 
0214         line.truncate(pos);
0215     }
0216 
0217     if (line.isEmpty()) {
0218         return false;
0219     }
0220 
0221     if (line.contains("DELIVERY_SYSTEM")) {
0222         delsys = line.split(" = ")[1];
0223         if (!delsys.compare("ATSC", Qt::CaseInsensitive)) {
0224             type = DvbTransponderBase::Atsc;
0225         } else if (!delsys.compare("DVBC/ANNEX_A", Qt::CaseInsensitive)) {
0226             type = DvbTransponderBase::DvbC;
0227         } else if (!delsys.compare("DVBC/ANNEX_B", Qt::CaseInsensitive)) {
0228             type = DvbTransponderBase::Atsc;
0229         } else if (!delsys.compare("DVBS", Qt::CaseInsensitive)) {
0230             type = DvbTransponderBase::DvbS;
0231         } else if (!delsys.compare("DVBS2", Qt::CaseInsensitive)) {
0232             type = DvbTransponderBase::DvbS2;
0233         } else if (!delsys.compare("DVBT", Qt::CaseInsensitive)) {
0234             type = DvbTransponderBase::DvbT;
0235         } else if (!delsys.compare("DVBT2", Qt::CaseInsensitive)) {
0236             type = DvbTransponderBase::DvbT2;
0237         } else if (!delsys.compare("ISDBT", Qt::CaseInsensitive)) {
0238             type = DvbTransponderBase::IsdbT;
0239         } else {
0240             type = DvbTransponderBase::Invalid;
0241         }
0242         return false;
0243     }
0244     if (line.contains("FREQUENCY = ")) {
0245         frq = line.split(" = ")[1];
0246         return false;
0247     }
0248     if (line.contains("INNER_FEC")) {
0249         fec = line.split(" = ")[1];
0250         return false;
0251     }
0252     if (line.contains("SYMBOL_RATE")) {
0253         symbolRate = line.split(" = ")[1];
0254         return false;
0255     }
0256     if (line.contains("MODULATION")) {
0257         modulation = line.split(" = ")[1];
0258         return false;
0259     }
0260     if (line.contains("POLARIZATION")) {
0261         polar = line.split(" = ")[1];
0262         return false;
0263     }
0264     if (line.contains("INVERSION")) {
0265         inversion = line.split(" = ")[1];
0266         return false;
0267     }
0268     if (line.contains("ROLLOFF")) {
0269         rollOff = line.split(" = ")[1];
0270         return false;
0271     }
0272     if (line.contains("STREAM_ID")) {
0273         streamid = line.split(" = ")[1].toInt();
0274         return false;
0275     }
0276     if (line.contains("PLS_CODE")) {
0277         plscode = line.split(" = ")[1];
0278         return false;
0279     }
0280     if (line.contains("PLS_MODE")) {
0281         plsmode = line.split(" = ")[1];
0282         return false;
0283     }
0284     if (line.contains("BANDWIDTH_HZ")) {
0285         bandwidth = line.split(" = ")[1];
0286         return false;
0287     }
0288     if (line.contains("TRANSMISSION_MODE")) {
0289         t_mode = line.split(" = ")[1];
0290         return false;
0291     }
0292     if (line.contains("CODE_RATE_HP")) {
0293         fec_hi = line.split(" = ")[1];
0294         return false;
0295     }
0296     if (line.contains("CODE_RATE_LP")) {
0297         fec_lo = line.split(" = ")[1];
0298         return false;
0299     }
0300     if (line.contains("HIERARCHY")) {
0301         hierarchy = line.split(" = ")[1];
0302         return false;
0303     }
0304     if (line.contains("GUARD_INTERVAL")) {
0305         g_interval = line.split(" = ")[1];
0306         return false;
0307     }
0308     if (line.contains("ISDBT_LAYER_ENABLED")) {
0309         isdbtLayerEnabled = line.split(" = ")[1];
0310         return false;
0311     }
0312     if (line.contains("ISDBT_PARTIAL_RECEPTION")) {
0313         isdbtPartialReception = line.split(" = ")[1];
0314         return false;
0315     }
0316     if (line.contains("ISDBT_SOUND_BROADCASTING")) {
0317         isdbtSb = line.split(" = ")[1];
0318         return false;
0319     }
0320     if (line.contains("ISDBT_SB_SUBCHANNEL_ID")) {
0321         isdbtSbSubchId = line.split(" = ")[1];
0322         return false;
0323     }
0324     if (line.contains("ISDBT_SB_SEGMENT_IDX")) {
0325         isdbtSbSegIdx = line.split(" = ")[1];
0326         return false;
0327     }
0328     if (line.contains("ISDBT_SB_SEGMENT_COUNT")) {
0329         isdbtSbSegCount = line.split(" = ")[1];
0330         return false;
0331     }
0332     // Layer A
0333     if (line.contains("ISDBT_LAYERA_FEC")) {
0334         isdbtLayerAFec = line.split(" = ")[1];
0335         isdbtLayers |= 1;
0336         return false;
0337     }
0338     if (line.contains("ISDBT_LAYERA_MODULATION")) {
0339         isdbtLayerAModulation = line.split(" = ")[1];
0340         isdbtLayers |= 1;
0341         return false;
0342     }
0343     if (line.contains("ISDBT_LAYERA_SEGMENT_COUNT")) {
0344         isdbtLayerASegCount = line.split(" = ")[1];
0345         isdbtLayers |= 1;
0346         return false;
0347     }
0348     if (line.contains("ISDBT_LAYERA_TIME_INTERLEAVING")) {
0349         isdbtLayerAInterleaving = line.split(" = ")[1];
0350         isdbtLayers |= 1;
0351         return false;
0352     }
0353     // Layer B
0354     if (line.contains("ISDBT_LAYERB_FEC")) {
0355         isdbtLayerBFec = line.split(" = ")[1];
0356         isdbtLayers |= 2;
0357         return false;
0358     }
0359     if (line.contains("ISDBT_LAYERB_MODULATION")) {
0360         isdbtLayerBModulation = line.split(" = ")[1];
0361         isdbtLayers |= 2;
0362         return false;
0363     }
0364     if (line.contains("ISDBT_LAYERB_SEGMENT_COUNT")) {
0365         isdbtLayerBSegCount = line.split(" = ")[1];
0366         isdbtLayers |= 2;
0367         return false;
0368     }
0369     if (line.contains("ISDBT_LAYERB_TIME_INTERLEAVING")) {
0370         isdbtLayerBInterleaving = line.split(" = ")[1];
0371         isdbtLayers |= 2;
0372         return false;
0373     }
0374     // Layer C
0375     if (line.contains("ISDBT_LAYERC_FEC")) {
0376         isdbtLayerCFec = line.split(" = ")[1];
0377         isdbtLayers |= 4;
0378         return false;
0379     }
0380     if (line.contains("ISDBT_LAYERC_MODULATION")) {
0381         isdbtLayerCModulation = line.split(" = ")[1];
0382         isdbtLayers |= 4;
0383         return false;
0384     }
0385     if (line.contains("ISDBT_LAYERC_SEGMENT_COUNT")) {
0386         isdbtLayerCSegCount = line.split(" = ")[1];
0387         isdbtLayers |= 4;
0388         return false;
0389     }
0390     if (line.contains("ISDBT_LAYERC_TIME_INTERLEAVING")) {
0391         isdbtLayerCInterleaving = line.split(" = ")[1];
0392         isdbtLayers |= 4;
0393         return false;
0394     }
0395     qWarning() << "Can't parse line" << lineno << ":" << line << " for " << name;
0396     return false;
0397 };
0398 
0399 
0400 QString parseDvbv5::outputLine()
0401 {
0402     QString line = "";
0403 
0404     if (frq.isEmpty()) {
0405         qWarning() << "frequency is empty  in pos "
0406                 << lineno << " file" << name;
0407         return line;
0408     }
0409 
0410     switch (type) {
0411     case DvbTransponderBase::Invalid:
0412         if (!hasTransponder)
0413             return "";
0414         qWarning() << "Invalid transponder type in pos "
0415                 << lineno << " file" << name;
0416         return line;
0417     case DvbTransponderBase::DvbC: {
0418         if (symbolRate.isEmpty()) {
0419             qWarning() << "No symbol rate in pos "
0420                     << lineno << " file" << name;
0421             return line;
0422         }
0423         if (modulation.isEmpty()) {
0424             qWarning() << "No symbol rate in pos "
0425                     << lineno << " file" << name;
0426             return line;
0427         }
0428         line = "C " + frq + ' ' + symbolRate + ' ' + fec + ' ' + modulation.remove('/');
0429         return line;
0430     }
0431     case DvbTransponderBase::DvbS: {
0432         if (rollOff.isEmpty() && (modulation.isEmpty() || !modulation.compare("QPSK"))) {
0433             line = "S " + frq + ' ' + polar[0] + ' ' + symbolRate + ' ' + fec;
0434             return line;
0435         }
0436         type = DvbTransponderBase::DvbS2;
0437 
0438         // Fall through DVB-S2 handling
0439         FALLTHROUGH
0440     }
0441     case DvbTransponderBase::DvbS2: {
0442         if (rollOff.isEmpty())
0443             rollOff = "25";
0444 
0445         if (modulation.isEmpty()) {
0446             modulation = "AUTO";
0447         } if (modulation.contains("/")) {
0448             QString temp1 = modulation.split('/')[0];
0449             QString temp2 = modulation.split('/')[1];
0450             modulation = temp2 + temp1;
0451         }
0452 
0453         line = "S2 " + frq + ' ' + polar[0] + ' ' + symbolRate + ' ' + fec + ' ' + rollOff + ' ' + modulation;
0454 
0455         return line;
0456     }
0457     case DvbTransponderBase::DvbT: {
0458         line = "T " + frq;
0459         if (!bandwidth.isEmpty()) {
0460             int number = bandwidth.toInt();
0461             number = number / 1000000;
0462             line += ' ' + QString::number(number) + "MHz";
0463         }
0464         if (!fec_hi.isEmpty()) {
0465             line += ' ' + fec_hi;
0466         } else {
0467             line += " AUTO";
0468         }
0469         if (!fec_lo.isEmpty()) {
0470             line += ' ' + fec_lo;
0471         } else {
0472             line += " AUTO";
0473         }
0474         if (!modulation.isEmpty()) {
0475             line += ' ' + modulation.remove('/').replace("QAMAUTO", "AUTO");
0476         } else {
0477             line += " AUTO";
0478         }
0479         if (!t_mode.isEmpty()) {
0480             line += ' ' + t_mode.replace('K', 'k');
0481         } else {
0482             line += " AUTO";
0483         }
0484         if (!g_interval.isEmpty()) {
0485             line += ' ' + g_interval;
0486         } else {
0487             line += " AUTO";
0488         }
0489         if (!hierarchy.isEmpty()) {
0490             line += ' ' + hierarchy;
0491         } else {
0492             line += " AUTO";
0493         }
0494         return line;
0495     }
0496     case DvbTransponderBase::DvbT2: {
0497         line = "T2 " + frq;
0498         if (!bandwidth.isEmpty()) {
0499             int number = bandwidth.toInt();
0500             number = number / 1000000;
0501             line += ' ' + QString::number(number) + "MHz";
0502         }
0503         if (!fec_hi.isEmpty()) {
0504             line += ' ' + fec_hi;
0505         } else {
0506             line += " AUTO";
0507         }
0508         if (!fec_lo.isEmpty()) {
0509             line += ' ' + fec_lo;
0510         } else {
0511             line += " AUTO";
0512         }
0513         if (!modulation.isEmpty()) {
0514             line += ' ' + modulation.remove('/').replace("QAMAUTO", "AUTO");
0515         } else {
0516             line += " AUTO";
0517         }
0518         if (!t_mode.isEmpty()) {
0519             line += ' ' + t_mode.replace('K', 'k');
0520         } else {
0521             line += " AUTO";
0522         }
0523         if (!g_interval.isEmpty()) {
0524             line += ' ' + g_interval;
0525         } else {
0526             line += " AUTO";
0527         }
0528         if (!hierarchy.isEmpty()) {
0529             line += ' ' + hierarchy;
0530         } else {
0531             line += " AUTO";
0532         }
0533         line += ' ' + QString::number(streamid);
0534         return line;
0535     }
0536     case DvbTransponderBase::Atsc: {
0537         line = "A " + frq;
0538         if (!modulation.isEmpty()) {
0539             QString temp1 = modulation.split('/')[0];
0540             QString temp2 = modulation.split('/')[1];
0541             if (!(temp1 == "QAM")) {
0542                 line += ' ' + temp2 + temp1;
0543             } else {
0544                 line += ' ' + temp1 + temp2;
0545             }
0546         } else {
0547             line += " AUTO";
0548         }
0549         return line;
0550     }
0551     case DvbTransponderBase::IsdbT: {
0552         line = "I " + frq;
0553         if (!bandwidth.isEmpty()) {
0554             int number = bandwidth.toInt();
0555             number = number / 1000000;
0556             line += ' ' + QString::number(number) + "MHz";
0557         } else {
0558             line += " 6MHz";
0559         }
0560         if (!t_mode.isEmpty()) {
0561             line += ' ' + t_mode.replace('K', 'k');
0562         } else {
0563             line += " AUTO";
0564         }
0565         if (!g_interval.isEmpty()) {
0566             line += ' ' + g_interval;
0567         } else {
0568             line += " AUTO";
0569         }
0570         if (!isdbtPartialReception.isEmpty()) {
0571             line += ' ' + isdbtPartialReception;
0572         } else {
0573             line += " AUTO";
0574         }
0575         if (!isdbtSb.isEmpty()) {
0576             line += ' ' + isdbtSb;
0577         } else {
0578             line += " AUTO";
0579         }
0580         if (!isdbtSbSubchId.isEmpty()) {
0581             line += ' ' + isdbtSbSubchId;
0582         } else {
0583             line += " AUTO";
0584         }
0585         if (!isdbtSbSegCount.isEmpty()) {
0586             line += ' ' + isdbtSbSegCount;
0587         } else {
0588             line += " AUTO";
0589         }
0590         if (!isdbtSbSegIdx.isEmpty()) {
0591             line += ' ' + isdbtSbSegIdx;
0592         } else {
0593             line += " AUTO";
0594         }
0595 
0596         line += ' ' + QString::number(isdbtLayers);
0597 
0598         // Layer A
0599         if (!isdbtLayerAModulation.isEmpty()) {
0600             line += ' ' + isdbtLayerAModulation.remove('/').replace("QAMAUTO", "AUTO");
0601         } else {
0602             line += " AUTO";
0603         }
0604         if (!isdbtLayerAFec.isEmpty()) {
0605             line += ' ' + isdbtLayerAFec;
0606         } else {
0607             line += " AUTO";
0608         }
0609         if (!isdbtLayerASegCount.isEmpty()) {
0610             line += ' ' + isdbtLayerASegCount;
0611         } else {
0612             line += " AUTO";
0613         }
0614         if (!isdbtLayerAInterleaving.isEmpty()) {
0615             line += ' ' + isdbtLayerAInterleaving;
0616         } else {
0617             line += " AUTO";
0618         }
0619         // Layer B
0620         if (!isdbtLayerBModulation.isEmpty()) {
0621             line += ' ' + isdbtLayerBModulation.remove('/').replace("QAMAUTO", "AUTO");
0622         } else {
0623             line += " AUTO";
0624         }
0625         if (!isdbtLayerBFec.isEmpty()) {
0626             line += ' ' + isdbtLayerBFec;
0627         } else {
0628             line += " AUTO";
0629         }
0630         if (!isdbtLayerBSegCount.isEmpty()) {
0631             line += ' ' + isdbtLayerBSegCount;
0632         } else {
0633             line += " AUTO";
0634         }
0635         if (!isdbtLayerBInterleaving.isEmpty()) {
0636             line += ' ' + isdbtLayerBInterleaving;
0637         } else {
0638             line += " AUTO";
0639         }
0640         // Layer C
0641         if (!isdbtLayerCModulation.isEmpty()) {
0642             line += ' ' + isdbtLayerCModulation.remove('/').replace("QAMAUTO", "AUTO");
0643         } else {
0644             line += " AUTO";
0645         }
0646         if (!isdbtLayerCFec.isEmpty()) {
0647             line += ' ' + isdbtLayerCFec;
0648         } else {
0649             line += " AUTO";
0650         }
0651         if (!isdbtLayerCSegCount.isEmpty()) {
0652             line += ' ' + isdbtLayerCSegCount;
0653         } else {
0654             line += " AUTO";
0655         }
0656         if (!isdbtLayerCInterleaving.isEmpty()) {
0657             line += ' ' + isdbtLayerCInterleaving;
0658         } else {
0659             line += " AUTO";
0660         }
0661         return line;
0662     }
0663     }
0664 
0665     return line;
0666 }
0667 
0668 static QString parseLine(DvbTransponderBase::TransmissionType type, const QString &line, const QString &fileName)
0669 {
0670     switch (type) {
0671     case DvbTransponderBase::Invalid:
0672         break;
0673     case DvbTransponderBase::DvbC: {
0674         DvbCTransponder transponder;
0675 
0676         if (!transponder.fromString(line)) {
0677             break;
0678         }
0679         if (transponder.modulation == DvbCTransponder::ModulationAuto) {
0680             qWarning() << "Warning: modulation == AUTO in file" << fileName;
0681         }
0682 
0683         if (transponder.fecRate != DvbCTransponder::FecNone) {
0684             qWarning() << "Warning: fec rate != NONE in file" << fileName;
0685         }
0686         return transponder.toString();
0687         }
0688     case DvbTransponderBase::DvbS: {
0689         DvbSTransponder transponder;
0690 
0691         if (!transponder.fromString(line)) {
0692             break;
0693         }
0694         if (transponder.fecRate == DvbSTransponder::FecNone) {
0695             qWarning() << "Warning: fec rate == NONE in file" << fileName;
0696         }
0697         // fecRate == AUTO is ok
0698 
0699         return transponder.toString();
0700         }
0701     case DvbTransponderBase::DvbS2: {
0702         DvbS2Transponder transponder;
0703 
0704         if (!transponder.fromString(line)) {
0705             break;
0706         }
0707         if (transponder.fecRate == DvbSTransponder::FecNone) {
0708             qWarning() << "Warning: fec rate == NONE in file" << fileName;
0709         }
0710         // fecRate == AUTO is ok
0711 
0712         return transponder.toString();
0713     }
0714     case DvbTransponderBase::DvbT2: {
0715         DvbT2Transponder transponder;
0716 
0717         if (!transponder.fromString(line)) {
0718             break;
0719         }
0720         return transponder.toString();
0721         }
0722     case DvbTransponderBase::DvbT: {
0723         DvbTTransponder transponder;
0724 
0725         if (!transponder.fromString(line)) {
0726             break;
0727         }
0728         if (transponder.bandwidth == DvbTTransponder::BandwidthAuto) {
0729             qWarning() << "Warning: bandwidth == AUTO in file" << fileName;
0730         }
0731 
0732         if ((transponder.modulation == DvbTTransponder::ModulationAuto) && !fileName.startsWith(QLatin1String("auto-"))) {
0733             qWarning() << "Warning: modulation == AUTO in file" << fileName;
0734         }
0735 
0736         if (transponder.fecRateHigh == DvbTTransponder::FecNone) {
0737             qWarning() << "Warning: fec rate high == NONE in file" << fileName;
0738         }
0739 
0740         // fecRateHigh == AUTO is ok
0741 
0742         if (transponder.fecRateLow != DvbTTransponder::FecNone) {
0743             qWarning() << "Warning: fec rate low != NONE in file" << fileName;
0744         }
0745 
0746         if ((transponder.transmissionMode == DvbTTransponder::TransmissionModeAuto) && !fileName.startsWith(QLatin1String("auto-"))) {
0747             qWarning() << "Warning: transmission mode == AUTO in file" << fileName;
0748         }
0749 
0750         if ((transponder.guardInterval == DvbTTransponder::GuardIntervalAuto) && !fileName.startsWith(QLatin1String("auto-"))) {
0751             qWarning() << "Warning: guard interval == AUTO in file" << fileName;
0752         }
0753 
0754         if (transponder.hierarchy != DvbTTransponder::HierarchyNone) {
0755             qWarning() << "Warning: hierarchy != NONE in file" << fileName;
0756         }
0757         return transponder.toString();
0758         }
0759     case DvbTransponderBase::Atsc: {
0760         AtscTransponder transponder;
0761 
0762         if (!transponder.fromString(line)) {
0763             break;
0764         }
0765         if (transponder.modulation == AtscTransponder::ModulationAuto) {
0766             qWarning() << "Warning: modulation == AUTO in file" << fileName;
0767         }
0768         return transponder.toString();
0769         }
0770     case DvbTransponderBase::IsdbT: {
0771         IsdbTTransponder transponder;
0772 
0773         if (!transponder.fromString(line)) {
0774             break;
0775         }
0776 
0777         return transponder.toString();
0778         }
0779     }
0780 
0781     return QString();
0782 }
0783 
0784 static void readScanDirectory(QTextStream &out, const QString &path)
0785 {
0786     QDir dir;
0787 
0788     dir.setPath(path);
0789     if (!dir.exists()) {
0790         qCritical() << "Error: can't open directory" << dir.path();
0791         return;
0792     }
0793 
0794     foreach (const QString &fileName, dir.entryList(QDir::Files, QDir::Name)) {
0795         QFile file(dir.filePath(fileName));
0796 
0797         if (!file.open(QIODevice::ReadOnly)) {
0798             qCritical() << "Error: can't open file" << file.fileName();
0799             return;
0800         }
0801 
0802         QTextStream stream(&file);
0803         stream.setCodec("UTF-8");
0804         QList<QString> transponders;
0805         QString name = dir.dirName() + '/' + fileName;
0806 
0807         parseDvbv5 parser(name);
0808 
0809         while (!stream.atEnd()) {
0810             QString line = stream.readLine();
0811 
0812             if (!parser.parseInputLine(line) && !stream.atEnd())
0813                 continue;
0814 
0815             if (!parser.hasTransponder)
0816                 continue;
0817 
0818             QString parsedLine = parser.outputLine();
0819             parser.resetParser();
0820 
0821             QString string = parseLine(parser.type, parsedLine, fileName);
0822 
0823             if (string.isEmpty()) {
0824                 qCritical() << "Error: can't parse Transponder pos " << parser.getPos() << "line" << parsedLine << "in file" << name;
0825                 continue;
0826             }
0827 
0828             // reduce multiple spaces to one space
0829 
0830             for (int i = 1; i < parsedLine.length(); ++i) {
0831                 if (parsedLine.at(i - 1) != ' ') {
0832                     continue;
0833                 }
0834 
0835                 if (parsedLine.at(i) == ' ') {
0836                     parsedLine.remove(i, 1);
0837                     --i;
0838                 }
0839             }
0840             if (parsedLine.at(parsedLine.length() - 1) == ' ') {
0841                     parsedLine.remove(parsedLine.length() - 1, 1);
0842             }
0843 
0844             if (parsedLine != string) {
0845                 qWarning() << "Warning: suboptimal representation:";
0846                 qWarning() << parsedLine << "<-->";
0847                 qWarning() << string << "in file" << name;
0848             }
0849 
0850             transponders.append(string);
0851         }
0852 
0853         if (transponders.isEmpty()) {
0854             qWarning() << "Warning: no transponder found in file" << name;
0855             continue;
0856         }
0857 
0858         if (parser.type == DvbTransponderBase::DvbS ||
0859             parser.type == DvbTransponderBase::DvbS2) {
0860             // use upper case for orbital position
0861             name[name.size() - 1] = name.at(name.size() - 1).toUpper();
0862 
0863             QString source = name;
0864             source.remove(0, source.lastIndexOf('-') + 1);
0865 
0866             bool ok = false;
0867 
0868             if (source.endsWith('E')) {
0869                 source.chop(1);
0870                 source.toDouble(&ok);
0871             } else if (source.endsWith('W')) {
0872                 source.chop(1);
0873                 source.toDouble(&ok);
0874             }
0875 
0876             if (!ok) {
0877                 qWarning() << "Warning: invalid orbital position for file" << name;
0878             }
0879         }
0880 
0881         out << '[' << name << "]\n";
0882 
0883         std::sort(transponders.begin(), transponders.end(), NumericalLessThan());
0884 
0885         foreach (const QString &transponder, transponders) {
0886             out << transponder << '\n';
0887         }
0888     }
0889 }
0890 
0891 int main(int argc, char *argv[])
0892 {
0893     // QCoreApplication is needed for proper file name handling
0894     QCoreApplication app(argc, argv);
0895 
0896     if (argc != 3) {
0897         qCritical() << "Syntax: convertscanfiles <scan file dir> <output file>";
0898         return 1;
0899     }
0900 
0901     QByteArray data;
0902     QTextStream out(&data);
0903     out.setCodec("UTF-8");
0904 
0905     out << "# this file is automatically generated from https://linuxtv.org/downloads/dtv-scan-tables\n";
0906     out << "[date]\n";
0907     out << QDate::currentDate().toString(Qt::ISODate) << '\n';
0908 
0909     QString path(argv[1]);
0910 
0911     readScanDirectory(out, path + "/dvb-c");
0912     readScanDirectory(out, path + "/dvb-s");
0913     readScanDirectory(out, path + "/dvb-t");
0914     readScanDirectory(out, path + "/atsc");
0915     readScanDirectory(out, path + "/isdb-t");
0916 
0917     out.flush();
0918 
0919     QFile file(argv[2]);
0920 
0921     if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
0922         qCritical() << "Error: can't open file" << file.fileName();
0923         return 1;
0924     }
0925 
0926     file.write(data);
0927 
0928     QFile compressedFile(QString(argv[2]) + ".qz");
0929 
0930     if (!compressedFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
0931         qCritical() << "Error: can't open file" << compressedFile.fileName();
0932         return 1;
0933     }
0934 
0935     compressedFile.write(qCompress(data, 9));
0936 
0937     return 0;
0938 }