diff --git a/companion/src/constants.h b/companion/src/constants.h index 1c6a3a5c5..4e66ff214 100644 --- a/companion/src/constants.h +++ b/companion/src/constants.h @@ -61,8 +61,8 @@ #define EEPE_FILES_FILTER "EEPE " % CPN_STR_FILES % " (*.eepe);;" #define OTX_FILES_FILTER "OpenTX " % CPN_STR_FILES % " (*.otx);;" #define ETX_FILES_FILTER "EdgeTX " % CPN_STR_FILES % " (*.etx);;" -#define YML_FILES_FILTER "YAML " % CPN_STR_FILES % " (*.yml);;" -#define EEPROM_FILES_FILTER CPN_STR_RAD_MOD_SETTINGS % " " % CPN_STR_FILES % " (*.etx *.otx *.eepe *.bin *.hex);;" % ETX_FILES_FILTER % OTX_FILES_FILTER % EEPE_FILES_FILTER % BIN_FILES_FILTER % HEX_FILES_FILTER +#define YML_FILES_FILTER "EdgeTX YAML " % CPN_STR_FILES % " (*.yml);;" +#define EEPROM_FILES_FILTER CPN_STR_RAD_MOD_SETTINGS % " " % CPN_STR_FILES % " (*.etx *.yml *.otx *.eepe *.bin *.hex);;" % ETX_FILES_FILTER % YML_FILES_FILTER % OTX_FILES_FILTER % EEPE_FILES_FILTER % BIN_FILES_FILTER % HEX_FILES_FILTER #define FLASH_FILES_FILTER "FLASH " % CPN_STR_FILES % " (*.bin *.hex *.dfu);;" % BIN_FILES_FILTER % HEX_FILES_FILTER % DFU_FILES_FILTER #define EXTERNAL_EEPROM_FILES_FILTER "EEPROM " % CPN_STR_FILES % " (*.bin *.hex);;" % BIN_FILES_FILTER % HEX_FILES_FILTER #define ER9X_EEPROM_FILE_TYPE "ER9X_EEPROM_FILE" diff --git a/companion/src/mdichild.cpp b/companion/src/mdichild.cpp index dde2f4da7..2c8732b4e 100644 --- a/companion/src/mdichild.cpp +++ b/companion/src/mdichild.cpp @@ -1375,7 +1375,7 @@ bool MdiChild::saveAs(bool isNew) #ifdef __APPLE__ QString filter; #else - QString filter(ETX_FILES_FILTER); + QString filter(ETX_FILES_FILTER % YML_FILES_FILTER); #endif QString fileName = QFileDialog::getSaveFileName(this, tr("Save As"), g.eepromDir() + "/" + fi.fileName(), filter); diff --git a/companion/src/storage/categorized.cpp b/companion/src/storage/categorized.cpp index 5786b3127..78ffd9c95 100644 --- a/companion/src/storage/categorized.cpp +++ b/companion/src/storage/categorized.cpp @@ -23,6 +23,34 @@ #include "miniz.c" // Can only be included once! bool CategorizedStorageFormat::load(RadioData & radioData) +{ + if (getStorageType(filename) == STORAGE_TYPE_ETX || + (getStorageType(filename) == STORAGE_TYPE_UNKNOWN && probeFormat() == STORAGE_TYPE_ETX)) + return loadYaml(radioData); + else + return loadBin(radioData); +} + +bool CategorizedStorageFormat::write(const RadioData & radioData) +{ + if (getStorageType(filename) == STORAGE_TYPE_ETX || + (getStorageType(filename) == STORAGE_TYPE_UNKNOWN && probeFormat() == STORAGE_TYPE_ETX)) + return writeYaml(radioData); + else + return writeBin(radioData); +} + +StorageType CategorizedStorageFormat::probeFormat() +{ + if (QFile(filename + "/RADIO/radio.yml").exists()) // converted + return getStorageType("radio.yml"); + else if (QFile(filename + "/RADIO/radio.bin").exists()) // unconverted + return getStorageType("radio.bin"); + else + return getStorageType(filename); +} + +bool CategorizedStorageFormat::loadBin(RadioData & radioData) { QByteArray radioSettingsBuffer; if (!loadFile(radioSettingsBuffer, "RADIO/radio.bin")) { @@ -114,7 +142,7 @@ bool CategorizedStorageFormat::load(RadioData & radioData) return true; } -bool CategorizedStorageFormat::write(const RadioData & radioData) +bool CategorizedStorageFormat::writeBin(const RadioData & radioData) { QByteArray modelsList; // models.txt QByteArray radioSettingsData; // radio.bin @@ -168,3 +196,185 @@ bool CategorizedStorageFormat::write(const RadioData & radioData) return true; } + +bool CategorizedStorageFormat::loadYaml(RadioData & radioData) +{ + if (getStorageType(filename) == STORAGE_TYPE_UNKNOWN && probeFormat() == STORAGE_TYPE_ETX) { + if (!QFile(filename + "/" + "RADIO/radio.yml").exists()) + qDebug() << tr("Can't find %1/RADIO/radio.yml").arg(filename); + else + qDebug() << tr("Found %1/RADIO/radio.yml").arg(filename); + + if (!QFile(filename + "/" + "MODELS/models.yml").exists()) + qDebug() << tr("Can't find %1/MODELS/models.yml").arg(filename); + else + qDebug() << tr("Found %1/MODELS/models.yml").arg(filename); + } + else { + if (!QFile(filename).exists()) + qDebug() << tr("Can't find %1").arg(filename); + else + qDebug() << tr("Found %1").arg(filename); + } + + setError(tr("Error: Yaml format ignored - under development")); + return false; // force failure until fully developed + + /* + This code a copy of loadBin with yaml file extensions ONLY + */ + + QByteArray radioSettingsBuffer; + if (!loadFile(radioSettingsBuffer, "RADIO/radio.yml")) { + setError(tr("Can't extract RADIO/radio.yml")); + return false; + } + + OpenTxEepromInterface * loadInterface = loadRadioSettingsFromByteArray(radioData.generalSettings, radioSettingsBuffer); + if (!loadInterface) { + return false; + } + + board = loadInterface->getBoard(); + + QByteArray modelsListBuffer; + if (!loadFile(modelsListBuffer, "MODELS/models.yml")) { + setError(tr("Can't extract MODELS/models.yml")); + return false; + } + + QList lines = modelsListBuffer.split('\n'); + int modelIndex = 0; + int categoryIndex = -1; + foreach (const QByteArray & lineArray, lines) { + QString line = QString(lineArray).trimmed(); + if (line.isEmpty()) continue; + // qDebug() << "parsing line" << line; + + if (line.startsWith('[') && line.endsWith(']')) { + // ignore categories for boards that do not support them + if (getCurrentFirmware()->getCapability(HasModelCategories)) { + QString name = line.mid(1, line.size() - 2); + CategoryData category(qPrintable(name)); + radioData.categories.push_back(category); + categoryIndex++; + qDebug() << "added category" << name; + } + continue; + } + + // determine if we have a model number + QStringList parts = line.split(QRegExp("\\s+"), QString::SkipEmptyParts); + if (parts.size() == 2) { + // parse model number + int modelNumber = parts[0].toInt(); + if (modelNumber > 0 && modelNumber > modelIndex && modelNumber < getCurrentFirmware()->getCapability(Models)) { + modelIndex = modelNumber; + qDebug() << "advanced model number to" << modelIndex; + } + else { + if (modelNumber != modelIndex) qDebug() << "Invalid model number" << parts[0]; + } + parts.removeFirst(); + } + if (parts.size() == 1) { + // parse model file name and load + QString fileName = parts[0]; + qDebug() << "Loading model from file" << fileName << "into slot" << modelIndex; + QByteArray modelBuffer; + if (!loadFile(modelBuffer, QString("MODELS/%1").arg(fileName))) { + setError(tr("Can't extract %1").arg(fileName)); + return false; + } + if ((int)radioData.models.size() <= modelIndex) { + radioData.models.resize(modelIndex + 1); + } + if (!loadModelFromByteArray(radioData.models[modelIndex], modelBuffer)) { + setError(tr("Error loading models")); + return false; + } + strncpy(radioData.models[modelIndex].filename, qPrintable(fileName), sizeof(radioData.models[modelIndex].filename)); + if (IS_FAMILY_HORUS_OR_T16(board) && !strcmp(radioData.generalSettings.currModelFilename, qPrintable(fileName))) { + radioData.generalSettings.currModelIndex = modelIndex; + qDebug() << "currModelIndex =" << modelIndex; + } + if (getCurrentFirmware()->getCapability(HasModelCategories)) { + radioData.models[modelIndex].category = categoryIndex; + } + radioData.models[modelIndex].used = true; + modelIndex++; + continue; + } + + // invalid line + // TODO add to parsing report + qDebug() << "Invalid line" <> sortedModels(numCategories); + + writeRadioSettingsToByteArray(radioData.generalSettings, radioSettingsData); + if (!writeFile(radioSettingsData, "RADIO/radio.yml")) { + return false; + } + + for (size_t m=0; mgetCapability(HasModelCategories)) { + // Use format with model number and file name. This is needed because + // radios without category support can have unused model slots + modelsList.append(QString("%1 %2\n").arg(m).arg(model.filename)); + } else { + sortedModels[model.category].push_back(QString("%1\n").arg(model.filename)); + } + } + + if (getCurrentFirmware()->getCapability(HasModelCategories)) { + for (size_t c=0; c