feat: add HalStorage (#656)

## Summary

Continue my changes to introduce the HAL infrastructure from
https://github.com/crosspoint-reader/crosspoint-reader/pull/522

This PR touches quite a lot of files, but most of them are just name
changing. It should not have any impacts to the end behavior.

## Additional Context

My plan is to firstly add this small shim layer, which sounds useless at
first, but then I'll implement an emulated driver which can be helpful
for testing and for development.

Currently, on my fork, I'm using a FS driver that allow "mounting" a
local directory from my computer to the device, much like the `-v` mount
option on docker. This allows me to quickly reset `.crosspoint`
directory if anything goes wrong. I plan to upstream this feature when
this PR get merged.

---

### AI Usage

While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.

Did you use AI tools to help write this code? NO
This commit is contained in:
Xuan-Son Nguyen
2026-02-08 21:29:14 +01:00
committed by GitHub
parent a87eacc6ab
commit 7f40c3f477
43 changed files with 318 additions and 197 deletions

View File

@@ -1,9 +1,9 @@
#include "Epub.h"
#include <FsHelpers.h>
#include <HalStorage.h>
#include <HardwareSerial.h>
#include <JpegToBmpConverter.h>
#include <SDCardManager.h>
#include <ZipFile.h>
#include "Epub/parsers/ContainerParser.h"
@@ -105,12 +105,12 @@ bool Epub::parseTocNcxFile() const {
const auto tmpNcxPath = getCachePath() + "/toc.ncx";
FsFile tempNcxFile;
if (!SdMan.openFileForWrite("EBP", tmpNcxPath, tempNcxFile)) {
if (!Storage.openFileForWrite("EBP", tmpNcxPath, tempNcxFile)) {
return false;
}
readItemContentsToStream(tocNcxItem, tempNcxFile, 1024);
tempNcxFile.close();
if (!SdMan.openFileForRead("EBP", tmpNcxPath, tempNcxFile)) {
if (!Storage.openFileForRead("EBP", tmpNcxPath, tempNcxFile)) {
return false;
}
const auto ncxSize = tempNcxFile.size();
@@ -145,7 +145,7 @@ bool Epub::parseTocNcxFile() const {
free(ncxBuffer);
tempNcxFile.close();
SdMan.remove(tmpNcxPath.c_str());
Storage.remove(tmpNcxPath.c_str());
Serial.printf("[%lu] [EBP] Parsed TOC items\n", millis());
return true;
@@ -162,12 +162,12 @@ bool Epub::parseTocNavFile() const {
const auto tmpNavPath = getCachePath() + "/toc.nav";
FsFile tempNavFile;
if (!SdMan.openFileForWrite("EBP", tmpNavPath, tempNavFile)) {
if (!Storage.openFileForWrite("EBP", tmpNavPath, tempNavFile)) {
return false;
}
readItemContentsToStream(tocNavItem, tempNavFile, 1024);
tempNavFile.close();
if (!SdMan.openFileForRead("EBP", tmpNavPath, tempNavFile)) {
if (!Storage.openFileForRead("EBP", tmpNavPath, tempNavFile)) {
return false;
}
const auto navSize = tempNavFile.size();
@@ -202,7 +202,7 @@ bool Epub::parseTocNavFile() const {
free(navBuffer);
tempNavFile.close();
SdMan.remove(tmpNavPath.c_str());
Storage.remove(tmpNavPath.c_str());
Serial.printf("[%lu] [EBP] Parsed TOC nav items\n", millis());
return true;
@@ -212,7 +212,7 @@ std::string Epub::getCssRulesCache() const { return cachePath + "/css_rules.cach
bool Epub::loadCssRulesFromCache() const {
FsFile cssCacheFile;
if (SdMan.openFileForRead("EBP", getCssRulesCache(), cssCacheFile)) {
if (Storage.openFileForRead("EBP", getCssRulesCache(), cssCacheFile)) {
if (cssParser->loadFromCache(cssCacheFile)) {
cssCacheFile.close();
Serial.printf("[%lu] [EBP] Loaded CSS rules from cache\n", millis());
@@ -238,32 +238,32 @@ void Epub::parseCssFiles() const {
// Extract CSS file to temp location
const auto tmpCssPath = getCachePath() + "/.tmp.css";
FsFile tempCssFile;
if (!SdMan.openFileForWrite("EBP", tmpCssPath, tempCssFile)) {
if (!Storage.openFileForWrite("EBP", tmpCssPath, tempCssFile)) {
Serial.printf("[%lu] [EBP] Could not create temp CSS file\n", millis());
continue;
}
if (!readItemContentsToStream(cssPath, tempCssFile, 1024)) {
Serial.printf("[%lu] [EBP] Could not read CSS file: %s\n", millis(), cssPath.c_str());
tempCssFile.close();
SdMan.remove(tmpCssPath.c_str());
Storage.remove(tmpCssPath.c_str());
continue;
}
tempCssFile.close();
// Parse the CSS file
if (!SdMan.openFileForRead("EBP", tmpCssPath, tempCssFile)) {
if (!Storage.openFileForRead("EBP", tmpCssPath, tempCssFile)) {
Serial.printf("[%lu] [EBP] Could not open temp CSS file for reading\n", millis());
SdMan.remove(tmpCssPath.c_str());
Storage.remove(tmpCssPath.c_str());
continue;
}
cssParser->loadFromStream(tempCssFile);
tempCssFile.close();
SdMan.remove(tmpCssPath.c_str());
Storage.remove(tmpCssPath.c_str());
}
// Save to cache for next time
FsFile cssCacheFile;
if (SdMan.openFileForWrite("EBP", getCssRulesCache(), cssCacheFile)) {
if (Storage.openFileForWrite("EBP", getCssRulesCache(), cssCacheFile)) {
cssParser->saveToCache(cssCacheFile);
cssCacheFile.close();
}
@@ -399,12 +399,12 @@ bool Epub::load(const bool buildIfMissing, const bool skipLoadingCss) {
}
bool Epub::clearCache() const {
if (!SdMan.exists(cachePath.c_str())) {
if (!Storage.exists(cachePath.c_str())) {
Serial.printf("[%lu] [EPB] Cache does not exist, no action needed\n", millis());
return true;
}
if (!SdMan.removeDir(cachePath.c_str())) {
if (!Storage.removeDir(cachePath.c_str())) {
Serial.printf("[%lu] [EPB] Failed to clear cache\n", millis());
return false;
}
@@ -414,11 +414,11 @@ bool Epub::clearCache() const {
}
void Epub::setupCacheDir() const {
if (SdMan.exists(cachePath.c_str())) {
if (Storage.exists(cachePath.c_str())) {
return;
}
SdMan.mkdir(cachePath.c_str());
Storage.mkdir(cachePath.c_str());
}
const std::string& Epub::getCachePath() const { return cachePath; }
@@ -459,7 +459,7 @@ std::string Epub::getCoverBmpPath(bool cropped) const {
bool Epub::generateCoverBmp(bool cropped) const {
// Already generated, return true
if (SdMan.exists(getCoverBmpPath(cropped).c_str())) {
if (Storage.exists(getCoverBmpPath(cropped).c_str())) {
return true;
}
@@ -480,29 +480,29 @@ bool Epub::generateCoverBmp(bool cropped) const {
const auto coverJpgTempPath = getCachePath() + "/.cover.jpg";
FsFile coverJpg;
if (!SdMan.openFileForWrite("EBP", coverJpgTempPath, coverJpg)) {
if (!Storage.openFileForWrite("EBP", coverJpgTempPath, coverJpg)) {
return false;
}
readItemContentsToStream(coverImageHref, coverJpg, 1024);
coverJpg.close();
if (!SdMan.openFileForRead("EBP", coverJpgTempPath, coverJpg)) {
if (!Storage.openFileForRead("EBP", coverJpgTempPath, coverJpg)) {
return false;
}
FsFile coverBmp;
if (!SdMan.openFileForWrite("EBP", getCoverBmpPath(cropped), coverBmp)) {
if (!Storage.openFileForWrite("EBP", getCoverBmpPath(cropped), coverBmp)) {
coverJpg.close();
return false;
}
const bool success = JpegToBmpConverter::jpegFileToBmpStream(coverJpg, coverBmp, cropped);
coverJpg.close();
coverBmp.close();
SdMan.remove(coverJpgTempPath.c_str());
Storage.remove(coverJpgTempPath.c_str());
if (!success) {
Serial.printf("[%lu] [EBP] Failed to generate BMP from JPG cover image\n", millis());
SdMan.remove(getCoverBmpPath(cropped).c_str());
Storage.remove(getCoverBmpPath(cropped).c_str());
}
Serial.printf("[%lu] [EBP] Generated BMP from JPG cover image, success: %s\n", millis(), success ? "yes" : "no");
return success;
@@ -518,7 +518,7 @@ std::string Epub::getThumbBmpPath(int height) const { return cachePath + "/thumb
bool Epub::generateThumbBmp(int height) const {
// Already generated, return true
if (SdMan.exists(getThumbBmpPath(height).c_str())) {
if (Storage.exists(getThumbBmpPath(height).c_str())) {
return true;
}
@@ -536,18 +536,18 @@ bool Epub::generateThumbBmp(int height) const {
const auto coverJpgTempPath = getCachePath() + "/.cover.jpg";
FsFile coverJpg;
if (!SdMan.openFileForWrite("EBP", coverJpgTempPath, coverJpg)) {
if (!Storage.openFileForWrite("EBP", coverJpgTempPath, coverJpg)) {
return false;
}
readItemContentsToStream(coverImageHref, coverJpg, 1024);
coverJpg.close();
if (!SdMan.openFileForRead("EBP", coverJpgTempPath, coverJpg)) {
if (!Storage.openFileForRead("EBP", coverJpgTempPath, coverJpg)) {
return false;
}
FsFile thumbBmp;
if (!SdMan.openFileForWrite("EBP", getThumbBmpPath(height), thumbBmp)) {
if (!Storage.openFileForWrite("EBP", getThumbBmpPath(height), thumbBmp)) {
coverJpg.close();
return false;
}
@@ -559,11 +559,11 @@ bool Epub::generateThumbBmp(int height) const {
THUMB_TARGET_HEIGHT);
coverJpg.close();
thumbBmp.close();
SdMan.remove(coverJpgTempPath.c_str());
Storage.remove(coverJpgTempPath.c_str());
if (!success) {
Serial.printf("[%lu] [EBP] Failed to generate thumb BMP from JPG cover image\n", millis());
SdMan.remove(getThumbBmpPath(height).c_str());
Storage.remove(getThumbBmpPath(height).c_str());
}
Serial.printf("[%lu] [EBP] Generated thumb BMP from JPG cover image, success: %s\n", millis(),
success ? "yes" : "no");
@@ -574,7 +574,7 @@ bool Epub::generateThumbBmp(int height) const {
// Write an empty bmp file to avoid generation attempts in the future
FsFile thumbBmp;
SdMan.openFileForWrite("EBP", getThumbBmpPath(height), thumbBmp);
Storage.openFileForWrite("EBP", getThumbBmpPath(height), thumbBmp);
thumbBmp.close();
return false;
}

View File

@@ -29,7 +29,7 @@ bool BookMetadataCache::beginContentOpfPass() {
Serial.printf("[%lu] [BMC] Beginning content opf pass\n", millis());
// Open spine file for writing
return SdMan.openFileForWrite("BMC", cachePath + tmpSpineBinFile, spineFile);
return Storage.openFileForWrite("BMC", cachePath + tmpSpineBinFile, spineFile);
}
bool BookMetadataCache::endContentOpfPass() {
@@ -40,10 +40,10 @@ bool BookMetadataCache::endContentOpfPass() {
bool BookMetadataCache::beginTocPass() {
Serial.printf("[%lu] [BMC] Beginning toc pass\n", millis());
if (!SdMan.openFileForRead("BMC", cachePath + tmpSpineBinFile, spineFile)) {
if (!Storage.openFileForRead("BMC", cachePath + tmpSpineBinFile, spineFile)) {
return false;
}
if (!SdMan.openFileForWrite("BMC", cachePath + tmpTocBinFile, tocFile)) {
if (!Storage.openFileForWrite("BMC", cachePath + tmpTocBinFile, tocFile)) {
spineFile.close();
return false;
}
@@ -98,16 +98,16 @@ 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 (!SdMan.openFileForWrite("BMC", cachePath + bookBinFile, bookFile)) {
if (!Storage.openFileForWrite("BMC", cachePath + bookBinFile, bookFile)) {
return false;
}
if (!SdMan.openFileForRead("BMC", cachePath + tmpSpineBinFile, spineFile)) {
if (!Storage.openFileForRead("BMC", cachePath + tmpSpineBinFile, spineFile)) {
bookFile.close();
return false;
}
if (!SdMan.openFileForRead("BMC", cachePath + tmpTocBinFile, tocFile)) {
if (!Storage.openFileForRead("BMC", cachePath + tmpTocBinFile, tocFile)) {
bookFile.close();
spineFile.close();
return false;
@@ -275,11 +275,11 @@ bool BookMetadataCache::buildBookBin(const std::string& epubPath, const BookMeta
}
bool BookMetadataCache::cleanupTmpFiles() const {
if (SdMan.exists((cachePath + tmpSpineBinFile).c_str())) {
SdMan.remove((cachePath + tmpSpineBinFile).c_str());
if (Storage.exists((cachePath + tmpSpineBinFile).c_str())) {
Storage.remove((cachePath + tmpSpineBinFile).c_str());
}
if (SdMan.exists((cachePath + tmpTocBinFile).c_str())) {
SdMan.remove((cachePath + tmpTocBinFile).c_str());
if (Storage.exists((cachePath + tmpTocBinFile).c_str())) {
Storage.remove((cachePath + tmpTocBinFile).c_str());
}
return true;
}
@@ -364,7 +364,7 @@ void BookMetadataCache::createTocEntry(const std::string& title, const std::stri
/* ============= READING / LOADING FUNCTIONS ================ */
bool BookMetadataCache::load() {
if (!SdMan.openFileForRead("BMC", cachePath + bookBinFile, bookFile)) {
if (!Storage.openFileForRead("BMC", cachePath + bookBinFile, bookFile)) {
return false;
}

View File

@@ -1,6 +1,6 @@
#pragma once
#include <SDCardManager.h>
#include <HalStorage.h>
#include <algorithm>
#include <string>

View File

@@ -1,5 +1,5 @@
#pragma once
#include <SdFat.h>
#include <HalStorage.h>
#include <utility>
#include <vector>

View File

@@ -1,6 +1,6 @@
#include "Section.h"
#include <SDCardManager.h>
#include <HalStorage.h>
#include <Serialization.h>
#include "Page.h"
@@ -60,7 +60,7 @@ void Section::writeSectionFileHeader(const int fontId, const float lineCompressi
bool Section::loadSectionFile(const int fontId, const float lineCompression, const bool extraParagraphSpacing,
const uint8_t paragraphAlignment, const uint16_t viewportWidth,
const uint16_t viewportHeight, const bool hyphenationEnabled, const bool embeddedStyle) {
if (!SdMan.openFileForRead("SCT", filePath, file)) {
if (!Storage.openFileForRead("SCT", filePath, file)) {
return false;
}
@@ -110,12 +110,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 (!SdMan.exists(filePath.c_str())) {
if (!Storage.exists(filePath.c_str())) {
Serial.printf("[%lu] [SCT] Cache does not exist, no action needed\n", millis());
return true;
}
if (!SdMan.remove(filePath.c_str())) {
if (!Storage.remove(filePath.c_str())) {
Serial.printf("[%lu] [SCT] Failed to clear cache\n", millis());
return false;
}
@@ -134,7 +134,7 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c
// Create cache directory if it doesn't exist
{
const auto sectionsDir = epub->getCachePath() + "/sections";
SdMan.mkdir(sectionsDir.c_str());
Storage.mkdir(sectionsDir.c_str());
}
// Retry logic for SD card timing issues
@@ -147,12 +147,12 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c
}
// Remove any incomplete file from previous attempt before retrying
if (SdMan.exists(tmpHtmlPath.c_str())) {
SdMan.remove(tmpHtmlPath.c_str());
if (Storage.exists(tmpHtmlPath.c_str())) {
Storage.remove(tmpHtmlPath.c_str());
}
FsFile tmpHtml;
if (!SdMan.openFileForWrite("SCT", tmpHtmlPath, tmpHtml)) {
if (!Storage.openFileForWrite("SCT", tmpHtmlPath, tmpHtml)) {
continue;
}
success = epub->readItemContentsToStream(localPath, tmpHtml, 1024);
@@ -160,8 +160,8 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c
tmpHtml.close();
// If streaming failed, remove the incomplete file immediately
if (!success && SdMan.exists(tmpHtmlPath.c_str())) {
SdMan.remove(tmpHtmlPath.c_str());
if (!success && Storage.exists(tmpHtmlPath.c_str())) {
Storage.remove(tmpHtmlPath.c_str());
Serial.printf("[%lu] [SCT] Removed incomplete temp file after failed attempt\n", millis());
}
}
@@ -173,7 +173,7 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c
Serial.printf("[%lu] [SCT] Streamed temp HTML to %s (%d bytes)\n", millis(), tmpHtmlPath.c_str(), fileSize);
if (!SdMan.openFileForWrite("SCT", filePath, file)) {
if (!Storage.openFileForWrite("SCT", filePath, file)) {
return false;
}
writeSectionFileHeader(fontId, lineCompression, extraParagraphSpacing, paragraphAlignment, viewportWidth,
@@ -188,11 +188,11 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c
Hyphenator::setPreferredLanguage(epub->getLanguage());
success = visitor.parseAndBuildPages();
SdMan.remove(tmpHtmlPath.c_str());
Storage.remove(tmpHtmlPath.c_str());
if (!success) {
Serial.printf("[%lu] [SCT] Failed to parse XML and build pages\n", millis());
file.close();
SdMan.remove(filePath.c_str());
Storage.remove(filePath.c_str());
return false;
}
@@ -210,7 +210,7 @@ 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();
SdMan.remove(filePath.c_str());
Storage.remove(filePath.c_str());
return false;
}
@@ -223,7 +223,7 @@ bool Section::createSectionFile(const int fontId, const float lineCompression, c
}
std::unique_ptr<Page> Section::loadPageFromSectionFile() {
if (!SdMan.openFileForRead("SCT", filePath, file)) {
if (!Storage.openFileForRead("SCT", filePath, file)) {
return nullptr;
}

View File

@@ -1,6 +1,6 @@
#pragma once
#include <EpdFontFamily.h>
#include <SdFat.h>
#include <HalStorage.h>
#include <list>
#include <memory>

View File

@@ -1,6 +1,6 @@
#pragma once
#include <SdFat.h>
#include <HalStorage.h>
#include <string>
#include <unordered_map>

View File

@@ -1,8 +1,8 @@
#include "ChapterHtmlSlimParser.h"
#include <GfxRenderer.h>
#include <HalStorage.h>
#include <HardwareSerial.h>
#include <SDCardManager.h>
#include <expat.h>
#include "../Page.h"
@@ -482,7 +482,7 @@ bool ChapterHtmlSlimParser::parseAndBuildPages() {
}
FsFile file;
if (!SdMan.openFileForRead("EHP", filepath, file)) {
if (!Storage.openFileForRead("EHP", filepath, file)) {
XML_ParserFree(parser);
return false;
}

View File

@@ -36,8 +36,8 @@ ContentOpfParser::~ContentOpfParser() {
if (tempItemStore) {
tempItemStore.close();
}
if (SdMan.exists((cachePath + itemCacheFile).c_str())) {
SdMan.remove((cachePath + itemCacheFile).c_str());
if (Storage.exists((cachePath + itemCacheFile).c_str())) {
Storage.remove((cachePath + itemCacheFile).c_str());
}
itemIndex.clear();
itemIndex.shrink_to_fit();
@@ -118,7 +118,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 (!SdMan.openFileForWrite("COF", self->cachePath + itemCacheFile, self->tempItemStore)) {
if (!Storage.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());
@@ -128,7 +128,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 (!SdMan.openFileForRead("COF", self->cachePath + itemCacheFile, self->tempItemStore)) {
if (!Storage.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());
@@ -149,7 +149,7 @@ void XMLCALL ContentOpfParser::startElement(void* userData, const XML_Char* name
self->state = IN_GUIDE;
// TODO Remove print
Serial.printf("[%lu] [COF] Entering guide state.\n", millis());
if (!SdMan.openFileForRead("COF", self->cachePath + itemCacheFile, self->tempItemStore)) {
if (!Storage.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());

View File

@@ -1,6 +1,6 @@
#pragma once
#include <SdFat.h>
#include <HalStorage.h>
#include <cstdint>

View File

@@ -1,7 +1,7 @@
#include "JpegToBmpConverter.h"
#include <HalStorage.h>
#include <HardwareSerial.h>
#include <SdFat.h>
#include <picojpeg.h>
#include <cstdio>

View File

@@ -1,8 +1,8 @@
#include "KOReaderCredentialStore.h"
#include <HalStorage.h>
#include <HardwareSerial.h>
#include <MD5Builder.h>
#include <SDCardManager.h>
#include <Serialization.h>
// Initialize the static instance
@@ -32,10 +32,10 @@ void KOReaderCredentialStore::obfuscate(std::string& data) const {
bool KOReaderCredentialStore::saveToFile() const {
// Make sure the directory exists
SdMan.mkdir("/.crosspoint");
Storage.mkdir("/.crosspoint");
FsFile file;
if (!SdMan.openFileForWrite("KRS", KOREADER_FILE, file)) {
if (!Storage.openFileForWrite("KRS", KOREADER_FILE, file)) {
return false;
}
@@ -64,7 +64,7 @@ bool KOReaderCredentialStore::saveToFile() const {
bool KOReaderCredentialStore::loadFromFile() {
FsFile file;
if (!SdMan.openFileForRead("KRS", KOREADER_FILE, file)) {
if (!Storage.openFileForRead("KRS", KOREADER_FILE, file)) {
Serial.printf("[%lu] [KRS] No credentials file found\n", millis());
return false;
}

View File

@@ -1,8 +1,8 @@
#include "KOReaderDocumentId.h"
#include <HalStorage.h>
#include <HardwareSerial.h>
#include <MD5Builder.h>
#include <SDCardManager.h>
namespace {
// Extract filename from path (everything after last '/')
@@ -43,7 +43,7 @@ size_t KOReaderDocumentId::getOffset(int i) {
std::string KOReaderDocumentId::calculate(const std::string& filePath) {
FsFile file;
if (!SdMan.openFileForRead("KODoc", filePath, file)) {
if (!Storage.openFileForRead("KODoc", filePath, file)) {
Serial.printf("[%lu] [KODoc] Failed to open file: %s\n", millis(), filePath.c_str());
return "";
}

View File

@@ -1,5 +1,5 @@
#pragma once
#include <SdFat.h>
#include <HalStorage.h>
#include <iostream>

View File

@@ -15,13 +15,13 @@ bool Txt::load() {
return true;
}
if (!SdMan.exists(filepath.c_str())) {
if (!Storage.exists(filepath.c_str())) {
Serial.printf("[%lu] [TXT] File does not exist: %s\n", millis(), filepath.c_str());
return false;
}
FsFile file;
if (!SdMan.openFileForRead("TXT", filepath, file)) {
if (!Storage.openFileForRead("TXT", filepath, file)) {
Serial.printf("[%lu] [TXT] Failed to open file: %s\n", millis(), filepath.c_str());
return false;
}
@@ -48,11 +48,11 @@ std::string Txt::getTitle() const {
}
void Txt::setupCacheDir() const {
if (!SdMan.exists(cacheBasePath.c_str())) {
SdMan.mkdir(cacheBasePath.c_str());
if (!Storage.exists(cacheBasePath.c_str())) {
Storage.mkdir(cacheBasePath.c_str());
}
if (!SdMan.exists(cachePath.c_str())) {
SdMan.mkdir(cachePath.c_str());
if (!Storage.exists(cachePath.c_str())) {
Storage.mkdir(cachePath.c_str());
}
}
@@ -73,7 +73,7 @@ std::string Txt::findCoverImage() const {
// First priority: look for image with same name as txt file (e.g., mybook.jpg)
for (const auto& ext : extensions) {
std::string coverPath = folder + "/" + baseName + ext;
if (SdMan.exists(coverPath.c_str())) {
if (Storage.exists(coverPath.c_str())) {
Serial.printf("[%lu] [TXT] Found matching cover image: %s\n", millis(), coverPath.c_str());
return coverPath;
}
@@ -84,7 +84,7 @@ std::string Txt::findCoverImage() const {
for (const auto& name : coverNames) {
for (const auto& ext : extensions) {
std::string coverPath = folder + "/" + std::string(name) + ext;
if (SdMan.exists(coverPath.c_str())) {
if (Storage.exists(coverPath.c_str())) {
Serial.printf("[%lu] [TXT] Found fallback cover image: %s\n", millis(), coverPath.c_str());
return coverPath;
}
@@ -98,7 +98,7 @@ std::string Txt::getCoverBmpPath() const { return cachePath + "/cover.bmp"; }
bool Txt::generateCoverBmp() const {
// Already generated, return true
if (SdMan.exists(getCoverBmpPath().c_str())) {
if (Storage.exists(getCoverBmpPath().c_str())) {
return true;
}
@@ -122,10 +122,10 @@ bool Txt::generateCoverBmp() const {
// Copy BMP file to cache
Serial.printf("[%lu] [TXT] Copying BMP cover image to cache\n", millis());
FsFile src, dst;
if (!SdMan.openFileForRead("TXT", coverImagePath, src)) {
if (!Storage.openFileForRead("TXT", coverImagePath, src)) {
return false;
}
if (!SdMan.openFileForWrite("TXT", getCoverBmpPath(), dst)) {
if (!Storage.openFileForWrite("TXT", getCoverBmpPath(), dst)) {
src.close();
return false;
}
@@ -144,10 +144,10 @@ bool Txt::generateCoverBmp() const {
// Convert JPG/JPEG to BMP (same approach as Epub)
Serial.printf("[%lu] [TXT] Generating BMP from JPG cover image\n", millis());
FsFile coverJpg, coverBmp;
if (!SdMan.openFileForRead("TXT", coverImagePath, coverJpg)) {
if (!Storage.openFileForRead("TXT", coverImagePath, coverJpg)) {
return false;
}
if (!SdMan.openFileForWrite("TXT", getCoverBmpPath(), coverBmp)) {
if (!Storage.openFileForWrite("TXT", getCoverBmpPath(), coverBmp)) {
coverJpg.close();
return false;
}
@@ -157,7 +157,7 @@ bool Txt::generateCoverBmp() const {
if (!success) {
Serial.printf("[%lu] [TXT] Failed to generate BMP from JPG cover image\n", millis());
SdMan.remove(getCoverBmpPath().c_str());
Storage.remove(getCoverBmpPath().c_str());
} else {
Serial.printf("[%lu] [TXT] Generated BMP from JPG cover image\n", millis());
}
@@ -175,7 +175,7 @@ bool Txt::readContent(uint8_t* buffer, size_t offset, size_t length) const {
}
FsFile file;
if (!SdMan.openFileForRead("TXT", filepath, file)) {
if (!Storage.openFileForRead("TXT", filepath, file)) {
return false;
}

View File

@@ -1,6 +1,6 @@
#pragma once
#include <SDCardManager.h>
#include <HalStorage.h>
#include <memory>
#include <string>

View File

@@ -7,8 +7,8 @@
#include "Xtc.h"
#include <HalStorage.h>
#include <HardwareSerial.h>
#include <SDCardManager.h>
bool Xtc::load() {
Serial.printf("[%lu] [XTC] Loading XTC: %s\n", millis(), filepath.c_str());
@@ -30,12 +30,12 @@ bool Xtc::load() {
}
bool Xtc::clearCache() const {
if (!SdMan.exists(cachePath.c_str())) {
if (!Storage.exists(cachePath.c_str())) {
Serial.printf("[%lu] [XTC] Cache does not exist, no action needed\n", millis());
return true;
}
if (!SdMan.removeDir(cachePath.c_str())) {
if (!Storage.removeDir(cachePath.c_str())) {
Serial.printf("[%lu] [XTC] Failed to clear cache\n", millis());
return false;
}
@@ -45,17 +45,17 @@ bool Xtc::clearCache() const {
}
void Xtc::setupCacheDir() const {
if (SdMan.exists(cachePath.c_str())) {
if (Storage.exists(cachePath.c_str())) {
return;
}
// Create directories recursively
for (size_t i = 1; i < cachePath.length(); i++) {
if (cachePath[i] == '/') {
SdMan.mkdir(cachePath.substr(0, i).c_str());
Storage.mkdir(cachePath.substr(0, i).c_str());
}
}
SdMan.mkdir(cachePath.c_str());
Storage.mkdir(cachePath.c_str());
}
std::string Xtc::getTitle() const {
@@ -114,7 +114,7 @@ std::string Xtc::getCoverBmpPath() const { return cachePath + "/cover.bmp"; }
bool Xtc::generateCoverBmp() const {
// Already generated
if (SdMan.exists(getCoverBmpPath().c_str())) {
if (Storage.exists(getCoverBmpPath().c_str())) {
return true;
}
@@ -166,7 +166,7 @@ bool Xtc::generateCoverBmp() const {
// Create BMP file
FsFile coverBmp;
if (!SdMan.openFileForWrite("XTC", getCoverBmpPath(), coverBmp)) {
if (!Storage.openFileForWrite("XTC", getCoverBmpPath(), coverBmp)) {
Serial.printf("[%lu] [XTC] Failed to create cover BMP file\n", millis());
free(pageBuffer);
return false;
@@ -306,7 +306,7 @@ std::string Xtc::getThumbBmpPath(int height) const { return cachePath + "/thumb_
bool Xtc::generateThumbBmp(int height) const {
// Already generated
if (SdMan.exists(getThumbBmpPath(height).c_str())) {
if (Storage.exists(getThumbBmpPath(height).c_str())) {
return true;
}
@@ -348,8 +348,8 @@ bool Xtc::generateThumbBmp(int height) const {
// Copy cover.bmp to thumb.bmp
if (generateCoverBmp()) {
FsFile src, dst;
if (SdMan.openFileForRead("XTC", getCoverBmpPath(), src)) {
if (SdMan.openFileForWrite("XTC", getThumbBmpPath(height), dst)) {
if (Storage.openFileForRead("XTC", getCoverBmpPath(), src)) {
if (Storage.openFileForWrite("XTC", getThumbBmpPath(height), dst)) {
uint8_t buffer[512];
while (src.available()) {
size_t bytesRead = src.read(buffer, sizeof(buffer));
@@ -360,7 +360,7 @@ bool Xtc::generateThumbBmp(int height) const {
src.close();
}
Serial.printf("[%lu] [XTC] Copied cover to thumb (no scaling needed)\n", millis());
return SdMan.exists(getThumbBmpPath(height).c_str());
return Storage.exists(getThumbBmpPath(height).c_str());
}
return false;
}
@@ -394,7 +394,7 @@ bool Xtc::generateThumbBmp(int height) const {
// Create thumbnail BMP file - use 1-bit format for fast home screen rendering (no gray passes)
FsFile thumbBmp;
if (!SdMan.openFileForWrite("XTC", getThumbBmpPath(height), thumbBmp)) {
if (!Storage.openFileForWrite("XTC", getThumbBmpPath(height), thumbBmp)) {
Serial.printf("[%lu] [XTC] Failed to create thumb BMP file\n", millis());
free(pageBuffer);
return false;

View File

@@ -8,8 +8,8 @@
#include "XtcParser.h"
#include <FsHelpers.h>
#include <HalStorage.h>
#include <HardwareSerial.h>
#include <SDCardManager.h>
#include <cstring>
@@ -34,7 +34,7 @@ XtcError XtcParser::open(const char* filepath) {
}
// Open file
if (!SdMan.openFileForRead("XTC", filepath, m_file)) {
if (!Storage.openFileForRead("XTC", filepath, m_file)) {
m_lastError = XtcError::FILE_NOT_FOUND;
return m_lastError;
}
@@ -444,7 +444,7 @@ XtcError XtcParser::loadPageStreaming(uint32_t pageIndex,
bool XtcParser::isValidXtcFile(const char* filepath) {
FsFile file;
if (!SdMan.openFileForRead("XTC", filepath, file)) {
if (!Storage.openFileForRead("XTC", filepath, file)) {
return false;
}

View File

@@ -7,7 +7,7 @@
#pragma once
#include <SdFat.h>
#include <HalStorage.h>
#include <functional>
#include <memory>

View File

@@ -1,7 +1,7 @@
#include "ZipFile.h"
#include <HalStorage.h>
#include <HardwareSerial.h>
#include <SDCardManager.h>
#include <miniz.h>
#include <algorithm>
@@ -279,7 +279,7 @@ bool ZipFile::loadZipDetails() {
}
bool ZipFile::open() {
if (!SdMan.openFileForRead("ZIP", filePath, file)) {
if (!Storage.openFileForRead("ZIP", filePath, file)) {
return false;
}
return true;

View File

@@ -1,5 +1,5 @@
#pragma once
#include <SdFat.h>
#include <HalStorage.h>
#include <string>
#include <unordered_map>

65
lib/hal/HalStorage.cpp Normal file
View File

@@ -0,0 +1,65 @@
#include "HalStorage.h"
#include <SDCardManager.h>
#define SDCard SDCardManager::getInstance()
HalStorage HalStorage::instance;
HalStorage::HalStorage() {}
bool HalStorage::begin() { return SDCard.begin(); }
bool HalStorage::ready() const { return SDCard.ready(); }
std::vector<String> HalStorage::listFiles(const char* path, int maxFiles) { return SDCard.listFiles(path, maxFiles); }
String HalStorage::readFile(const char* path) { return SDCard.readFile(path); }
bool HalStorage::readFileToStream(const char* path, Print& out, size_t chunkSize) {
return SDCard.readFileToStream(path, out, chunkSize);
}
size_t HalStorage::readFileToBuffer(const char* path, char* buffer, size_t bufferSize, size_t maxBytes) {
return SDCard.readFileToBuffer(path, buffer, bufferSize, maxBytes);
}
bool HalStorage::writeFile(const char* path, const String& content) { return SDCard.writeFile(path, content); }
bool HalStorage::ensureDirectoryExists(const char* path) { return SDCard.ensureDirectoryExists(path); }
FsFile HalStorage::open(const char* path, const oflag_t oflag) { return SDCard.open(path, oflag); }
bool HalStorage::mkdir(const char* path, const bool pFlag) { return SDCard.mkdir(path, pFlag); }
bool HalStorage::exists(const char* path) { return SDCard.exists(path); }
bool HalStorage::remove(const char* path) { return SDCard.remove(path); }
bool HalStorage::rmdir(const char* path) { return SDCard.rmdir(path); }
bool HalStorage::openFileForRead(const char* moduleName, const char* path, FsFile& file) {
return SDCard.openFileForRead(moduleName, path, file);
}
bool HalStorage::openFileForRead(const char* moduleName, const std::string& path, FsFile& file) {
return openFileForRead(moduleName, path.c_str(), file);
}
bool HalStorage::openFileForRead(const char* moduleName, const String& path, FsFile& file) {
return openFileForRead(moduleName, path.c_str(), file);
}
bool HalStorage::openFileForWrite(const char* moduleName, const char* path, FsFile& file) {
return SDCard.openFileForWrite(moduleName, path, file);
}
bool HalStorage::openFileForWrite(const char* moduleName, const std::string& path, FsFile& file) {
return openFileForWrite(moduleName, path.c_str(), file);
}
bool HalStorage::openFileForWrite(const char* moduleName, const String& path, FsFile& file) {
return openFileForWrite(moduleName, path.c_str(), file);
}
bool HalStorage::removeDir(const char* path) { return SDCard.removeDir(path); }

54
lib/hal/HalStorage.h Normal file
View File

@@ -0,0 +1,54 @@
#pragma once
#include <SDCardManager.h>
#include <vector>
class HalStorage {
public:
HalStorage();
bool begin();
bool ready() const;
std::vector<String> listFiles(const char* path = "/", int maxFiles = 200);
// Read the entire file at `path` into a String. Returns empty string on failure.
String readFile(const char* path);
// Low-memory helpers:
// Stream the file contents to a `Print` (e.g. `Serial`, or any `Print`-derived object).
// Returns true on success, false on failure.
bool readFileToStream(const char* path, Print& out, size_t chunkSize = 256);
// Read up to `bufferSize-1` bytes into `buffer`, null-terminating it. Returns bytes read.
size_t readFileToBuffer(const char* path, char* buffer, size_t bufferSize, size_t maxBytes = 0);
// Write a string to `path` on the SD card. Overwrites existing file.
// Returns true on success.
bool writeFile(const char* path, const String& content);
// Ensure a directory exists, creating it if necessary. Returns true on success.
bool ensureDirectoryExists(const char* path);
FsFile open(const char* path, const oflag_t oflag = O_RDONLY);
bool mkdir(const char* path, const bool pFlag = true);
bool exists(const char* path);
bool remove(const char* path);
bool rmdir(const char* path);
bool openFileForRead(const char* moduleName, const char* path, FsFile& file);
bool openFileForRead(const char* moduleName, const std::string& path, FsFile& file);
bool openFileForRead(const char* moduleName, const String& path, FsFile& file);
bool openFileForWrite(const char* moduleName, const char* path, FsFile& file);
bool openFileForWrite(const char* moduleName, const std::string& path, FsFile& file);
bool openFileForWrite(const char* moduleName, const String& path, FsFile& file);
bool removeDir(const char* path);
static HalStorage& getInstance() { return instance; }
private:
static HalStorage instance;
bool initialized = false;
};
#define Storage HalStorage::getInstance()
// Downstream code must use Storage instead of SdMan
#ifdef SdMan
#undef SdMan
#endif

View File

@@ -1,7 +1,7 @@
#include "CrossPointSettings.h"
#include <HalStorage.h>
#include <HardwareSerial.h>
#include <SDCardManager.h>
#include <Serialization.h>
#include <cstring>
@@ -79,10 +79,10 @@ void applyLegacyFrontButtonLayout(CrossPointSettings& settings) {
bool CrossPointSettings::saveToFile() const {
// Make sure the directory exists
SdMan.mkdir("/.crosspoint");
Storage.mkdir("/.crosspoint");
FsFile outputFile;
if (!SdMan.openFileForWrite("CPS", SETTINGS_FILE, outputFile)) {
if (!Storage.openFileForWrite("CPS", SETTINGS_FILE, outputFile)) {
return false;
}
@@ -127,7 +127,7 @@ bool CrossPointSettings::saveToFile() const {
bool CrossPointSettings::loadFromFile() {
FsFile inputFile;
if (!SdMan.openFileForRead("CPS", SETTINGS_FILE, inputFile)) {
if (!Storage.openFileForRead("CPS", SETTINGS_FILE, inputFile)) {
return false;
}

View File

@@ -1,7 +1,7 @@
#include "CrossPointState.h"
#include <HalStorage.h>
#include <HardwareSerial.h>
#include <SDCardManager.h>
#include <Serialization.h>
namespace {
@@ -13,7 +13,7 @@ CrossPointState CrossPointState::instance;
bool CrossPointState::saveToFile() const {
FsFile outputFile;
if (!SdMan.openFileForWrite("CPS", STATE_FILE, outputFile)) {
if (!Storage.openFileForWrite("CPS", STATE_FILE, outputFile)) {
return false;
}
@@ -28,7 +28,7 @@ bool CrossPointState::saveToFile() const {
bool CrossPointState::loadFromFile() {
FsFile inputFile;
if (!SdMan.openFileForRead("CPS", STATE_FILE, inputFile)) {
if (!Storage.openFileForRead("CPS", STATE_FILE, inputFile)) {
return false;
}

View File

@@ -1,8 +1,8 @@
#include "RecentBooksStore.h"
#include <Epub.h>
#include <HalStorage.h>
#include <HardwareSerial.h>
#include <SDCardManager.h>
#include <Serialization.h>
#include <Xtc.h>
@@ -53,10 +53,10 @@ void RecentBooksStore::updateBook(const std::string& path, const std::string& ti
bool RecentBooksStore::saveToFile() const {
// Make sure the directory exists
SdMan.mkdir("/.crosspoint");
Storage.mkdir("/.crosspoint");
FsFile outputFile;
if (!SdMan.openFileForWrite("RBS", RECENT_BOOKS_FILE, outputFile)) {
if (!Storage.openFileForWrite("RBS", RECENT_BOOKS_FILE, outputFile)) {
return false;
}
@@ -106,7 +106,7 @@ RecentBook RecentBooksStore::getDataFromBook(std::string path) const {
bool RecentBooksStore::loadFromFile() {
FsFile inputFile;
if (!SdMan.openFileForRead("RBS", RECENT_BOOKS_FILE, inputFile)) {
if (!Storage.openFileForRead("RBS", RECENT_BOOKS_FILE, inputFile)) {
return false;
}

View File

@@ -1,7 +1,7 @@
#include "WifiCredentialStore.h"
#include <HalStorage.h>
#include <HardwareSerial.h>
#include <SDCardManager.h>
#include <Serialization.h>
// Initialize the static instance
@@ -29,10 +29,10 @@ void WifiCredentialStore::obfuscate(std::string& data) const {
bool WifiCredentialStore::saveToFile() const {
// Make sure the directory exists
SdMan.mkdir("/.crosspoint");
Storage.mkdir("/.crosspoint");
FsFile file;
if (!SdMan.openFileForWrite("WCS", WIFI_FILE, file)) {
if (!Storage.openFileForWrite("WCS", WIFI_FILE, file)) {
return false;
}
@@ -60,7 +60,7 @@ bool WifiCredentialStore::saveToFile() const {
bool WifiCredentialStore::loadFromFile() {
FsFile file;
if (!SdMan.openFileForRead("WCS", WIFI_FILE, file)) {
if (!Storage.openFileForRead("WCS", WIFI_FILE, file)) {
return false;
}

View File

@@ -2,7 +2,7 @@
#include <Epub.h>
#include <GfxRenderer.h>
#include <SDCardManager.h>
#include <HalStorage.h>
#include <Txt.h>
#include <Xtc.h>
@@ -32,7 +32,7 @@ void SleepActivity::onEnter() {
void SleepActivity::renderCustomSleepScreen() const {
// Check if we have a /sleep directory
auto dir = SdMan.open("/sleep");
auto dir = Storage.open("/sleep");
if (dir && dir.isDirectory()) {
std::vector<std::string> files;
char name[500];
@@ -75,7 +75,7 @@ void SleepActivity::renderCustomSleepScreen() const {
APP_STATE.saveToFile();
const auto filename = "/sleep/" + files[randomFileIndex];
FsFile file;
if (SdMan.openFileForRead("SLP", filename, file)) {
if (Storage.openFileForRead("SLP", filename, file)) {
Serial.printf("[%lu] [SLP] Randomly loading: /sleep/%s\n", millis(), files[randomFileIndex].c_str());
delay(100);
Bitmap bitmap(file, true);
@@ -92,7 +92,7 @@ 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.
FsFile file;
if (SdMan.openFileForRead("SLP", "/sleep.bmp", file)) {
if (Storage.openFileForRead("SLP", "/sleep.bmp", file)) {
Bitmap bitmap(file, true);
if (bitmap.parseHeaders() == BmpReaderError::Ok) {
Serial.printf("[%lu] [SLP] Loading: /sleep.bmp\n", millis());
@@ -262,7 +262,7 @@ void SleepActivity::renderCoverSleepScreen() const {
}
FsFile file;
if (SdMan.openFileForRead("SLP", coverBmpPath, file)) {
if (Storage.openFileForRead("SLP", coverBmpPath, file)) {
Bitmap bitmap(file);
if (bitmap.parseHeaders() == BmpReaderError::Ok) {
Serial.printf("[SLP] Rendering sleep cover: %s\n", coverBmpPath.c_str());

View File

@@ -3,7 +3,7 @@
#include <Bitmap.h>
#include <Epub.h>
#include <GfxRenderer.h>
#include <SDCardManager.h>
#include <HalStorage.h>
#include <Utf8.h>
#include <Xtc.h>
@@ -47,7 +47,7 @@ void HomeActivity::loadRecentBooks(int maxBooks) {
}
// Skip if file no longer exists
if (!SdMan.exists(book.path.c_str())) {
if (!Storage.exists(book.path.c_str())) {
continue;
}
@@ -64,7 +64,7 @@ void HomeActivity::loadRecentCovers(int coverHeight) {
for (RecentBook& book : recentBooks) {
if (!book.coverBmpPath.empty()) {
std::string coverPath = UITheme::getCoverThumbPath(book.coverBmpPath, coverHeight);
if (!SdMan.exists(coverPath.c_str())) {
if (!Storage.exists(coverPath.c_str())) {
// If epub, try to load the metadata for title/author and cover
if (StringUtils::checkFileExtension(book.path, ".epub")) {
Epub epub(book.path, "/.crosspoint");

View File

@@ -1,7 +1,7 @@
#include "MyLibraryActivity.h"
#include <GfxRenderer.h>
#include <SDCardManager.h>
#include <HalStorage.h>
#include <algorithm>
@@ -33,7 +33,7 @@ void MyLibraryActivity::taskTrampoline(void* param) {
void MyLibraryActivity::loadFiles() {
files.clear();
auto root = SdMan.open(basepath.c_str());
auto root = Storage.open(basepath.c_str());
if (!root || !root.isDirectory()) {
if (root) root.close();
return;

View File

@@ -1,7 +1,7 @@
#include "RecentBooksActivity.h"
#include <GfxRenderer.h>
#include <SDCardManager.h>
#include <HalStorage.h>
#include <algorithm>
@@ -28,7 +28,7 @@ void RecentBooksActivity::loadRecentBooks() {
for (const auto& book : books) {
// Skip if file no longer exists
if (!SdMan.exists(book.path.c_str())) {
if (!Storage.exists(book.path.c_str())) {
continue;
}
recentBooks.push_back(book);

View File

@@ -3,7 +3,7 @@
#include <Epub/Page.h>
#include <FsHelpers.h>
#include <GfxRenderer.h>
#include <SDCardManager.h>
#include <HalStorage.h>
#include "CrossPointSettings.h"
#include "CrossPointState.h"
@@ -77,7 +77,7 @@ void EpubReaderActivity::onEnter() {
epub->setupCacheDir();
FsFile f;
if (SdMan.openFileForRead("ERS", epub->getCachePath() + "/progress.bin", f)) {
if (Storage.openFileForRead("ERS", epub->getCachePath() + "/progress.bin", f)) {
uint8_t data[6];
int dataSize = f.read(data, 6);
if (dataSize == 4 || dataSize == 6) {
@@ -654,7 +654,7 @@ void EpubReaderActivity::renderScreen() {
void EpubReaderActivity::saveProgress(int spineIndex, int currentPage, int pageCount) {
FsFile f;
if (SdMan.openFileForWrite("ERS", epub->getCachePath() + "/progress.bin", f)) {
if (Storage.openFileForWrite("ERS", epub->getCachePath() + "/progress.bin", f)) {
uint8_t data[6];
data[0] = currentSpineIndex & 0xFF;
data[1] = (currentSpineIndex >> 8) & 0xFF;

View File

@@ -1,5 +1,7 @@
#include "ReaderActivity.h"
#include <HalStorage.h>
#include "Epub.h"
#include "EpubReaderActivity.h"
#include "Txt.h"
@@ -27,7 +29,7 @@ bool ReaderActivity::isTxtFile(const std::string& path) {
}
std::unique_ptr<Epub> ReaderActivity::loadEpub(const std::string& path) {
if (!SdMan.exists(path.c_str())) {
if (!Storage.exists(path.c_str())) {
Serial.printf("[%lu] [ ] File does not exist: %s\n", millis(), path.c_str());
return nullptr;
}
@@ -42,7 +44,7 @@ std::unique_ptr<Epub> ReaderActivity::loadEpub(const std::string& path) {
}
std::unique_ptr<Xtc> ReaderActivity::loadXtc(const std::string& path) {
if (!SdMan.exists(path.c_str())) {
if (!Storage.exists(path.c_str())) {
Serial.printf("[%lu] [ ] File does not exist: %s\n", millis(), path.c_str());
return nullptr;
}
@@ -57,7 +59,7 @@ std::unique_ptr<Xtc> ReaderActivity::loadXtc(const std::string& path) {
}
std::unique_ptr<Txt> ReaderActivity::loadTxt(const std::string& path) {
if (!SdMan.exists(path.c_str())) {
if (!Storage.exists(path.c_str())) {
Serial.printf("[%lu] [ ] File does not exist: %s\n", millis(), path.c_str());
return nullptr;
}

View File

@@ -1,7 +1,7 @@
#include "TxtReaderActivity.h"
#include <GfxRenderer.h>
#include <SDCardManager.h>
#include <HalStorage.h>
#include <Serialization.h>
#include <Utf8.h>
@@ -565,7 +565,7 @@ void TxtReaderActivity::renderStatusBar(const int orientedMarginRight, const int
void TxtReaderActivity::saveProgress() const {
FsFile f;
if (SdMan.openFileForWrite("TRS", txt->getCachePath() + "/progress.bin", f)) {
if (Storage.openFileForWrite("TRS", txt->getCachePath() + "/progress.bin", f)) {
uint8_t data[4];
data[0] = currentPage & 0xFF;
data[1] = (currentPage >> 8) & 0xFF;
@@ -578,7 +578,7 @@ void TxtReaderActivity::saveProgress() const {
void TxtReaderActivity::loadProgress() {
FsFile f;
if (SdMan.openFileForRead("TRS", txt->getCachePath() + "/progress.bin", f)) {
if (Storage.openFileForRead("TRS", txt->getCachePath() + "/progress.bin", f)) {
uint8_t data[4];
if (f.read(data, 4) == 4) {
currentPage = data[0] + (data[1] << 8);
@@ -609,7 +609,7 @@ bool TxtReaderActivity::loadPageIndexCache() {
std::string cachePath = txt->getCachePath() + "/index.bin";
FsFile f;
if (!SdMan.openFileForRead("TRS", cachePath, f)) {
if (!Storage.openFileForRead("TRS", cachePath, f)) {
Serial.printf("[%lu] [TRS] No page index cache found\n", millis());
return false;
}
@@ -701,7 +701,7 @@ bool TxtReaderActivity::loadPageIndexCache() {
void TxtReaderActivity::savePageIndexCache() const {
std::string cachePath = txt->getCachePath() + "/index.bin";
FsFile f;
if (!SdMan.openFileForWrite("TRS", cachePath, f)) {
if (!Storage.openFileForWrite("TRS", cachePath, f)) {
Serial.printf("[%lu] [TRS] Failed to save page index cache\n", millis());
return;
}

View File

@@ -9,7 +9,7 @@
#include <FsHelpers.h>
#include <GfxRenderer.h>
#include <SDCardManager.h>
#include <HalStorage.h>
#include "CrossPointSettings.h"
#include "CrossPointState.h"
@@ -372,7 +372,7 @@ void XtcReaderActivity::renderPage() {
void XtcReaderActivity::saveProgress() const {
FsFile f;
if (SdMan.openFileForWrite("XTR", xtc->getCachePath() + "/progress.bin", f)) {
if (Storage.openFileForWrite("XTR", xtc->getCachePath() + "/progress.bin", f)) {
uint8_t data[4];
data[0] = currentPage & 0xFF;
data[1] = (currentPage >> 8) & 0xFF;
@@ -385,7 +385,7 @@ void XtcReaderActivity::saveProgress() const {
void XtcReaderActivity::loadProgress() {
FsFile f;
if (SdMan.openFileForRead("XTR", xtc->getCachePath() + "/progress.bin", f)) {
if (Storage.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);

View File

@@ -1,8 +1,8 @@
#include "ClearCacheActivity.h"
#include <GfxRenderer.h>
#include <HalStorage.h>
#include <HardwareSerial.h>
#include <SDCardManager.h>
#include "MappedInputManager.h"
#include "components/UITheme.h"
@@ -107,7 +107,7 @@ void ClearCacheActivity::clearCache() {
Serial.printf("[%lu] [CLEAR_CACHE] Clearing cache...\n", millis());
// Open .crosspoint directory
auto root = SdMan.open("/.crosspoint");
auto root = Storage.open("/.crosspoint");
if (!root || !root.isDirectory()) {
Serial.printf("[%lu] [CLEAR_CACHE] Failed to open cache directory\n", millis());
if (root) root.close();
@@ -132,7 +132,7 @@ void ClearCacheActivity::clearCache() {
file.close(); // Close before attempting to delete
if (SdMan.removeDir(fullPath.c_str())) {
if (Storage.removeDir(fullPath.c_str())) {
clearedCount++;
} else {
Serial.printf("[%lu] [CLEAR_CACHE] Failed to remove: %s\n", millis(), fullPath.c_str());

View File

@@ -1,7 +1,7 @@
#include "BaseTheme.h"
#include <GfxRenderer.h>
#include <SDCardManager.h>
#include <HalStorage.h>
#include <Utf8.h>
#include <cstdint>
@@ -308,7 +308,7 @@ void BaseTheme::drawRecentBookCover(GfxRenderer& renderer, Rect rect, const std:
// First time: load cover from SD and render
FsFile file;
if (SdMan.openFileForRead("HOME", coverBmpPath, file)) {
if (Storage.openFileForRead("HOME", coverBmpPath, file)) {
Bitmap bitmap(file);
if (bitmap.parseHeaders() == BmpReaderError::Ok) {
Serial.printf("Rendering bmp\n");

View File

@@ -1,7 +1,7 @@
#include "LyraTheme.h"
#include <GfxRenderer.h>
#include <SDCardManager.h>
#include <HalStorage.h>
#include <cstdint>
#include <string>
@@ -283,7 +283,7 @@ void LyraTheme::drawRecentBookCover(GfxRenderer& renderer, Rect rect, const std:
// First time: load cover from SD and render
FsFile file;
if (SdMan.openFileForRead("HOME", coverBmpPath, file)) {
if (Storage.openFileForRead("HOME", coverBmpPath, file)) {
Bitmap bitmap(file);
if (bitmap.parseHeaders() == BmpReaderError::Ok) {
float coverHeight = static_cast<float>(bitmap.getHeight());

View File

@@ -3,7 +3,7 @@
#include <GfxRenderer.h>
#include <HalDisplay.h>
#include <HalGPIO.h>
#include <SDCardManager.h>
#include <HalStorage.h>
#include <SPI.h>
#include <builtinFonts/all.h>
@@ -293,7 +293,7 @@ void setup() {
// SD Card Initialization
// We need 6 open files concurrently when parsing a new chapter
if (!SdMan.begin()) {
if (!Storage.begin()) {
Serial.printf("[%lu] [ ] SD card initialization failed\n", millis());
setupDisplayAndFonts();
exitActivity();

View File

@@ -3,7 +3,7 @@
#include <ArduinoJson.h>
#include <Epub.h>
#include <FsHelpers.h>
#include <SDCardManager.h>
#include <HalStorage.h>
#include <WiFi.h>
#include <esp_task_wdt.h>
@@ -316,7 +316,7 @@ void CrossPointWebServer::handleStatus() const {
}
void CrossPointWebServer::scanFiles(const char* path, const std::function<void(FileInfo)>& callback) const {
FsFile root = SdMan.open(path);
FsFile root = Storage.open(path);
if (!root) {
Serial.printf("[%lu] [WEB] Failed to open directory: %s\n", millis(), path);
return;
@@ -458,12 +458,12 @@ void CrossPointWebServer::handleDownload() const {
}
}
if (!SdMan.exists(itemPath.c_str())) {
if (!Storage.exists(itemPath.c_str())) {
server->send(404, "text/plain", "Item not found");
return;
}
FsFile file = SdMan.open(itemPath.c_str());
FsFile file = Storage.open(itemPath.c_str());
if (!file) {
server->send(500, "text/plain", "Failed to open file");
return;
@@ -574,15 +574,15 @@ void CrossPointWebServer::handleUpload(UploadState& state) const {
// Check if file already exists - SD operations can be slow
esp_task_wdt_reset();
if (SdMan.exists(filePath.c_str())) {
if (Storage.exists(filePath.c_str())) {
Serial.printf("[%lu] [WEB] [UPLOAD] Overwriting existing file: %s\n", millis(), filePath.c_str());
esp_task_wdt_reset();
SdMan.remove(filePath.c_str());
Storage.remove(filePath.c_str());
}
// Open file for writing - this can be slow due to FAT cluster allocation
esp_task_wdt_reset();
if (!SdMan.openFileForWrite("WEB", filePath, state.file)) {
if (!Storage.openFileForWrite("WEB", filePath, state.file)) {
state.error = "Failed to create file on SD card";
Serial.printf("[%lu] [WEB] [UPLOAD] FAILED to create file: %s\n", millis(), filePath.c_str());
return;
@@ -660,7 +660,7 @@ void CrossPointWebServer::handleUpload(UploadState& state) const {
String filePath = state.path;
if (!filePath.endsWith("/")) filePath += "/";
filePath += state.fileName;
SdMan.remove(filePath.c_str());
Storage.remove(filePath.c_str());
}
state.error = "Upload aborted";
Serial.printf("[%lu] [WEB] Upload aborted\n", millis());
@@ -711,13 +711,13 @@ void CrossPointWebServer::handleCreateFolder() const {
Serial.printf("[%lu] [WEB] Creating folder: %s\n", millis(), folderPath.c_str());
// Check if already exists
if (SdMan.exists(folderPath.c_str())) {
if (Storage.exists(folderPath.c_str())) {
server->send(400, "text/plain", "Folder already exists");
return;
}
// Create the folder
if (SdMan.mkdir(folderPath.c_str())) {
if (Storage.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 {
@@ -763,12 +763,12 @@ void CrossPointWebServer::handleRename() const {
return;
}
if (!SdMan.exists(itemPath.c_str())) {
if (!Storage.exists(itemPath.c_str())) {
server->send(404, "text/plain", "Item not found");
return;
}
FsFile file = SdMan.open(itemPath.c_str());
FsFile file = Storage.open(itemPath.c_str());
if (!file) {
server->send(500, "text/plain", "Failed to open file");
return;
@@ -789,7 +789,7 @@ void CrossPointWebServer::handleRename() const {
}
newPath += newName;
if (SdMan.exists(newPath.c_str())) {
if (Storage.exists(newPath.c_str())) {
file.close();
server->send(409, "text/plain", "Target already exists");
return;
@@ -839,12 +839,12 @@ void CrossPointWebServer::handleMove() const {
}
}
if (!SdMan.exists(itemPath.c_str())) {
if (!Storage.exists(itemPath.c_str())) {
server->send(404, "text/plain", "Item not found");
return;
}
FsFile file = SdMan.open(itemPath.c_str());
FsFile file = Storage.open(itemPath.c_str());
if (!file) {
server->send(500, "text/plain", "Failed to open file");
return;
@@ -855,12 +855,12 @@ void CrossPointWebServer::handleMove() const {
return;
}
if (!SdMan.exists(destPath.c_str())) {
if (!Storage.exists(destPath.c_str())) {
file.close();
server->send(404, "text/plain", "Destination not found");
return;
}
FsFile destDir = SdMan.open(destPath.c_str());
FsFile destDir = Storage.open(destPath.c_str());
if (!destDir || !destDir.isDirectory()) {
if (destDir) {
destDir.close();
@@ -882,7 +882,7 @@ void CrossPointWebServer::handleMove() const {
server->send(200, "text/plain", "Already in destination");
return;
}
if (SdMan.exists(newPath.c_str())) {
if (Storage.exists(newPath.c_str())) {
file.close();
server->send(409, "text/plain", "Target already exists");
return;
@@ -942,7 +942,7 @@ void CrossPointWebServer::handleDelete() const {
}
// Check if item exists
if (!SdMan.exists(itemPath.c_str())) {
if (!Storage.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;
@@ -954,7 +954,7 @@ void CrossPointWebServer::handleDelete() const {
if (itemType == "folder") {
// For folders, try to remove (will fail if not empty)
FsFile dir = SdMan.open(itemPath.c_str());
FsFile dir = Storage.open(itemPath.c_str());
if (dir && dir.isDirectory()) {
// Check if folder is empty
FsFile entry = dir.openNextFile();
@@ -968,10 +968,10 @@ void CrossPointWebServer::handleDelete() const {
}
dir.close();
}
success = SdMan.rmdir(itemPath.c_str());
success = Storage.rmdir(itemPath.c_str());
} else {
// For files, use remove
success = SdMan.remove(itemPath.c_str());
success = Storage.remove(itemPath.c_str());
}
if (success) {
@@ -1007,7 +1007,7 @@ void CrossPointWebServer::onWebSocketEvent(uint8_t num, WStype_t type, uint8_t*
String filePath = wsUploadPath;
if (!filePath.endsWith("/")) filePath += "/";
filePath += wsUploadFileName;
SdMan.remove(filePath.c_str());
Storage.remove(filePath.c_str());
Serial.printf("[%lu] [WS] Deleted incomplete upload: %s\n", millis(), filePath.c_str());
}
wsUploadInProgress = false;
@@ -1051,13 +1051,13 @@ void CrossPointWebServer::onWebSocketEvent(uint8_t num, WStype_t type, uint8_t*
// Check if file exists and remove it
esp_task_wdt_reset();
if (SdMan.exists(filePath.c_str())) {
SdMan.remove(filePath.c_str());
if (Storage.exists(filePath.c_str())) {
Storage.remove(filePath.c_str());
}
// Open file for writing
esp_task_wdt_reset();
if (!SdMan.openFileForWrite("WS", filePath, wsUploadFile)) {
if (!Storage.openFileForWrite("WS", filePath, wsUploadFile)) {
wsServer->sendTXT(num, "ERROR:Failed to create file");
wsUploadInProgress = false;
return;

View File

@@ -1,6 +1,6 @@
#pragma once
#include <SDCardManager.h>
#include <HalStorage.h>
#include <WebServer.h>
#include <WebSocketsServer.h>
#include <WiFiUdp.h>

View File

@@ -100,13 +100,13 @@ HttpDownloader::DownloadError HttpDownloader::downloadToFile(const std::string&
Serial.printf("[%lu] [HTTP] Content-Length: %zu\n", millis(), contentLength);
// Remove existing file if present
if (SdMan.exists(destPath.c_str())) {
SdMan.remove(destPath.c_str());
if (Storage.exists(destPath.c_str())) {
Storage.remove(destPath.c_str());
}
// Open file for writing
FsFile file;
if (!SdMan.openFileForWrite("HTTP", destPath.c_str(), file)) {
if (!Storage.openFileForWrite("HTTP", destPath.c_str(), file)) {
Serial.printf("[%lu] [HTTP] Failed to open file for writing\n", millis());
http.end();
return FILE_ERROR;
@@ -117,7 +117,7 @@ HttpDownloader::DownloadError HttpDownloader::downloadToFile(const std::string&
if (!stream) {
Serial.printf("[%lu] [HTTP] Failed to get stream\n", millis());
file.close();
SdMan.remove(destPath.c_str());
Storage.remove(destPath.c_str());
http.end();
return HTTP_ERROR;
}
@@ -145,7 +145,7 @@ HttpDownloader::DownloadError HttpDownloader::downloadToFile(const std::string&
if (written != bytesRead) {
Serial.printf("[%lu] [HTTP] Write failed: wrote %zu of %zu bytes\n", millis(), written, bytesRead);
file.close();
SdMan.remove(destPath.c_str());
Storage.remove(destPath.c_str());
http.end();
return FILE_ERROR;
}
@@ -165,7 +165,7 @@ HttpDownloader::DownloadError HttpDownloader::downloadToFile(const std::string&
// Verify download size if known
if (contentLength > 0 && downloaded != contentLength) {
Serial.printf("[%lu] [HTTP] Size mismatch: got %zu, expected %zu\n", millis(), downloaded, contentLength);
SdMan.remove(destPath.c_str());
Storage.remove(destPath.c_str());
return HTTP_ERROR;
}

View File

@@ -1,5 +1,5 @@
#pragma once
#include <SDCardManager.h>
#include <HalStorage.h>
#include <functional>
#include <string>