From 85188afc8e2ca28c56f9bcafd852c16dc8ec8553 Mon Sep 17 00:00:00 2001 From: Dave Allie Date: Mon, 29 Dec 2025 12:45:15 +1100 Subject: [PATCH] Move section data into new cache directory --- README.md | 29 +++++++++----------- lib/Epub/Epub/Section.cpp | 20 +++++--------- lib/Epub/Epub/Section.h | 5 ++-- src/activities/reader/EpubReaderActivity.cpp | 2 -- 4 files changed, 22 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index 60a2ed2e..6f484f41 100644 --- a/README.md +++ b/README.md @@ -98,9 +98,9 @@ CrossPoint Reader is pretty aggressive about caching data down to the SD card to has ~380KB of usable RAM, so we have to be careful. A lot of the decisions made in the design of the firmware were based on this constraint. -### EPUB caching +### Data caching -The first time chapters of an EPUB are loaded, they are cached to the SD card. Subsequent loads are served from the +The first time chapters of a book are loaded, they are cached to the SD card. Subsequent loads are served from the cache. This cache directory exists at `.crosspoint` on the SD card. The structure is as follows: @@ -108,25 +108,22 @@ cache. This cache directory exists at `.crosspoint` on the SD card. The structur .crosspoint/ ├── epub_12471232/ # Each EPUB is cached to a subdirectory named `epub_` │ ├── progress.bin # Stores reading progress (chapter, page, etc.) -│ ├── 0/ # Each chapter is stored in a subdirectory named by its index (based on the spine order) -│ │ ├── section.bin # Section metadata (page count) -│ │ ├── page_0.bin # Each page is stored in a separate file, it -│ │ ├── page_1.bin # contains the position (x, y) and text for each word -│ │ └── ... -│ ├── 1/ -│ │ ├── section.bin -│ │ ├── page_0.bin -│ │ ├── page_1.bin -│ │ └── ... -│ └── ... +│ ├── cover.bmp # Book cover image (once generated) +│ ├── book.bin # Book metadata (title, author, spine, table of contents, etc.) +│ └── sections/ # All chapter data is stored in the sections subdirectory +│ ├── 0.bin # Chapter data (screen count, all text layout info, etc.) +│ ├── 1.bin # files are named by their index in the spine +│ └── ... │ └── epub_189013891/ ``` -Deleting the `.crosspoint` directory will clear the cache. +Deleting the `.crosspoint` directory will clear the entire cache. -Due the way it's currently implemented, the cache is not automatically cleared when the EPUB is deleted and moving an -EPUB file will reset the reading progress. +Due the way it's currently implemented, the cache is not automatically cleared when a book is deleted and moving a book +file will use a new cache directory, resetting the reading progress. + +For more details on the internal file structures, see the [file formats document](./docs/file-formats.md). ## Contributing diff --git a/lib/Epub/Epub/Section.cpp b/lib/Epub/Epub/Section.cpp index f190a3aa..10b3cd53 100644 --- a/lib/Epub/Epub/Section.cpp +++ b/lib/Epub/Epub/Section.cpp @@ -11,7 +11,6 @@ namespace { constexpr uint8_t SECTION_FILE_VERSION = 7; constexpr size_t HEADER_SIZE = sizeof(uint8_t) + sizeof(int) + sizeof(float) + sizeof(bool) + sizeof(int) + sizeof(int) + sizeof(int) + sizeof(size_t); -constexpr char sectionFileName[] = "/section.bin"; } // namespace size_t Section::onPageComplete(std::unique_ptr page) { @@ -43,13 +42,13 @@ void Section::writeSectionFileHeader(const int fontId, const float lineCompressi serialization::writePod(file, extraParagraphSpacing); serialization::writePod(file, viewportWidth); serialization::writePod(file, viewportHeight); - serialization::writePod(file, pageCount); // Placeholder for page count (will be initially 0 when written) + serialization::writePod(file, pageCount); // Placeholder for page count (will be initially 0 when written) serialization::writePod(file, static_cast(0)); // Placeholder for LUT offset } bool Section::loadSectionFile(const int fontId, const float lineCompression, const bool extraParagraphSpacing, const int viewportWidth, const int viewportHeight) { - if (!FsHelpers::openFileForRead("SCT", cachePath + sectionFileName, file)) { + if (!FsHelpers::openFileForRead("SCT", filePath, file)) { return false; } @@ -89,19 +88,14 @@ bool Section::loadSectionFile(const int fontId, const float lineCompression, con return true; } -void Section::setupCacheDir() const { - epub->setupCacheDir(); - SD.mkdir(cachePath.c_str()); -} - // Your updated class method (assuming you are using the 'SD' object, which is a wrapper for a specific filesystem) bool Section::clearCache() const { - if (!SD.exists(cachePath.c_str())) { + if (!SD.exists(filePath.c_str())) { Serial.printf("[%lu] [SCT] Cache does not exist, no action needed\n", millis()); return true; } - if (!FsHelpers::removeDir(cachePath.c_str())) { + if (!SD.remove(filePath.c_str())) { Serial.printf("[%lu] [SCT] Failed to clear cache\n", millis()); return false; } @@ -159,7 +153,7 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c progressSetupFn(); } - if (!FsHelpers::openFileForWrite("SCT", cachePath + sectionFileName, file)) { + if (!FsHelpers::openFileForWrite("SCT", filePath, file)) { return false; } writeSectionFileHeader(fontId, lineCompression, extraParagraphSpacing, viewportWidth, viewportHeight); @@ -175,7 +169,7 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c if (!success) { Serial.printf("[%lu] [SCT] Failed to parse XML and build pages\n", millis()); file.close(); - SD.remove((cachePath + sectionFileName).c_str()); + SD.remove(filePath.c_str()); return false; } @@ -193,7 +187,7 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c } std::unique_ptr Section::loadPageFromSectionFile() { - if (!FsHelpers::openFileForRead("SCT", cachePath + sectionFileName, file)) { + if (!FsHelpers::openFileForRead("SCT", filePath, file)) { return nullptr; } diff --git a/lib/Epub/Epub/Section.h b/lib/Epub/Epub/Section.h index de335dce..93e0d6c3 100644 --- a/lib/Epub/Epub/Section.h +++ b/lib/Epub/Epub/Section.h @@ -11,7 +11,7 @@ class Section { std::shared_ptr epub; const int spineIndex; GfxRenderer& renderer; - std::string cachePath; + std::string filePath; File file; void writeSectionFileHeader(int fontId, float lineCompression, bool extraParagraphSpacing, int viewportWidth, @@ -26,11 +26,10 @@ class Section { : epub(epub), spineIndex(spineIndex), renderer(renderer), - cachePath(epub->getCachePath() + "/" + std::to_string(spineIndex)) {} + filePath(epub->getCachePath() + "/sections/" + std::to_string(spineIndex) + ".bin") {} ~Section() = default; bool loadSectionFile(int fontId, float lineCompression, bool extraParagraphSpacing, int viewportWidth, int viewportHeight); - void setupCacheDir() const; bool clearCache() const; bool createSectionFile(int fontId, float lineCompression, bool extraParagraphSpacing, int viewportWidth, int viewportHeight, const std::function& progressSetupFn = nullptr, diff --git a/src/activities/reader/EpubReaderActivity.cpp b/src/activities/reader/EpubReaderActivity.cpp index 7c3cecdf..ebf5b0a7 100644 --- a/src/activities/reader/EpubReaderActivity.cpp +++ b/src/activities/reader/EpubReaderActivity.cpp @@ -282,8 +282,6 @@ void EpubReaderActivity::renderScreen() { pagesUntilFullRefresh = 0; } - section->setupCacheDir(); - // Setup callback - only called for chapters >= 50KB, redraws with progress bar auto progressSetup = [this, boxXWithBar, boxWidthWithBar, boxHeightWithBar, barX, barY] { renderer.fillRect(boxXWithBar, boxY, boxWidthWithBar, boxHeightWithBar, false);