#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS /* Re-usable NicheGraphics tool Save settings / data to flash, without use of the Meshtastic Protobufs Avoid bloating everyone's protobuf code for our one-off UI implementations */ #pragma once #include "configuration.h" #include "SafeFile.h" namespace NicheGraphics { template class FlashData { private: static std::string getFilename(const char *label) { std::string filename; filename += "/NicheGraphics"; filename += "/"; filename += label; filename += ".data"; return filename; } static uint32_t getHash(T *data) { uint32_t hash = 0; // Sum all bytes of the image buffer together for (uint32_t i = 0; i < sizeof(T); i++) hash ^= ((uint8_t *)data)[i] + 1; return hash; } public: static bool load(T *data, const char *label) { // Set false if we run into issues bool okay = true; // Get a filename based on the label std::string filename = getFilename(label); #ifdef FSCom // Check that the file *does* actually exist if (!FSCom.exists(filename.c_str())) { LOG_WARN("'%s' not found. Using default values", filename.c_str()); okay = false; return okay; } // Open the file auto f = FSCom.open(filename.c_str(), FILE_O_READ); // If opened, start reading if (f) { LOG_INFO("Loading NicheGraphics data '%s'", filename.c_str()); // Create an object which will received data from flash // We read here first, so we can verify the checksum, without committing to overwriting the *data object // Allows us to retain any defaults that might be set after we declared *data, but before loading settings, // in case the flash values are corrupt T flashData; // Read the actual data f.readBytes((char *)&flashData, sizeof(T)); // Read the hash uint32_t savedHash = 0; f.readBytes((char *)&savedHash, sizeof(savedHash)); // Calculate hash of the loaded data, then compare with the saved hash // If hash looks good, copy the values to the main data object uint32_t calculatedHash = getHash(&flashData); if (savedHash != calculatedHash) { LOG_WARN("'%s' is corrupt (hash mismatch). Using default values", filename.c_str()); okay = false; } else *data = flashData; f.close(); } else { LOG_ERROR("Could not open / read %s", filename.c_str()); okay = false; } #else LOG_ERROR("Filesystem not implemented"); state = LoadFileState::NO_FILESYSTEM; okay = false; #endif return okay; } // Save module's custom data (settings?) to flash. Does use protobufs static void save(T *data, const char *label) { // Get a filename based on the label std::string filename = getFilename(label); #ifdef FSCom FSCom.mkdir("/NicheGraphics"); auto f = SafeFile(filename.c_str(), true); // "true": full atomic. Write new data to temp file, then rename. LOG_INFO("Saving %s", filename.c_str()); // Calculate a hash of the data uint32_t hash = getHash(data); f.write((uint8_t *)data, sizeof(T)); // Write the actual data f.write((uint8_t *)&hash, sizeof(hash)); // Append the hash // f.flush(); bool writeSucceeded = f.close(); if (!writeSucceeded) { LOG_ERROR("Can't write data!"); } #else LOG_ERROR("ERROR: Filesystem not implemented\n"); #endif } }; } // namespace NicheGraphics #endif