Move section data into new cache directory

This commit is contained in:
Dave Allie 2025-12-29 12:45:15 +11:00
parent ea3584a918
commit 85188afc8e
No known key found for this signature in database
GPG Key ID: F2FDDB3AD8D0276F
4 changed files with 22 additions and 34 deletions

View File

@ -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 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. 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: 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/ .crosspoint/
├── epub_12471232/ # Each EPUB is cached to a subdirectory named `epub_<hash>` ├── epub_12471232/ # Each EPUB is cached to a subdirectory named `epub_<hash>`
│ ├── progress.bin # Stores reading progress (chapter, page, etc.) │ ├── 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) │ ├── cover.bmp # Book cover image (once generated)
│ │ ├── section.bin # Section metadata (page count) │ ├── book.bin # Book metadata (title, author, spine, table of contents, etc.)
│ │ ├── page_0.bin # Each page is stored in a separate file, it │ └── sections/ # All chapter data is stored in the sections subdirectory
│ │ ├── page_1.bin # contains the position (x, y) and text for each word │ ├── 0.bin # Chapter data (screen count, all text layout info, etc.)
│ │ └── ... │ ├── 1.bin # files are named by their index in the spine
│ ├── 1/
│ │ ├── section.bin
│ │ ├── page_0.bin
│ │ ├── page_1.bin
│ │ └── ...
│ └── ... │ └── ...
└── epub_189013891/ └── 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 Due the way it's currently implemented, the cache is not automatically cleared when a book is deleted and moving a book
EPUB file will reset the reading progress. 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 ## Contributing

View File

@ -11,7 +11,6 @@ namespace {
constexpr uint8_t SECTION_FILE_VERSION = 7; constexpr uint8_t SECTION_FILE_VERSION = 7;
constexpr size_t HEADER_SIZE = sizeof(uint8_t) + sizeof(int) + sizeof(float) + sizeof(bool) + sizeof(int) + constexpr size_t HEADER_SIZE = sizeof(uint8_t) + sizeof(int) + sizeof(float) + sizeof(bool) + sizeof(int) +
sizeof(int) + sizeof(int) + sizeof(size_t); sizeof(int) + sizeof(int) + sizeof(size_t);
constexpr char sectionFileName[] = "/section.bin";
} // namespace } // namespace
size_t Section::onPageComplete(std::unique_ptr<Page> page) { size_t Section::onPageComplete(std::unique_ptr<Page> page) {
@ -49,7 +48,7 @@ void Section::writeSectionFileHeader(const int fontId, const float lineCompressi
bool Section::loadSectionFile(const int fontId, const float lineCompression, const bool extraParagraphSpacing, bool Section::loadSectionFile(const int fontId, const float lineCompression, const bool extraParagraphSpacing,
const int viewportWidth, const int viewportHeight) { const int viewportWidth, const int viewportHeight) {
if (!FsHelpers::openFileForRead("SCT", cachePath + sectionFileName, file)) { if (!FsHelpers::openFileForRead("SCT", filePath, file)) {
return false; return false;
} }
@ -89,19 +88,14 @@ bool Section::loadSectionFile(const int fontId, const float lineCompression, con
return true; 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) // Your updated class method (assuming you are using the 'SD' object, which is a wrapper for a specific filesystem)
bool Section::clearCache() const { 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()); Serial.printf("[%lu] [SCT] Cache does not exist, no action needed\n", millis());
return true; return true;
} }
if (!FsHelpers::removeDir(cachePath.c_str())) { if (!SD.remove(filePath.c_str())) {
Serial.printf("[%lu] [SCT] Failed to clear cache\n", millis()); Serial.printf("[%lu] [SCT] Failed to clear cache\n", millis());
return false; return false;
} }
@ -159,7 +153,7 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c
progressSetupFn(); progressSetupFn();
} }
if (!FsHelpers::openFileForWrite("SCT", cachePath + sectionFileName, file)) { if (!FsHelpers::openFileForWrite("SCT", filePath, file)) {
return false; return false;
} }
writeSectionFileHeader(fontId, lineCompression, extraParagraphSpacing, viewportWidth, viewportHeight); writeSectionFileHeader(fontId, lineCompression, extraParagraphSpacing, viewportWidth, viewportHeight);
@ -175,7 +169,7 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c
if (!success) { if (!success) {
Serial.printf("[%lu] [SCT] Failed to parse XML and build pages\n", millis()); Serial.printf("[%lu] [SCT] Failed to parse XML and build pages\n", millis());
file.close(); file.close();
SD.remove((cachePath + sectionFileName).c_str()); SD.remove(filePath.c_str());
return false; return false;
} }
@ -193,7 +187,7 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c
} }
std::unique_ptr<Page> Section::loadPageFromSectionFile() { std::unique_ptr<Page> Section::loadPageFromSectionFile() {
if (!FsHelpers::openFileForRead("SCT", cachePath + sectionFileName, file)) { if (!FsHelpers::openFileForRead("SCT", filePath, file)) {
return nullptr; return nullptr;
} }

View File

@ -11,7 +11,7 @@ class Section {
std::shared_ptr<Epub> epub; std::shared_ptr<Epub> epub;
const int spineIndex; const int spineIndex;
GfxRenderer& renderer; GfxRenderer& renderer;
std::string cachePath; std::string filePath;
File file; File file;
void writeSectionFileHeader(int fontId, float lineCompression, bool extraParagraphSpacing, int viewportWidth, void writeSectionFileHeader(int fontId, float lineCompression, bool extraParagraphSpacing, int viewportWidth,
@ -26,11 +26,10 @@ class Section {
: epub(epub), : epub(epub),
spineIndex(spineIndex), spineIndex(spineIndex),
renderer(renderer), renderer(renderer),
cachePath(epub->getCachePath() + "/" + std::to_string(spineIndex)) {} filePath(epub->getCachePath() + "/sections/" + std::to_string(spineIndex) + ".bin") {}
~Section() = default; ~Section() = default;
bool loadSectionFile(int fontId, float lineCompression, bool extraParagraphSpacing, int viewportWidth, bool loadSectionFile(int fontId, float lineCompression, bool extraParagraphSpacing, int viewportWidth,
int viewportHeight); int viewportHeight);
void setupCacheDir() const;
bool clearCache() const; bool clearCache() const;
bool createSectionFile(int fontId, float lineCompression, bool extraParagraphSpacing, int viewportWidth, bool createSectionFile(int fontId, float lineCompression, bool extraParagraphSpacing, int viewportWidth,
int viewportHeight, const std::function<void()>& progressSetupFn = nullptr, int viewportHeight, const std::function<void()>& progressSetupFn = nullptr,

View File

@ -282,8 +282,6 @@ void EpubReaderActivity::renderScreen() {
pagesUntilFullRefresh = 0; pagesUntilFullRefresh = 0;
} }
section->setupCacheDir();
// Setup callback - only called for chapters >= 50KB, redraws with progress bar // Setup callback - only called for chapters >= 50KB, redraws with progress bar
auto progressSetup = [this, boxXWithBar, boxWidthWithBar, boxHeightWithBar, barX, barY] { auto progressSetup = [this, boxXWithBar, boxWidthWithBar, boxHeightWithBar, barX, barY] {
renderer.fillRect(boxXWithBar, boxY, boxWidthWithBar, boxHeightWithBar, false); renderer.fillRect(boxXWithBar, boxY, boxWidthWithBar, boxHeightWithBar, false);