From 7e9c95312765853bedfdb0eda07623fb917433fa Mon Sep 17 00:00:00 2001 From: jpirnay Date: Sun, 26 Apr 2026 23:11:18 +0200 Subject: [PATCH 1/5] cppcheck complaint --- src/activities/reader/XtcReaderActivity.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/activities/reader/XtcReaderActivity.cpp b/src/activities/reader/XtcReaderActivity.cpp index d65bab38..b0cdfcc1 100644 --- a/src/activities/reader/XtcReaderActivity.cpp +++ b/src/activities/reader/XtcReaderActivity.cpp @@ -12,6 +12,8 @@ #include #include +#include + #include "CrossPointSettings.h" #include "CrossPointState.h" #include "MappedInputManager.h" @@ -462,12 +464,11 @@ void XtcReaderActivity::onButtonAction(const CrossPointSettings::BUTTON_ACTION a case BA::BTN_NEXT_SECTION: if (xtc->hasChapters()) { const auto& chapters = xtc->getChapters(); - for (const auto& ch : chapters) { - if (ch.startPage > currentPage) { - currentPage = ch.startPage; - requestUpdate(); - break; - } + const auto it = std::find_if(chapters.begin(), chapters.end(), + [this](const auto& ch) { return ch.startPage > currentPage; }); + if (it != chapters.end()) { + currentPage = it->startPage; + requestUpdate(); } } break; From 5b0a7743d16df423a4aaec877f046011fe9e8bfd Mon Sep 17 00:00:00 2001 From: jpirnay Date: Sun, 26 Apr 2026 23:21:19 +0200 Subject: [PATCH 2/5] 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) { From 874373a1678686bb455a7df505669f35b909ff21 Mon Sep 17 00:00:00 2001 From: jpirnay Date: Mon, 27 Apr 2026 10:42:39 +0200 Subject: [PATCH 3/5] Simplify --- src/activities/reader/EpubReaderActivity.cpp | 54 ++++++++++---------- src/activities/reader/MdReaderActivity.cpp | 9 ++-- src/activities/reader/ReaderUtils.h | 25 +++++++++ src/activities/reader/TxtReaderActivity.cpp | 3 +- src/activities/reader/XtcReaderActivity.cpp | 5 +- src/components/themes/lyra/LyraTheme.cpp | 6 +-- 6 files changed, 62 insertions(+), 40 deletions(-) diff --git a/src/activities/reader/EpubReaderActivity.cpp b/src/activities/reader/EpubReaderActivity.cpp index 954fd1c1..26621ec8 100644 --- a/src/activities/reader/EpubReaderActivity.cpp +++ b/src/activities/reader/EpubReaderActivity.cpp @@ -51,22 +51,37 @@ void logReaderMemSnapshot(const char* stage) { inline void logReaderMemSnapshot(const char*) {} #endif +// Computes the [0..100] EPUB progress percent. Returns 0 when pageCount is unknown (sync/bookmark +// pre-render writes), in which case the next saveProgress() will overwrite progress.bin with the +// real value before the user can leave the reader. +uint8_t epubProgressPercentByte(const Epub& epub, const int spineIndex, const int currentPage, const int pageCount) { + if (pageCount <= 0) { + return 0; + } + const float chapterProgress = static_cast(currentPage) / static_cast(pageCount); + return ReaderUtils::fractionProgressPercentByte(epub.calculateProgress(spineIndex, chapterProgress)); +} + +// Writes the canonical EPUB progress.bin layout: spine(2) + page(2) + pageCount(2) + percent(1). +// Used by the per-page saveProgress() and by transient writers (sync restore, bookmark jump) so +// the on-disk format stays consistent regardless of caller. bool writeReaderProgressCache(const std::string& cachePath, const int spineIndex, const int currentPage, - const int pageCount) { + const int pageCount, const uint8_t percent) { FsFile f; if (!Storage.openFileForWrite("ERS", cachePath + "/progress.bin", f)) { - LOG_ERR("ERS", "Failed to open progress cache for sync restore: %s", cachePath.c_str()); + LOG_ERR("ERS", "Failed to open progress cache: %s", cachePath.c_str()); return false; } - uint8_t data[6]; + 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] = percent; + f.write(data, 7); f.close(); return true; } @@ -900,7 +915,9 @@ void EpubReaderActivity::applyPendingSyncSession() { // Store 0 to disable rescaling; the paragraph lookup handles precise positioning. const int restorePageCount = (restoreSpineIndex == sync.spineIndex) ? sync.totalPagesInSpine : 0; - if (writeReaderProgressCache(epub->getCachePath(), restoreSpineIndex, restorePage, restorePageCount)) { + // Transient write — the next render's saveProgress() supplies the real percent before the user + // can return to the home screen, so a placeholder 0 here is harmless. + if (writeReaderProgressCache(epub->getCachePath(), restoreSpineIndex, restorePage, restorePageCount, 0)) { cachedSpineIndex = restoreSpineIndex; cachedChapterTotalPageCount = restorePageCount; LOG_DBG("ERS", "Prepared progress.bin for sync restore: spine=%d page=%d/%d", restoreSpineIndex, restorePage, @@ -924,7 +941,8 @@ void EpubReaderActivity::applyPendingBookmarkJump() { return; } LOG_DBG("ERS", "Applying pending bookmark jump: spine=%u page=%u", jump.spineIndex, jump.pageNumber); - if (writeReaderProgressCache(epub->getCachePath(), jump.spineIndex, jump.pageNumber, 0)) { + // Transient write before initializeReader; saveProgress() overwrites with the real percent. + if (writeReaderProgressCache(epub->getCachePath(), jump.spineIndex, jump.pageNumber, 0, 0)) { cachedSpineIndex = jump.spineIndex; cachedChapterTotalPageCount = 0; } else { @@ -1383,28 +1401,12 @@ 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 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; - data[6] = overallPercent; - f.write(data, 7); - f.close(); - LOG_DBG("ERS", "Progress saved: Chapter %d, Page %d (%d%%)", spineIndex, currentPage, overallPercent); - } else { + const uint8_t percent = epubProgressPercentByte(*epub, spineIndex, currentPage, pageCount); + if (!writeReaderProgressCache(epub->getCachePath(), spineIndex, currentPage, pageCount, percent)) { LOG_ERR("ERS", "Could not save progress!"); + return; } + LOG_DBG("ERS", "Progress saved: Chapter %d, Page %d (%d%%)", spineIndex, currentPage, percent); } void EpubReaderActivity::renderContents(std::unique_ptr page, const int orientedMarginTop, const int orientedMarginRight, const int orientedMarginBottom, diff --git a/src/activities/reader/MdReaderActivity.cpp b/src/activities/reader/MdReaderActivity.cpp index e38ed44e..7d68b586 100644 --- a/src/activities/reader/MdReaderActivity.cpp +++ b/src/activities/reader/MdReaderActivity.cpp @@ -750,7 +750,6 @@ void MdReaderActivity::saveProgress() const { // 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; @@ -758,7 +757,7 @@ void MdReaderActivity::saveProgress() const { data[3] = (offset >> 8) & 0xFF; data[4] = (offset >> 16) & 0xFF; data[5] = (offset >> 24) & 0xFF; - data[6] = overallPercent; + data[6] = ReaderUtils::pageProgressPercentByte(currentPage, totalPages); f.write(data, 7); } } @@ -770,10 +769,8 @@ void MdReaderActivity::loadProgress() { 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. + // Page sits in bytes 0-1 in both the old 4-byte uint32 format and the new 7-byte format + // (page counts stay well under 65536, so the upper bytes were always zero). int loadedPage = data[0] + (data[1] << 8); if (totalPages == 0) { currentPage = 0; diff --git a/src/activities/reader/ReaderUtils.h b/src/activities/reader/ReaderUtils.h index 1e326803..9650bd25 100644 --- a/src/activities/reader/ReaderUtils.h +++ b/src/activities/reader/ReaderUtils.h @@ -4,12 +4,37 @@ #include #include +#include + #include "MappedInputManager.h" namespace ReaderUtils { constexpr unsigned long GO_HOME_MS = 1000; +// Round-half-up integer division clamped to [0, 100], used as the percent byte appended to +// progress.bin so the home screen can render a per-book badge without re-loading the document. +// All reader types must funnel through this so the displayed value matches across formats. +inline uint8_t pageProgressPercentByte(int currentPage, int totalPages) { + if (totalPages <= 0 || currentPage < 0) { + return 0; + } + const long numerator = static_cast(currentPage + 1) * 200L + totalPages; + const long percent = numerator / (2L * totalPages); + if (percent < 0) return 0; + if (percent > 100) return 100; + return static_cast(percent); +} + +// Round-half-up clamp for a pre-computed [0,1] progress fraction (used by EPUB, where progress +// is byte-weighted across spine items rather than a simple page ratio). +inline uint8_t fractionProgressPercentByte(float fraction) { + const int percent = static_cast(fraction * 100.0f + 0.5f); + if (percent < 0) return 0; + if (percent > 100) return 100; + return static_cast(percent); +} + inline void applyOrientation(GfxRenderer& renderer, const uint8_t orientation) { switch (orientation) { case CrossPointSettings::ORIENTATION::PORTRAIT: diff --git a/src/activities/reader/TxtReaderActivity.cpp b/src/activities/reader/TxtReaderActivity.cpp index 2c6afdd1..bed59e7b 100644 --- a/src/activities/reader/TxtReaderActivity.cpp +++ b/src/activities/reader/TxtReaderActivity.cpp @@ -411,7 +411,6 @@ void TxtReaderActivity::saveProgress() const { // 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; - 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; @@ -419,7 +418,7 @@ void TxtReaderActivity::saveProgress() const { data[3] = (offset >> 8) & 0xFF; data[4] = (offset >> 16) & 0xFF; data[5] = (offset >> 24) & 0xFF; - data[6] = overallPercent; + data[6] = ReaderUtils::pageProgressPercentByte(currentPage, totalPages); f.write(data, 7); f.close(); } diff --git a/src/activities/reader/XtcReaderActivity.cpp b/src/activities/reader/XtcReaderActivity.cpp index 89f4d306..63bfc244 100644 --- a/src/activities/reader/XtcReaderActivity.cpp +++ b/src/activities/reader/XtcReaderActivity.cpp @@ -334,14 +334,13 @@ void XtcReaderActivity::renderPage() { void XtcReaderActivity::saveProgress() const { FsFile f; if (Storage.openFileForWrite("XTR", xtc->getCachePath() + "/progress.bin", f)) { - 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; - data[4] = overallPercent; + data[4] = + ReaderUtils::pageProgressPercentByte(static_cast(currentPage), static_cast(xtc->getPageCount())); f.write(data, 5); f.close(); } diff --git a/src/components/themes/lyra/LyraTheme.cpp b/src/components/themes/lyra/LyraTheme.cpp index e352b8d1..aac3a156 100644 --- a/src/components/themes/lyra/LyraTheme.cpp +++ b/src/components/themes/lyra/LyraTheme.cpp @@ -136,7 +136,7 @@ int LyraTheme::getRecentBookProgressPercent(const RecentBook& book) { } std::string cachePath; - int percentByteOffset; // byte index of the percent field in progress.bin + int percentByteOffset = 0; // byte index of the percent field in progress.bin if (FsHelpers::hasEpubExtension(book.path)) { cachePath = Epub(book.path, "/.crosspoint").getCachePath(); @@ -160,8 +160,8 @@ int LyraTheme::getRecentBookProgressPercent(const RecentBook& book) { const int dataSize = progressFile.read(data, 7); progressFile.close(); - if (dataSize <= percentByteOffset) { - return -1; // old format without percent byte — not yet read by new firmware + if (dataSize < percentByteOffset + 1) { + return -1; // old format (or pre-render placeholder) without the percent byte } return clampProgressPercent(static_cast(data[percentByteOffset])); From f627d0e24bb65d7c76fbe3b3831a709e2497c2e4 Mon Sep 17 00:00:00 2001 From: jpirnay Date: Mon, 27 Apr 2026 11:31:00 +0200 Subject: [PATCH 4/5] Explicit close --- src/activities/reader/MdReaderActivity.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/activities/reader/MdReaderActivity.cpp b/src/activities/reader/MdReaderActivity.cpp index 7d68b586..468c44a7 100644 --- a/src/activities/reader/MdReaderActivity.cpp +++ b/src/activities/reader/MdReaderActivity.cpp @@ -759,6 +759,7 @@ void MdReaderActivity::saveProgress() const { data[5] = (offset >> 24) & 0xFF; data[6] = ReaderUtils::pageProgressPercentByte(currentPage, totalPages); f.write(data, 7); + f.close(); } } From 727d63bebcfe5eee77ecae84a3cdf177b257948f Mon Sep 17 00:00:00 2001 From: jpirnay Date: Mon, 27 Apr 2026 11:32:18 +0200 Subject: [PATCH 5/5] yaclf --- src/activities/reader/XtcReaderActivity.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/activities/reader/XtcReaderActivity.cpp b/src/activities/reader/XtcReaderActivity.cpp index 50011cb3..f9176e51 100644 --- a/src/activities/reader/XtcReaderActivity.cpp +++ b/src/activities/reader/XtcReaderActivity.cpp @@ -14,8 +14,6 @@ #include -#include - #include "CrossPointSettings.h" #include "CrossPointState.h" #include "MappedInputManager.h"