This commit is contained in:
jpirnay
2026-04-27 10:42:39 +02:00
parent 5b0a7743d1
commit 874373a167
6 changed files with 62 additions and 40 deletions
+28 -26
View File
@@ -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<float>(currentPage) / static_cast<float>(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<float>(currentPage) / static_cast<float>(pageCount);
overallPercent = static_cast<uint8_t>(
clampPercent(static_cast<int>(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> page, const int orientedMarginTop,
const int orientedMarginRight, const int orientedMarginBottom,
+3 -6
View File
@@ -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<int>(pageOffsets.size())) ? pageOffsets[currentPage] : 0;
const uint8_t overallPercent = (totalPages > 0) ? static_cast<uint8_t>((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;
+25
View File
@@ -4,12 +4,37 @@
#include <GfxRenderer.h>
#include <Logging.h>
#include <cstdint>
#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<long>(currentPage + 1) * 200L + totalPages;
const long percent = numerator / (2L * totalPages);
if (percent < 0) return 0;
if (percent > 100) return 100;
return static_cast<uint8_t>(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<int>(fraction * 100.0f + 0.5f);
if (percent < 0) return 0;
if (percent > 100) return 100;
return static_cast<uint8_t>(percent);
}
inline void applyOrientation(GfxRenderer& renderer, const uint8_t orientation) {
switch (orientation) {
case CrossPointSettings::ORIENTATION::PORTRAIT:
+1 -2
View File
@@ -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<int>(pageOffsets.size())) ? pageOffsets[currentPage] : 0;
const uint8_t overallPercent = (totalPages > 0) ? static_cast<uint8_t>((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();
}
+2 -3
View File
@@ -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<uint8_t>((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<int>(currentPage), static_cast<int>(xtc->getPageCount()));
f.write(data, 5);
f.close();
}
+3 -3
View File
@@ -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<int>(data[percentByteOffset]));