#pragma once #include #include // for oflag_t #include #include #include #include class HalFile; class HalStorage { public: HalStorage(); bool begin(); bool ready() const; std::vector listFiles(const char* path = "/", int maxFiles = 200); // Read the entire file at `path` into a String. Returns empty string on failure. String readFile(const char* path); // Low-memory helpers: // Stream the file contents to a `Print` (e.g. `Serial`, or any `Print`-derived object). // Returns true on success, false on failure. bool readFileToStream(const char* path, Print& out, size_t chunkSize = 256); // Read up to `bufferSize-1` bytes into `buffer`, null-terminating it. Returns bytes read. size_t readFileToBuffer(const char* path, char* buffer, size_t bufferSize, size_t maxBytes = 0); // Write a string to `path` on the SD card. Overwrites existing file. // Returns true on success. bool writeFile(const char* path, const String& content); // Ensure a directory exists, creating it if necessary. Returns true on success. bool ensureDirectoryExists(const char* path); HalFile open(const char* path, const oflag_t oflag = O_RDONLY); bool mkdir(const char* path, const bool pFlag = true); bool exists(const char* path); bool remove(const char* path); bool rename(const char* oldPath, const char* newPath); bool rmdir(const char* path); bool openFileForRead(const char* moduleName, const char* path, HalFile& file); bool openFileForRead(const char* moduleName, const std::string& path, HalFile& file); bool openFileForRead(const char* moduleName, const String& path, HalFile& file); bool openFileForWrite(const char* moduleName, const char* path, HalFile& file); bool openFileForWrite(const char* moduleName, const std::string& path, HalFile& file); bool openFileForWrite(const char* moduleName, const String& path, HalFile& file); bool removeDir(const char* path); static HalStorage& getInstance() { return instance; } class StorageLock; // private class, used internally private: static HalStorage instance; bool initialized = false; SemaphoreHandle_t storageMutex = nullptr; }; #define Storage HalStorage::getInstance() class HalFile : public Print { friend class HalStorage; class Impl; std::unique_ptr impl; explicit HalFile(std::unique_ptr impl); public: HalFile(); ~HalFile(); HalFile(HalFile&&); HalFile& operator=(HalFile&&); HalFile(const HalFile&) = delete; HalFile& operator=(const HalFile&) = delete; void flush(); size_t getName(char* name, size_t len); size_t size(); size_t fileSize(); // Get modification date/time (FAT format: packed 16-bit date and time). Returns false if unavailable. bool getModifyDateTime(uint16_t* pdate, uint16_t* ptime); bool seek(size_t pos); bool seekCur(int64_t offset); bool seekSet(size_t offset); int available() const; size_t position() const; int read(void* buf, size_t count); int read(); // read a single byte size_t write(const void* buf, size_t count); size_t write(uint8_t b) override; bool rename(const char* newPath); bool isDirectory() const; void rewindDirectory(); bool close(); HalFile openNextFile(); bool isOpen() const; operator bool() const; }; // Only do renaming FsFile to HalFile if this header is included by downstream code // The renaming is to allow using the thread-safe HalFile instead of the raw FsFile, without needing to change the // downstream code #ifndef HAL_STORAGE_IMPL using FsFile = HalFile; #endif // Downstream code must use Storage instead of SdMan #ifdef SdMan #undef SdMan #endif