diff --git a/lib/Epub/Epub.cpp b/lib/Epub/Epub.cpp index 941e11b..f4a2bfa 100644 --- a/lib/Epub/Epub.cpp +++ b/lib/Epub/Epub.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include "Epub/parsers/ContainerParser.h" @@ -94,13 +94,13 @@ bool Epub::parseTocNcxFile() const { Serial.printf("[%lu] [EBP] Parsing toc ncx file: %s\n", millis(), tocNcxItem.c_str()); const auto tmpNcxPath = getCachePath() + "/toc.ncx"; - File tempNcxFile; - if (!FsHelpers::openFileForWrite("EBP", tmpNcxPath, tempNcxFile)) { + FsFile tempNcxFile; + if (!SdMan.openFileForWrite("EBP", tmpNcxPath, tempNcxFile)) { return false; } readItemContentsToStream(tocNcxItem, tempNcxFile, 1024); tempNcxFile.close(); - if (!FsHelpers::openFileForRead("EBP", tmpNcxPath, tempNcxFile)) { + if (!SdMan.openFileForRead("EBP", tmpNcxPath, tempNcxFile)) { return false; } const auto ncxSize = tempNcxFile.size(); @@ -132,7 +132,7 @@ bool Epub::parseTocNcxFile() const { free(ncxBuffer); tempNcxFile.close(); - SD.remove(tmpNcxPath.c_str()); + SdMan.remove(tmpNcxPath.c_str()); Serial.printf("[%lu] [EBP] Parsed TOC items\n", millis()); return true; @@ -218,12 +218,12 @@ bool Epub::load() { } bool Epub::clearCache() const { - if (!SD.exists(cachePath.c_str())) { + if (!SdMan.exists(cachePath.c_str())) { Serial.printf("[%lu] [EPB] Cache does not exist, no action needed\n", millis()); return true; } - if (!FsHelpers::removeDir(cachePath.c_str())) { + if (!SdMan.removeDir(cachePath.c_str())) { Serial.printf("[%lu] [EPB] Failed to clear cache\n", millis()); return false; } @@ -233,17 +233,11 @@ bool Epub::clearCache() const { } void Epub::setupCacheDir() const { - if (SD.exists(cachePath.c_str())) { + if (SdMan.exists(cachePath.c_str())) { return; } - // Loop over each segment of the cache path and create directories as needed - for (size_t i = 1; i < cachePath.length(); i++) { - if (cachePath[i] == '/') { - SD.mkdir(cachePath.substr(0, i).c_str()); - } - } - SD.mkdir(cachePath.c_str()); + SdMan.mkdir(cachePath.c_str()); } const std::string& Epub::getCachePath() const { return cachePath; } @@ -263,7 +257,7 @@ std::string Epub::getCoverBmpPath() const { return cachePath + "/cover.bmp"; } bool Epub::generateCoverBmp() const { // Already generated, return true - if (SD.exists(getCoverBmpPath().c_str())) { + if (SdMan.exists(getCoverBmpPath().c_str())) { return true; } @@ -283,30 +277,30 @@ bool Epub::generateCoverBmp() const { Serial.printf("[%lu] [EBP] Generating BMP from JPG cover image\n", millis()); const auto coverJpgTempPath = getCachePath() + "/.cover.jpg"; - File coverJpg; - if (!FsHelpers::openFileForWrite("EBP", coverJpgTempPath, coverJpg)) { + FsFile coverJpg; + if (!SdMan.openFileForWrite("EBP", coverJpgTempPath, coverJpg)) { return false; } readItemContentsToStream(coverImageHref, coverJpg, 1024); coverJpg.close(); - if (!FsHelpers::openFileForRead("EBP", coverJpgTempPath, coverJpg)) { + if (!SdMan.openFileForRead("EBP", coverJpgTempPath, coverJpg)) { return false; } - File coverBmp; - if (!FsHelpers::openFileForWrite("EBP", getCoverBmpPath(), coverBmp)) { + FsFile coverBmp; + if (!SdMan.openFileForWrite("EBP", getCoverBmpPath(), coverBmp)) { coverJpg.close(); return false; } const bool success = JpegToBmpConverter::jpegFileToBmpStream(coverJpg, coverBmp); coverJpg.close(); coverBmp.close(); - SD.remove(coverJpgTempPath.c_str()); + SdMan.remove(coverJpgTempPath.c_str()); if (!success) { Serial.printf("[%lu] [EBP] Failed to generate BMP from JPG cover image\n", millis()); - SD.remove(getCoverBmpPath().c_str()); + SdMan.remove(getCoverBmpPath().c_str()); } Serial.printf("[%lu] [EBP] Generated BMP from JPG cover image, success: %s\n", millis(), success ? "yes" : "no"); return success; @@ -318,6 +312,11 @@ bool Epub::generateCoverBmp() const { } uint8_t* Epub::readItemContentsToBytes(const std::string& itemHref, size_t* size, const bool trailingNullByte) const { + if (itemHref.empty()) { + Serial.printf("[%lu] [EBP] Failed to read item, empty href\n", millis()); + return nullptr; + } + const std::string path = FsHelpers::normalisePath(itemHref); const auto content = ZipFile(filepath).readFileToMemory(path.c_str(), size, trailingNullByte); @@ -330,6 +329,11 @@ uint8_t* Epub::readItemContentsToBytes(const std::string& itemHref, size_t* size } bool Epub::readItemContentsToStream(const std::string& itemHref, Print& out, const size_t chunkSize) const { + if (itemHref.empty()) { + Serial.printf("[%lu] [EBP] Failed to read item, empty href\n", millis()); + return false; + } + const std::string path = FsHelpers::normalisePath(itemHref); return ZipFile(filepath).readFileToStream(path.c_str(), out, chunkSize); } diff --git a/lib/Epub/Epub.h b/lib/Epub/Epub.h index c785008..8ce1843 100644 --- a/lib/Epub/Epub.h +++ b/lib/Epub/Epub.h @@ -1,5 +1,7 @@ #pragma once +#include + #include #include #include diff --git a/lib/Epub/Epub/BookMetadataCache.cpp b/lib/Epub/Epub/BookMetadataCache.cpp index 8fcee28..6272414 100644 --- a/lib/Epub/Epub/BookMetadataCache.cpp +++ b/lib/Epub/Epub/BookMetadataCache.cpp @@ -1,7 +1,6 @@ #include "BookMetadataCache.h" #include -#include #include #include @@ -30,7 +29,7 @@ bool BookMetadataCache::beginContentOpfPass() { Serial.printf("[%lu] [BMC] Beginning content opf pass\n", millis()); // Open spine file for writing - return FsHelpers::openFileForWrite("BMC", cachePath + tmpSpineBinFile, spineFile); + return SdMan.openFileForWrite("BMC", cachePath + tmpSpineBinFile, spineFile); } bool BookMetadataCache::endContentOpfPass() { @@ -42,10 +41,10 @@ bool BookMetadataCache::beginTocPass() { Serial.printf("[%lu] [BMC] Beginning toc pass\n", millis()); // Open spine file for reading - if (!FsHelpers::openFileForRead("BMC", cachePath + tmpSpineBinFile, spineFile)) { + if (!SdMan.openFileForRead("BMC", cachePath + tmpSpineBinFile, spineFile)) { return false; } - if (!FsHelpers::openFileForWrite("BMC", cachePath + tmpTocBinFile, tocFile)) { + if (!SdMan.openFileForWrite("BMC", cachePath + tmpTocBinFile, tocFile)) { spineFile.close(); return false; } @@ -71,27 +70,27 @@ bool BookMetadataCache::endWrite() { bool BookMetadataCache::buildBookBin(const std::string& epubPath, const BookMetadata& metadata) { // Open all three files, writing to meta, reading from spine and toc - if (!FsHelpers::openFileForWrite("BMC", cachePath + bookBinFile, bookFile)) { + if (!SdMan.openFileForWrite("BMC", cachePath + bookBinFile, bookFile)) { return false; } - if (!FsHelpers::openFileForRead("BMC", cachePath + tmpSpineBinFile, spineFile)) { + if (!SdMan.openFileForRead("BMC", cachePath + tmpSpineBinFile, spineFile)) { bookFile.close(); return false; } - if (!FsHelpers::openFileForRead("BMC", cachePath + tmpTocBinFile, tocFile)) { + if (!SdMan.openFileForRead("BMC", cachePath + tmpTocBinFile, tocFile)) { bookFile.close(); spineFile.close(); return false; } - constexpr size_t headerASize = - sizeof(BOOK_CACHE_VERSION) + /* LUT Offset */ sizeof(size_t) + sizeof(spineCount) + sizeof(tocCount); - const size_t metadataSize = + constexpr uint32_t headerASize = + sizeof(BOOK_CACHE_VERSION) + /* LUT Offset */ sizeof(uint32_t) + sizeof(spineCount) + sizeof(tocCount); + const uint32_t metadataSize = metadata.title.size() + metadata.author.size() + metadata.coverItemHref.size() + sizeof(uint32_t) * 3; - const size_t lutSize = sizeof(size_t) * spineCount + sizeof(size_t) * tocCount; - const size_t lutOffset = headerASize + metadataSize; + const uint32_t lutSize = sizeof(uint32_t) * spineCount + sizeof(uint32_t) * tocCount; + const uint32_t lutOffset = headerASize + metadataSize; // Header A serialization::writePod(bookFile, BOOK_CACHE_VERSION); @@ -106,7 +105,7 @@ bool BookMetadataCache::buildBookBin(const std::string& epubPath, const BookMeta // Loop through spine entries, writing LUT positions spineFile.seek(0); for (int i = 0; i < spineCount; i++) { - auto pos = spineFile.position(); + uint32_t pos = spineFile.position(); auto spineEntry = readSpineEntry(spineFile); serialization::writePod(bookFile, pos + lutOffset + lutSize); } @@ -114,9 +113,9 @@ bool BookMetadataCache::buildBookBin(const std::string& epubPath, const BookMeta // Loop through toc entries, writing LUT positions tocFile.seek(0); for (int i = 0; i < tocCount; i++) { - auto pos = tocFile.position(); + uint32_t pos = tocFile.position(); auto tocEntry = readTocEntry(tocFile); - serialization::writePod(bookFile, pos + lutOffset + lutSize + spineFile.position()); + serialization::writePod(bookFile, pos + lutOffset + lutSize + static_cast(spineFile.position())); } // LUTs complete @@ -142,7 +141,7 @@ bool BookMetadataCache::buildBookBin(const std::string& epubPath, const BookMeta zip.close(); return false; } - size_t cumSize = 0; + uint32_t cumSize = 0; spineFile.seek(0); for (int i = 0; i < spineCount; i++) { auto spineEntry = readSpineEntry(spineFile); @@ -195,25 +194,25 @@ bool BookMetadataCache::buildBookBin(const std::string& epubPath, const BookMeta } bool BookMetadataCache::cleanupTmpFiles() const { - if (SD.exists((cachePath + tmpSpineBinFile).c_str())) { - SD.remove((cachePath + tmpSpineBinFile).c_str()); + if (SdMan.exists((cachePath + tmpSpineBinFile).c_str())) { + SdMan.remove((cachePath + tmpSpineBinFile).c_str()); } - if (SD.exists((cachePath + tmpTocBinFile).c_str())) { - SD.remove((cachePath + tmpTocBinFile).c_str()); + if (SdMan.exists((cachePath + tmpTocBinFile).c_str())) { + SdMan.remove((cachePath + tmpTocBinFile).c_str()); } return true; } -size_t BookMetadataCache::writeSpineEntry(File& file, const SpineEntry& entry) const { - const auto pos = file.position(); +uint32_t BookMetadataCache::writeSpineEntry(FsFile& file, const SpineEntry& entry) const { + const uint32_t pos = file.position(); serialization::writeString(file, entry.href); serialization::writePod(file, entry.cumulativeSize); serialization::writePod(file, entry.tocIndex); return pos; } -size_t BookMetadataCache::writeTocEntry(File& file, const TocEntry& entry) const { - const auto pos = file.position(); +uint32_t BookMetadataCache::writeTocEntry(FsFile& file, const TocEntry& entry) const { + const uint32_t pos = file.position(); serialization::writeString(file, entry.title); serialization::writeString(file, entry.href); serialization::writeString(file, entry.anchor); @@ -267,7 +266,7 @@ void BookMetadataCache::createTocEntry(const std::string& title, const std::stri /* ============= READING / LOADING FUNCTIONS ================ */ bool BookMetadataCache::load() { - if (!FsHelpers::openFileForRead("BMC", cachePath + bookBinFile, bookFile)) { + if (!SdMan.openFileForRead("BMC", cachePath + bookBinFile, bookFile)) { return false; } @@ -304,8 +303,8 @@ BookMetadataCache::SpineEntry BookMetadataCache::getSpineEntry(const int index) } // Seek to spine LUT item, read from LUT and get out data - bookFile.seek(lutOffset + sizeof(size_t) * index); - size_t spineEntryPos; + bookFile.seek(lutOffset + sizeof(uint32_t) * index); + uint32_t spineEntryPos; serialization::readPod(bookFile, spineEntryPos); bookFile.seek(spineEntryPos); return readSpineEntry(bookFile); @@ -323,14 +322,14 @@ BookMetadataCache::TocEntry BookMetadataCache::getTocEntry(const int index) { } // Seek to TOC LUT item, read from LUT and get out data - bookFile.seek(lutOffset + sizeof(size_t) * spineCount + sizeof(size_t) * index); - size_t tocEntryPos; + bookFile.seek(lutOffset + sizeof(uint32_t) * spineCount + sizeof(uint32_t) * index); + uint32_t tocEntryPos; serialization::readPod(bookFile, tocEntryPos); bookFile.seek(tocEntryPos); return readTocEntry(bookFile); } -BookMetadataCache::SpineEntry BookMetadataCache::readSpineEntry(File& file) const { +BookMetadataCache::SpineEntry BookMetadataCache::readSpineEntry(FsFile& file) const { SpineEntry entry; serialization::readString(file, entry.href); serialization::readPod(file, entry.cumulativeSize); @@ -338,7 +337,7 @@ BookMetadataCache::SpineEntry BookMetadataCache::readSpineEntry(File& file) cons return entry; } -BookMetadataCache::TocEntry BookMetadataCache::readTocEntry(File& file) const { +BookMetadataCache::TocEntry BookMetadataCache::readTocEntry(FsFile& file) const { TocEntry entry; serialization::readString(file, entry.title); serialization::readString(file, entry.href); diff --git a/lib/Epub/Epub/BookMetadataCache.h b/lib/Epub/Epub/BookMetadataCache.h index 7f9f419..a6cf945 100644 --- a/lib/Epub/Epub/BookMetadataCache.h +++ b/lib/Epub/Epub/BookMetadataCache.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include @@ -46,15 +46,15 @@ class BookMetadataCache { bool loaded; bool buildMode; - File bookFile; + FsFile bookFile; // Temp file handles during build - File spineFile; - File tocFile; + FsFile spineFile; + FsFile tocFile; - size_t writeSpineEntry(File& file, const SpineEntry& entry) const; - size_t writeTocEntry(File& file, const TocEntry& entry) const; - SpineEntry readSpineEntry(File& file) const; - TocEntry readTocEntry(File& file) const; + uint32_t writeSpineEntry(FsFile& file, const SpineEntry& entry) const; + uint32_t writeTocEntry(FsFile& file, const TocEntry& entry) const; + SpineEntry readSpineEntry(FsFile& file) const; + TocEntry readTocEntry(FsFile& file) const; public: BookMetadata coreMetadata; diff --git a/lib/Epub/Epub/Page.cpp b/lib/Epub/Epub/Page.cpp index c50fe30..65ce569 100644 --- a/lib/Epub/Epub/Page.cpp +++ b/lib/Epub/Epub/Page.cpp @@ -7,7 +7,7 @@ void PageLine::render(GfxRenderer& renderer, const int fontId, const int xOffset block->render(renderer, fontId, xPos + xOffset, yPos + yOffset); } -bool PageLine::serialize(File& file) { +bool PageLine::serialize(FsFile& file) { serialization::writePod(file, xPos); serialization::writePod(file, yPos); @@ -15,7 +15,7 @@ bool PageLine::serialize(File& file) { return block->serialize(file); } -std::unique_ptr PageLine::deserialize(File& file) { +std::unique_ptr PageLine::deserialize(FsFile& file) { int16_t xPos; int16_t yPos; serialization::readPod(file, xPos); @@ -31,7 +31,7 @@ void Page::render(GfxRenderer& renderer, const int fontId, const int xOffset, co } } -bool Page::serialize(File& file) const { +bool Page::serialize(FsFile& file) const { const uint32_t count = elements.size(); serialization::writePod(file, count); @@ -46,7 +46,7 @@ bool Page::serialize(File& file) const { return true; } -std::unique_ptr Page::deserialize(File& file) { +std::unique_ptr Page::deserialize(FsFile& file) { auto page = std::unique_ptr(new Page()); uint32_t count; diff --git a/lib/Epub/Epub/Page.h b/lib/Epub/Epub/Page.h index 841ef6b..2006194 100644 --- a/lib/Epub/Epub/Page.h +++ b/lib/Epub/Epub/Page.h @@ -1,5 +1,5 @@ #pragma once -#include +#include #include #include @@ -18,7 +18,7 @@ class PageElement { explicit PageElement(const int16_t xPos, const int16_t yPos) : xPos(xPos), yPos(yPos) {} virtual ~PageElement() = default; virtual void render(GfxRenderer& renderer, int fontId, int xOffset, int yOffset) = 0; - virtual bool serialize(File& file) = 0; + virtual bool serialize(FsFile& file) = 0; }; // a line from a block element @@ -29,8 +29,8 @@ class PageLine final : public PageElement { PageLine(std::shared_ptr block, const int16_t xPos, const int16_t yPos) : PageElement(xPos, yPos), block(std::move(block)) {} void render(GfxRenderer& renderer, int fontId, int xOffset, int yOffset) override; - bool serialize(File& file) override; - static std::unique_ptr deserialize(File& file); + bool serialize(FsFile& file) override; + static std::unique_ptr deserialize(FsFile& file); }; class Page { @@ -38,6 +38,6 @@ class Page { // the list of block index and line numbers on this page std::vector> elements; void render(GfxRenderer& renderer, int fontId, int xOffset, int yOffset) const; - bool serialize(File& file) const; - static std::unique_ptr deserialize(File& file); + bool serialize(FsFile& file) const; + static std::unique_ptr deserialize(FsFile& file); }; diff --git a/lib/Epub/Epub/Section.cpp b/lib/Epub/Epub/Section.cpp index 9cc19ea..b153f4f 100644 --- a/lib/Epub/Epub/Section.cpp +++ b/lib/Epub/Epub/Section.cpp @@ -1,7 +1,6 @@ #include "Section.h" -#include -#include +#include #include #include "Page.h" @@ -9,17 +8,17 @@ 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 uint32_t HEADER_SIZE = sizeof(uint8_t) + sizeof(int) + sizeof(float) + sizeof(bool) + sizeof(int) + + sizeof(int) + sizeof(int) + sizeof(uint32_t); } // namespace -size_t Section::onPageComplete(std::unique_ptr page) { +uint32_t Section::onPageComplete(std::unique_ptr page) { if (!file) { Serial.printf("[%lu] [SCT] File not open for writing page %d\n", millis(), pageCount); return 0; } - const auto position = file.position(); + const uint32_t position = file.position(); if (!page->serialize(file)) { Serial.printf("[%lu] [SCT] Failed to serialize page %d\n", millis(), pageCount); return 0; @@ -38,7 +37,7 @@ void Section::writeSectionFileHeader(const int fontId, const float lineCompressi } static_assert(HEADER_SIZE == sizeof(SECTION_FILE_VERSION) + sizeof(fontId) + sizeof(lineCompression) + sizeof(extraParagraphSpacing) + sizeof(viewportWidth) + sizeof(viewportHeight) + - sizeof(pageCount) + sizeof(size_t), + sizeof(pageCount) + sizeof(uint32_t), "Header size mismatch"); serialization::writePod(file, SECTION_FILE_VERSION); serialization::writePod(file, fontId); @@ -47,12 +46,12 @@ void Section::writeSectionFileHeader(const int fontId, const float lineCompressi 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, static_cast(0)); // Placeholder for LUT offset + 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", filePath, file)) { + if (!SdMan.openFileForRead("SCT", filePath, file)) { return false; } @@ -94,12 +93,12 @@ bool Section::loadSectionFile(const int fontId, const float lineCompression, con // 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(filePath.c_str())) { + if (!SdMan.exists(filePath.c_str())) { Serial.printf("[%lu] [SCT] Cache does not exist, no action needed\n", millis()); return true; } - if (!SD.remove(filePath.c_str())) { + if (!SdMan.remove(filePath.c_str())) { Serial.printf("[%lu] [SCT] Failed to clear cache\n", millis()); return false; } @@ -112,13 +111,19 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c const int viewportWidth, const int viewportHeight, const std::function& progressSetupFn, const std::function& progressFn) { - constexpr size_t MIN_SIZE_FOR_PROGRESS = 50 * 1024; // 50KB + constexpr uint32_t MIN_SIZE_FOR_PROGRESS = 50 * 1024; // 50KB const auto localPath = epub->getSpineItem(spineIndex).href; const auto tmpHtmlPath = epub->getCachePath() + "/.tmp_" + std::to_string(spineIndex) + ".html"; + // Create cache directory if it doesn't exist + { + const auto sectionsDir = epub->getCachePath() + "/sections"; + SdMan.mkdir(sectionsDir.c_str()); + } + // Retry logic for SD card timing issues bool success = false; - size_t fileSize = 0; + uint32_t fileSize = 0; for (int attempt = 0; attempt < 3 && !success; attempt++) { if (attempt > 0) { Serial.printf("[%lu] [SCT] Retrying stream (attempt %d)...\n", millis(), attempt + 1); @@ -126,12 +131,12 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c } // Remove any incomplete file from previous attempt before retrying - if (SD.exists(tmpHtmlPath.c_str())) { - SD.remove(tmpHtmlPath.c_str()); + if (SdMan.exists(tmpHtmlPath.c_str())) { + SdMan.remove(tmpHtmlPath.c_str()); } - File tmpHtml; - if (!FsHelpers::openFileForWrite("SCT", tmpHtmlPath, tmpHtml)) { + FsFile tmpHtml; + if (!SdMan.openFileForWrite("SCT", tmpHtmlPath, tmpHtml)) { continue; } success = epub->readItemContentsToStream(localPath, tmpHtml, 1024); @@ -139,8 +144,8 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c tmpHtml.close(); // If streaming failed, remove the incomplete file immediately - if (!success && SD.exists(tmpHtmlPath.c_str())) { - SD.remove(tmpHtmlPath.c_str()); + if (!success && SdMan.exists(tmpHtmlPath.c_str())) { + SdMan.remove(tmpHtmlPath.c_str()); Serial.printf("[%lu] [SCT] Removed incomplete temp file after failed attempt\n", millis()); } } @@ -157,11 +162,11 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c progressSetupFn(); } - if (!FsHelpers::openFileForWrite("SCT", filePath, file)) { + if (!SdMan.openFileForWrite("SCT", filePath, file)) { return false; } writeSectionFileHeader(fontId, lineCompression, extraParagraphSpacing, viewportWidth, viewportHeight); - std::vector lut = {}; + std::vector lut = {}; ChapterHtmlSlimParser visitor( tmpHtmlPath, renderer, fontId, lineCompression, extraParagraphSpacing, viewportWidth, viewportHeight, @@ -169,18 +174,18 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c progressFn); success = visitor.parseAndBuildPages(); - SD.remove(tmpHtmlPath.c_str()); + SdMan.remove(tmpHtmlPath.c_str()); if (!success) { Serial.printf("[%lu] [SCT] Failed to parse XML and build pages\n", millis()); file.close(); - SD.remove(filePath.c_str()); + SdMan.remove(filePath.c_str()); return false; } - const auto lutOffset = file.position(); + const uint32_t lutOffset = file.position(); bool hasFailedLutRecords = false; // Write LUT - for (const auto& pos : lut) { + for (const uint32_t& pos : lut) { if (pos == 0) { hasFailedLutRecords = true; break; @@ -191,12 +196,12 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c if (hasFailedLutRecords) { Serial.printf("[%lu] [SCT] Failed to write LUT due to invalid page positions\n", millis()); file.close(); - SD.remove(filePath.c_str()); + SdMan.remove(filePath.c_str()); return false; } // Go back and write LUT offset - file.seek(HEADER_SIZE - sizeof(size_t) - sizeof(pageCount)); + file.seek(HEADER_SIZE - sizeof(uint32_t) - sizeof(pageCount)); serialization::writePod(file, pageCount); serialization::writePod(file, lutOffset); file.close(); @@ -204,15 +209,15 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c } std::unique_ptr Section::loadPageFromSectionFile() { - if (!FsHelpers::openFileForRead("SCT", filePath, file)) { + if (!SdMan.openFileForRead("SCT", filePath, file)) { return nullptr; } - file.seek(HEADER_SIZE - sizeof(size_t)); - size_t lutOffset; + file.seek(HEADER_SIZE - sizeof(uint32_t)); + uint32_t lutOffset; serialization::readPod(file, lutOffset); - file.seek(lutOffset + sizeof(size_t) * currentPage); - size_t pagePos; + file.seek(lutOffset + sizeof(uint32_t) * currentPage); + uint32_t pagePos; serialization::readPod(file, pagePos); file.seek(pagePos); diff --git a/lib/Epub/Epub/Section.h b/lib/Epub/Epub/Section.h index 93e0d6c..bc2efab 100644 --- a/lib/Epub/Epub/Section.h +++ b/lib/Epub/Epub/Section.h @@ -12,11 +12,11 @@ class Section { const int spineIndex; GfxRenderer& renderer; std::string filePath; - File file; + FsFile file; void writeSectionFileHeader(int fontId, float lineCompression, bool extraParagraphSpacing, int viewportWidth, int viewportHeight); - size_t onPageComplete(std::unique_ptr page); + uint32_t onPageComplete(std::unique_ptr page); public: int pageCount = 0; diff --git a/lib/Epub/Epub/blocks/TextBlock.cpp b/lib/Epub/Epub/blocks/TextBlock.cpp index c20b37d..3119305 100644 --- a/lib/Epub/Epub/blocks/TextBlock.cpp +++ b/lib/Epub/Epub/blocks/TextBlock.cpp @@ -24,7 +24,7 @@ void TextBlock::render(const GfxRenderer& renderer, const int fontId, const int } } -bool TextBlock::serialize(File& file) const { +bool TextBlock::serialize(FsFile& file) const { if (words.size() != wordXpos.size() || words.size() != wordStyles.size()) { Serial.printf("[%lu] [TXB] Serialization failed: size mismatch (words=%u, xpos=%u, styles=%u)\n", millis(), words.size(), wordXpos.size(), wordStyles.size()); @@ -43,7 +43,7 @@ bool TextBlock::serialize(File& file) const { return true; } -std::unique_ptr TextBlock::deserialize(File& file) { +std::unique_ptr TextBlock::deserialize(FsFile& file) { uint32_t wc; std::list words; std::list wordXpos; diff --git a/lib/Epub/Epub/blocks/TextBlock.h b/lib/Epub/Epub/blocks/TextBlock.h index 9dfde60..95d8884 100644 --- a/lib/Epub/Epub/blocks/TextBlock.h +++ b/lib/Epub/Epub/blocks/TextBlock.h @@ -1,6 +1,6 @@ #pragma once #include -#include +#include #include #include @@ -36,6 +36,6 @@ class TextBlock final : public Block { // given a renderer works out where to break the words into lines void render(const GfxRenderer& renderer, int fontId, int x, int y) const; BlockType getType() override { return TEXT_BLOCK; } - bool serialize(File& file) const; - static std::unique_ptr deserialize(File& file); + bool serialize(FsFile& file) const; + static std::unique_ptr deserialize(FsFile& file); }; diff --git a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp index b2dc2c0..a2b6189 100644 --- a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp +++ b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp @@ -1,8 +1,8 @@ #include "ChapterHtmlSlimParser.h" -#include #include #include +#include #include #include "../Page.h" @@ -218,8 +218,8 @@ bool ChapterHtmlSlimParser::parseAndBuildPages() { return false; } - File file; - if (!FsHelpers::openFileForRead("EHP", filepath, file)) { + FsFile file; + if (!SdMan.openFileForRead("EHP", filepath, file)) { XML_ParserFree(parser); return false; } diff --git a/lib/Epub/Epub/parsers/ContentOpfParser.cpp b/lib/Epub/Epub/parsers/ContentOpfParser.cpp index a62b2d0..ae964ce 100644 --- a/lib/Epub/Epub/parsers/ContentOpfParser.cpp +++ b/lib/Epub/Epub/parsers/ContentOpfParser.cpp @@ -35,8 +35,8 @@ ContentOpfParser::~ContentOpfParser() { if (tempItemStore) { tempItemStore.close(); } - if (SD.exists((cachePath + itemCacheFile).c_str())) { - SD.remove((cachePath + itemCacheFile).c_str()); + if (SdMan.exists((cachePath + itemCacheFile).c_str())) { + SdMan.remove((cachePath + itemCacheFile).c_str()); } } @@ -104,7 +104,7 @@ void XMLCALL ContentOpfParser::startElement(void* userData, const XML_Char* name if (self->state == IN_PACKAGE && (strcmp(name, "manifest") == 0 || strcmp(name, "opf:manifest") == 0)) { self->state = IN_MANIFEST; - if (!FsHelpers::openFileForWrite("COF", self->cachePath + itemCacheFile, self->tempItemStore)) { + if (!SdMan.openFileForWrite("COF", self->cachePath + itemCacheFile, self->tempItemStore)) { Serial.printf( "[%lu] [COF] Couldn't open temp items file for writing. This is probably going to be a fatal error.\n", millis()); @@ -114,7 +114,7 @@ void XMLCALL ContentOpfParser::startElement(void* userData, const XML_Char* name if (self->state == IN_PACKAGE && (strcmp(name, "spine") == 0 || strcmp(name, "opf:spine") == 0)) { self->state = IN_SPINE; - if (!FsHelpers::openFileForRead("COF", self->cachePath + itemCacheFile, self->tempItemStore)) { + if (!SdMan.openFileForRead("COF", self->cachePath + itemCacheFile, self->tempItemStore)) { Serial.printf( "[%lu] [COF] Couldn't open temp items file for reading. This is probably going to be a fatal error.\n", millis()); diff --git a/lib/Epub/Epub/parsers/ContentOpfParser.h b/lib/Epub/Epub/parsers/ContentOpfParser.h index 5415de6..2a7dd48 100644 --- a/lib/Epub/Epub/parsers/ContentOpfParser.h +++ b/lib/Epub/Epub/parsers/ContentOpfParser.h @@ -22,7 +22,7 @@ class ContentOpfParser final : public Print { XML_Parser parser = nullptr; ParserState state = START; BookMetadataCache* cache; - File tempItemStore; + FsFile tempItemStore; std::string coverItemId; static void startElement(void* userData, const XML_Char* name, const XML_Char** atts); diff --git a/lib/FsHelpers/FsHelpers.cpp b/lib/FsHelpers/FsHelpers.cpp index c8b59ce..4bd8fbf 100644 --- a/lib/FsHelpers/FsHelpers.cpp +++ b/lib/FsHelpers/FsHelpers.cpp @@ -1,81 +1,7 @@ #include "FsHelpers.h" -#include - #include -bool FsHelpers::openFileForRead(const char* moduleName, const char* path, File& file) { - if (!SD.exists(path)) { - Serial.printf("[%lu] [%s] File does not exist: %s\n", millis(), moduleName, path); - return false; - } - - file = SD.open(path, FILE_READ); - if (!file) { - Serial.printf("[%lu] [%s] Failed to open file for reading: %s\n", millis(), moduleName, path); - return false; - } - return true; -} - -bool FsHelpers::openFileForRead(const char* moduleName, const std::string& path, File& file) { - return openFileForRead(moduleName, path.c_str(), file); -} - -bool FsHelpers::openFileForRead(const char* moduleName, const String& path, File& file) { - return openFileForRead(moduleName, path.c_str(), file); -} - -bool FsHelpers::openFileForWrite(const char* moduleName, const char* path, File& file) { - file = SD.open(path, FILE_WRITE, true); - if (!file) { - Serial.printf("[%lu] [%s] Failed to open file for writing: %s\n", millis(), moduleName, path); - return false; - } - return true; -} - -bool FsHelpers::openFileForWrite(const char* moduleName, const std::string& path, File& file) { - return openFileForWrite(moduleName, path.c_str(), file); -} - -bool FsHelpers::openFileForWrite(const char* moduleName, const String& path, File& file) { - return openFileForWrite(moduleName, path.c_str(), file); -} - -bool FsHelpers::removeDir(const char* path) { - // 1. Open the directory - File dir = SD.open(path); - if (!dir) { - return false; - } - if (!dir.isDirectory()) { - return false; - } - - File file = dir.openNextFile(); - while (file) { - String filePath = path; - if (!filePath.endsWith("/")) { - filePath += "/"; - } - filePath += file.name(); - - if (file.isDirectory()) { - if (!removeDir(filePath.c_str())) { - return false; - } - } else { - if (!SD.remove(filePath.c_str())) { - return false; - } - } - file = dir.openNextFile(); - } - - return SD.rmdir(path); -} - std::string FsHelpers::normalisePath(const std::string& path) { std::vector components; std::string component; diff --git a/lib/FsHelpers/FsHelpers.h b/lib/FsHelpers/FsHelpers.h index 0dff145..5bb4218 100644 --- a/lib/FsHelpers/FsHelpers.h +++ b/lib/FsHelpers/FsHelpers.h @@ -1,14 +1,7 @@ #pragma once -#include +#include class FsHelpers { public: - static bool openFileForRead(const char* moduleName, const char* path, File& file); - static bool openFileForRead(const char* moduleName, const std::string& path, File& file); - static bool openFileForRead(const char* moduleName, const String& path, File& file); - static bool openFileForWrite(const char* moduleName, const char* path, File& file); - static bool openFileForWrite(const char* moduleName, const std::string& path, File& file); - static bool openFileForWrite(const char* moduleName, const String& path, File& file); - static bool removeDir(const char* path); static std::string normalisePath(const std::string& path); }; diff --git a/lib/GfxRenderer/Bitmap.cpp b/lib/GfxRenderer/Bitmap.cpp index a034c75..7c46df1 100644 --- a/lib/GfxRenderer/Bitmap.cpp +++ b/lib/GfxRenderer/Bitmap.cpp @@ -123,7 +123,7 @@ Bitmap::~Bitmap() { delete[] errorNextRow; } -uint16_t Bitmap::readLE16(File& f) { +uint16_t Bitmap::readLE16(FsFile& f) { const int c0 = f.read(); const int c1 = f.read(); const auto b0 = static_cast(c0 < 0 ? 0 : c0); @@ -131,7 +131,7 @@ uint16_t Bitmap::readLE16(File& f) { return static_cast(b0) | (static_cast(b1) << 8); } -uint32_t Bitmap::readLE32(File& f) { +uint32_t Bitmap::readLE32(FsFile& f) { const int c0 = f.read(); const int c1 = f.read(); const int c2 = f.read(); @@ -192,7 +192,7 @@ BmpReaderError Bitmap::parseHeaders() { const uint16_t bfType = readLE16(file); if (bfType != 0x4D42) return BmpReaderError::NotBMP; - file.seek(8, SeekCur); + file.seekCur(8); bfOffBits = readLE32(file); // --- DIB HEADER --- @@ -214,10 +214,10 @@ BmpReaderError Bitmap::parseHeaders() { // Allow BI_RGB (0) for all, and BI_BITFIELDS (3) for 32bpp which is common for BGRA masks. if (!(comp == 0 || (bpp == 32 && comp == 3))) return BmpReaderError::UnsupportedCompression; - file.seek(12, SeekCur); // biSizeImage, biXPelsPerMeter, biYPelsPerMeter + file.seekCur(12); // biSizeImage, biXPelsPerMeter, biYPelsPerMeter const uint32_t colorsUsed = readLE32(file); if (colorsUsed > 256u) return BmpReaderError::PaletteTooLarge; - file.seek(4, SeekCur); // biClrImportant + file.seekCur(4); // biClrImportant if (width <= 0 || height <= 0) return BmpReaderError::BadDimensions; diff --git a/lib/GfxRenderer/Bitmap.h b/lib/GfxRenderer/Bitmap.h index 744cb61..7e79964 100644 --- a/lib/GfxRenderer/Bitmap.h +++ b/lib/GfxRenderer/Bitmap.h @@ -1,6 +1,6 @@ #pragma once -#include +#include enum class BmpReaderError : uint8_t { Ok = 0, @@ -28,7 +28,7 @@ class Bitmap { public: static const char* errorToString(BmpReaderError err); - explicit Bitmap(File& file) : file(file) {} + explicit Bitmap(FsFile& file) : file(file) {} ~Bitmap(); BmpReaderError parseHeaders(); BmpReaderError readRow(uint8_t* data, uint8_t* rowBuffer, int rowY) const; @@ -40,10 +40,10 @@ class Bitmap { int getRowBytes() const { return rowBytes; } private: - static uint16_t readLE16(File& f); - static uint32_t readLE32(File& f); + static uint16_t readLE16(FsFile& f); + static uint32_t readLE32(FsFile& f); - File& file; + FsFile& file; int width = 0; int height = 0; bool topDown = false; diff --git a/lib/GfxRenderer/GfxRenderer.h b/lib/GfxRenderer/GfxRenderer.h index 241c76e..f6f5fe0 100644 --- a/lib/GfxRenderer/GfxRenderer.h +++ b/lib/GfxRenderer/GfxRenderer.h @@ -2,7 +2,6 @@ #include #include -#include #include diff --git a/lib/JpegToBmpConverter/JpegToBmpConverter.cpp b/lib/JpegToBmpConverter/JpegToBmpConverter.cpp index 0a19701..9c61ef0 100644 --- a/lib/JpegToBmpConverter/JpegToBmpConverter.cpp +++ b/lib/JpegToBmpConverter/JpegToBmpConverter.cpp @@ -1,5 +1,7 @@ #include "JpegToBmpConverter.h" +#include +#include #include #include @@ -7,7 +9,7 @@ // Context structure for picojpeg callback struct JpegReadContext { - File& file; + FsFile& file; uint8_t buffer[512]; size_t bufferPos; size_t bufferFilled; @@ -426,7 +428,7 @@ unsigned char JpegToBmpConverter::jpegReadCallback(unsigned char* pBuf, const un } // Core function: Convert JPEG file to 2-bit BMP -bool JpegToBmpConverter::jpegFileToBmpStream(File& jpegFile, Print& bmpOut) { +bool JpegToBmpConverter::jpegFileToBmpStream(FsFile& jpegFile, Print& bmpOut) { Serial.printf("[%lu] [JPG] Converting JPEG to BMP\n", millis()); // Setup context for picojpeg callback diff --git a/lib/JpegToBmpConverter/JpegToBmpConverter.h b/lib/JpegToBmpConverter/JpegToBmpConverter.h index 1cb76e5..f61bd8e 100644 --- a/lib/JpegToBmpConverter/JpegToBmpConverter.h +++ b/lib/JpegToBmpConverter/JpegToBmpConverter.h @@ -1,7 +1,7 @@ #pragma once -#include - +class FsFile; +class Print; class ZipFile; class JpegToBmpConverter { @@ -11,5 +11,5 @@ class JpegToBmpConverter { unsigned char* pBytes_actually_read, void* pCallback_data); public: - static bool jpegFileToBmpStream(File& jpegFile, Print& bmpOut); + static bool jpegFileToBmpStream(FsFile& jpegFile, Print& bmpOut); }; diff --git a/lib/Serialization/Serialization.h b/lib/Serialization/Serialization.h index e6bcbf2..afea564 100644 --- a/lib/Serialization/Serialization.h +++ b/lib/Serialization/Serialization.h @@ -1,5 +1,5 @@ #pragma once -#include +#include #include @@ -10,7 +10,7 @@ static void writePod(std::ostream& os, const T& value) { } template -static void writePod(File& file, const T& value) { +static void writePod(FsFile& file, const T& value) { file.write(reinterpret_cast(&value), sizeof(T)); } @@ -20,7 +20,7 @@ static void readPod(std::istream& is, T& value) { } template -static void readPod(File& file, T& value) { +static void readPod(FsFile& file, T& value) { file.read(reinterpret_cast(&value), sizeof(T)); } @@ -30,7 +30,7 @@ static void writeString(std::ostream& os, const std::string& s) { os.write(s.data(), len); } -static void writeString(File& file, const std::string& s) { +static void writeString(FsFile& file, const std::string& s) { const uint32_t len = s.size(); writePod(file, len); file.write(reinterpret_cast(s.data()), len); @@ -43,10 +43,10 @@ static void readString(std::istream& is, std::string& s) { is.read(&s[0], len); } -static void readString(File& file, std::string& s) { +static void readString(FsFile& file, std::string& s) { uint32_t len; readPod(file, len); s.resize(len); - file.read(reinterpret_cast(&s[0]), len); + file.read(&s[0], len); } } // namespace serialization diff --git a/lib/Xtc/Xtc.cpp b/lib/Xtc/Xtc.cpp index 9e948ff..8f79c9d 100644 --- a/lib/Xtc/Xtc.cpp +++ b/lib/Xtc/Xtc.cpp @@ -9,7 +9,7 @@ #include #include -#include +#include bool Xtc::load() { Serial.printf("[%lu] [XTC] Loading XTC: %s\n", millis(), filepath.c_str()); @@ -31,12 +31,12 @@ bool Xtc::load() { } bool Xtc::clearCache() const { - if (!SD.exists(cachePath.c_str())) { + if (!SdMan.exists(cachePath.c_str())) { Serial.printf("[%lu] [XTC] Cache does not exist, no action needed\n", millis()); return true; } - if (!FsHelpers::removeDir(cachePath.c_str())) { + if (!SdMan.removeDir(cachePath.c_str())) { Serial.printf("[%lu] [XTC] Failed to clear cache\n", millis()); return false; } @@ -46,17 +46,17 @@ bool Xtc::clearCache() const { } void Xtc::setupCacheDir() const { - if (SD.exists(cachePath.c_str())) { + if (SdMan.exists(cachePath.c_str())) { return; } // Create directories recursively for (size_t i = 1; i < cachePath.length(); i++) { if (cachePath[i] == '/') { - SD.mkdir(cachePath.substr(0, i).c_str()); + SdMan.mkdir(cachePath.substr(0, i).c_str()); } } - SD.mkdir(cachePath.c_str()); + SdMan.mkdir(cachePath.c_str()); } std::string Xtc::getTitle() const { @@ -106,7 +106,7 @@ std::string Xtc::getCoverBmpPath() const { return cachePath + "/cover.bmp"; } bool Xtc::generateCoverBmp() const { // Already generated - if (SD.exists(getCoverBmpPath().c_str())) { + if (SdMan.exists(getCoverBmpPath().c_str())) { return true; } @@ -157,8 +157,8 @@ bool Xtc::generateCoverBmp() const { } // Create BMP file - File coverBmp; - if (!FsHelpers::openFileForWrite("XTC", getCoverBmpPath(), coverBmp)) { + FsFile coverBmp; + if (!SdMan.openFileForWrite("XTC", getCoverBmpPath(), coverBmp)) { Serial.printf("[%lu] [XTC] Failed to create cover BMP file\n", millis()); free(pageBuffer); return false; diff --git a/lib/Xtc/Xtc/XtcParser.cpp b/lib/Xtc/Xtc/XtcParser.cpp index b1f4f90..c33e719 100644 --- a/lib/Xtc/Xtc/XtcParser.cpp +++ b/lib/Xtc/Xtc/XtcParser.cpp @@ -9,6 +9,7 @@ #include #include +#include #include @@ -33,7 +34,7 @@ XtcError XtcParser::open(const char* filepath) { } // Open file - if (!FsHelpers::openFileForRead("XTC", filepath, m_file)) { + if (!SdMan.openFileForRead("XTC", filepath, m_file)) { m_lastError = XtcError::FILE_NOT_FOUND; return m_lastError; } @@ -419,8 +420,8 @@ XtcError XtcParser::loadPageStreaming(uint32_t pageIndex, } bool XtcParser::isValidXtcFile(const char* filepath) { - File file = SD.open(filepath, FILE_READ); - if (!file) { + FsFile file; + if (!SdMan.openFileForRead("XTC", filepath, file)) { return false; } diff --git a/lib/Xtc/Xtc/XtcParser.h b/lib/Xtc/Xtc/XtcParser.h index 25f1c33..2d2b780 100644 --- a/lib/Xtc/Xtc/XtcParser.h +++ b/lib/Xtc/Xtc/XtcParser.h @@ -7,7 +7,7 @@ #pragma once -#include +#include #include #include @@ -80,7 +80,7 @@ class XtcParser { XtcError getLastError() const { return m_lastError; } private: - File m_file; + FsFile m_file; bool m_isOpen; XtcHeader m_header; std::vector m_pageTable; diff --git a/lib/ZipFile/ZipFile.cpp b/lib/ZipFile/ZipFile.cpp index 23cf0e8..2a97858 100644 --- a/lib/ZipFile/ZipFile.cpp +++ b/lib/ZipFile/ZipFile.cpp @@ -1,7 +1,7 @@ #include "ZipFile.h" -#include #include +#include #include bool inflateOneShot(const uint8_t* inputBuf, const size_t deflatedSize, uint8_t* outputBuf, const size_t inflatedSize) { @@ -49,29 +49,29 @@ bool ZipFile::loadAllFileStatSlims() { fileStatSlimCache.reserve(zipDetails.totalEntries); while (file.available()) { - file.read(reinterpret_cast(&sig), 4); + file.read(&sig, 4); if (sig != 0x02014b50) break; // End of list FileStatSlim fileStat = {}; - file.seek(6, SeekCur); - file.read(reinterpret_cast(&fileStat.method), 2); - file.seek(8, SeekCur); - file.read(reinterpret_cast(&fileStat.compressedSize), 4); - file.read(reinterpret_cast(&fileStat.uncompressedSize), 4); + file.seekCur(6); + file.read(&fileStat.method, 2); + file.seekCur(8); + file.read(&fileStat.compressedSize, 4); + file.read(&fileStat.uncompressedSize, 4); uint16_t nameLen, m, k; - file.read(reinterpret_cast(&nameLen), 2); - file.read(reinterpret_cast(&m), 2); - file.read(reinterpret_cast(&k), 2); - file.seek(8, SeekCur); - file.read(reinterpret_cast(&fileStat.localHeaderOffset), 4); - file.read(reinterpret_cast(itemName), nameLen); + file.read(&nameLen, 2); + file.read(&m, 2); + file.read(&k, 2); + file.seekCur(8); + file.read(&fileStat.localHeaderOffset, 4); + file.read(itemName, nameLen); itemName[nameLen] = '\0'; fileStatSlimCache.emplace(itemName, fileStat); // Skip the rest of this entry (extra field + comment) - file.seek(m + k, SeekCur); + file.seekCur(m + k); } if (!wasOpen) { @@ -109,21 +109,21 @@ bool ZipFile::loadFileStatSlim(const char* filename, FileStatSlim* fileStat) { bool found = false; while (file.available()) { - file.read(reinterpret_cast(&sig), 4); + file.read(&sig, 4); if (sig != 0x02014b50) break; // End of list - file.seek(6, SeekCur); - file.read(reinterpret_cast(&fileStat->method), 2); - file.seek(8, SeekCur); - file.read(reinterpret_cast(&fileStat->compressedSize), 4); - file.read(reinterpret_cast(&fileStat->uncompressedSize), 4); + file.seekCur(6); + file.read(&fileStat->method, 2); + file.seekCur(8); + file.read(&fileStat->compressedSize, 4); + file.read(&fileStat->uncompressedSize, 4); uint16_t nameLen, m, k; - file.read(reinterpret_cast(&nameLen), 2); - file.read(reinterpret_cast(&m), 2); - file.read(reinterpret_cast(&k), 2); - file.seek(8, SeekCur); - file.read(reinterpret_cast(&fileStat->localHeaderOffset), 4); - file.read(reinterpret_cast(itemName), nameLen); + file.read(&nameLen, 2); + file.read(&m, 2); + file.read(&k, 2); + file.seekCur(8); + file.read(&fileStat->localHeaderOffset, 4); + file.read(itemName, nameLen); itemName[nameLen] = '\0'; if (strcmp(itemName, filename) == 0) { @@ -132,7 +132,7 @@ bool ZipFile::loadFileStatSlim(const char* filename, FileStatSlim* fileStat) { } // Skip the rest of this entry (extra field + comment) - file.seek(m + k, SeekCur); + file.seekCur(m + k); } if (!wasOpen) { @@ -243,7 +243,7 @@ bool ZipFile::loadZipDetails() { } bool ZipFile::open() { - if (!FsHelpers::openFileForRead("ZIP", filePath, file)) { + if (!SdMan.openFileForRead("ZIP", filePath, file)) { return false; } return true; diff --git a/lib/ZipFile/ZipFile.h b/lib/ZipFile/ZipFile.h index 4758f16..0144ed4 100644 --- a/lib/ZipFile/ZipFile.h +++ b/lib/ZipFile/ZipFile.h @@ -1,5 +1,5 @@ #pragma once -#include +#include #include #include @@ -21,7 +21,7 @@ class ZipFile { private: const std::string& filePath; - File file; + FsFile file; ZipDetails zipDetails = {0, 0, false}; std::unordered_map fileStatSlimCache; diff --git a/open-x4-sdk b/open-x4-sdk index 98a5aa1..bd4e670 160000 --- a/open-x4-sdk +++ b/open-x4-sdk @@ -1 +1 @@ -Subproject commit 98a5aa1f8969ccd317c9b45bf0fa84b6c82e167f +Subproject commit bd4e6707503ab9c97d13ee0d8f8c69e9ff03cd12 diff --git a/platformio.ini b/platformio.ini index fb520e5..142307c 100644 --- a/platformio.ini +++ b/platformio.ini @@ -21,6 +21,7 @@ build_flags = -DARDUINO_USB_CDC_ON_BOOT=1 -DMINIZ_NO_ZLIB_COMPATIBLE_NAMES=1 -DEINK_DISPLAY_SINGLE_BUFFER_MODE=1 + -DDISABLE_FS_H_WARNING=1 # https://libexpat.github.io/doc/api/latest/#XML_GE -DXML_GE=0 -DXML_CONTEXT_BYTES=1024 @@ -39,6 +40,7 @@ lib_deps = BatteryMonitor=symlink://open-x4-sdk/libs/hardware/BatteryMonitor InputManager=symlink://open-x4-sdk/libs/hardware/InputManager EInkDisplay=symlink://open-x4-sdk/libs/display/EInkDisplay + SDCardManager=symlink://open-x4-sdk/libs/hardware/SDCardManager ArduinoJson @ 7.4.2 QRCode @ 0.0.1 diff --git a/src/CrossPointSettings.cpp b/src/CrossPointSettings.cpp index 41c3322..0f2f13e 100644 --- a/src/CrossPointSettings.cpp +++ b/src/CrossPointSettings.cpp @@ -1,8 +1,7 @@ #include "CrossPointSettings.h" -#include #include -#include +#include #include // Initialize the static instance @@ -17,10 +16,10 @@ constexpr char SETTINGS_FILE[] = "/.crosspoint/settings.bin"; bool CrossPointSettings::saveToFile() const { // Make sure the directory exists - SD.mkdir("/.crosspoint"); + SdMan.mkdir("/.crosspoint"); - File outputFile; - if (!FsHelpers::openFileForWrite("CPS", SETTINGS_FILE, outputFile)) { + FsFile outputFile; + if (!SdMan.openFileForWrite("CPS", SETTINGS_FILE, outputFile)) { return false; } @@ -40,8 +39,8 @@ bool CrossPointSettings::saveToFile() const { } bool CrossPointSettings::loadFromFile() { - File inputFile; - if (!FsHelpers::openFileForRead("CPS", SETTINGS_FILE, inputFile)) { + FsFile inputFile; + if (!SdMan.openFileForRead("CPS", SETTINGS_FILE, inputFile)) { return false; } diff --git a/src/CrossPointState.cpp b/src/CrossPointState.cpp index 9010822..31cb2ac 100644 --- a/src/CrossPointState.cpp +++ b/src/CrossPointState.cpp @@ -1,7 +1,7 @@ #include "CrossPointState.h" -#include #include +#include #include namespace { @@ -12,8 +12,8 @@ constexpr char STATE_FILE[] = "/.crosspoint/state.bin"; CrossPointState CrossPointState::instance; bool CrossPointState::saveToFile() const { - File outputFile; - if (!FsHelpers::openFileForWrite("CPS", STATE_FILE, outputFile)) { + FsFile outputFile; + if (!SdMan.openFileForWrite("CPS", STATE_FILE, outputFile)) { return false; } @@ -24,8 +24,8 @@ bool CrossPointState::saveToFile() const { } bool CrossPointState::loadFromFile() { - File inputFile; - if (!FsHelpers::openFileForRead("CPS", STATE_FILE, inputFile)) { + FsFile inputFile; + if (!SdMan.openFileForRead("CPS", STATE_FILE, inputFile)) { return false; } diff --git a/src/MappedInputManager.cpp b/src/MappedInputManager.cpp index 7ee2295..17be4cf 100644 --- a/src/MappedInputManager.cpp +++ b/src/MappedInputManager.cpp @@ -1,5 +1,7 @@ #include "MappedInputManager.h" +#include "CrossPointSettings.h" + decltype(InputManager::BTN_BACK) MappedInputManager::mapButton(const Button button) const { const auto frontLayout = static_cast(SETTINGS.frontButtonLayout); const auto sideLayout = static_cast(SETTINGS.sideButtonLayout); diff --git a/src/MappedInputManager.h b/src/MappedInputManager.h index 138d793..62065fe 100644 --- a/src/MappedInputManager.h +++ b/src/MappedInputManager.h @@ -2,8 +2,6 @@ #include -#include "CrossPointSettings.h" - class MappedInputManager { public: enum class Button { Back, Confirm, Left, Right, Up, Down, Power, PageBack, PageForward }; diff --git a/src/WifiCredentialStore.cpp b/src/WifiCredentialStore.cpp index 856098f..be865b8 100644 --- a/src/WifiCredentialStore.cpp +++ b/src/WifiCredentialStore.cpp @@ -1,8 +1,7 @@ #include "WifiCredentialStore.h" -#include #include -#include +#include #include // Initialize the static instance @@ -30,10 +29,10 @@ void WifiCredentialStore::obfuscate(std::string& data) const { bool WifiCredentialStore::saveToFile() const { // Make sure the directory exists - SD.mkdir("/.crosspoint"); + SdMan.mkdir("/.crosspoint"); - File file; - if (!FsHelpers::openFileForWrite("WCS", WIFI_FILE, file)) { + FsFile file; + if (!SdMan.openFileForWrite("WCS", WIFI_FILE, file)) { return false; } @@ -60,8 +59,8 @@ bool WifiCredentialStore::saveToFile() const { } bool WifiCredentialStore::loadFromFile() { - File file; - if (!FsHelpers::openFileForRead("WCS", WIFI_FILE, file)) { + FsFile file; + if (!SdMan.openFileForRead("WCS", WIFI_FILE, file)) { return false; } diff --git a/src/activities/Activity.h b/src/activities/Activity.h index 66dfde1..aad5591 100644 --- a/src/activities/Activity.h +++ b/src/activities/Activity.h @@ -5,8 +5,7 @@ #include #include -#include "../MappedInputManager.h" - +class MappedInputManager; class GfxRenderer; class Activity { diff --git a/src/activities/boot_sleep/SleepActivity.cpp b/src/activities/boot_sleep/SleepActivity.cpp index fdf84b6..fa60523 100644 --- a/src/activities/boot_sleep/SleepActivity.cpp +++ b/src/activities/boot_sleep/SleepActivity.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include @@ -58,29 +58,31 @@ void SleepActivity::renderPopup(const char* message) const { void SleepActivity::renderCustomSleepScreen() const { // Check if we have a /sleep directory - auto dir = SD.open("/sleep"); + auto dir = SdMan.open("/sleep"); if (dir && dir.isDirectory()) { std::vector files; + char name[128]; // collect all valid BMP files - for (File file = dir.openNextFile(); file; file = dir.openNextFile()) { + for (auto file = dir.openNextFile(); file; file = dir.openNextFile()) { if (file.isDirectory()) { file.close(); continue; } - auto filename = std::string(file.name()); + file.getName(name, sizeof(name)); + auto filename = std::string(name); if (filename[0] == '.') { file.close(); continue; } if (filename.substr(filename.length() - 4) != ".bmp") { - Serial.printf("[%lu] [SLP] Skipping non-.bmp file name: %s\n", millis(), file.name()); + Serial.printf("[%lu] [SLP] Skipping non-.bmp file name: %s\n", millis(), name); file.close(); continue; } Bitmap bitmap(file); if (bitmap.parseHeaders() != BmpReaderError::Ok) { - Serial.printf("[%lu] [SLP] Skipping invalid BMP file: %s\n", millis(), file.name()); + Serial.printf("[%lu] [SLP] Skipping invalid BMP file: %s\n", millis(), name); file.close(); continue; } @@ -92,8 +94,8 @@ void SleepActivity::renderCustomSleepScreen() const { // Generate a random number between 1 and numFiles const auto randomFileIndex = random(numFiles); const auto filename = "/sleep/" + files[randomFileIndex]; - File file; - if (FsHelpers::openFileForRead("SLP", filename, file)) { + FsFile file; + if (SdMan.openFileForRead("SLP", filename, file)) { Serial.printf("[%lu] [SLP] Randomly loading: /sleep/%s\n", millis(), files[randomFileIndex].c_str()); delay(100); Bitmap bitmap(file); @@ -109,8 +111,8 @@ void SleepActivity::renderCustomSleepScreen() const { // Look for sleep.bmp on the root of the sd card to determine if we should // render a custom sleep screen instead of the default. - File file; - if (FsHelpers::openFileForRead("SLP", "/sleep.bmp", file)) { + FsFile file; + if (SdMan.openFileForRead("SLP", "/sleep.bmp", file)) { Bitmap bitmap(file); if (bitmap.parseHeaders() == BmpReaderError::Ok) { Serial.printf("[%lu] [SLP] Loading: /sleep.bmp\n", millis()); @@ -224,8 +226,8 @@ void SleepActivity::renderCoverSleepScreen() const { coverBmpPath = lastEpub.getCoverBmpPath(); } - File file; - if (FsHelpers::openFileForRead("SLP", coverBmpPath, file)) { + FsFile file; + if (SdMan.openFileForRead("SLP", coverBmpPath, file)) { Bitmap bitmap(file); if (bitmap.parseHeaders() == BmpReaderError::Ok) { renderBitmapSleepScreen(bitmap); diff --git a/src/activities/home/HomeActivity.cpp b/src/activities/home/HomeActivity.cpp index 1dee518..8291826 100644 --- a/src/activities/home/HomeActivity.cpp +++ b/src/activities/home/HomeActivity.cpp @@ -1,10 +1,10 @@ #include "HomeActivity.h" #include -#include -#include +#include #include "CrossPointState.h" +#include "MappedInputManager.h" #include "config.h" void HomeActivity::taskTrampoline(void* param) { @@ -20,7 +20,7 @@ void HomeActivity::onEnter() { renderingMutex = xSemaphoreCreateMutex(); // Check if we have a book to continue reading - hasContinueReading = !APP_STATE.openEpubPath.empty() && SD.exists(APP_STATE.openEpubPath.c_str()); + hasContinueReading = !APP_STATE.openEpubPath.empty() && SdMan.exists(APP_STATE.openEpubPath.c_str()); selectorIndex = 0; diff --git a/src/activities/network/CrossPointWebServerActivity.cpp b/src/activities/network/CrossPointWebServerActivity.cpp index eda20a8..a0db3c3 100644 --- a/src/activities/network/CrossPointWebServerActivity.cpp +++ b/src/activities/network/CrossPointWebServerActivity.cpp @@ -3,12 +3,12 @@ #include #include #include -#include #include #include #include +#include "MappedInputManager.h" #include "NetworkModeSelectionActivity.h" #include "WifiSelectionActivity.h" #include "config.h" diff --git a/src/activities/network/NetworkModeSelectionActivity.cpp b/src/activities/network/NetworkModeSelectionActivity.cpp index 0cc3594..57afc6e 100644 --- a/src/activities/network/NetworkModeSelectionActivity.cpp +++ b/src/activities/network/NetworkModeSelectionActivity.cpp @@ -1,8 +1,8 @@ #include "NetworkModeSelectionActivity.h" #include -#include +#include "MappedInputManager.h" #include "config.h" namespace { diff --git a/src/activities/network/WifiSelectionActivity.cpp b/src/activities/network/WifiSelectionActivity.cpp index 9f0e502..8977a78 100644 --- a/src/activities/network/WifiSelectionActivity.cpp +++ b/src/activities/network/WifiSelectionActivity.cpp @@ -5,6 +5,7 @@ #include +#include "MappedInputManager.h" #include "WifiCredentialStore.h" #include "activities/util/KeyboardEntryActivity.h" #include "config.h" diff --git a/src/activities/reader/EpubReaderActivity.cpp b/src/activities/reader/EpubReaderActivity.cpp index 6864a56..ab03550 100644 --- a/src/activities/reader/EpubReaderActivity.cpp +++ b/src/activities/reader/EpubReaderActivity.cpp @@ -3,12 +3,13 @@ #include #include #include -#include +#include #include "Battery.h" #include "CrossPointSettings.h" #include "CrossPointState.h" #include "EpubReaderChapterSelectionActivity.h" +#include "MappedInputManager.h" #include "config.h" namespace { @@ -54,8 +55,8 @@ void EpubReaderActivity::onEnter() { epub->setupCacheDir(); - File f; - if (FsHelpers::openFileForRead("ERS", epub->getCachePath() + "/progress.bin", f)) { + FsFile f; + if (SdMan.openFileForRead("ERS", epub->getCachePath() + "/progress.bin", f)) { uint8_t data[4]; if (f.read(data, 4) == 4) { currentSpineIndex = data[0] + (data[1] << 8); @@ -346,8 +347,8 @@ void EpubReaderActivity::renderScreen() { Serial.printf("[%lu] [ERS] Rendered page in %dms\n", millis(), millis() - start); } - File f; - if (FsHelpers::openFileForWrite("ERS", epub->getCachePath() + "/progress.bin", f)) { + FsFile f; + if (SdMan.openFileForWrite("ERS", epub->getCachePath() + "/progress.bin", f)) { uint8_t data[4]; data[0] = currentSpineIndex & 0xFF; data[1] = (currentSpineIndex >> 8) & 0xFF; diff --git a/src/activities/reader/EpubReaderChapterSelectionActivity.cpp b/src/activities/reader/EpubReaderChapterSelectionActivity.cpp index dc0bee5..8245ed2 100644 --- a/src/activities/reader/EpubReaderChapterSelectionActivity.cpp +++ b/src/activities/reader/EpubReaderChapterSelectionActivity.cpp @@ -1,9 +1,8 @@ #include "EpubReaderChapterSelectionActivity.h" #include -#include -#include +#include "MappedInputManager.h" #include "config.h" namespace { diff --git a/src/activities/reader/FileSelectionActivity.cpp b/src/activities/reader/FileSelectionActivity.cpp index 447db6d..45ef6ef 100644 --- a/src/activities/reader/FileSelectionActivity.cpp +++ b/src/activities/reader/FileSelectionActivity.cpp @@ -1,9 +1,9 @@ #include "FileSelectionActivity.h" #include -#include -#include +#include +#include "MappedInputManager.h" #include "config.h" namespace { @@ -30,17 +30,19 @@ void FileSelectionActivity::taskTrampoline(void* param) { void FileSelectionActivity::loadFiles() { files.clear(); selectorIndex = 0; - auto root = SD.open(basepath.c_str()); - for (File file = root.openNextFile(); file; file = root.openNextFile()) { - auto filename = std::string(file.name()); - if (filename[0] == '.') { + auto root = SdMan.open(basepath.c_str()); + char name[128]; + for (auto file = root.openNextFile(); file; file = root.openNextFile()) { + file.getName(name, sizeof(name)); + if (name[0] == '.') { file.close(); continue; } if (file.isDirectory()) { - files.emplace_back(filename + "/"); + files.emplace_back(std::string(name) + "/"); } else { + auto filename = std::string(name); std::string ext4 = filename.length() >= 4 ? filename.substr(filename.length() - 4) : ""; std::string ext5 = filename.length() >= 5 ? filename.substr(filename.length() - 5) : ""; if (ext5 == ".epub" || ext5 == ".xtch" || ext4 == ".xtc") { diff --git a/src/activities/reader/ReaderActivity.cpp b/src/activities/reader/ReaderActivity.cpp index d98e167..d6a3aa6 100644 --- a/src/activities/reader/ReaderActivity.cpp +++ b/src/activities/reader/ReaderActivity.cpp @@ -1,7 +1,5 @@ #include "ReaderActivity.h" -#include - #include "Epub.h" #include "EpubReaderActivity.h" #include "FileSelectionActivity.h" @@ -29,7 +27,7 @@ bool ReaderActivity::isXtcFile(const std::string& path) { } std::unique_ptr ReaderActivity::loadEpub(const std::string& path) { - if (!SD.exists(path.c_str())) { + if (!SdMan.exists(path.c_str())) { Serial.printf("[%lu] [ ] File does not exist: %s\n", millis(), path.c_str()); return nullptr; } @@ -44,7 +42,7 @@ std::unique_ptr ReaderActivity::loadEpub(const std::string& path) { } std::unique_ptr ReaderActivity::loadXtc(const std::string& path) { - if (!SD.exists(path.c_str())) { + if (!SdMan.exists(path.c_str())) { Serial.printf("[%lu] [ ] File does not exist: %s\n", millis(), path.c_str()); return nullptr; } diff --git a/src/activities/reader/XtcReaderActivity.cpp b/src/activities/reader/XtcReaderActivity.cpp index 317594c..421b789 100644 --- a/src/activities/reader/XtcReaderActivity.cpp +++ b/src/activities/reader/XtcReaderActivity.cpp @@ -9,10 +9,10 @@ #include #include -#include +#include -#include "CrossPointSettings.h" #include "CrossPointState.h" +#include "MappedInputManager.h" #include "XtcReaderChapterSelectionActivity.h" #include "config.h" @@ -357,8 +357,8 @@ void XtcReaderActivity::renderPage() { } void XtcReaderActivity::saveProgress() const { - File f; - if (FsHelpers::openFileForWrite("XTR", xtc->getCachePath() + "/progress.bin", f)) { + FsFile f; + if (SdMan.openFileForWrite("XTR", xtc->getCachePath() + "/progress.bin", f)) { uint8_t data[4]; data[0] = currentPage & 0xFF; data[1] = (currentPage >> 8) & 0xFF; @@ -370,8 +370,8 @@ void XtcReaderActivity::saveProgress() const { } void XtcReaderActivity::loadProgress() { - File f; - if (FsHelpers::openFileForRead("XTR", xtc->getCachePath() + "/progress.bin", f)) { + FsFile f; + if (SdMan.openFileForRead("XTR", xtc->getCachePath() + "/progress.bin", f)) { uint8_t data[4]; if (f.read(data, 4) == 4) { currentPage = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); diff --git a/src/activities/reader/XtcReaderChapterSelectionActivity.cpp b/src/activities/reader/XtcReaderChapterSelectionActivity.cpp index 6c747f0..d00fc59 100644 --- a/src/activities/reader/XtcReaderChapterSelectionActivity.cpp +++ b/src/activities/reader/XtcReaderChapterSelectionActivity.cpp @@ -1,9 +1,8 @@ #include "XtcReaderChapterSelectionActivity.h" #include -#include -#include +#include "MappedInputManager.h" #include "config.h" namespace { diff --git a/src/activities/settings/OtaUpdateActivity.cpp b/src/activities/settings/OtaUpdateActivity.cpp index 846438b..8a04578 100644 --- a/src/activities/settings/OtaUpdateActivity.cpp +++ b/src/activities/settings/OtaUpdateActivity.cpp @@ -1,9 +1,9 @@ #include "OtaUpdateActivity.h" #include -#include #include +#include "MappedInputManager.h" #include "activities/network/WifiSelectionActivity.h" #include "config.h" #include "network/OtaUpdater.h" diff --git a/src/activities/settings/SettingsActivity.cpp b/src/activities/settings/SettingsActivity.cpp index 6a66ede..eea7a47 100644 --- a/src/activities/settings/SettingsActivity.cpp +++ b/src/activities/settings/SettingsActivity.cpp @@ -1,9 +1,9 @@ #include "SettingsActivity.h" #include -#include #include "CrossPointSettings.h" +#include "MappedInputManager.h" #include "OtaUpdateActivity.h" #include "config.h" diff --git a/src/activities/util/KeyboardEntryActivity.cpp b/src/activities/util/KeyboardEntryActivity.cpp index 7a13aab..dbfd109 100644 --- a/src/activities/util/KeyboardEntryActivity.cpp +++ b/src/activities/util/KeyboardEntryActivity.cpp @@ -1,6 +1,7 @@ #include "KeyboardEntryActivity.h" #include "../../config.h" +#include "MappedInputManager.h" // Keyboard layouts - lowercase const char* const KeyboardEntryActivity::keyboard[NUM_ROWS] = { diff --git a/src/main.cpp b/src/main.cpp index b617ef8..e1bba59 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include #include @@ -37,7 +37,6 @@ #define UART0_RXD 20 // Used for USB connection detection -#define SD_SPI_CS 12 #define SD_SPI_MISO 7 EInkDisplay einkDisplay(EPD_SCLK, EPD_MOSI, EPD_CS, EPD_DC, EPD_RST, EPD_BUSY); @@ -189,7 +188,7 @@ void setup() { // SD Card Initialization // We need 6 open files concurrently when parsing a new chapter - if (!SD.begin(SD_SPI_CS, SPI, SPI_FQ, "/sd", 6)) { + if (!SdMan.begin()) { Serial.printf("[%lu] [ ] SD card initialization failed\n", millis()); setupDisplayAndFonts(); exitActivity(); diff --git a/src/network/CrossPointWebServer.cpp b/src/network/CrossPointWebServer.cpp index 041273f..916f6a2 100644 --- a/src/network/CrossPointWebServer.cpp +++ b/src/network/CrossPointWebServer.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include #include @@ -170,7 +170,7 @@ void CrossPointWebServer::handleStatus() const { } void CrossPointWebServer::scanFiles(const char* path, const std::function& callback) const { - File root = SD.open(path); + FsFile root = SdMan.open(path); if (!root) { Serial.printf("[%lu] [WEB] Failed to open directory: %s\n", millis(), path); return; @@ -184,9 +184,11 @@ void CrossPointWebServer::scanFiles(const char* path, const std::function