#include "display/bitmap_store.h" #include #include #include "display/bitmaps.h" static const char *DIR = "/bitmaps"; struct BuiltinBitmap { const char *name; const uint8_t *data; // PROGMEM, BITMAP_BYTES }; static const BuiltinBitmap BUILTIN[] = { {"Nintendo", nintendo_bitmap_Nintendo_new}, {"Pikachu", pikachu_clock_bitmap}, {"Pikachu yawning", pikachu_yawning_clock_bitmap}, }; static bool mounted = false; static uint32_t revision = 0; static const BuiltinBitmap *builtin(const char *name) { for (const BuiltinBitmap &b : BUILTIN) if (strcasecmp(b.name, name) == 0) return &b; return nullptr; } static String path(const char *name) { return String(DIR) + "/" + name + ".bin"; } bool bitmapStoreBegin() { mounted = LittleFS.begin(true); for (const char *dir : {DIR, "/pages", "/fonts"}) if (mounted && !LittleFS.exists(dir)) LittleFS.mkdir(dir); Serial.printf("Bitmap storage %s\n", mounted ? "ready" : "not available (old flash layout?)"); return mounted; } bool bitmapStorageAvailable() { return mounted; } void bitmapStorageUsage(size_t &used, size_t &total) { used = mounted ? LittleFS.usedBytes() : 0; total = mounted ? LittleFS.totalBytes() : 0; } std::vector storedNames(const char *dir_path, const char *extension) { std::vector names; if (!mounted) return names; File dir = LittleFS.open(dir_path); if (!dir) return names; for (File file = dir.openNextFile(); file; file = dir.openNextFile()) { String name = file.name(); int slash = name.lastIndexOf('/'); if (slash >= 0) name = name.substring(slash + 1); if (!name.endsWith(extension)) continue; name.remove(name.length() - strlen(extension)); if (validBitmapName(name.c_str())) names.push_back(name); } return names; } std::vector listBitmaps() { std::vector list; for (const BuiltinBitmap &b : BUILTIN) list.push_back({b.name, true}); for (const String &name : storedNames(DIR, ".bin")) if (!builtin(name.c_str())) list.push_back({name, false}); return list; } uint32_t bitmapListRevision() { return revision; } bool bitmapExists(const char *name) { if (builtin(name)) return true; return mounted && validBitmapName(name) && LittleFS.exists(path(name)); } bool loadBitmap(const char *name, uint8_t *bitmap) { const BuiltinBitmap *b = builtin(name); if (b) { memcpy_P(bitmap, b->data, BITMAP_BYTES); return true; } // Checked first: open() logs an error for a missing file (images try names by state) if (!mounted || !validBitmapName(name) || !LittleFS.exists(path(name))) return false; File file = LittleFS.open(path(name), "r"); if (!file) return false; bool ok = file.read(bitmap, BITMAP_BYTES) == (size_t)BITMAP_BYTES; file.close(); return ok; } const char *saveBitmap(const char *name, const uint8_t *bitmap) { if (!validBitmapName(name)) return "Invalid name: use 1-24 letters, digits, spaces, - or _"; if (builtin(name)) return "A built-in bitmap has this name"; if (!mounted) return "No bitmap storage (install the new flash layout over USB, see README)"; if (!LittleFS.exists(path(name)) && listBitmaps().size() >= (size_t)MAX_BITMAPS + (sizeof(BUILTIN) / sizeof(BUILTIN[0]))) return "Too many bitmaps, delete some first"; File file = LittleFS.open(path(name), "w"); if (!file) return "Could not write the file"; bool ok = file.write(bitmap, BITMAP_BYTES) == (size_t)BITMAP_BYTES; file.close(); revision++; return ok ? nullptr : "Storage full"; } const char *deleteBitmap(const char *name) { if (builtin(name)) return "Built-in bitmaps can't be deleted"; if (!mounted || !validBitmapName(name) || !LittleFS.exists(path(name))) return "Bitmap not found"; if (!LittleFS.remove(path(name))) return "Could not delete the file"; revision++; return nullptr; }