#pragma once #include #include #include #include "NodePrefs.h" #include "ScopeList.h" class DataStoreHost { public: virtual bool onContactLoaded(const ContactInfo& contact) =0; virtual bool getContactForSave(uint32_t idx, ContactInfo& contact) =0; virtual bool onChannelLoaded(uint8_t channel_idx, const ChannelDetails& ch) =0; virtual bool getChannelForSave(uint8_t channel_idx, ChannelDetails& ch) =0; }; class DataStore { FILESYSTEM* _fs; FILESYSTEM* _fsExtra; mesh::RTCClock* _clock; IdentityStore identity_store; void loadPrefsInt(const char *filename, NodePrefs& prefs, double& node_lat, double& node_lon); #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) void checkAdvBlobFile(); #endif public: DataStore(FILESYSTEM& fs, mesh::RTCClock& clock); DataStore(FILESYSTEM& fs, FILESYSTEM& fsExtra, mesh::RTCClock& clock); void begin(); bool formatFileSystem(); FILESYSTEM* getPrimaryFS() const { return _fs; } FILESYSTEM* getSecondaryFS() const { return _fsExtra; } bool loadMainIdentity(mesh::LocalIdentity &identity); bool saveMainIdentity(const mesh::LocalIdentity &identity); void loadPrefs(NodePrefs& prefs, double& node_lat, double& node_lon); void savePrefs(const NodePrefs& prefs, double node_lat, double node_lon); void loadContacts(DataStoreHost* host); void saveContacts(DataStoreHost* host, bool (*filter)(const ContactInfo& c) = NULL); // Returns true if a channels file (current or legacy) existed and was // loaded -- false only on a genuinely fresh device with neither file, so // the caller knows whether it's safe to seed default channels (see // MyMesh::begin()'s addChannel("Public", ...) -- seeding unconditionally // would resurrect a channel the user had deliberately deleted, since a // deleted slot is simply absent from the file, not written as empty). bool loadChannels(DataStoreHost* host); void saveChannels(DataStoreHost* host); // /scopes1: the shared named-scope list (see ScopeList.h). `prefs` is only // read, for a one-time migration of a pre-existing single // default_scope_name/key into list entry 1 -- the file is authoritative // once it exists. // // Returns true ONLY when that legacy migration just ran, i.e. this boot is // the first on a device that had a Scope configured the old way. The caller // uses that to seed the channels that already exist with the migrated entry // (see MyMesh::begin) -- channels carry their own scope now, so without the // seed an upgrader's channel traffic would silently drop to unscoped even // though their DMs kept the old scope. Returns false when /scopes1 was // already there, and on a genuinely fresh device with nothing to migrate // (list left empty, default_idx 0 == "*"). bool loadScopeList(ScopeList& list, const NodePrefs& prefs); void saveScopeList(const ScopeList& list); void migrateToSecondaryFS(); uint8_t getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]); bool putBlobByKey(const uint8_t key[], int key_len, const uint8_t src_buf[], uint8_t len); bool deleteBlobByKey(const uint8_t key[], int key_len); File openRead(const char* filename); File openRead(FILESYSTEM* fs, const char* filename); File openWrite(const char* filename); // Atomically replace final_path with a fully-written temp file (see // openWrite()). Use for small custom records that want the same crash-safety // as contacts/channels: write everything to a .tmp, then commit. Returns // false if the swap fails (the previous good file is left untouched). bool commitFile(const char* tmp_path, const char* final_path); bool removeFile(const char* filename); bool removeFile(FILESYSTEM* fs, const char* filename); uint32_t getStorageUsedKb() const; uint32_t getStorageTotalKb() const; void saveRTCTime(); void restoreRTCTime(); private: FILESYSTEM* _getContactsChannelsFS() const { if (_fsExtra) return _fsExtra; return _fs;}; };