#pragma once #include #include #include struct SdCardFontFileInfo { std::string path; // v4 on-disk naming: "/.crosspoint/fonts//_.cpfont" // e.g. "/.crosspoint/fonts/NotoSansCJK/NotoSansCJK_14.cpfont" uint8_t pointSize; // parsed from filename: 14 uint8_t style; // always 0 in v4 (all 4 styles bundled in one file); // kept for potential future formats }; struct SdCardFontFamilyInfo { std::string name; // directory name, e.g. "NotoSansCJK" std::vector files; const SdCardFontFileInfo* findFile(uint8_t size, uint8_t style = 0) const; bool hasSize(uint8_t size) const; std::vector availableSizes() const; // Pick the file whose pointSize is closest to targetPtSize. On ties (equal // distance) prefers the smaller pointSize so behaviour is deterministic // across SD card layouts. Returns nullptr when files is empty. const SdCardFontFileInfo* pickClosestSize(uint8_t targetPtSize) const; }; class SdCardFontRegistry { public: static constexpr int MAX_SD_FAMILIES = 128; static constexpr const char* FONTS_DIR = "/.crosspoint/fonts"; // Scan SD card, populate families_. Returns true if any families found. bool discover(); const std::vector& getFamilies() const { return families_; } const SdCardFontFamilyInfo* findFamily(const std::string& name) const; int getFamilyIndex(const std::string& name) const; int getFamilyCount() const { return static_cast(families_.size()); } private: std::vector families_; // sorted alphabetically static bool parseFilename(const char* filename, uint8_t& size, uint8_t& style); void scanDirectory(const char* dirPath, SdCardFontFamilyInfo& family); };