WiP storage format conversions

This commit is contained in:
Raphael Coeffic
2021-11-10 11:07:01 +01:00
parent 0503e6fb8e
commit 8d04e373b6
12 changed files with 412 additions and 97 deletions
@@ -6,6 +6,7 @@ if(STORAGE_CONVERSIONS)
${SRC}
storage/conversions/conversions.cpp
storage/conversions/conversions_219_220.cpp
storage/conversions/conversions_220_221.cpp
)
include(storage/conversions/yaml/CMakeLists.txt)
+56 -27
View File
@@ -54,9 +54,9 @@ static void drawProgressScreen(const char* filename, int progress, int total)
#endif
}
void convertRadioData(int version)
void convertBinRadioData(const char * path, int version)
{
TRACE("convertRadioData(%d)", version);
TRACE("convertRadioData(%s,%d)", path, version);
#if defined(COLORLCD)
// the theme has not been loaded before
@@ -71,6 +71,7 @@ void convertRadioData(int version)
AU_NONE);
// Load models list before converting
// TODO: force old .txt format
modelslist.load();
unsigned converted = 0;
@@ -81,13 +82,18 @@ void convertRadioData(int version)
#if STORAGE_CONVERSIONS < 220
if (version == 219) {
convertRadioData_219_to_220(g_eeGeneral);
storageDirty(EE_GENERAL);
// TODO: alloc specific structure for destination
convertRadioData_219_to_220(path);
}
#endif
#if STORAGE_CONVERSIONS < 220
if (version == 220) {
convertRadioData_220_to_221(path);
// g_eeGeneral.version = EEPROM_VER;
// storageDirty(EE_GENERAL);
// storageCheck(true);
}
#endif
g_eeGeneral.version = EEPROM_VER;
storageDirty(EE_GENERAL);
storageCheck(true);
converted++;
#if defined(SIMU)
@@ -107,17 +113,20 @@ void convertRadioData(int version)
error = readModelBin(filename, (uint8_t *)&g_model, sizeof(g_model), &model_version);
if (!error) {
convertModelData(model_version);
// TODO: error handling
convertBinModelData(filename, model_version);
#if defined(SDCARD_YAML)
//TODO: convert filename & output to YAML directly
// patch model filename
const char* ext = strrchr(filename, '.');
if ((ext != nullptr) && !strncmp(ext, MODELS_EXT, 4)) {
memcpy((void*)ext, (void*)YAML_EXT, sizeof(YAML_EXT) - 1);
}
// in case it's the current model, patch it as well
if (!strncmp(filename, g_eeGeneral.currModelFilename, LEN_MODEL_FILENAME)) {
strncpy(g_eeGeneral.currModelFilename, filename, LEN_MODEL_FILENAME+1);
}
// and write to YAML file
error = writeModelYaml(filename);
#else
char path[256];
@@ -146,34 +155,47 @@ void convertRadioData(int version)
modelslist.clear();
modelslist.load();
}
#endif
void convertModelData(int version)
void convertBinModelData(const char* filename, int version)
{
TRACE("convertModelData(%d)", version);
TRACE("convertModelData(%s,%d)", filename);
#if STORAGE_CONVERSIONS < 220
if (version == 219) {
version = 219;
convertModelData_219_to_220(g_model);
convertModelData_219_to_220(filename);
version = 220;
}
#endif
#if STORAGE_CONVERSIONS < 221
if (version == 220) {
convertModelData_220_to_221(filename);
version = 221;
}
#endif
// TODO: error handling
}
#endif
#if defined(EEPROM) || defined(EEPROM_RLC)
#include "storage/eeprom_common.h"
void eeConvertModel(int id, int version)
{
eeLoadModelData(id);
convertModelData(version);
uint8_t currModel = g_eeGeneral.currModel;
g_eeGeneral.currModel = id;
storageDirty(EE_MODEL);
storageCheck(true);
g_eeGeneral.currModel = currModel;
TRACE("eeConvertModel(%d,%d)", id, version);
#if STORAGE_CONVERSIONS < 220
if (version == 219) {
convertModelData_219_to_220(id);
version = 220;
}
#endif
#if STORAGE_CONVERSIONS < 221
if (version == 220) {
convertModelData_220_to_221(id);
version = 221;
}
#endif
// TODO: error handling
}
bool eeConvert()
@@ -209,12 +231,19 @@ bool eeConvert()
#if STORAGE_CONVERSIONS < 220
if (version == 219) {
version = 220;
convertRadioData_219_to_220(g_eeGeneral);
convertRadioData_219_to_220();
}
#endif
#if STORAGE_CONVERSIONS < 221
if (version == 220) {
version = 221;
convertRadioData_220_to_221();
}
#endif
//TODO: reload from YAML
g_eeGeneral.version = EEPROM_VER;
storageDirty(EE_GENERAL);
storageCheck(true);
// storageDirty(EE_GENERAL);
// storageCheck(true);
#if defined(STORAGE_MODELSLIST)
modelslist.clear();
+15 -4
View File
@@ -19,8 +19,9 @@
* GNU General Public License for more details.
*/
void convertRadioData(int version);
void convertModelData(int version);
// Entry points for converting radio & model data
void convertBinRadioData(const char * path, int version);
void convertBinModelData(const char * path, int version);
bool eeConvert();
void eeConvertModel(int id, int version);
@@ -39,5 +40,15 @@ void eeConvertModel(int id, int version);
// Conversions 219 to 220
void convertModelData_219_to_220(ModelData &model);
void convertRadioData_219_to_220(RadioData &settings);
const char* convertModelData_219_to_220(uint8_t id);
const char* convertModelData_219_to_220(const char* filename);
const char* convertRadioData_219_to_220();
const char* convertRadioData_219_to_220(const char* path);
// Conversions 220 to 221
const char* convertModelData_220_to_221(uint8_t id);
const char* convertModelData_220_to_221(const char* filename);
const char* convertRadioData_220_to_221();
const char* convertRadioData_220_to_221(const char* path);
@@ -21,11 +21,16 @@
#include "opentx.h"
#include "datastructs_219.h"
#include "datastructs_220.h"
#if defined(COLORLCD)
#include "theme.h"
#endif
#if defined(EEPROM) || defined(EEPROM_RLC)
#include <storage/eeprom_common.h>
#endif
//
// Summary of all changes between 219 & 220
//
@@ -99,18 +104,21 @@ extern const LayoutFactory * defaultLayout;
extern OpenTxTheme * defaultTheme;
#endif
void convertModelData_219_to_220(ModelData &model)
void convertModelData_219_to_220(void* data)
{
ModelData* oldModelAllocated = (ModelData*)malloc(sizeof(ModelData));
ModelData& oldModel = *oldModelAllocated;
constexpr unsigned md_size = sizeof(bin_storage_220::ModelData);
auto& model = *reinterpret_cast<bin_storage_220::ModelData*>(data);
memcpy(&oldModel, &model, sizeof(ModelData));
ModelData& newModel = (ModelData&)model;
auto oldModelAllocated = reinterpret_cast<bin_storage_220::ModelData*>(malloc(md_size));
bin_storage_220::ModelData& oldModel = *oldModelAllocated;
memcpy(&oldModel, &model, md_size);
bin_storage_220::ModelData& newModel = (bin_storage_220::ModelData&)model;
convertToStr(model.header.name, LEN_MODEL_NAME_219);
for (uint8_t i=0; i<MAX_TIMERS_219; i++) {
// convert to new timers v220
TimerData& timer = newModel.timers[i];
bin_storage_220::TimerData& timer = newModel.timers[i];
convertToStr(timer.name, LEN_TIMER_NAME_219);
TimerData_v219& timer_219 = *(TimerData_v219*)(&oldModel.timers[i]);
@@ -212,29 +220,88 @@ void convertModelData_219_to_220(ModelData &model)
sizeof(newModel.topbarData));
if (defaultLayout) {
strcpy(newModel.screenData[0].LayoutId, defaultLayout->getId());
defaultLayout->initPersistentData(&newModel.screenData[0].layoutData, true);
strcpy(newModel.screenData[0].LayoutId, "Layout2P1");
// TODO: copy the raw structure for the default layout in version 220
//defaultLayout->initPersistentData(&newModel.screenData[0].layoutData, true);
auto persistentData = &newModel.screenData[0].layoutData;
// top bar
persistentData->options[0].type = bin_storage_220::ZOV_Bool;
persistentData->options[0].value.boolValue = true;
// flight mode
persistentData->options[1].type = bin_storage_220::ZOV_Bool;
persistentData->options[1].value.boolValue = true;
// sliders
persistentData->options[2].type = bin_storage_220::ZOV_Bool;
persistentData->options[2].value.boolValue = true;
// trims
persistentData->options[3].type = bin_storage_220::ZOV_Bool;
persistentData->options[3].value.boolValue = true;
// mirrored
persistentData->options[4].type = bin_storage_220::ZOV_Bool;
persistentData->options[4].value.boolValue = false;
}
#endif
free(oldModelAllocated);
}
void convertRadioData_219_to_220(RadioData & settings)
#if !defined(EEPROM_RLC)
const char* convertModelData_219_to_220(const char* path)
{
constexpr unsigned md_size = sizeof(bin_storage_220::ModelData);
auto md_220 = reinterpret_cast<bin_storage_220::ModelData*>(malloc(md_size));
memset(md_220, 0, md_size);
uint8_t version;
const char* error = loadFileBin(path, (uint8_t *)md_220, md_size, &version);
if (!error) {
convertModelData_219_to_220((void*)md_220);
error = writeFileBin(path, (uint8_t *)md_220, md_size, 220);
}
free(md_220);
return error;
}
#else
const char* convertModelData_219_to_220(uint8_t id)
{
constexpr unsigned md_size = sizeof(bin_storage_220::ModelData);
auto md_220 = reinterpret_cast<uint8_t*>(malloc(md_size));
memset(md_220, 0, md_size);
uint16_t read = eeLoadModelData(id, md_220, md_size);
if (read == md_size) {
convertModelData_219_to_220((void*)md_220);
eeWriteModelData(id, (uint8_t *)md_220, md_size, true);
} else {
// ERROR: size mismatch !!!
}
free(md_220);
return nullptr;
}
#endif
void convertRadioData_219_to_220(void* data)
{
TRACE("Radio conversion from v219 to v220");
auto& settings = *reinterpret_cast<bin_storage_220::RadioData*>(data);
settings.version = 220;
settings.variant = EEPROM_VARIANT;
RadioData* oldSettingsAllocated = (RadioData*)malloc(sizeof(RadioData));
RadioData& oldSettings = *oldSettingsAllocated;
memcpy(&oldSettings, &settings, sizeof(RadioData));
#if defined(COLORLCD)
// Clear CustomScreenData + TopBarPersistentData
// as they cannot be converted (missing option types)
strcpy(g_eeGeneral.themeName, defaultTheme->getName());
defaultTheme->init();
strcpy(settings.themeName, defaultTheme->getName());
defaultTheme->init(); // TODO: just dump the data into the settings
#endif
for (int i = 0; i < NUM_SWITCHES_219; ++i) {
@@ -250,6 +317,41 @@ void convertRadioData_219_to_220(RadioData & settings)
#if defined(PCBHORUS) || defined(PCBNV14)
convertToStr(settings.bluetoothName, LEN_BLUETOOTH_NAME_219);
#endif
free(oldSettingsAllocated);
}
#if !defined(EEPROM_RLC)
const char* convertRadioData_219_to_220(const char* path)
{
constexpr unsigned rd_size = sizeof(bin_storage_220::RadioData);
auto rd_220 = reinterpret_cast<bin_storage_220::RadioData*>(malloc(rd_size));
memset(rd_220, 0, rd_size);
uint8_t version;
const char* error = loadFileBin(path, (uint8_t *)rd_220, rd_size, &version);
if (!error) {
convertRadioData_219_to_220((void*)rd_220);
error = writeFileBin(path, (uint8_t *)rd_220, rd_size, 220);
}
free(rd_220);
return error;
}
#else
const char* convertRadioData_219_to_220()
{
constexpr unsigned rd_size = sizeof(bin_storage_220::RadioData);
auto rd_220 = reinterpret_cast<uint8_t*>(malloc(rd_size));
uint16_t read = eeLoadGeneralSettingsData(rd_220, rd_size);
if (read == rd_size) {
convertRadioData_219_to_220((void*)rd_220);
eeWriteGeneralSettingData((uint8_t *)rd_220, rd_size, true);
} else {
// ERROR: size mismatch !!!
}
free(rd_220);
return nullptr;
}
#endif
@@ -0,0 +1,117 @@
/*
* 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 "opentx.h"
#include "datastructs_220.h"
// see "yaml/yaml_datastructs.h"
namespace yaml_conv_220 {
const YamlNode* get_radiodata_nodes();
const YamlNode* get_modeldata_nodes();
const YamlNode* get_partialmodel_nodes();
};
#if !defined(EEPROM_RLC)
static const char* convertData_220_to_221(const char* path, unsigned size,
const YamlNode* root_node)
{
auto data = reinterpret_cast<uint8_t*>(malloc(size));
memset(data, 0, size);
uint8_t version;
const char* error = loadFileBin(path, data, size, &version);
if (!error) {
// TODO: patch file name into .yml
error = writeFileYaml(path, root_node, data);
}
free(data);
return error;
}
const char* convertModelData_220_to_221(const char* path)
{
constexpr unsigned md_size = sizeof(bin_storage_220::ModelData);
return convertData_220_to_221(path, md_size, yaml_conv_220::get_modeldata_nodes());
}
const char* convertRadioData_220_to_221(const char* path)
{
constexpr unsigned rd_size = sizeof(bin_storage_220::RadioData);
return convertData_220_to_221(path, rd_size, yaml_conv_220::get_radiodata_nodes());
}
#else
#include <storage/eeprom_common.h>
constexpr uint8_t MODELIDX_STRLEN = sizeof(MODEL_FILENAME_PREFIX "00");
static void getModelNumberStr(uint8_t idx, char* model_idx)
{
memcpy(model_idx, MODEL_FILENAME_PREFIX, sizeof(MODEL_FILENAME_PREFIX));
model_idx[sizeof(MODEL_FILENAME_PREFIX)-1] = '0' + idx / 10;
model_idx[sizeof(MODEL_FILENAME_PREFIX)] = '0' + idx % 10;
model_idx[sizeof(MODEL_FILENAME_PREFIX)+1] = '\0';
}
const char* convertModelData_220_to_221(uint8_t id)
{
constexpr unsigned size = sizeof(bin_storage_220::ModelData);
auto data = reinterpret_cast<uint8_t*>(malloc(size));
memset(data, 0, size);
const char* error = nullptr;
uint16_t read = eeLoadModelData(id, data, size);
if (read == size) {
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);
} else {
// ERROR: size mismatch !!!
}
free(data);
return error;
}
const char* convertRadioData_220_to_221()
{
constexpr unsigned size = sizeof(bin_storage_220::RadioData);
auto data = reinterpret_cast<uint8_t*>(malloc(size));
memset(data, 0, size);
const char* error = nullptr;
uint16_t read = eeLoadGeneralSettingsData(data, size);
if (read == size) {
error = writeFileYaml(RADIO_SETTINGS_YAML_PATH,
yaml_conv_220::get_radiodata_nodes(), data);
} else {
// ERROR: size mismatch !!!
}
free(data);
return error;
}
#endif
@@ -0,0 +1,30 @@
/*
* 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.
*/
#pragma once
namespace bin_storage_220 {
#include "yaml/dataconstants_220.h"
#include "yaml/datastructs_220.h"
#include "yaml/myeeprom_220.h"
};
+5
View File
@@ -25,7 +25,12 @@
#define EEPROM_MIN_MODEL_SIZE 256
uint16_t eeLoadModelData(uint8_t id);
uint16_t eeLoadModelData(uint8_t index, uint8_t* data, unsigned size);
void eeWriteModelData(uint8_t index, uint8_t* data, unsigned size, bool immediately);
uint16_t eeLoadGeneralSettingsData();
uint16_t eeLoadGeneralSettingsData(uint8_t* data, unsigned size);
void eeWriteGeneralSettingData(uint8_t* data, unsigned size, bool immediately);
bool eeModelExists(uint8_t id);
void eeLoadModel(uint8_t id);
+35 -6
View File
@@ -746,19 +746,48 @@ void RlcFile::flush()
ENABLE_SYNC_WRITE(false);
}
static uint16_t eeLoadData(uint8_t idx, uint8_t* data, unsigned size)
{
memset(data, 0, size);
theFile.openRlc(idx);
return theFile.readRlc(data, size);
}
static void eeWriteData(uint8_t idx, uint8_t typ, uint8_t* data,
unsigned size, bool immediately)
{
theFile.writeRlc(idx, typ, data, size, immediately);
}
// For conversions ...
void eeWriteGeneralSettingData(uint8_t* data, unsigned size, bool immediately)
{
eeWriteData(FILE_GENERAL, FILE_TYP_GENERAL, data, size, immediately);
}
uint16_t eeLoadGeneralSettingsData(uint8_t* data, unsigned size)
{
return eeLoadData(FILE_GENERAL, data, size);
}
uint16_t eeLoadGeneralSettingsData()
{
memset(&g_eeGeneral, 0, sizeof(g_eeGeneral));
theFile.openRlc(FILE_GENERAL);
return theFile.readRlc((uint8_t*)&g_eeGeneral, sizeof(g_eeGeneral));
return eeLoadGeneralSettingsData((uint8_t*)&g_eeGeneral, sizeof(g_eeGeneral));
}
void eeWriteModelData(uint8_t index, uint8_t* data, unsigned size, bool immediately)
{
eeWriteData(FILE_MODEL(index), FILE_TYP_MODEL, data, size, immediately);
}
uint16_t eeLoadModelData(uint8_t index, uint8_t* data, unsigned size)
{
return eeLoadData(FILE_MODEL(index), data, size);
}
uint16_t eeLoadModelData(uint8_t index)
{
memset(&g_model, 0, sizeof(g_model));
theFile.openRlc(FILE_MODEL(index));
return theFile.readRlc((uint8_t*)&g_model, sizeof(g_model));
return eeLoadModelData(index, (uint8_t*)&g_model, sizeof(g_model));
}
bool eeLoadGeneral(bool allowFixes)
+5 -2
View File
@@ -137,10 +137,13 @@ const char * loadModel(const char * filename, bool alarms)
return error;
}
#if defined(STORAGE_CONVERSIONS)
// Conversions from EEPROM are done in batch when converting the radio file.
// It is not supported on a model by model base when loaded.
#if defined(STORAGE_CONVERSIONS) && !defined(EEPROM_RLC)
if (version < EEPROM_VER) {
convertModelData(version);
convertBinModelData(filename, version);
}
// TODO: reload model afterwards
#endif
postModelLoad(alarms);
+9 -1
View File
@@ -44,7 +44,15 @@ const char* openFileBin(const char* fullpath, FIL* file, uint16_t* size,
uint8_t* version);
const char* writeFileBin(const char* fullpath, const uint8_t* data,
uint16_t size);
uint16_t size, uint8_t version = EEPROM_VER);
// open, read, and close the file
const char *loadFileBin(const char *fullpath, uint8_t *data,
uint16_t maxsize, uint8_t *version);
// writes a complete YAML file
struct YamlNode;
const char* writeFileYaml(const char* path, const YamlNode* root_node, uint8_t* data);
void getModelPath(char * path, const char * filename);
+9 -8
View File
@@ -23,7 +23,8 @@
#include "modelslist.h"
#include "conversions/conversions.h"
const char * writeFileBin(const char * filename, const uint8_t * data, uint16_t size)
const char *writeFileBin(const char *filename, const uint8_t *data,
uint16_t size, uint8_t version)
{
TRACE("writeFileBin(%s)", filename);
@@ -37,7 +38,7 @@ const char * writeFileBin(const char * filename, const uint8_t * data, uint16_t
}
*(uint32_t*)&buf[0] = OTX_FOURCC;
buf[4] = EEPROM_VER;
buf[4] = version;
buf[5] = 'M';
*(uint16_t*)&buf[6] = size;
@@ -108,14 +109,14 @@ const char* openFileBin(const char * fullpath, FIL * file, uint16_t * size, uint
return nullptr;
}
static const char *loadFile(const char *fullpath, uint8_t *data,
uint16_t maxsize, uint8_t *version)
const char *loadFileBin(const char *fullpath, uint8_t *data,
uint16_t maxsize, uint8_t *version)
{
FIL file;
UINT read;
uint16_t size;
TRACE("loadFile(%s)", fullpath);
TRACE("loadFileBin(%s)", fullpath);
const char * err = openFileBin(fullpath, &file, &size, version);
if (err)
@@ -136,14 +137,14 @@ const char * readModelBin(const char * filename, uint8_t * buffer, uint32_t size
{
char path[256];
getModelPath(path, filename);
return loadFile(path, buffer, size, version);
return loadFileBin(path, buffer, size, version);
}
const char * loadRadioSettingsBin(const char * path)
{
uint8_t version;
const char * error = loadFile(path, (uint8_t *)&g_eeGeneral, sizeof(g_eeGeneral), &version);
const char * error = loadFileBin(path, (uint8_t *)&g_eeGeneral, sizeof(g_eeGeneral), &version);
if (error) {
TRACE("loadRadioSettings error=%s", error);
return error;
@@ -151,7 +152,7 @@ const char * loadRadioSettingsBin(const char * path)
#if defined(STORAGE_CONVERSIONS)
if (version < EEPROM_VER) {
convertRadioData(version);
convertBinRadioData(path, version);
}
#endif
+11 -32
View File
@@ -127,20 +127,17 @@ static bool yaml_writer(void* opaque, const char* str, size_t len)
return (ctx->result == FR_OK) && (bytes_written == len);
}
const char * writeGeneralSettings()
const char* writeFileYaml(const char* path, const YamlNode* root_node, uint8_t* data)
{
// YAML reader
TRACE("YAML radio settings writer");
FIL file;
FRESULT result = f_open(&file, RADIO_SETTINGS_YAML_PATH, FA_CREATE_ALWAYS | FA_WRITE);
FRESULT result = f_open(&file, path, FA_CREATE_ALWAYS | FA_WRITE);
if (result != FR_OK) {
return SDCARD_ERROR(result);
}
YamlTreeWalker tree;
tree.reset(get_radiodata_nodes(), (uint8_t*)&g_eeGeneral);
tree.reset(root_node, data);
yaml_writer_ctx ctx;
ctx.file = &file;
@@ -157,6 +154,13 @@ const char * writeGeneralSettings()
return NULL;
}
const char * writeGeneralSettings()
{
TRACE("YAML radio settings writer");
return writeFileYaml(RADIO_SETTINGS_YAML_PATH, get_radiodata_nodes(),
(uint8_t*)&g_eeGeneral);
}
const char * readModelYaml(const char * filename, uint8_t * buffer, uint32_t size, uint8_t * version)
{
@@ -216,35 +220,10 @@ const char* readModel(const char* filename, uint8_t* buffer, uint32_t size,
const char * writeModelYaml(const char* filename)
{
// YAML reader
TRACE("YAML model writer");
char path[256];
getModelPath(path, filename);
FIL file;
FRESULT result = f_open(&file, path, FA_CREATE_ALWAYS | FA_WRITE);
if (result != FR_OK) {
return SDCARD_ERROR(result);
}
YamlTreeWalker tree;
tree.reset(get_modeldata_nodes(), (uint8_t*)&g_model);
yaml_writer_ctx ctx;
ctx.file = &file;
ctx.result = FR_OK;
if (!tree.generate(yaml_writer, &ctx)) {
if (ctx.result != FR_OK) {
f_close(&file);
return SDCARD_ERROR(ctx.result);
}
}
f_close(&file);
return NULL;
return writeFileYaml(path, get_modeldata_nodes(), (uint8_t*)&g_model);
}
#if !defined(STORAGE_MODELSLIST)