#include "RecentBooksStore.h" #include #include #include #include namespace { constexpr uint8_t RECENT_BOOKS_FILE_VERSION = 2; constexpr char RECENT_BOOKS_FILE[] = "/.crosspoint/recent.bin"; constexpr int MAX_RECENT_BOOKS = 10; } // namespace RecentBooksStore RecentBooksStore::instance; void RecentBooksStore::addBook(const std::string& path, const std::string& title, const std::string& author) { // Remove existing entry if present auto it = std::find_if(recentBooks.begin(), recentBooks.end(), [&](const RecentBook& book) { return book.path == path; }); if (it != recentBooks.end()) { recentBooks.erase(it); } // Add to front recentBooks.insert(recentBooks.begin(), {path, title, author}); // Trim to max size if (recentBooks.size() > MAX_RECENT_BOOKS) { recentBooks.resize(MAX_RECENT_BOOKS); } saveToFile(); } bool RecentBooksStore::saveToFile() const { // Make sure the directory exists SdMan.mkdir("/.crosspoint"); FsFile outputFile; if (!SdMan.openFileForWrite("RBS", RECENT_BOOKS_FILE, outputFile)) { return false; } serialization::writePod(outputFile, RECENT_BOOKS_FILE_VERSION); const uint8_t count = static_cast(recentBooks.size()); serialization::writePod(outputFile, count); for (const auto& book : recentBooks) { serialization::writeString(outputFile, book.path); serialization::writeString(outputFile, book.title); serialization::writeString(outputFile, book.author); } outputFile.close(); Serial.printf("[%lu] [RBS] Recent books saved to file (%d entries)\n", millis(), count); return true; } bool RecentBooksStore::loadFromFile() { FsFile inputFile; if (!SdMan.openFileForRead("RBS", RECENT_BOOKS_FILE, inputFile)) { return false; } uint8_t version; serialization::readPod(inputFile, version); if (version != RECENT_BOOKS_FILE_VERSION) { if (version == 1) { // Old version, just read paths uint8_t count; serialization::readPod(inputFile, count); recentBooks.clear(); recentBooks.reserve(count); for (uint8_t i = 0; i < count; i++) { std::string path; serialization::readString(inputFile, path); // Title and author will be empty, they will be filled when the book is // opened again recentBooks.push_back({path, "", ""}); } } else { Serial.printf("[%lu] [RBS] Deserialization failed: Unknown version %u\n", millis(), version); inputFile.close(); return false; } } else { uint8_t count; serialization::readPod(inputFile, count); recentBooks.clear(); recentBooks.reserve(count); for (uint8_t i = 0; i < count; i++) { std::string path, title, author; serialization::readString(inputFile, path); serialization::readString(inputFile, title); serialization::readString(inputFile, author); recentBooks.push_back({path, title, author}); } } inputFile.close(); Serial.printf("[%lu] [RBS] Recent books loaded from file (%d entries)\n", millis(), recentBooks.size()); return true; }