From 5b0a7743d16df423a4aaec877f046011fe9e8bfd Mon Sep 17 00:00:00 2001 From: jpirnay Date: Sun, 26 Apr 2026 23:21:19 +0200 Subject: [PATCH] Cache reader progress --- src/activities/home/HomeActivity.h | 6 +- src/activities/reader/EpubReaderActivity.cpp | 13 +- src/activities/reader/MdReaderActivity.cpp | 37 ++++-- src/activities/reader/TxtReaderActivity.cpp | 8 +- src/activities/reader/XtcReaderActivity.cpp | 7 +- src/components/themes/lyra/LyraTheme.cpp | 123 ++++--------------- 6 files changed, 72 insertions(+), 122 deletions(-) diff --git a/src/activities/home/HomeActivity.h b/src/activities/home/HomeActivity.h index 97274215..c41d1506 100644 --- a/src/activities/home/HomeActivity.h +++ b/src/activities/home/HomeActivity.h @@ -51,9 +51,9 @@ class HomeActivity final : public Activity { void dispatchMenuAction(MenuAction action); void rebuildMenuEntries(); - bool storeCoverBuffer(); // Store frame buffer for cover image - bool restoreCoverBuffer(); // Restore frame buffer from stored cover - void freeCoverBuffer(); // Free the stored cover buffer + bool storeCoverBuffer(); + bool restoreCoverBuffer(); + void freeCoverBuffer(); void loadRecentBooks(int maxBooks); void loadRecentCovers(int coverHeight); diff --git a/src/activities/reader/EpubReaderActivity.cpp b/src/activities/reader/EpubReaderActivity.cpp index 96019f1b..954fd1c1 100644 --- a/src/activities/reader/EpubReaderActivity.cpp +++ b/src/activities/reader/EpubReaderActivity.cpp @@ -1385,16 +1385,23 @@ void EpubReaderActivity::silentIndexNextChapterIfNeeded(const uint16_t viewportW void EpubReaderActivity::saveProgress(int spineIndex, int currentPage, int pageCount) { FsFile f; if (Storage.openFileForWrite("ERS", epub->getCachePath() + "/progress.bin", f)) { - uint8_t data[6]; + uint8_t overallPercent = 0; + if (epub->getBookSize() > 0 && pageCount > 0) { + const float chapterProgress = static_cast(currentPage) / static_cast(pageCount); + overallPercent = static_cast( + clampPercent(static_cast(epub->calculateProgress(spineIndex, chapterProgress) * 100.0f + 0.5f))); + } + uint8_t data[7]; data[0] = spineIndex & 0xFF; data[1] = (spineIndex >> 8) & 0xFF; data[2] = currentPage & 0xFF; data[3] = (currentPage >> 8) & 0xFF; data[4] = pageCount & 0xFF; data[5] = (pageCount >> 8) & 0xFF; - f.write(data, 6); + data[6] = overallPercent; + f.write(data, 7); f.close(); - LOG_DBG("ERS", "Progress saved: Chapter %d, Page %d", spineIndex, currentPage); + LOG_DBG("ERS", "Progress saved: Chapter %d, Page %d (%d%%)", spineIndex, currentPage, overallPercent); } else { LOG_ERR("ERS", "Could not save progress!"); } diff --git a/src/activities/reader/MdReaderActivity.cpp b/src/activities/reader/MdReaderActivity.cpp index 5310a938..e38ed44e 100644 --- a/src/activities/reader/MdReaderActivity.cpp +++ b/src/activities/reader/MdReaderActivity.cpp @@ -747,29 +747,40 @@ void MdReaderActivity::renderStatusBar() const { void MdReaderActivity::saveProgress() const { FsFile f; if (Storage.openFileForWrite("MDR", txt->getCachePath() + "/progress.bin", f)) { - uint32_t page = static_cast(currentPage < 0 ? 0 : currentPage); - uint8_t data[4]; - data[0] = page & 0xFF; - data[1] = (page >> 8) & 0xFF; - data[2] = (page >> 16) & 0xFF; - data[3] = (page >> 24) & 0xFF; - f.write(data, 4); + // 7-byte format matching TxtReaderActivity: page(2 bytes LE) + file offset(4 bytes LE) + overallPercent(1 byte) + const size_t offset = + (currentPage >= 0 && currentPage < static_cast(pageOffsets.size())) ? pageOffsets[currentPage] : 0; + const uint8_t overallPercent = (totalPages > 0) ? static_cast((currentPage + 1) * 100 / totalPages) : 0; + uint8_t data[7]; + data[0] = currentPage & 0xFF; + data[1] = (currentPage >> 8) & 0xFF; + data[2] = offset & 0xFF; + data[3] = (offset >> 8) & 0xFF; + data[4] = (offset >> 16) & 0xFF; + data[5] = (offset >> 24) & 0xFF; + data[6] = overallPercent; + f.write(data, 7); } } void MdReaderActivity::loadProgress() { FsFile f; if (Storage.openFileForRead("MDR", txt->getCachePath() + "/progress.bin", f)) { - uint8_t data[4]; - if (f.read(data, 4) == 4) { - uint32_t loadedPage = static_cast(data[0]) | (static_cast(data[1]) << 8) | - (static_cast(data[2]) << 16) | (static_cast(data[3]) << 24); + uint8_t data[7]; + const int dataSize = f.read(data, 7); + f.close(); + if (dataSize >= 4) { + // Old 4-byte format: uint32 page — detect by checking if bytes 2-3 are non-zero + // (new format stores page in bytes 0-1 only, bytes 2-3 are offset low bytes). + // Since page counts are small (< 65536), bytes 2-3 of the old uint32 page are always 0. + // We can safely read bytes 0-1 as the page number in both formats. + int loadedPage = data[0] + (data[1] << 8); if (totalPages == 0) { currentPage = 0; - } else if (loadedPage >= static_cast(totalPages)) { + } else if (loadedPage >= totalPages) { currentPage = totalPages - 1; } else { - currentPage = static_cast(loadedPage); + currentPage = loadedPage; } LOG_DBG("MDR", "Loaded progress: page %d/%d", currentPage, totalPages); } diff --git a/src/activities/reader/TxtReaderActivity.cpp b/src/activities/reader/TxtReaderActivity.cpp index 14a88241..2c6afdd1 100644 --- a/src/activities/reader/TxtReaderActivity.cpp +++ b/src/activities/reader/TxtReaderActivity.cpp @@ -408,17 +408,19 @@ void TxtReaderActivity::renderStatusBar() const { void TxtReaderActivity::saveProgress() const { FsFile f; if (Storage.openFileForWrite("TRS", txt->getCachePath() + "/progress.bin", f)) { - // 6-byte format: page(2 bytes LE) + file offset(4 bytes LE) + // 7-byte format: page(2 bytes LE) + file offset(4 bytes LE) + overallPercent(1 byte) // The offset lets drawCurrentPageToBuffer render without requiring index.bin. const size_t offset = (currentPage < static_cast(pageOffsets.size())) ? pageOffsets[currentPage] : 0; - uint8_t data[6]; + const uint8_t overallPercent = (totalPages > 0) ? static_cast((currentPage + 1) * 100 / totalPages) : 0; + uint8_t data[7]; data[0] = currentPage & 0xFF; data[1] = (currentPage >> 8) & 0xFF; data[2] = offset & 0xFF; data[3] = (offset >> 8) & 0xFF; data[4] = (offset >> 16) & 0xFF; data[5] = (offset >> 24) & 0xFF; - f.write(data, 6); + data[6] = overallPercent; + f.write(data, 7); f.close(); } } diff --git a/src/activities/reader/XtcReaderActivity.cpp b/src/activities/reader/XtcReaderActivity.cpp index b0cdfcc1..89f4d306 100644 --- a/src/activities/reader/XtcReaderActivity.cpp +++ b/src/activities/reader/XtcReaderActivity.cpp @@ -334,12 +334,15 @@ void XtcReaderActivity::renderPage() { void XtcReaderActivity::saveProgress() const { FsFile f; if (Storage.openFileForWrite("XTR", xtc->getCachePath() + "/progress.bin", f)) { - uint8_t data[4]; + const uint32_t pageCount = xtc->getPageCount(); + const uint8_t overallPercent = (pageCount > 0) ? static_cast((currentPage + 1) * 100 / pageCount) : 0; + uint8_t data[5]; data[0] = currentPage & 0xFF; data[1] = (currentPage >> 8) & 0xFF; data[2] = (currentPage >> 16) & 0xFF; data[3] = (currentPage >> 24) & 0xFF; - f.write(data, 4); + data[4] = overallPercent; + f.write(data, 5); f.close(); } } diff --git a/src/components/themes/lyra/LyraTheme.cpp b/src/components/themes/lyra/LyraTheme.cpp index f65a6c1e..e352b8d1 100644 --- a/src/components/themes/lyra/LyraTheme.cpp +++ b/src/components/themes/lyra/LyraTheme.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include #include @@ -58,30 +57,6 @@ int clampProgressPercent(const int progressPercent) { return progressPercent; } -int readTxtTotalPages(const std::string& cachePath) { - FsFile indexFile; - if (!Storage.openFileForRead("LYR", cachePath + "/index.bin", indexFile)) { - return 0; - } - - uint32_t magic = 0; - uint8_t version = 0; - serialization::readPod(indexFile, magic); - serialization::readPod(indexFile, version); - static constexpr uint32_t INDEX_CACHE_MAGIC = 0x54585449; // "TXTI" - static constexpr uint8_t INDEX_CACHE_VERSION = 2; - if (magic != INDEX_CACHE_MAGIC || version != INDEX_CACHE_VERSION) { - indexFile.close(); - return 0; - } - - indexFile.seek(32); - uint32_t totalPages = 0; - serialization::readPod(indexFile, totalPages); - indexFile.close(); - return static_cast(totalPages); -} - void drawLyraBatteryIcon(const GfxRenderer& renderer, int x, int y, int battWidth, int rectHeight, uint16_t percentage) { BaseTheme::drawBatteryOutline(renderer, x, y, battWidth, rectHeight); @@ -152,92 +127,44 @@ const uint8_t* iconForName(UIIcon icon, int size) { } } // namespace +// Reads the overall progress percent stored as the last byte of progress.bin. +// The cache path is derived from the book path alone (no epub/xtc/txt loading needed). +// Returns -1 if the file is absent or the percent byte is not yet written. int LyraTheme::getRecentBookProgressPercent(const RecentBook& book) { if (book.path.empty()) { return -1; } + std::string cachePath; + int percentByteOffset; // byte index of the percent field in progress.bin + if (FsHelpers::hasEpubExtension(book.path)) { - Epub epub(book.path, "/.crosspoint"); - if (!epub.load(true, true)) { - return -1; - } - - FsFile progressFile; - if (!Storage.openFileForRead("LYR", epub.getCachePath() + "/progress.bin", progressFile)) { - return -1; - } - - uint8_t data[6]; - const int dataSize = progressFile.read(data, 6); - progressFile.close(); - if (dataSize != 4 && dataSize != 6) { - return -1; - } - - const int currentSpineIndex = data[0] + (data[1] << 8); - const int currentPage = data[2] + (data[3] << 8); - const int pageCount = (dataSize == 6) ? (data[4] + (data[5] << 8)) : 0; - if (pageCount <= 0) { - return -1; - } - - const float chapterProgress = static_cast(currentPage) / static_cast(pageCount); - return clampProgressPercent( - static_cast(std::lround(epub.calculateProgress(currentSpineIndex, chapterProgress) * 100.0f))); + cachePath = Epub(book.path, "/.crosspoint").getCachePath(); + percentByteOffset = 6; // epub: [spineIdx(2), page(2), chapterPageCount(2), percent(1)] + } else if (FsHelpers::hasXtcExtension(book.path)) { + cachePath = Xtc(book.path, "/.crosspoint").getCachePath(); + percentByteOffset = 4; // xtc: [page(4), percent(1)] + } else if (FsHelpers::hasTxtExtension(book.path) || FsHelpers::hasMarkdownExtension(book.path)) { + cachePath = Txt(book.path, "/.crosspoint").getCachePath(); + percentByteOffset = 6; // [page(2), offset(4), percent(1)] + } else { + return -1; } - if (FsHelpers::hasXtcExtension(book.path)) { - Xtc xtc(book.path, "/.crosspoint"); - if (!xtc.load()) { - return -1; - } - - FsFile progressFile; - if (!Storage.openFileForRead("LYR", xtc.getCachePath() + "/progress.bin", progressFile)) { - return -1; - } - - uint8_t data[4]; - if (progressFile.read(data, 4) != 4) { - progressFile.close(); - return -1; - } - progressFile.close(); - - const uint32_t currentPage = static_cast(data[0]) | (static_cast(data[1]) << 8) | - (static_cast(data[2]) << 16) | (static_cast(data[3]) << 24); - return clampProgressPercent(static_cast(xtc.calculateProgress(currentPage))); + FsFile progressFile; + if (!Storage.openFileForRead("LYR", cachePath + "/progress.bin", progressFile)) { + return -1; } - if (FsHelpers::hasTxtExtension(book.path) || FsHelpers::hasMarkdownExtension(book.path)) { - Txt txt(book.path, "/.crosspoint"); - if (!txt.load()) { - return -1; - } + uint8_t data[7]; + const int dataSize = progressFile.read(data, 7); + progressFile.close(); - FsFile progressFile; - if (!Storage.openFileForRead("LYR", txt.getCachePath() + "/progress.bin", progressFile)) { - return -1; - } - - uint8_t data[4]; - if (progressFile.read(data, 4) != 4) { - progressFile.close(); - return -1; - } - progressFile.close(); - - const int currentPage = data[0] + (data[1] << 8); - const int totalPages = readTxtTotalPages(txt.getCachePath()); - if (totalPages <= 0) { - return -1; - } - - return clampProgressPercent(static_cast(std::lround((currentPage + 1) * 100.0f / totalPages))); + if (dataSize <= percentByteOffset) { + return -1; // old format without percent byte — not yet read by new firmware } - return -1; + return clampProgressPercent(static_cast(data[percentByteOffset])); } void LyraTheme::drawProgressBadge(const GfxRenderer& renderer, Rect anchorRect, int progressPercent) {