feat: Reduce risk of corrupt radio.yml (#1481)
* reduce risk of corrupt radio.yml #1412 Adds CRC16 checksum to radio.yml when saved. Adds save to temporary file, folowed by rename to radio.yml Adds check on load, with fall-back to temporary file * Made sure that only the first read block of radio.yml is checked for the 'checksum' value. Added checksum generation on radio.yml produced by conversion. * Added namespace. Github actions triggered build failed on this - compiler difference. * Fixed typo in code not run locally. * Made checksum result code a enum class. Fixed null-poionter dereference bug. * Added check and handling for radio.yml file not read (will be handled as if checksum failed) Added evaluation of checksum when radio_new.yml is read. Added f_unlink() calls before f_rename() to ensure the target filename does not exist. * Added radio.yml cheksum handling to Simulator/Companion. Cleaned radio firmware handling of checksum field up to be newline agnostic. * Added handling of files without checksum. - Letting files pass the check if no checksum field is present (old files) and the size is realistic (25 bytes) * Fixed bug causing new conversion from binary if no radio.yml BUT a radio_new.yaml is present in the filesystem. Now the conversion is only performed if neither file exsits. * repair rebase problems * rerun yaml generator * fix another rebase problem * update text in companion to be more meaningful when the radio.yml file has a checksum error fix wrong error message in trace * add trigger to check radio config when leavin menu_radio * fix: add handling for temporary and error yaml radio settings files to simu/simulator/libsimulator * fix corner cases when comverting non crc-ed files * chore: Tweak radio data warnings for screen sizes - Changed the define to be a little shorter - Added missing define for TW - Three string lengths to suit 128, 212 and basically colorlcd Co-authored-by: gagarinlg <malte@langermann.net> Co-authored-by: Peter Feerick <peter.feerick@gmail.com>
This commit is contained in:
co-authored by
gagarinlg
Peter Feerick
parent
e6697bca89
commit
e576925bee
@@ -20,12 +20,47 @@
|
||||
|
||||
#include "edgetxinterface.h"
|
||||
|
||||
#include "crc.h"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include "yaml_ops.h"
|
||||
#include "yaml_generalsettings.h"
|
||||
#include "yaml_modeldata.h"
|
||||
|
||||
#include <QMessageBox>
|
||||
|
||||
|
||||
static uint16_t calculateChecksum(const QByteArray& data, uint16_t checksum)
|
||||
{
|
||||
std::string fileContent = data.toStdString();
|
||||
|
||||
size_t startPos = 0;
|
||||
size_t pos = 0;
|
||||
uint8_t isCRLF = 0;
|
||||
std::string newLine[2] = {"\n","\r\n"};
|
||||
|
||||
pos = fileContent.find("\n", 0);
|
||||
if(pos == 0) {
|
||||
return checksum;
|
||||
}
|
||||
|
||||
if(fileContent[pos-1] == '\r') {
|
||||
isCRLF = 1;
|
||||
qDebug() << "** File has CRLF line endings";
|
||||
} else {
|
||||
isCRLF = 0;
|
||||
qDebug() << "** File has LF line endings";
|
||||
}
|
||||
|
||||
// The first line contains the "checksum" value - if present. Skip it
|
||||
if ( fileContent.find("checksum:") == 0) {
|
||||
startPos = fileContent.find(newLine[isCRLF]) + newLine[isCRLF].length();
|
||||
}
|
||||
|
||||
checksum = crc16(0, (const uint8_t *) &fileContent[startPos], fileContent.length() - startPos, checksum);
|
||||
return checksum;
|
||||
}
|
||||
|
||||
static YAML::Node loadYamlFromByteArray(const QByteArray& data)
|
||||
{
|
||||
// TODO: use real streaming to avoid memory copies
|
||||
@@ -33,15 +68,25 @@ static YAML::Node loadYamlFromByteArray(const QByteArray& data)
|
||||
return YAML::Load(data_istream);
|
||||
}
|
||||
|
||||
static void writeYamlToByteArray(const YAML::Node& node, QByteArray& data)
|
||||
static void writeYamlToByteArray(const YAML::Node& node, QByteArray& data, bool addChecksum = false)
|
||||
{
|
||||
// TODO: use real streaming to avoid memory copies
|
||||
std::stringstream data_ostream;
|
||||
|
||||
data_ostream << node;
|
||||
data = QByteArray::fromStdString(data_ostream.str());
|
||||
|
||||
qDebug() << "Saving YAML:";
|
||||
qDebug() << data_ostream.str().c_str();
|
||||
|
||||
if(addChecksum) {
|
||||
uint16_t checksum = 0xFFFF;
|
||||
checksum = calculateChecksum(data, checksum);
|
||||
std::stringstream checksum_ostream;
|
||||
checksum_ostream << "checksum: " << checksum << std::endl;
|
||||
data.prepend(checksum_ostream.str().c_str());
|
||||
}
|
||||
|
||||
qDebug() << data.toStdString().c_str();
|
||||
}
|
||||
|
||||
bool loadModelsListFromYaml(std::vector<CategoryData>& categories,
|
||||
@@ -87,6 +132,28 @@ bool loadModelFromYaml(ModelData& model, const QByteArray& data)
|
||||
|
||||
bool loadRadioSettingsFromYaml(GeneralSettings& settings, const QByteArray& data)
|
||||
{
|
||||
if(data.indexOf("checksum:") == 0) {
|
||||
int startPos = strlen("checksum:");
|
||||
int endPos = data.indexOf("\n");
|
||||
if (endPos > 0) {
|
||||
while( ((const char)data[startPos] == ' ') && (startPos < data.size())) {
|
||||
startPos++;
|
||||
}
|
||||
if (startPos < data.size()) {
|
||||
QByteArray checksumStr = data.mid(startPos, endPos - startPos);
|
||||
uint16_t fileChecksum = std::stoi(checksumStr.toStdString());
|
||||
uint16_t calculatedChecksum = calculateChecksum(data, 0xFFFF);
|
||||
qDebug() << "File checksum:" << fileChecksum;
|
||||
qDebug() << "Calculated checksum:" << calculatedChecksum;
|
||||
if (fileChecksum != calculatedChecksum) {
|
||||
qDebug() << "File checksum mismatch! Got: " << fileChecksum << ", expected: " << calculatedChecksum;
|
||||
QMessageBox::critical(NULL, CPN_STR_APP_NAME, QCoreApplication::translate("EdgeTXInterface", "Radio settings file checksum error. You are advised to review the settings"));
|
||||
//return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
YAML::Node node = loadYamlFromByteArray(data);
|
||||
node >> settings;
|
||||
if (settings.version < CPN_CURRENT_SETTINGS_VERSION) {
|
||||
@@ -159,7 +226,7 @@ bool writeRadioSettingsToYaml(const GeneralSettings& settings, QByteArray& data)
|
||||
YAML::Node node;
|
||||
node = settings;
|
||||
|
||||
writeYamlToByteArray(node, data);
|
||||
writeYamlToByteArray(node, data, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ set(storage_NAMES
|
||||
etx
|
||||
otx
|
||||
yaml
|
||||
crc
|
||||
)
|
||||
|
||||
set(storage_SRCS
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "crc.h"
|
||||
|
||||
static const unsigned short * crc16tab[] = {
|
||||
crc16tab_1021,
|
||||
crc16tab_1189
|
||||
};
|
||||
|
||||
uint16_t crc16(uint8_t index, const uint8_t * buf, uint32_t len, uint16_t start)
|
||||
{
|
||||
uint16_t crc = start;
|
||||
const unsigned short * tab = crc16tab[index];
|
||||
for (uint32_t i=0; i<len; i++) {
|
||||
crc = (crc<<8) ^ tab[((crc>>8) ^ *buf++) & 0x00FF];
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
// CRC8 implementation with polynom = x^8+x^7+x^6+x^4+x^2+1 (0xD5)
|
||||
const unsigned char crc8tab[256] = {
|
||||
0x00, 0xD5, 0x7F, 0xAA, 0xFE, 0x2B, 0x81, 0x54,
|
||||
0x29, 0xFC, 0x56, 0x83, 0xD7, 0x02, 0xA8, 0x7D,
|
||||
0x52, 0x87, 0x2D, 0xF8, 0xAC, 0x79, 0xD3, 0x06,
|
||||
0x7B, 0xAE, 0x04, 0xD1, 0x85, 0x50, 0xFA, 0x2F,
|
||||
0xA4, 0x71, 0xDB, 0x0E, 0x5A, 0x8F, 0x25, 0xF0,
|
||||
0x8D, 0x58, 0xF2, 0x27, 0x73, 0xA6, 0x0C, 0xD9,
|
||||
0xF6, 0x23, 0x89, 0x5C, 0x08, 0xDD, 0x77, 0xA2,
|
||||
0xDF, 0x0A, 0xA0, 0x75, 0x21, 0xF4, 0x5E, 0x8B,
|
||||
0x9D, 0x48, 0xE2, 0x37, 0x63, 0xB6, 0x1C, 0xC9,
|
||||
0xB4, 0x61, 0xCB, 0x1E, 0x4A, 0x9F, 0x35, 0xE0,
|
||||
0xCF, 0x1A, 0xB0, 0x65, 0x31, 0xE4, 0x4E, 0x9B,
|
||||
0xE6, 0x33, 0x99, 0x4C, 0x18, 0xCD, 0x67, 0xB2,
|
||||
0x39, 0xEC, 0x46, 0x93, 0xC7, 0x12, 0xB8, 0x6D,
|
||||
0x10, 0xC5, 0x6F, 0xBA, 0xEE, 0x3B, 0x91, 0x44,
|
||||
0x6B, 0xBE, 0x14, 0xC1, 0x95, 0x40, 0xEA, 0x3F,
|
||||
0x42, 0x97, 0x3D, 0xE8, 0xBC, 0x69, 0xC3, 0x16,
|
||||
0xEF, 0x3A, 0x90, 0x45, 0x11, 0xC4, 0x6E, 0xBB,
|
||||
0xC6, 0x13, 0xB9, 0x6C, 0x38, 0xED, 0x47, 0x92,
|
||||
0xBD, 0x68, 0xC2, 0x17, 0x43, 0x96, 0x3C, 0xE9,
|
||||
0x94, 0x41, 0xEB, 0x3E, 0x6A, 0xBF, 0x15, 0xC0,
|
||||
0x4B, 0x9E, 0x34, 0xE1, 0xB5, 0x60, 0xCA, 0x1F,
|
||||
0x62, 0xB7, 0x1D, 0xC8, 0x9C, 0x49, 0xE3, 0x36,
|
||||
0x19, 0xCC, 0x66, 0xB3, 0xE7, 0x32, 0x98, 0x4D,
|
||||
0x30, 0xE5, 0x4F, 0x9A, 0xCE, 0x1B, 0xB1, 0x64,
|
||||
0x72, 0xA7, 0x0D, 0xD8, 0x8C, 0x59, 0xF3, 0x26,
|
||||
0x5B, 0x8E, 0x24, 0xF1, 0xA5, 0x70, 0xDA, 0x0F,
|
||||
0x20, 0xF5, 0x5F, 0x8A, 0xDE, 0x0B, 0xA1, 0x74,
|
||||
0x09, 0xDC, 0x76, 0xA3, 0xF7, 0x22, 0x88, 0x5D,
|
||||
0xD6, 0x03, 0xA9, 0x7C, 0x28, 0xFD, 0x57, 0x82,
|
||||
0xFF, 0x2A, 0x80, 0x55, 0x01, 0xD4, 0x7E, 0xAB,
|
||||
0x84, 0x51, 0xFB, 0x2E, 0x7A, 0xAF, 0x05, 0xD0,
|
||||
0xAD, 0x78, 0xD2, 0x07, 0x53, 0x86, 0x2C, 0xF9
|
||||
};
|
||||
|
||||
uint8_t crc8(const uint8_t * ptr, uint32_t len)
|
||||
{
|
||||
uint8_t crc = 0;
|
||||
for (uint32_t i=0; i<len; i++) {
|
||||
crc = crc8tab[crc ^ *ptr++];
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
// CRC8 implementation with polynom = 0xBA
|
||||
const unsigned char crc8tab_BA[256] = {
|
||||
0x00, 0xBA, 0xCE, 0x74, 0x26, 0x9C, 0xE8, 0x52,
|
||||
0x4C, 0xF6, 0x82, 0x38, 0x6A, 0xD0, 0xA4, 0x1E,
|
||||
0x98, 0x22, 0x56, 0xEC, 0xBE, 0x04, 0x70, 0xCA,
|
||||
0xD4, 0x6E, 0x1A, 0xA0, 0xF2, 0x48, 0x3C, 0x86,
|
||||
0x8A, 0x30, 0x44, 0xFE, 0xAC, 0x16, 0x62, 0xD8,
|
||||
0xC6, 0x7C, 0x08, 0xB2, 0xE0, 0x5A, 0x2E, 0x94,
|
||||
0x12, 0xA8, 0xDC, 0x66, 0x34, 0x8E, 0xFA, 0x40,
|
||||
0x5E, 0xE4, 0x90, 0x2A, 0x78, 0xC2, 0xB6, 0x0C,
|
||||
0xAE, 0x14, 0x60, 0xDA, 0x88, 0x32, 0x46, 0xFC,
|
||||
0xE2, 0x58, 0x2C, 0x96, 0xC4, 0x7E, 0x0A, 0xB0,
|
||||
0x36, 0x8C, 0xF8, 0x42, 0x10, 0xAA, 0xDE, 0x64,
|
||||
0x7A, 0xC0, 0xB4, 0x0E, 0x5C, 0xE6, 0x92, 0x28,
|
||||
0x24, 0x9E, 0xEA, 0x50, 0x02, 0xB8, 0xCC, 0x76,
|
||||
0x68, 0xD2, 0xA6, 0x1C, 0x4E, 0xF4, 0x80, 0x3A,
|
||||
0xBC, 0x06, 0x72, 0xC8, 0x9A, 0x20, 0x54, 0xEE,
|
||||
0xF0, 0x4A, 0x3E, 0x84, 0xD6, 0x6C, 0x18, 0xA2,
|
||||
0xE6, 0x5C, 0x28, 0x92, 0xC0, 0x7A, 0x0E, 0xB4,
|
||||
0xAA, 0x10, 0x64, 0xDE, 0x8C, 0x36, 0x42, 0xF8,
|
||||
0x7E, 0xC4, 0xB0, 0x0A, 0x58, 0xE2, 0x96, 0x2C,
|
||||
0x32, 0x88, 0xFC, 0x46, 0x14, 0xAE, 0xDA, 0x60,
|
||||
0x6C, 0xD6, 0xA2, 0x18, 0x4A, 0xF0, 0x84, 0x3E,
|
||||
0x20, 0x9A, 0xEE, 0x54, 0x06, 0xBC, 0xC8, 0x72,
|
||||
0xF4, 0x4E, 0x3A, 0x80, 0xD2, 0x68, 0x1C, 0xA6,
|
||||
0xB8, 0x02, 0x76, 0xCC, 0x9E, 0x24, 0x50, 0xEA,
|
||||
0x48, 0xF2, 0x86, 0x3C, 0x6E, 0xD4, 0xA0, 0x1A,
|
||||
0x04, 0xBE, 0xCA, 0x70, 0x22, 0x98, 0xEC, 0x56,
|
||||
0xD0, 0x6A, 0x1E, 0xA4, 0xF6, 0x4C, 0x38, 0x82,
|
||||
0x9C, 0x26, 0x52, 0xE8, 0xBA, 0x00, 0x74, 0xCE,
|
||||
0xC2, 0x78, 0x0C, 0xB6, 0xE4, 0x5E, 0x2A, 0x90,
|
||||
0x8E, 0x34, 0x40, 0xFA, 0xA8, 0x12, 0x66, 0xDC,
|
||||
0x5A, 0xE0, 0x94, 0x2E, 0x7C, 0xC6, 0xB2, 0x08,
|
||||
0x16, 0xAC, 0xD8, 0x62, 0x30, 0x8A, 0xFE, 0x44
|
||||
};
|
||||
|
||||
uint8_t crc8_BA(const uint8_t * ptr, uint32_t len)
|
||||
{
|
||||
uint8_t crc = 0;
|
||||
for (uint32_t i=0; i<len; i++) {
|
||||
crc = crc8tab_BA[crc ^ *ptr++];
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __CRC_H__
|
||||
#define __CRC_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
enum {
|
||||
CRC_1021,
|
||||
CRC_1189,
|
||||
};
|
||||
|
||||
uint8_t crc8(const uint8_t * ptr, uint32_t len);
|
||||
uint8_t crc8_BA(const uint8_t * ptr, uint32_t len);
|
||||
uint16_t crc16(uint8_t index, const uint8_t * buf, uint32_t len, uint16_t start = 0);
|
||||
|
||||
// CRC16 implementation according to CCITT standards
|
||||
static const unsigned short crc16tab_1021[256] = {
|
||||
0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7,
|
||||
0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef,
|
||||
0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6,
|
||||
0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de,
|
||||
0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485,
|
||||
0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d,
|
||||
0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4,
|
||||
0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc,
|
||||
0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823,
|
||||
0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b,
|
||||
0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12,
|
||||
0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a,
|
||||
0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41,
|
||||
0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49,
|
||||
0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70,
|
||||
0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78,
|
||||
0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f,
|
||||
0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067,
|
||||
0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e,
|
||||
0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256,
|
||||
0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d,
|
||||
0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405,
|
||||
0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c,
|
||||
0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634,
|
||||
0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab,
|
||||
0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3,
|
||||
0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a,
|
||||
0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92,
|
||||
0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9,
|
||||
0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1,
|
||||
0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8,
|
||||
0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0
|
||||
};
|
||||
|
||||
static const unsigned short crc16tab_1189[256] = {
|
||||
0x0000,0x1189,0x2312,0x329b,0x4624,0x57ad,0x6536,0x74bf,
|
||||
0x8c48,0x9dc1,0xaf5a,0xbed3,0xca6c,0xdbe5,0xe97e,0xf8f7,
|
||||
0x1081,0x0108,0x3393,0x221a,0x56a5,0x472c,0x75b7,0x643e,
|
||||
0x9cc9,0x8d40,0xbfdb,0xae52,0xdaed,0xcb64,0xf9ff,0xe876,
|
||||
0x2102,0x308b,0x0210,0x1399,0x6726,0x76af,0x4434,0x55bd,
|
||||
0xad4a,0xbcc3,0x8e58,0x9fd1,0xeb6e,0xfae7,0xc87c,0xd9f5,
|
||||
0x3183,0x200a,0x1291,0x0318,0x77a7,0x662e,0x54b5,0x453c,
|
||||
0xbdcb,0xac42,0x9ed9,0x8f50,0xfbef,0xea66,0xd8fd,0xc974,
|
||||
0x4204,0x538d,0x6116,0x709f,0x0420,0x15a9,0x2732,0x36bb,
|
||||
0xce4c,0xdfc5,0xed5e,0xfcd7,0x8868,0x99e1,0xab7a,0xbaf3,
|
||||
0x5285,0x430c,0x7197,0x601e,0x14a1,0x0528,0x37b3,0x263a,
|
||||
0xdecd,0xcf44,0xfddf,0xec56,0x98e9,0x8960,0xbbfb,0xaa72,
|
||||
0x6306,0x728f,0x4014,0x519d,0x2522,0x34ab,0x0630,0x17b9,
|
||||
0xef4e,0xfec7,0xcc5c,0xddd5,0xa96a,0xb8e3,0x8a78,0x9bf1,
|
||||
0x7387,0x620e,0x5095,0x411c,0x35a3,0x242a,0x16b1,0x0738,
|
||||
0xffcf,0xee46,0xdcdd,0xcd54,0xb9eb,0xa862,0x9af9,0x8b70,
|
||||
0x8408,0x9581,0xa71a,0xb693,0xc22c,0xd3a5,0xe13e,0xf0b7,
|
||||
0x0840,0x19c9,0x2b52,0x3adb,0x4e64,0x5fed,0x6d76,0x7cff,
|
||||
0x9489,0x8500,0xb79b,0xa612,0xd2ad,0xc324,0xf1bf,0xe036,
|
||||
0x18c1,0x0948,0x3bd3,0x2a5a,0x5ee5,0x4f6c,0x7df7,0x6c7e,
|
||||
0xa50a,0xb483,0x8618,0x9791,0xe32e,0xf2a7,0xc03c,0xd1b5,
|
||||
0x2942,0x38cb,0x0a50,0x1bd9,0x6f66,0x7eef,0x4c74,0x5dfd,
|
||||
0xb58b,0xa402,0x9699,0x8710,0xf3af,0xe226,0xd0bd,0xc134,
|
||||
0x39c3,0x284a,0x1ad1,0x0b58,0x7fe7,0x6e6e,0x5cf5,0x4d7c,
|
||||
0xc60c,0xd785,0xe51e,0xf497,0x8028,0x91a1,0xa33a,0xb2b3,
|
||||
0x4a44,0x5bcd,0x6956,0x78df,0x0c60,0x1de9,0x2f72,0x3efb,
|
||||
0xd68d,0xc704,0xf59f,0xe416,0x90a9,0x8120,0xb3bb,0xa232,
|
||||
0x5ac5,0x4b4c,0x79d7,0x685e,0x1ce1,0x0d68,0x3ff3,0x2e7a,
|
||||
0xe70e,0xf687,0xc41c,0xd595,0xa12a,0xb0a3,0x8238,0x93b1,
|
||||
0x6b46,0x7acf,0x4854,0x59dd,0x2d62,0x3ceb,0x0e70,0x1ff9,
|
||||
0xf78f,0xe606,0xd49d,0xc514,0xb1ab,0xa022,0x92b9,0x8330,
|
||||
0x7bc7,0x6a4e,0x58d5,0x495c,0x3de3,0x2c6a,0x1ef1,0x0f78
|
||||
};
|
||||
|
||||
#endif
|
||||
+10
-10
@@ -105,7 +105,7 @@ static inline void check_struct()
|
||||
CHKSIZE(TimerData, 3);
|
||||
|
||||
CHKSIZE(FlightModeData, 30);
|
||||
CHKSIZE(RadioData, 85);
|
||||
CHKSIZE(RadioData, 86);
|
||||
|
||||
#endif /* board specific ifdefs*/
|
||||
|
||||
@@ -117,33 +117,33 @@ static inline void check_struct()
|
||||
CHKSIZE(TrainerData, 16);
|
||||
|
||||
#if defined(PCBXLITES)
|
||||
CHKSIZE(RadioData, 861);
|
||||
CHKSIZE(RadioData, 862);
|
||||
CHKSIZE(ModelData, 6164);
|
||||
#elif defined(PCBXLITE)
|
||||
CHKSIZE(RadioData, 859);
|
||||
CHKSIZE(RadioData, 860);
|
||||
CHKSIZE(ModelData, 6164);
|
||||
#elif defined(RADIO_TPRO)
|
||||
CHKSIZE(RadioData, 842);
|
||||
CHKSIZE(RadioData, 843);
|
||||
CHKSIZE(ModelData, 6189);
|
||||
#elif defined(PCBX7)
|
||||
CHKSIZE(RadioData, 865);
|
||||
CHKSIZE(RadioData, 866);
|
||||
CHKSIZE(ModelData, 6164);
|
||||
#elif defined(PCBX9E)
|
||||
CHKSIZE(RadioData, 955);
|
||||
CHKSIZE(RadioData, 956);
|
||||
CHKSIZE(ModelData, 6616);
|
||||
#elif defined(PCBX9D) || defined(PCBX9DP)
|
||||
CHKSIZE(RadioData, 897);
|
||||
CHKSIZE(RadioData, 898);
|
||||
CHKSIZE(ModelData, 6608);
|
||||
#elif defined(PCBHORUS)
|
||||
#if defined(PCBX10)
|
||||
CHKSIZE(RadioData, 923);
|
||||
CHKSIZE(RadioData, 924);
|
||||
CHKSIZE(ModelData, 11028);
|
||||
#else
|
||||
CHKSIZE(RadioData, 905);
|
||||
CHKSIZE(RadioData, 906);
|
||||
CHKSIZE(ModelData, 11026);
|
||||
#endif
|
||||
#elif defined(PCBNV14)
|
||||
CHKSIZE(RadioData, 851);
|
||||
CHKSIZE(RadioData, 852);
|
||||
CHKSIZE(ModelData, 10842);
|
||||
#endif
|
||||
|
||||
|
||||
@@ -804,6 +804,8 @@ PACK(struct TrainerData {
|
||||
PACK(struct RadioData {
|
||||
|
||||
// Real attributes
|
||||
NOBACKUP(uint8_t manuallyEdited:1);
|
||||
NOBACKUP(int8_t spare0:7 SKIP);
|
||||
CUST_ATTR(semver,nullptr,w_semver);
|
||||
CUST_ATTR(board,nullptr,w_board);
|
||||
CalibData calib[NUM_STICKS + STORAGE_NUM_POTS + STORAGE_NUM_SLIDERS + STORAGE_NUM_MOUSE_ANALOGS] NO_IDX;
|
||||
|
||||
@@ -44,3 +44,8 @@ RadioMenu::RadioMenu():
|
||||
addTab(new RadioHardwarePage());
|
||||
addTab(new RadioVersionPage());
|
||||
}
|
||||
|
||||
RadioMenu::~RadioMenu()
|
||||
{
|
||||
storageCheck(true);
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
class RadioMenu: public TabsGroup {
|
||||
public:
|
||||
RadioMenu();
|
||||
~RadioMenu();
|
||||
};
|
||||
|
||||
#endif // _MENU_RADIO_H_
|
||||
|
||||
@@ -68,6 +68,10 @@ const char RADIO_SETTINGS_PATH[] = RADIO_PATH PATH_SEPARATOR RADIO_FILENAME;
|
||||
const char MODELSLIST_YAML_PATH[] = MODELS_PATH PATH_SEPARATOR "models.yml";
|
||||
const char FALLBACK_MODELSLIST_YAML_PATH[] = RADIO_PATH PATH_SEPARATOR "models.yml";
|
||||
const char RADIO_SETTINGS_YAML_PATH[] = RADIO_PATH PATH_SEPARATOR "radio.yml";
|
||||
const char RADIO_SETTINGS_TMPFILE_YAML_PATH[] = RADIO_PATH PATH_SEPARATOR "radio_new.yml";
|
||||
const char RADIO_SETTINGS_ERRORFILE_YAML_PATH[] = RADIO_PATH PATH_SEPARATOR "radio_error.yml";
|
||||
|
||||
const char YAMLFILE_CHECKSUM_TAG_NAME[] = "checksum";
|
||||
#endif
|
||||
#define SPLASH_FILE "splash.png"
|
||||
#endif
|
||||
|
||||
@@ -58,6 +58,7 @@ static const char* convertData_220_to_221(
|
||||
memset(data, 0, size);
|
||||
|
||||
uint8_t version;
|
||||
|
||||
const char* error = loadFileBin(path, data, size, &version);
|
||||
if (!error) {
|
||||
if (patchBinary) patchBinary(data);
|
||||
@@ -65,9 +66,11 @@ static const char* convertData_220_to_221(
|
||||
char output_fname[FF_MAX_LFN+1];
|
||||
strncpy(output_fname, path, FF_MAX_LFN);
|
||||
output_fname[FF_MAX_LFN] = '\0';
|
||||
|
||||
|
||||
uint16_t file_checksum = 0;
|
||||
YamlFileChecksum(root_node, data, &file_checksum);
|
||||
patchFilenameToYaml(output_fname);
|
||||
error = writeFileYaml(output_fname, root_node, data);
|
||||
error = writeFileYaml(output_fname, root_node, data, file_checksum);
|
||||
}
|
||||
|
||||
free(data);
|
||||
@@ -157,7 +160,7 @@ const char* convertModelData_220_to_221(uint8_t id)
|
||||
char model_idx[MODELIDX_STRLEN + sizeof(YAML_EXT)];
|
||||
getModelNumberStr(id, model_idx);
|
||||
GET_FILENAME(path, MODELS_PATH, model_idx, YAML_EXT);
|
||||
error = writeFileYaml(path, yaml_conv_220::get_modeldata_nodes(), data);
|
||||
error = writeFileYaml(path, yaml_conv_220::get_modeldata_nodes(), data, 0);
|
||||
} else {
|
||||
// ERROR: size mismatch !!!
|
||||
}
|
||||
@@ -174,9 +177,13 @@ const char* convertRadioData_220_to_221()
|
||||
|
||||
const char* error = nullptr;
|
||||
uint16_t read = eeLoadGeneralSettingsData(data, size);
|
||||
uint16_t file_checksum = 0;
|
||||
|
||||
YamlFileChecksum(yaml_conv_220::get_radiodata_nodes(), data, &file_checksum);
|
||||
|
||||
if (read == size) {
|
||||
error = writeFileYaml(RADIO_SETTINGS_YAML_PATH,
|
||||
yaml_conv_220::get_radiodata_nodes(), data);
|
||||
yaml_conv_220::get_radiodata_nodes(), data, file_checksum);
|
||||
} else {
|
||||
// ERROR: size mismatch !!!
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ const char *loadFileBin(const char *fullpath, uint8_t *data,
|
||||
|
||||
// writes a complete YAML file
|
||||
struct YamlNode;
|
||||
const char* writeFileYaml(const char* path, const YamlNode* root_node, uint8_t* data);
|
||||
const char* writeFileYaml(const char* path, const YamlNode* root_node, uint8_t* data, uint16_t checksum);
|
||||
|
||||
void getModelPath(char * path, const char * filename, const char* pathName = STR_MODELS_PATH);
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
#include "yaml/yaml_tree_walker.h"
|
||||
#include "yaml/yaml_parser.h"
|
||||
#include "yaml/yaml_datastructs.h"
|
||||
#include "yaml/yaml_bits.h"
|
||||
|
||||
|
||||
#if defined(EEPROM_RLC)
|
||||
#include "storage/eeprom_common.h"
|
||||
@@ -38,32 +40,85 @@
|
||||
|
||||
#include "storage/conversions/conversions.h"
|
||||
|
||||
const char * readYamlFile(const char* fullpath, const YamlParserCalls* calls, void* parser_ctx)
|
||||
const char * readYamlFile(const char* fullpath, const YamlParserCalls* calls, void* parser_ctx, ChecksumResult* checksum_result)
|
||||
{
|
||||
FIL file;
|
||||
UINT bytes_read;
|
||||
UINT total_bytes = 0;
|
||||
|
||||
FRESULT result = f_open(&file, fullpath, FA_OPEN_EXISTING | FA_READ);
|
||||
if (result != FR_OK) {
|
||||
return SDCARD_ERROR(result);
|
||||
}
|
||||
|
||||
|
||||
YamlParser yp; //TODO: move to re-usable buffer
|
||||
yp.init(calls, parser_ctx);
|
||||
|
||||
char buffer[32];
|
||||
while (f_read(&file, buffer, sizeof(buffer), &bytes_read) == FR_OK) {
|
||||
uint16_t calculated_checksum = 0xFFFF;
|
||||
uint16_t file_checksum = 0;
|
||||
|
||||
// reached EOF?
|
||||
if (bytes_read == 0)
|
||||
bool first_block = true;
|
||||
char buffer[32];
|
||||
while (f_read(&file, buffer, sizeof(buffer)-1, &bytes_read) == FR_OK) {
|
||||
if (bytes_read == 0) // EOF
|
||||
break;
|
||||
|
||||
total_bytes += bytes_read;
|
||||
|
||||
uint16_t skip = 0;
|
||||
if(first_block) {
|
||||
// Get the 'checksum' value and skip from further YAML processing
|
||||
// The checksum must be first in the first buffer read from file
|
||||
first_block = false;
|
||||
const char *skipValue = "checksum: ";
|
||||
if(strncmp(buffer, skipValue, strlen(skipValue)) == 0) {
|
||||
skip = 10;
|
||||
char* startPos = buffer + strlen(skipValue);
|
||||
char* endPos = startPos;
|
||||
// Advance through the value
|
||||
while((*endPos != '\r') && (*endPos != '\n')) {
|
||||
if (endPos > buffer + bytes_read) {
|
||||
return SDCARD_ERROR( FR_INT_ERR );
|
||||
}
|
||||
endPos++;
|
||||
}
|
||||
// Skip trailing newline
|
||||
while((*endPos == '\r') || (*endPos == '\n')) {
|
||||
*endPos = 0;
|
||||
endPos++;
|
||||
}
|
||||
|
||||
file_checksum = atoi(startPos);
|
||||
skip = endPos - buffer;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate checksum on read block only if we are called with a pointer to write the resulting checksum
|
||||
if (checksum_result != NULL) {
|
||||
calculated_checksum = crc16(0, (const uint8_t *)buffer + skip, bytes_read - skip, calculated_checksum);
|
||||
}
|
||||
|
||||
if (f_eof(&file)) yp.set_eof();
|
||||
if (yp.parse(buffer, bytes_read) != YamlParser::CONTINUE_PARSING)
|
||||
if (yp.parse(buffer + skip, bytes_read - skip) != YamlParser::CONTINUE_PARSING)
|
||||
break;
|
||||
}
|
||||
|
||||
f_close(&file);
|
||||
|
||||
if (checksum_result != NULL) {
|
||||
// Special case to handle "old" files with no checksum field
|
||||
// 25 was arbitrarily chosen as the minimum realistic file size
|
||||
// - The issue is to allow old files to pass, while still detecting garbled files
|
||||
if ( (file_checksum == 0) && (total_bytes > 25) ) {
|
||||
*checksum_result = ChecksumResult::Success;
|
||||
} else {
|
||||
// Normal case - compare read and calculated checksum
|
||||
if (calculated_checksum == file_checksum) {
|
||||
*checksum_result = ChecksumResult::Success;
|
||||
} else {
|
||||
*checksum_result = ChecksumResult::Failed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -92,21 +147,57 @@ void storageCreateModelsList()
|
||||
// SDCARD storage interface
|
||||
//
|
||||
|
||||
static const char * attemptLoad(const char *filename, ChecksumResult* checksum_status)
|
||||
{
|
||||
YamlTreeWalker tree;
|
||||
tree.reset(get_radiodata_nodes(), (uint8_t*)&g_eeGeneral);
|
||||
return readYamlFile(filename, YamlTreeWalker::get_parser_calls(), &tree, checksum_status);
|
||||
}
|
||||
|
||||
const char * loadRadioSettingsYaml()
|
||||
{
|
||||
// YAML reader
|
||||
TRACE("YAML radio settings reader");
|
||||
|
||||
YamlTreeWalker tree;
|
||||
tree.reset(get_radiodata_nodes(), (uint8_t*)&g_eeGeneral);
|
||||
ChecksumResult checksum_status;
|
||||
const char* p = attemptLoad(RADIO_SETTINGS_YAML_PATH, &checksum_status);
|
||||
|
||||
return readYamlFile(RADIO_SETTINGS_YAML_PATH, YamlTreeWalker::get_parser_calls(), &tree);
|
||||
if((p != NULL) || (checksum_status != ChecksumResult::Success) ) {
|
||||
// Read failed or checksum check failed
|
||||
FRESULT result = FR_OK;
|
||||
TRACE("radio settings: Reading failed");
|
||||
if ( (p == NULL) && g_eeGeneral.manuallyEdited) {
|
||||
// Read sussessfull, checksum failed, manuallyEdited set
|
||||
TRACE("File has been manually edited - ignoring checksum mismatch");
|
||||
g_eeGeneral.manuallyEdited = 0;
|
||||
storageDirty(EE_GENERAL); // Trigger a save on sucessfull recovery
|
||||
} else {
|
||||
TRACE("File is corrupted, attempting alternative file");
|
||||
f_unlink(RADIO_SETTINGS_ERRORFILE_YAML_PATH);
|
||||
result = f_rename(RADIO_SETTINGS_YAML_PATH, RADIO_SETTINGS_ERRORFILE_YAML_PATH); // Save corrupted file for later analysis
|
||||
p = attemptLoad(RADIO_SETTINGS_TMPFILE_YAML_PATH, &checksum_status);
|
||||
if (p == NULL && (checksum_status == ChecksumResult::Success)) {
|
||||
f_unlink(RADIO_SETTINGS_YAML_PATH);
|
||||
result = f_rename(RADIO_SETTINGS_TMPFILE_YAML_PATH, RADIO_SETTINGS_YAML_PATH); // Rename previously saved file to active file
|
||||
if (result != FR_OK) {
|
||||
ALERT(STR_STORAGE_WARNING, TR_RADIO_DATA_UNRECOVERABLE, AU_BAD_RADIODATA);
|
||||
return SDCARD_ERROR(result);
|
||||
}
|
||||
}
|
||||
TRACE("Unable to recover radio data");
|
||||
ALERT(STR_STORAGE_WARNING, p == NULL ? TR_RADIO_DATA_RECOVERED : TR_RADIO_DATA_UNRECOVERABLE, AU_BAD_RADIODATA);
|
||||
}
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
const char * loadRadioSettings()
|
||||
{
|
||||
FILINFO fno;
|
||||
if (f_stat(RADIO_SETTINGS_YAML_PATH, &fno) != FR_OK) {
|
||||
|
||||
if ( (f_stat(RADIO_SETTINGS_YAML_PATH, &fno) != FR_OK) && ((f_stat(RADIO_SETTINGS_TMPFILE_YAML_PATH, &fno) != FR_OK)) ) {
|
||||
// If neither the radio configuraion YAML file or the temporary file generated on write exist, this must be a first run with YAML support.
|
||||
// - thus requiring a conversion from binary to YAML.
|
||||
#if STORAGE_CONVERSIONS < 221
|
||||
#if defined(STORAGE_MODELSLIST)
|
||||
uint8_t version;
|
||||
@@ -142,6 +233,47 @@ const char * loadRadioSettings()
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct yaml_checksummer_ctx {
|
||||
FRESULT result;
|
||||
uint16_t checksum;
|
||||
bool checksum_invalid;
|
||||
};
|
||||
|
||||
static bool yaml_checksummer(void* opaque, const char* str, size_t len)
|
||||
{
|
||||
yaml_checksummer_ctx* ctx = (yaml_checksummer_ctx*)opaque;
|
||||
|
||||
ctx->checksum = crc16(0, (const uint8_t *) str, len, ctx->checksum);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool YamlFileChecksum(const YamlNode* root_node, uint8_t* data, uint16_t* checksum)
|
||||
{
|
||||
YamlTreeWalker tree;
|
||||
tree.reset(root_node, data);
|
||||
|
||||
yaml_checksummer_ctx ctx;
|
||||
ctx.result = FR_OK;
|
||||
ctx.checksum = 0xFFFF;
|
||||
ctx.checksum_invalid = false;
|
||||
|
||||
if (!tree.generate(yaml_checksummer, &ctx)) {
|
||||
if (ctx.result != FR_OK) {
|
||||
ctx.checksum_invalid = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(checksum != NULL) {
|
||||
*checksum = ctx.checksum;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
struct yaml_writer_ctx {
|
||||
FIL* file;
|
||||
FRESULT result;
|
||||
@@ -160,7 +292,7 @@ static bool yaml_writer(void* opaque, const char* str, size_t len)
|
||||
return (ctx->result == FR_OK) && (bytes_written == len);
|
||||
}
|
||||
|
||||
const char* writeFileYaml(const char* path, const YamlNode* root_node, uint8_t* data)
|
||||
const char* writeFileYaml(const char* path, const YamlNode* root_node, uint8_t* data, uint16_t checksum)
|
||||
{
|
||||
FIL file;
|
||||
|
||||
@@ -168,14 +300,24 @@ const char* writeFileYaml(const char* path, const YamlNode* root_node, uint8_t*
|
||||
if (result != FR_OK) {
|
||||
return SDCARD_ERROR(result);
|
||||
}
|
||||
|
||||
YamlTreeWalker tree;
|
||||
tree.reset(root_node, data);
|
||||
|
||||
yaml_writer_ctx ctx;
|
||||
ctx.file = &file;
|
||||
ctx.result = FR_OK;
|
||||
|
||||
|
||||
// Try to add CRC
|
||||
if (checksum != 0) {
|
||||
if (!yaml_writer(&ctx, YAMLFILE_CHECKSUM_TAG_NAME, strlen(YAMLFILE_CHECKSUM_TAG_NAME))) return NULL;
|
||||
if (!yaml_writer(&ctx, ": ", 2)) return SDCARD_ERROR(FR_INVALID_PARAMETER);
|
||||
const char* p_out = NULL;
|
||||
p_out = yaml_unsigned2str((int)checksum);
|
||||
if (p_out && !yaml_writer(&ctx, p_out, strlen(p_out))) return SDCARD_ERROR(FR_INVALID_PARAMETER);
|
||||
yaml_writer(&ctx, "\r\n", 2);
|
||||
}
|
||||
|
||||
|
||||
if (!tree.generate(yaml_writer, &ctx)) {
|
||||
if (ctx.result != FR_OK) {
|
||||
f_close(&file);
|
||||
@@ -190,8 +332,25 @@ const char* writeFileYaml(const char* path, const YamlNode* root_node, uint8_t*
|
||||
const char * writeGeneralSettings()
|
||||
{
|
||||
TRACE("YAML radio settings writer");
|
||||
return writeFileYaml(RADIO_SETTINGS_YAML_PATH, get_radiodata_nodes(),
|
||||
(uint8_t*)&g_eeGeneral);
|
||||
uint16_t file_checksum = 0;
|
||||
|
||||
YamlFileChecksum(get_radiodata_nodes(), (uint8_t*)&g_eeGeneral, &file_checksum);
|
||||
g_eeGeneral.manuallyEdited = false;
|
||||
|
||||
const char *p = writeFileYaml(RADIO_SETTINGS_TMPFILE_YAML_PATH, get_radiodata_nodes(),
|
||||
(uint8_t*)&g_eeGeneral, file_checksum);
|
||||
TRACE("generalSettings written with checksum %u", file_checksum);
|
||||
|
||||
if (p != NULL) {
|
||||
return p;
|
||||
}
|
||||
f_unlink(RADIO_SETTINGS_YAML_PATH);
|
||||
|
||||
FRESULT result = f_rename(RADIO_SETTINGS_TMPFILE_YAML_PATH, RADIO_SETTINGS_YAML_PATH);
|
||||
if(result != FR_OK)
|
||||
return SDCARD_ERROR(result);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -213,7 +372,7 @@ const char * readModelYaml(const char * filename, uint8_t * buffer, uint32_t siz
|
||||
TRACE("cannot find YAML data nodes for object size (size=%d)", size);
|
||||
return "YAML size error";
|
||||
}
|
||||
|
||||
|
||||
char path[256];
|
||||
getModelPath(path, filename, pathName);
|
||||
|
||||
@@ -241,7 +400,7 @@ const char * readModelYaml(const char * filename, uint8_t * buffer, uint32_t siz
|
||||
// md->swashR.elevatorWeight = 100;
|
||||
}
|
||||
|
||||
return readYamlFile(path, YamlTreeWalker::get_parser_calls(), &tree);
|
||||
return readYamlFile(path, YamlTreeWalker::get_parser_calls(), &tree, NULL);
|
||||
}
|
||||
|
||||
static const char _wrongExtentionError[] = "wrong file extension";
|
||||
@@ -261,7 +420,7 @@ const char * writeModelYaml(const char* filename)
|
||||
TRACE("YAML model writer");
|
||||
char path[256];
|
||||
getModelPath(path, filename);
|
||||
return writeFileYaml(path, get_modeldata_nodes(), (uint8_t*)&g_model);
|
||||
return writeFileYaml(path, get_modeldata_nodes(), (uint8_t*)&g_model,0 );
|
||||
}
|
||||
|
||||
#if !defined(STORAGE_MODELSLIST)
|
||||
@@ -329,7 +488,7 @@ bool copyModel(uint8_t dst, uint8_t src)
|
||||
char model_idx_dst[MODELIDX_STRLEN];
|
||||
getModelNumberStr(src, model_idx_src);
|
||||
getModelNumberStr(dst, model_idx_dst);
|
||||
|
||||
|
||||
GET_FILENAME(fname_src, MODELS_PATH, model_idx_src, YAML_EXT);
|
||||
GET_FILENAME(fname_dst, MODELS_PATH, model_idx_dst, YAML_EXT);
|
||||
|
||||
@@ -350,7 +509,7 @@ void swapModels(uint8_t id1, uint8_t id2)
|
||||
char model_idx_2[MODELIDX_STRLEN];
|
||||
getModelNumberStr(id1, model_idx_1);
|
||||
getModelNumberStr(id2, model_idx_2);
|
||||
|
||||
|
||||
GET_FILENAME(fname1, MODELS_PATH, model_idx_1, YAML_EXT);
|
||||
GET_FILENAME(fname1_tmp, MODELS_PATH, model_idx_1, ".tmp");
|
||||
GET_FILENAME(fname2, MODELS_PATH, model_idx_2, YAML_EXT);
|
||||
@@ -451,7 +610,7 @@ const char * backupModel(uint8_t idx)
|
||||
char model_idx[MODELIDX_STRLEN + sizeof(YAML_EXT)];
|
||||
getModelNumberStr(idx, model_idx);
|
||||
strcat(model_idx, STR_YAML_EXT);
|
||||
|
||||
|
||||
return sdCopyFile(model_idx, STR_MODELS_PATH, buf, STR_BACKUP_PATH);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,9 +20,14 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
enum class ChecksumResult {Success, Failed, None};
|
||||
|
||||
constexpr uint8_t MODELIDX_STRLEN = sizeof(MODEL_FILENAME_PREFIX "00");
|
||||
|
||||
const char * loadRadioSettingsYaml();
|
||||
const char * writeModelYaml(const char* filename);
|
||||
const char * readModelYaml(const char * filename, uint8_t * buffer, uint32_t size, const char* pathName = STR_MODELS_PATH);
|
||||
bool YamlFileChecksum(const YamlNode* root_node, uint8_t* data, uint16_t* checksum);
|
||||
|
||||
void getModelNumberStr(uint8_t idx, char* model_idx);
|
||||
|
||||
@@ -355,6 +355,8 @@ static const struct YamlNode struct_OpenTxTheme__PersistentData[] = {
|
||||
YAML_END
|
||||
};
|
||||
static const struct YamlNode struct_RadioData[] = {
|
||||
YAML_UNSIGNED( "manuallyEdited", 1 ),
|
||||
YAML_PADDING( 7 ),
|
||||
YAML_CUSTOM("semver",nullptr,w_semver),
|
||||
YAML_CUSTOM("board",nullptr,w_board),
|
||||
YAML_ARRAY("calib", 48, 9, struct_CalibData, NULL),
|
||||
|
||||
@@ -326,6 +326,8 @@ static const struct YamlNode struct_string_24[] = {
|
||||
YAML_END
|
||||
};
|
||||
static const struct YamlNode struct_RadioData[] = {
|
||||
YAML_UNSIGNED( "manuallyEdited", 1 ),
|
||||
YAML_PADDING( 7 ),
|
||||
YAML_CUSTOM("semver",nullptr,w_semver),
|
||||
YAML_CUSTOM("board",nullptr,w_board),
|
||||
YAML_ARRAY("calib", 48, 6, struct_CalibData, NULL),
|
||||
|
||||
@@ -326,6 +326,8 @@ static const struct YamlNode struct_string_24[] = {
|
||||
YAML_END
|
||||
};
|
||||
static const struct YamlNode struct_RadioData[] = {
|
||||
YAML_UNSIGNED( "manuallyEdited", 1 ),
|
||||
YAML_PADDING( 7 ),
|
||||
YAML_CUSTOM("semver",nullptr,w_semver),
|
||||
YAML_CUSTOM("board",nullptr,w_board),
|
||||
YAML_ARRAY("calib", 48, 6, struct_CalibData, NULL),
|
||||
|
||||
@@ -326,6 +326,8 @@ static const struct YamlNode struct_string_24[] = {
|
||||
YAML_END
|
||||
};
|
||||
static const struct YamlNode struct_RadioData[] = {
|
||||
YAML_UNSIGNED( "manuallyEdited", 1 ),
|
||||
YAML_PADDING( 7 ),
|
||||
YAML_CUSTOM("semver",nullptr,w_semver),
|
||||
YAML_CUSTOM("board",nullptr,w_board),
|
||||
YAML_ARRAY("calib", 48, 6, struct_CalibData, NULL),
|
||||
|
||||
@@ -334,6 +334,8 @@ static const struct YamlNode struct_string_24[] = {
|
||||
YAML_END
|
||||
};
|
||||
static const struct YamlNode struct_RadioData[] = {
|
||||
YAML_UNSIGNED( "manuallyEdited", 1 ),
|
||||
YAML_PADDING( 7 ),
|
||||
YAML_CUSTOM("semver",nullptr,w_semver),
|
||||
YAML_CUSTOM("board",nullptr,w_board),
|
||||
YAML_ARRAY("calib", 48, 6, struct_CalibData, NULL),
|
||||
|
||||
@@ -326,6 +326,8 @@ static const struct YamlNode struct_string_24[] = {
|
||||
YAML_END
|
||||
};
|
||||
static const struct YamlNode struct_RadioData[] = {
|
||||
YAML_UNSIGNED( "manuallyEdited", 1 ),
|
||||
YAML_PADDING( 7 ),
|
||||
YAML_CUSTOM("semver",nullptr,w_semver),
|
||||
YAML_CUSTOM("board",nullptr,w_board),
|
||||
YAML_ARRAY("calib", 48, 6, struct_CalibData, NULL),
|
||||
|
||||
@@ -326,6 +326,8 @@ static const struct YamlNode struct_string_24[] = {
|
||||
YAML_END
|
||||
};
|
||||
static const struct YamlNode struct_RadioData[] = {
|
||||
YAML_UNSIGNED( "manuallyEdited", 1 ),
|
||||
YAML_PADDING( 7 ),
|
||||
YAML_CUSTOM("semver",nullptr,w_semver),
|
||||
YAML_CUSTOM("board",nullptr,w_board),
|
||||
YAML_ARRAY("calib", 48, 6, struct_CalibData, NULL),
|
||||
|
||||
@@ -380,6 +380,8 @@ static const struct YamlNode struct_OpenTxTheme__PersistentData[] = {
|
||||
YAML_END
|
||||
};
|
||||
static const struct YamlNode struct_RadioData[] = {
|
||||
YAML_UNSIGNED( "manuallyEdited", 1 ),
|
||||
YAML_PADDING( 7 ),
|
||||
YAML_CUSTOM("semver",nullptr,w_semver),
|
||||
YAML_CUSTOM("board",nullptr,w_board),
|
||||
YAML_ARRAY("calib", 48, 17, struct_CalibData, NULL),
|
||||
|
||||
@@ -378,6 +378,8 @@ static const struct YamlNode struct_OpenTxTheme__PersistentData[] = {
|
||||
YAML_END
|
||||
};
|
||||
static const struct YamlNode struct_RadioData[] = {
|
||||
YAML_UNSIGNED( "manuallyEdited", 1 ),
|
||||
YAML_PADDING( 7 ),
|
||||
YAML_CUSTOM("semver",nullptr,w_semver),
|
||||
YAML_CUSTOM("board",nullptr,w_board),
|
||||
YAML_ARRAY("calib", 48, 15, struct_CalibData, NULL),
|
||||
|
||||
@@ -326,6 +326,8 @@ static const struct YamlNode struct_string_24[] = {
|
||||
YAML_END
|
||||
};
|
||||
static const struct YamlNode struct_RadioData[] = {
|
||||
YAML_UNSIGNED( "manuallyEdited", 1 ),
|
||||
YAML_PADDING( 7 ),
|
||||
YAML_CUSTOM("semver",nullptr,w_semver),
|
||||
YAML_CUSTOM("board",nullptr,w_board),
|
||||
YAML_ARRAY("calib", 48, 6, struct_CalibData, NULL),
|
||||
|
||||
@@ -333,6 +333,8 @@ static const struct YamlNode struct_string_24[] = {
|
||||
YAML_END
|
||||
};
|
||||
static const struct YamlNode struct_RadioData[] = {
|
||||
YAML_UNSIGNED( "manuallyEdited", 1 ),
|
||||
YAML_PADDING( 7 ),
|
||||
YAML_CUSTOM("semver",nullptr,w_semver),
|
||||
YAML_CUSTOM("board",nullptr,w_board),
|
||||
YAML_ARRAY("calib", 48, 9, struct_CalibData, NULL),
|
||||
|
||||
@@ -372,6 +372,8 @@ static const struct YamlNode struct_string_24[] = {
|
||||
YAML_END
|
||||
};
|
||||
static const struct YamlNode struct_RadioData[] = {
|
||||
YAML_UNSIGNED( "manuallyEdited", 1 ),
|
||||
YAML_PADDING( 7 ),
|
||||
YAML_CUSTOM("semver",nullptr,w_semver),
|
||||
YAML_CUSTOM("board",nullptr,w_board),
|
||||
YAML_ARRAY("calib", 48, 12, struct_CalibData, NULL),
|
||||
|
||||
@@ -313,6 +313,8 @@ static const struct YamlNode struct_string_24[] = {
|
||||
YAML_END
|
||||
};
|
||||
static const struct YamlNode struct_RadioData[] = {
|
||||
YAML_UNSIGNED( "manuallyEdited", 1 ),
|
||||
YAML_PADDING( 7 ),
|
||||
YAML_CUSTOM("semver",nullptr,w_semver),
|
||||
YAML_CUSTOM("board",nullptr,w_board),
|
||||
YAML_ARRAY("calib", 48, 5, struct_CalibData, NULL),
|
||||
|
||||
@@ -321,6 +321,8 @@ static const struct YamlNode struct_string_24[] = {
|
||||
YAML_END
|
||||
};
|
||||
static const struct YamlNode struct_RadioData[] = {
|
||||
YAML_UNSIGNED( "manuallyEdited", 1 ),
|
||||
YAML_PADDING( 7 ),
|
||||
YAML_CUSTOM("semver",nullptr,w_semver),
|
||||
YAML_CUSTOM("board",nullptr,w_board),
|
||||
YAML_ARRAY("calib", 48, 5, struct_CalibData, NULL),
|
||||
|
||||
@@ -318,6 +318,8 @@ static const struct YamlNode struct_string_24[] = {
|
||||
YAML_END
|
||||
};
|
||||
static const struct YamlNode struct_RadioData[] = {
|
||||
YAML_UNSIGNED( "manuallyEdited", 1 ),
|
||||
YAML_PADDING( 7 ),
|
||||
YAML_CUSTOM("semver",nullptr,w_semver),
|
||||
YAML_CUSTOM("board",nullptr,w_board),
|
||||
YAML_ARRAY("calib", 48, 6, struct_CalibData, NULL),
|
||||
|
||||
@@ -320,6 +320,8 @@ static const struct YamlNode struct_string_24[] = {
|
||||
YAML_END
|
||||
};
|
||||
static const struct YamlNode struct_RadioData[] = {
|
||||
YAML_UNSIGNED( "manuallyEdited", 1 ),
|
||||
YAML_PADDING( 7 ),
|
||||
YAML_CUSTOM("semver",nullptr,w_semver),
|
||||
YAML_CUSTOM("board",nullptr,w_board),
|
||||
YAML_ARRAY("calib", 48, 6, struct_CalibData, NULL),
|
||||
|
||||
@@ -326,6 +326,8 @@ static const struct YamlNode struct_string_24[] = {
|
||||
YAML_END
|
||||
};
|
||||
static const struct YamlNode struct_RadioData[] = {
|
||||
YAML_UNSIGNED( "manuallyEdited", 1 ),
|
||||
YAML_PADDING( 7 ),
|
||||
YAML_CUSTOM("semver",nullptr,w_semver),
|
||||
YAML_CUSTOM("board",nullptr,w_board),
|
||||
YAML_ARRAY("calib", 48, 6, struct_CalibData, NULL),
|
||||
|
||||
@@ -134,7 +134,7 @@ bool redirectToSettingsDirectory(const std::string & path)
|
||||
#endif
|
||||
#endif
|
||||
#if defined(SDCARD_YAML)
|
||||
if (path == MODELSLIST_YAML_PATH || path == RADIO_SETTINGS_YAML_PATH)
|
||||
if (path == MODELSLIST_YAML_PATH || path == RADIO_SETTINGS_YAML_PATH || path == RADIO_SETTINGS_TMPFILE_YAML_PATH || path == RADIO_SETTINGS_ERRORFILE_YAML_PATH)
|
||||
return true;
|
||||
if (startsWith(path, MODELS_PATH) && endsWith(path, YAML_EXT)) return true;
|
||||
#endif
|
||||
|
||||
@@ -859,6 +859,8 @@ const char STR_STORAGE_WARNING[] = TR_STORAGE_WARNING;
|
||||
const char STR_STORAGE_FORMAT[] = TR_STORAGE_FORMAT;
|
||||
const char STR_SDCARD_CONVERSION_REQUIRE[] = TR_SDCARD_CONVERSION_REQUIRE;
|
||||
const char STR_CONVERTING[] = TR_CONVERTING;
|
||||
const char STR_RADIO_DATA_UNRECOVERABLE[] = TR_RADIO_DATA_UNRECOVERABLE;
|
||||
const char STR_RADIO_DATA_RECOVERED[] = TR_RADIO_DATA_RECOVERED;
|
||||
#endif
|
||||
const char STR_EEPROMOVERFLOW[] = TR_EEPROMOVERFLOW;
|
||||
const char STR_EEPROM_CONVERTING[] = TR_EEPROM_CONVERTING;
|
||||
|
||||
@@ -403,6 +403,8 @@ extern const char STR_THROTTLE_NOT_IDLE[];
|
||||
extern const char STR_ALARMSDISABLED[];
|
||||
extern const char STR_PRESSANYKEY[];
|
||||
extern const char STR_BAD_RADIO_DATA[];
|
||||
extern const char STR_RADIO_DATA_UNRECOVERABLE[];
|
||||
extern const char STR_RADIO_DATA_RECOVERED[];
|
||||
extern const char STR_STORAGE_FORMAT[];
|
||||
extern const char STR_EEPROMOVERFLOW[];
|
||||
extern const char STR_TRIMS2OFFSETS[];
|
||||
|
||||
@@ -517,6 +517,8 @@
|
||||
#define TR_PRESSANYKEY TR("\010按任意键", "按任意键")
|
||||
#define TR_BADEEPROMDATA "存储数据错误"
|
||||
#define TR_BAD_RADIO_DATA "系统数据错误"
|
||||
#define TR_RADIO_DATA_RECOVERED TR3("Using backup radio data","Using backup radio settings","Radio settings recovered from backup")
|
||||
#define TR_RADIO_DATA_UNRECOVERABLE TR3("Radio settings invalid","Radio settings not valid", "Unable to read valid radio settings")
|
||||
#define TR_EEPROMFORMATTING "格式化存储"
|
||||
#define TR_STORAGE_FORMAT "存储准备"
|
||||
#define TR_EEPROMOVERFLOW "存储超限"
|
||||
|
||||
@@ -538,6 +538,8 @@
|
||||
#define TR_PRESSANYKEY TR("\006Stiskni klávesu", "Stiskni klávesu")
|
||||
#define TR_BADEEPROMDATA TR("\006Chyba dat EEPROM", "Chyba dat EEPROM")
|
||||
#define TR_BAD_RADIO_DATA "Chybná data rádia"
|
||||
#define TR_RADIO_DATA_RECOVERED TR3("Using backup radio data","Using backup radio settings","Radio settings recovered from backup")
|
||||
#define TR_RADIO_DATA_UNRECOVERABLE TR3("Radio settings invalid","Radio settings not valid", "Unable to read valid radio settings")
|
||||
#define TR_EEPROMFORMATTING TR("\004Formatování EEPROM", "Formatování EEPROM")
|
||||
#define TR_STORAGE_FORMAT "Storage Preparation"
|
||||
#define TR_EEPROMOVERFLOW "Přetekla EEPROM"
|
||||
|
||||
@@ -518,6 +518,8 @@
|
||||
#define TR_PRESSANYKEY TR("\010Tryk en tast", "Tryk en tast")
|
||||
#define TR_BADEEPROMDATA "Dårlig EEprom data"
|
||||
#define TR_BAD_RADIO_DATA "Dårlig radio data"
|
||||
#define TR_RADIO_DATA_RECOVERED TR3("Using backup radio data","Using backup radio settings","Radio settings recovered from backup")
|
||||
#define TR_RADIO_DATA_UNRECOVERABLE TR3("Radio settings invalid","Radio settings not valid", "Unable to read valid radio settings")
|
||||
#define TR_EEPROMFORMATTING "Formaterer EEPROM"
|
||||
#define TR_STORAGE_FORMAT "Lager klargøres"
|
||||
#define TR_EEPROMOVERFLOW "EEPROM overflow"
|
||||
|
||||
@@ -523,6 +523,8 @@
|
||||
#define TR_PRESSANYKEY TR("Taste drücken",CENTER"Taste drücken")
|
||||
#define TR_BADEEPROMDATA "EEPROM ungültig"
|
||||
#define TR_BAD_RADIO_DATA "Bad Radio Data"
|
||||
#define TR_RADIO_DATA_RECOVERED TR3("Using backup radio data","Using backup radio settings","Radio settings recovered from backup")
|
||||
#define TR_RADIO_DATA_UNRECOVERABLE TR3("Radio settings invalid","Radio settings not valid", "Unable to read valid radio settings")
|
||||
#define TR_EEPROMFORMATTING "EEPROM Initialisieren"
|
||||
#define TR_STORAGE_FORMAT "Speicher Vorbereiten"
|
||||
#define TR_EEPROMOVERFLOW "EEPROM Überlauf"
|
||||
|
||||
@@ -517,6 +517,8 @@
|
||||
#define TR_PRESSANYKEY TR("\010Press any Key", "Press any key")
|
||||
#define TR_BADEEPROMDATA "Bad EEprom data"
|
||||
#define TR_BAD_RADIO_DATA "Bad radio data"
|
||||
#define TR_RADIO_DATA_RECOVERED TR3("Using backup radio data","Using backup radio settings","Radio settings recovered from backup")
|
||||
#define TR_RADIO_DATA_UNRECOVERABLE TR3("Radio settings invalid","Radio settings not valid", "Unable to read valid radio settings")
|
||||
#define TR_EEPROMFORMATTING "Formatting EEPROM"
|
||||
#define TR_STORAGE_FORMAT "Storage preparation"
|
||||
#define TR_EEPROMOVERFLOW "EEPROM overflow"
|
||||
|
||||
@@ -513,6 +513,8 @@
|
||||
#define TR_PRESSANYKEY TR("\010Pulsa una tecla", "Pulsa una tecla")
|
||||
#define TR_BADEEPROMDATA "Error datos EEPROM"
|
||||
#define TR_BAD_RADIO_DATA "Error datos radio"
|
||||
#define TR_RADIO_DATA_RECOVERED TR3("Using backup radio data","Using backup radio settings","Radio settings recovered from backup")
|
||||
#define TR_RADIO_DATA_UNRECOVERABLE TR3("Radio settings invalid","Radio settings not valid", "Unable to read valid radio settings")
|
||||
#define TR_EEPROMFORMATTING "Formateo EEPROM"
|
||||
#define TR_STORAGE_FORMAT "Preparación alamacenamiento"
|
||||
#define TR_EEPROMOVERFLOW "Desborde EEPROM"
|
||||
|
||||
@@ -528,6 +528,8 @@
|
||||
#define TR_PRESSANYKEY TR("\010Press any Key", "Press any Key")
|
||||
#define TR_BADEEPROMDATA "Bad EEPROM Data"
|
||||
#define TR_BAD_RADIO_DATA "Bad Radio Data"
|
||||
#define TR_RADIO_DATA_RECOVERED TR3("Using backup radio data","Using backup radio settings","Radio settings recovered from backup")
|
||||
#define TR_RADIO_DATA_UNRECOVERABLE TR3("Radio settings invalid","Radio settings not valid", "Unable to read valid radio settings")
|
||||
#define TR_EEPROMFORMATTING "Formatting EEPROM"
|
||||
#define TR_STORAGE_FORMAT "Storage Preparation"
|
||||
#define TR_EEPROMOVERFLOW "EEPROM overflow"
|
||||
|
||||
@@ -542,6 +542,8 @@
|
||||
#define TR_PRESSANYKEY TR("Touche pour continuer", "Touche pour continuer")
|
||||
#define TR_BADEEPROMDATA "EEPROM corrompue"
|
||||
#define TR_BAD_RADIO_DATA "Réglages radio corrompus"
|
||||
#define TR_RADIO_DATA_RECOVERED TR3("Using backup radio data","Using backup radio settings","Radio settings recovered from backup")
|
||||
#define TR_RADIO_DATA_UNRECOVERABLE TR3("Radio settings invalid","Radio settings not valid", "Unable to read valid radio settings")
|
||||
#define TR_EEPROMFORMATTING "Formatage EEPROM"
|
||||
#define TR_STORAGE_FORMAT "Préparation stockage"
|
||||
#define TR_EEPROMOVERFLOW "Dépassement EEPROM"
|
||||
|
||||
@@ -522,6 +522,8 @@
|
||||
#define TR_PRESSANYKEY "\010Premi un tasto"
|
||||
#define TR_BADEEPROMDATA "Dati corrotti!"
|
||||
#define TR_BAD_RADIO_DATA "Dati radio errati"
|
||||
#define TR_RADIO_DATA_RECOVERED TR3("Using backup radio data","Using backup radio settings","Radio settings recovered from backup")
|
||||
#define TR_RADIO_DATA_UNRECOVERABLE TR3("Radio settings invalid","Radio settings not valid", "Unable to read valid radio settings")
|
||||
#define TR_EEPROMFORMATTING "Formatto EEPROM..."
|
||||
#define TR_STORAGE_FORMAT "Preparazione storage"
|
||||
#define TR_EEPROMOVERFLOW "EEPROM Piena"
|
||||
|
||||
@@ -524,6 +524,8 @@
|
||||
#define TR_PRESSANYKEY TR("\010Druk een Toets", "Druk een Toets")
|
||||
#define TR_BADEEPROMDATA "EEPROM Ongeldig"
|
||||
#define TR_BAD_RADIO_DATA "Bad Radio Data"
|
||||
#define TR_RADIO_DATA_RECOVERED TR3("Using backup radio data","Using backup radio settings","Radio settings recovered from backup")
|
||||
#define TR_RADIO_DATA_UNRECOVERABLE TR3("Radio settings invalid","Radio settings not valid", "Unable to read valid radio settings")
|
||||
#define TR_EEPROMFORMATTING "EEPROM Initialiseren"
|
||||
#define TR_STORAGE_FORMAT "Storage Preparation"
|
||||
#define TR_EEPROMOVERFLOW "EEPROM Overflow"
|
||||
|
||||
@@ -518,6 +518,8 @@
|
||||
#define TR_PRESSANYKEY TR("\010Wciśnij jakiś klawisz","Wciśnij jakiś klawisz")
|
||||
#define TR_BADEEPROMDATA "\006Błąd danych EEPROM"
|
||||
#define TR_BAD_RADIO_DATA "Bad Radio Data"
|
||||
#define TR_RADIO_DATA_RECOVERED TR3("Using backup radio data","Using backup radio settings","Radio settings recovered from backup")
|
||||
#define TR_RADIO_DATA_UNRECOVERABLE TR3("Radio settings invalid","Radio settings not valid", "Unable to read valid radio settings")
|
||||
#define TR_EEPROMFORMATTING "\004Formatowanie EEPROM"
|
||||
#define TR_STORAGE_FORMAT "Storage Preparation"
|
||||
#define TR_EEPROMOVERFLOW "Przeładowany EEPROM"
|
||||
|
||||
@@ -515,6 +515,8 @@
|
||||
#define TR_PRESSANYKEY "\010Pressione Tecla"
|
||||
#define TR_BADEEPROMDATA "EEPRON INVALIDA"
|
||||
#define TR_BAD_RADIO_DATA "Bad Radio Data"
|
||||
#define TR_RADIO_DATA_RECOVERED TR3("Using backup radio data","Using backup radio settings","Radio settings recovered from backup")
|
||||
#define TR_RADIO_DATA_UNRECOVERABLE TR3("Radio settings invalid","Radio settings not valid", "Unable to read valid radio settings")
|
||||
#define TR_EEPROMFORMATTING "Formatando EEPROM"
|
||||
#define TR_STORAGE_FORMAT "Storage Preparation"
|
||||
#define TR_EEPROMOVERFLOW "EEPROM CHEIA"
|
||||
|
||||
@@ -549,6 +549,8 @@
|
||||
#define TR_PRESSANYKEY TR("\010Tryck på en knapp", "Tryck på valfri knapp")
|
||||
#define TR_BADEEPROMDATA "Minnet kan inte tolkas"
|
||||
#define TR_BAD_RADIO_DATA "Data från radion kan inte tolkas"
|
||||
#define TR_RADIO_DATA_RECOVERED TR3("Using backup radio data","Using backup radio settings","Radio settings recovered from backup")
|
||||
#define TR_RADIO_DATA_UNRECOVERABLE TR3("Radio settings invalid","Radio settings not valid", "Unable to read valid radio settings")
|
||||
#define TR_EEPROMFORMATTING "Minnet formateras"
|
||||
#define TR_STORAGE_FORMAT "SD-Lagring förbereds"
|
||||
#define TR_EEPROMOVERFLOW "Minnesfel"
|
||||
|
||||
@@ -517,6 +517,8 @@
|
||||
#define TR_PRESSANYKEY TR("\010按任意鍵", "按任意鍵")
|
||||
#define TR_BADEEPROMDATA "存儲數據錯誤"
|
||||
#define TR_BAD_RADIO_DATA "系統數據錯誤"
|
||||
#define TR_RADIO_DATA_RECOVERED TR3("Using backup radio data","Using backup radio settings","Radio settings recovered from backup")
|
||||
#define TR_RADIO_DATA_UNRECOVERABLE TR3("Radio settings invalid","Radio settings not valid", "Unable to read valid radio settings")
|
||||
#define TR_EEPROMFORMATTING "格式化存儲"
|
||||
#define TR_STORAGE_FORMAT "存儲準備"
|
||||
#define TR_EEPROMOVERFLOW "存儲超限"
|
||||
|
||||
Reference in New Issue
Block a user