mirror of
https://github.com/daveallie/crosspoint-reader.git
synced 2026-02-04 06:37:38 +03:00
Add exFAT support (#150)
## Summary * Swap to updated SDCardManager which uses SdFat * Add exFAT support * Swap to using FsFile everywhere * Use newly exposed `SdMan` macro to get to static instance of SDCardManager * Move a bunch of FsHelpers up to SDCardManager
This commit is contained in:
parent
d4bd119950
commit
fb5fc32c5d
@ -3,7 +3,7 @@
|
||||
#include <FsHelpers.h>
|
||||
#include <HardwareSerial.h>
|
||||
#include <JpegToBmpConverter.h>
|
||||
#include <SD.h>
|
||||
#include <SDCardManager.h>
|
||||
#include <ZipFile.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <Print.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
#include "BookMetadataCache.h"
|
||||
|
||||
#include <HardwareSerial.h>
|
||||
#include <SD.h>
|
||||
#include <Serialization.h>
|
||||
#include <ZipFile.h>
|
||||
|
||||
@ -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<uint32_t>(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);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <SD.h>
|
||||
#include <SDCardManager.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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> PageLine::deserialize(File& file) {
|
||||
std::unique_ptr<PageLine> 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> Page::deserialize(File& file) {
|
||||
std::unique_ptr<Page> Page::deserialize(FsFile& file) {
|
||||
auto page = std::unique_ptr<Page>(new Page());
|
||||
|
||||
uint32_t count;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#include <FS.h>
|
||||
#include <SdFat.h>
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@ -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<TextBlock> 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<PageLine> deserialize(File& file);
|
||||
bool serialize(FsFile& file) override;
|
||||
static std::unique_ptr<PageLine> deserialize(FsFile& file);
|
||||
};
|
||||
|
||||
class Page {
|
||||
@ -38,6 +38,6 @@ class Page {
|
||||
// the list of block index and line numbers on this page
|
||||
std::vector<std::shared_ptr<PageElement>> elements;
|
||||
void render(GfxRenderer& renderer, int fontId, int xOffset, int yOffset) const;
|
||||
bool serialize(File& file) const;
|
||||
static std::unique_ptr<Page> deserialize(File& file);
|
||||
bool serialize(FsFile& file) const;
|
||||
static std::unique_ptr<Page> deserialize(FsFile& file);
|
||||
};
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
#include "Section.h"
|
||||
|
||||
#include <FsHelpers.h>
|
||||
#include <SD.h>
|
||||
#include <SDCardManager.h>
|
||||
#include <Serialization.h>
|
||||
|
||||
#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> page) {
|
||||
uint32_t Section::onPageComplete(std::unique_ptr<Page> 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<size_t>(0)); // Placeholder for LUT offset
|
||||
serialization::writePod(file, static_cast<uint32_t>(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<void()>& progressSetupFn,
|
||||
const std::function<void(int)>& 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<size_t> lut = {};
|
||||
std::vector<uint32_t> 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<Page> 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);
|
||||
|
||||
|
||||
@ -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> page);
|
||||
uint32_t onPageComplete(std::unique_ptr<Page> page);
|
||||
|
||||
public:
|
||||
int pageCount = 0;
|
||||
|
||||
@ -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> TextBlock::deserialize(File& file) {
|
||||
std::unique_ptr<TextBlock> TextBlock::deserialize(FsFile& file) {
|
||||
uint32_t wc;
|
||||
std::list<std::string> words;
|
||||
std::list<uint16_t> wordXpos;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
#include <EpdFontFamily.h>
|
||||
#include <FS.h>
|
||||
#include <SdFat.h>
|
||||
|
||||
#include <list>
|
||||
#include <memory>
|
||||
@ -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<TextBlock> deserialize(File& file);
|
||||
bool serialize(FsFile& file) const;
|
||||
static std::unique_ptr<TextBlock> deserialize(FsFile& file);
|
||||
};
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
#include "ChapterHtmlSlimParser.h"
|
||||
|
||||
#include <FsHelpers.h>
|
||||
#include <GfxRenderer.h>
|
||||
#include <HardwareSerial.h>
|
||||
#include <SDCardManager.h>
|
||||
#include <expat.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
@ -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());
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -1,81 +1,7 @@
|
||||
#include "FsHelpers.h"
|
||||
|
||||
#include <SD.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
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<std::string> components;
|
||||
std::string component;
|
||||
|
||||
@ -1,14 +1,7 @@
|
||||
#pragma once
|
||||
#include <FS.h>
|
||||
#include <string>
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
@ -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<uint8_t>(c0 < 0 ? 0 : c0);
|
||||
@ -131,7 +131,7 @@ uint16_t Bitmap::readLE16(File& f) {
|
||||
return static_cast<uint16_t>(b0) | (static_cast<uint16_t>(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;
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <FS.h>
|
||||
#include <SdFat.h>
|
||||
|
||||
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;
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
|
||||
#include <EInkDisplay.h>
|
||||
#include <EpdFontFamily.h>
|
||||
#include <FS.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
#include "JpegToBmpConverter.h"
|
||||
|
||||
#include <HardwareSerial.h>
|
||||
#include <SdFat.h>
|
||||
#include <picojpeg.h>
|
||||
|
||||
#include <cstdio>
|
||||
@ -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
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <FS.h>
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#include <FS.h>
|
||||
#include <SdFat.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
@ -10,7 +10,7 @@ static void writePod(std::ostream& os, const T& value) {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void writePod(File& file, const T& value) {
|
||||
static void writePod(FsFile& file, const T& value) {
|
||||
file.write(reinterpret_cast<const uint8_t*>(&value), sizeof(T));
|
||||
}
|
||||
|
||||
@ -20,7 +20,7 @@ static void readPod(std::istream& is, T& value) {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void readPod(File& file, T& value) {
|
||||
static void readPod(FsFile& file, T& value) {
|
||||
file.read(reinterpret_cast<uint8_t*>(&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<const uint8_t*>(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<uint8_t*>(&s[0]), len);
|
||||
file.read(&s[0], len);
|
||||
}
|
||||
} // namespace serialization
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
|
||||
#include <FsHelpers.h>
|
||||
#include <HardwareSerial.h>
|
||||
#include <SD.h>
|
||||
#include <SDCardManager.h>
|
||||
|
||||
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;
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
|
||||
#include <FsHelpers.h>
|
||||
#include <HardwareSerial.h>
|
||||
#include <SDCardManager.h>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <SD.h>
|
||||
#include <SdFat.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
@ -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<PageInfo> m_pageTable;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#include "ZipFile.h"
|
||||
|
||||
#include <FsHelpers.h>
|
||||
#include <HardwareSerial.h>
|
||||
#include <SDCardManager.h>
|
||||
#include <miniz.h>
|
||||
|
||||
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<uint8_t*>(&sig), 4);
|
||||
file.read(&sig, 4);
|
||||
if (sig != 0x02014b50) break; // End of list
|
||||
|
||||
FileStatSlim fileStat = {};
|
||||
|
||||
file.seek(6, SeekCur);
|
||||
file.read(reinterpret_cast<uint8_t*>(&fileStat.method), 2);
|
||||
file.seek(8, SeekCur);
|
||||
file.read(reinterpret_cast<uint8_t*>(&fileStat.compressedSize), 4);
|
||||
file.read(reinterpret_cast<uint8_t*>(&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<uint8_t*>(&nameLen), 2);
|
||||
file.read(reinterpret_cast<uint8_t*>(&m), 2);
|
||||
file.read(reinterpret_cast<uint8_t*>(&k), 2);
|
||||
file.seek(8, SeekCur);
|
||||
file.read(reinterpret_cast<uint8_t*>(&fileStat.localHeaderOffset), 4);
|
||||
file.read(reinterpret_cast<uint8_t*>(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<uint8_t*>(&sig), 4);
|
||||
file.read(&sig, 4);
|
||||
if (sig != 0x02014b50) break; // End of list
|
||||
|
||||
file.seek(6, SeekCur);
|
||||
file.read(reinterpret_cast<uint8_t*>(&fileStat->method), 2);
|
||||
file.seek(8, SeekCur);
|
||||
file.read(reinterpret_cast<uint8_t*>(&fileStat->compressedSize), 4);
|
||||
file.read(reinterpret_cast<uint8_t*>(&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<uint8_t*>(&nameLen), 2);
|
||||
file.read(reinterpret_cast<uint8_t*>(&m), 2);
|
||||
file.read(reinterpret_cast<uint8_t*>(&k), 2);
|
||||
file.seek(8, SeekCur);
|
||||
file.read(reinterpret_cast<uint8_t*>(&fileStat->localHeaderOffset), 4);
|
||||
file.read(reinterpret_cast<uint8_t*>(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;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#include <FS.h>
|
||||
#include <SdFat.h>
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
@ -21,7 +21,7 @@ class ZipFile {
|
||||
|
||||
private:
|
||||
const std::string& filePath;
|
||||
File file;
|
||||
FsFile file;
|
||||
ZipDetails zipDetails = {0, 0, false};
|
||||
std::unordered_map<std::string, FileStatSlim> fileStatSlimCache;
|
||||
|
||||
|
||||
@ -1 +1 @@
|
||||
Subproject commit 98a5aa1f8969ccd317c9b45bf0fa84b6c82e167f
|
||||
Subproject commit bd4e6707503ab9c97d13ee0d8f8c69e9ff03cd12
|
||||
@ -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
|
||||
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
#include "CrossPointSettings.h"
|
||||
|
||||
#include <FsHelpers.h>
|
||||
#include <HardwareSerial.h>
|
||||
#include <SD.h>
|
||||
#include <SDCardManager.h>
|
||||
#include <Serialization.h>
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#include "CrossPointState.h"
|
||||
|
||||
#include <FsHelpers.h>
|
||||
#include <HardwareSerial.h>
|
||||
#include <SDCardManager.h>
|
||||
#include <Serialization.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@ -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<CrossPointSettings::FRONT_BUTTON_LAYOUT>(SETTINGS.frontButtonLayout);
|
||||
const auto sideLayout = static_cast<CrossPointSettings::SIDE_BUTTON_LAYOUT>(SETTINGS.sideButtonLayout);
|
||||
|
||||
@ -2,8 +2,6 @@
|
||||
|
||||
#include <InputManager.h>
|
||||
|
||||
#include "CrossPointSettings.h"
|
||||
|
||||
class MappedInputManager {
|
||||
public:
|
||||
enum class Button { Back, Confirm, Left, Right, Up, Down, Power, PageBack, PageForward };
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
#include "WifiCredentialStore.h"
|
||||
|
||||
#include <FsHelpers.h>
|
||||
#include <HardwareSerial.h>
|
||||
#include <SD.h>
|
||||
#include <SDCardManager.h>
|
||||
#include <Serialization.h>
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
@ -5,8 +5,7 @@
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "../MappedInputManager.h"
|
||||
|
||||
class MappedInputManager;
|
||||
class GfxRenderer;
|
||||
|
||||
class Activity {
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
#include <Epub.h>
|
||||
#include <FsHelpers.h>
|
||||
#include <GfxRenderer.h>
|
||||
#include <SD.h>
|
||||
#include <SDCardManager.h>
|
||||
#include <Xtc.h>
|
||||
|
||||
#include <vector>
|
||||
@ -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<std::string> 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);
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
#include "HomeActivity.h"
|
||||
|
||||
#include <GfxRenderer.h>
|
||||
#include <InputManager.h>
|
||||
#include <SD.h>
|
||||
#include <SDCardManager.h>
|
||||
|
||||
#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;
|
||||
|
||||
|
||||
@ -3,12 +3,12 @@
|
||||
#include <DNSServer.h>
|
||||
#include <ESPmDNS.h>
|
||||
#include <GfxRenderer.h>
|
||||
#include <InputManager.h>
|
||||
#include <WiFi.h>
|
||||
#include <qrcode.h>
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include "MappedInputManager.h"
|
||||
#include "NetworkModeSelectionActivity.h"
|
||||
#include "WifiSelectionActivity.h"
|
||||
#include "config.h"
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
#include "NetworkModeSelectionActivity.h"
|
||||
|
||||
#include <GfxRenderer.h>
|
||||
#include <InputManager.h>
|
||||
|
||||
#include "MappedInputManager.h"
|
||||
#include "config.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "MappedInputManager.h"
|
||||
#include "WifiCredentialStore.h"
|
||||
#include "activities/util/KeyboardEntryActivity.h"
|
||||
#include "config.h"
|
||||
|
||||
@ -3,12 +3,13 @@
|
||||
#include <Epub/Page.h>
|
||||
#include <FsHelpers.h>
|
||||
#include <GfxRenderer.h>
|
||||
#include <InputManager.h>
|
||||
#include <SDCardManager.h>
|
||||
|
||||
#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;
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
#include "EpubReaderChapterSelectionActivity.h"
|
||||
|
||||
#include <GfxRenderer.h>
|
||||
#include <InputManager.h>
|
||||
#include <SD.h>
|
||||
|
||||
#include "MappedInputManager.h"
|
||||
#include "config.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
#include "FileSelectionActivity.h"
|
||||
|
||||
#include <GfxRenderer.h>
|
||||
#include <InputManager.h>
|
||||
#include <SD.h>
|
||||
#include <SDCardManager.h>
|
||||
|
||||
#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") {
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
#include "ReaderActivity.h"
|
||||
|
||||
#include <SD.h>
|
||||
|
||||
#include "Epub.h"
|
||||
#include "EpubReaderActivity.h"
|
||||
#include "FileSelectionActivity.h"
|
||||
@ -29,7 +27,7 @@ bool ReaderActivity::isXtcFile(const std::string& path) {
|
||||
}
|
||||
|
||||
std::unique_ptr<Epub> 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<Epub> ReaderActivity::loadEpub(const std::string& path) {
|
||||
}
|
||||
|
||||
std::unique_ptr<Xtc> 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;
|
||||
}
|
||||
|
||||
@ -9,10 +9,10 @@
|
||||
|
||||
#include <FsHelpers.h>
|
||||
#include <GfxRenderer.h>
|
||||
#include <InputManager.h>
|
||||
#include <SDCardManager.h>
|
||||
|
||||
#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);
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
#include "XtcReaderChapterSelectionActivity.h"
|
||||
|
||||
#include <GfxRenderer.h>
|
||||
#include <InputManager.h>
|
||||
#include <SD.h>
|
||||
|
||||
#include "MappedInputManager.h"
|
||||
#include "config.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
#include "OtaUpdateActivity.h"
|
||||
|
||||
#include <GfxRenderer.h>
|
||||
#include <InputManager.h>
|
||||
#include <WiFi.h>
|
||||
|
||||
#include "MappedInputManager.h"
|
||||
#include "activities/network/WifiSelectionActivity.h"
|
||||
#include "config.h"
|
||||
#include "network/OtaUpdater.h"
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
#include "SettingsActivity.h"
|
||||
|
||||
#include <GfxRenderer.h>
|
||||
#include <InputManager.h>
|
||||
|
||||
#include "CrossPointSettings.h"
|
||||
#include "MappedInputManager.h"
|
||||
#include "OtaUpdateActivity.h"
|
||||
#include "config.h"
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
#include "KeyboardEntryActivity.h"
|
||||
|
||||
#include "../../config.h"
|
||||
#include "MappedInputManager.h"
|
||||
|
||||
// Keyboard layouts - lowercase
|
||||
const char* const KeyboardEntryActivity::keyboard[NUM_ROWS] = {
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
#include <Epub.h>
|
||||
#include <GfxRenderer.h>
|
||||
#include <InputManager.h>
|
||||
#include <SD.h>
|
||||
#include <SDCardManager.h>
|
||||
#include <SPI.h>
|
||||
#include <builtinFonts/bookerly_2b.h>
|
||||
#include <builtinFonts/bookerly_bold_2b.h>
|
||||
@ -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();
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <FsHelpers.h>
|
||||
#include <SD.h>
|
||||
#include <SDCardManager.h>
|
||||
#include <WiFi.h>
|
||||
|
||||
#include <algorithm>
|
||||
@ -170,7 +170,7 @@ void CrossPointWebServer::handleStatus() const {
|
||||
}
|
||||
|
||||
void CrossPointWebServer::scanFiles(const char* path, const std::function<void(FileInfo)>& 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<void(F
|
||||
|
||||
Serial.printf("[%lu] [WEB] Scanning files in: %s\n", millis(), path);
|
||||
|
||||
File file = root.openNextFile();
|
||||
FsFile file = root.openNextFile();
|
||||
char name[128];
|
||||
while (file) {
|
||||
auto fileName = String(file.name());
|
||||
file.getName(name, sizeof(name));
|
||||
auto fileName = String(name);
|
||||
|
||||
// Skip hidden items (starting with ".")
|
||||
bool shouldHide = fileName.startsWith(".");
|
||||
@ -279,7 +281,7 @@ void CrossPointWebServer::handleFileListData() const {
|
||||
}
|
||||
|
||||
// Static variables for upload handling
|
||||
static File uploadFile;
|
||||
static FsFile uploadFile;
|
||||
static String uploadFileName;
|
||||
static String uploadPath = "/";
|
||||
static size_t uploadSize = 0;
|
||||
@ -334,13 +336,13 @@ void CrossPointWebServer::handleUpload() const {
|
||||
filePath += uploadFileName;
|
||||
|
||||
// Check if file already exists
|
||||
if (SD.exists(filePath.c_str())) {
|
||||
if (SdMan.exists(filePath.c_str())) {
|
||||
Serial.printf("[%lu] [WEB] [UPLOAD] Overwriting existing file: %s\n", millis(), filePath.c_str());
|
||||
SD.remove(filePath.c_str());
|
||||
SdMan.remove(filePath.c_str());
|
||||
}
|
||||
|
||||
// Open file for writing
|
||||
if (!FsHelpers::openFileForWrite("WEB", filePath, uploadFile)) {
|
||||
if (!SdMan.openFileForWrite("WEB", filePath, uploadFile)) {
|
||||
uploadError = "Failed to create file on SD card";
|
||||
Serial.printf("[%lu] [WEB] [UPLOAD] FAILED to create file: %s\n", millis(), filePath.c_str());
|
||||
return;
|
||||
@ -393,7 +395,7 @@ void CrossPointWebServer::handleUpload() const {
|
||||
String filePath = uploadPath;
|
||||
if (!filePath.endsWith("/")) filePath += "/";
|
||||
filePath += uploadFileName;
|
||||
SD.remove(filePath.c_str());
|
||||
SdMan.remove(filePath.c_str());
|
||||
}
|
||||
uploadError = "Upload aborted";
|
||||
Serial.printf("[%lu] [WEB] Upload aborted\n", millis());
|
||||
@ -444,13 +446,13 @@ void CrossPointWebServer::handleCreateFolder() const {
|
||||
Serial.printf("[%lu] [WEB] Creating folder: %s\n", millis(), folderPath.c_str());
|
||||
|
||||
// Check if already exists
|
||||
if (SD.exists(folderPath.c_str())) {
|
||||
if (SdMan.exists(folderPath.c_str())) {
|
||||
server->send(400, "text/plain", "Folder already exists");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the folder
|
||||
if (SD.mkdir(folderPath.c_str())) {
|
||||
if (SdMan.mkdir(folderPath.c_str())) {
|
||||
Serial.printf("[%lu] [WEB] Folder created successfully: %s\n", millis(), folderPath.c_str());
|
||||
server->send(200, "text/plain", "Folder created: " + folderName);
|
||||
} else {
|
||||
@ -500,7 +502,7 @@ void CrossPointWebServer::handleDelete() const {
|
||||
}
|
||||
|
||||
// Check if item exists
|
||||
if (!SD.exists(itemPath.c_str())) {
|
||||
if (!SdMan.exists(itemPath.c_str())) {
|
||||
Serial.printf("[%lu] [WEB] Delete failed - item not found: %s\n", millis(), itemPath.c_str());
|
||||
server->send(404, "text/plain", "Item not found");
|
||||
return;
|
||||
@ -512,10 +514,10 @@ void CrossPointWebServer::handleDelete() const {
|
||||
|
||||
if (itemType == "folder") {
|
||||
// For folders, try to remove (will fail if not empty)
|
||||
File dir = SD.open(itemPath.c_str());
|
||||
FsFile dir = SdMan.open(itemPath.c_str());
|
||||
if (dir && dir.isDirectory()) {
|
||||
// Check if folder is empty
|
||||
File entry = dir.openNextFile();
|
||||
FsFile entry = dir.openNextFile();
|
||||
if (entry) {
|
||||
// Folder is not empty
|
||||
entry.close();
|
||||
@ -526,10 +528,10 @@ void CrossPointWebServer::handleDelete() const {
|
||||
}
|
||||
dir.close();
|
||||
}
|
||||
success = SD.rmdir(itemPath.c_str());
|
||||
success = SdMan.rmdir(itemPath.c_str());
|
||||
} else {
|
||||
// For files, use remove
|
||||
success = SD.remove(itemPath.c_str());
|
||||
success = SdMan.remove(itemPath.c_str());
|
||||
}
|
||||
|
||||
if (success) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user