Cleanup
This commit is contained in:
parent
a325f12656
commit
dc3869ac1c
@ -44,7 +44,15 @@ bool Epub::findContentOpfFile(std::string* contentOpfFile) const {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Epub::parseContentOpf(const std::string& contentOpfFilePath) {
|
bool Epub::parseContentOpf(bool useCache) {
|
||||||
|
std::string contentOpfFilePath;
|
||||||
|
if (!findContentOpfFile(&contentOpfFilePath)) {
|
||||||
|
Serial.printf("[%lu] [EBP] Could not find content.opf in zip\n", millis());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
contentBasePath = contentOpfFilePath.substr(0, contentOpfFilePath.find_last_of('/') + 1);
|
||||||
|
|
||||||
Serial.printf("[%lu] [EBP] Parsing content.opf: %s\n", millis(), contentOpfFilePath.c_str());
|
Serial.printf("[%lu] [EBP] Parsing content.opf: %s\n", millis(), contentOpfFilePath.c_str());
|
||||||
|
|
||||||
size_t contentOpfSize;
|
size_t contentOpfSize;
|
||||||
@ -53,7 +61,7 @@ bool Epub::parseContentOpf(const std::string& contentOpfFilePath) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ContentOpfParser opfParser(getBasePath(), contentOpfSize, spineTocCache.get());
|
ContentOpfParser opfParser(getBasePath(), contentOpfSize, useCache ? spineTocCache.get() : nullptr);
|
||||||
|
|
||||||
if (!opfParser.setup()) {
|
if (!opfParser.setup()) {
|
||||||
Serial.printf("[%lu] [EBP] Could not setup content.opf parser\n", millis());
|
Serial.printf("[%lu] [EBP] Could not setup content.opf parser\n", millis());
|
||||||
@ -140,76 +148,32 @@ bool Epub::load() {
|
|||||||
Serial.printf("[%lu] [EBP] Loaded spine/TOC from cache\n", millis());
|
Serial.printf("[%lu] [EBP] Loaded spine/TOC from cache\n", millis());
|
||||||
|
|
||||||
// Still need to parse content.opf for title and cover
|
// Still need to parse content.opf for title and cover
|
||||||
// TODO: Should this data go in the cache?
|
if (!parseContentOpf(false)) {
|
||||||
std::string contentOpfFilePath;
|
Serial.printf("[%lu] [EBP] Could not parse content.opf\n", millis());
|
||||||
if (!findContentOpfFile(&contentOpfFilePath)) {
|
|
||||||
Serial.printf("[%lu] [EBP] Could not find content.opf in zip\n", millis());
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
contentBasePath = contentOpfFilePath.substr(0, contentOpfFilePath.find_last_of('/') + 1);
|
|
||||||
|
|
||||||
// Parse content.opf but without cache (we already have it)
|
|
||||||
size_t contentOpfSize;
|
|
||||||
if (!getItemSize(contentOpfFilePath, &contentOpfSize)) {
|
|
||||||
Serial.printf("[%lu] [EBP] Could not get size of content.opf\n", millis());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentOpfParser opfParser(getBasePath(), contentOpfSize, nullptr);
|
|
||||||
if (!opfParser.setup()) {
|
|
||||||
Serial.printf("[%lu] [EBP] Could not setup content.opf parser\n", millis());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!readItemContentsToStream(contentOpfFilePath, opfParser, 1024)) {
|
|
||||||
Serial.printf("[%lu] [EBP] Could not read content.opf\n", millis());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
title = opfParser.title;
|
|
||||||
if (!opfParser.coverItemId.empty() && opfParser.items.count(opfParser.coverItemId) > 0) {
|
|
||||||
coverImageItem = opfParser.items.at(opfParser.coverItemId);
|
|
||||||
}
|
|
||||||
|
|
||||||
Serial.printf("[%lu] [EBP] Loaded ePub: %s\n", millis(), filepath.c_str());
|
Serial.printf("[%lu] [EBP] Loaded ePub: %s\n", millis(), filepath.c_str());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cache doesn't exist or is invalid, build it
|
// Cache doesn't exist or is invalid, build it
|
||||||
Serial.printf("[%lu] [EBP] Cache not found, building spine/TOC cache\n", millis());
|
Serial.printf("[%lu] [EBP] Cache not found, building spine/TOC cache\n", millis());
|
||||||
|
|
||||||
std::string contentOpfFilePath;
|
|
||||||
if (!findContentOpfFile(&contentOpfFilePath)) {
|
|
||||||
Serial.printf("[%lu] [EBP] Could not find content.opf in zip\n", millis());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Serial.printf("[%lu] [EBP] Found content.opf at: %s\n", millis(), contentOpfFilePath.c_str());
|
|
||||||
|
|
||||||
contentBasePath = contentOpfFilePath.substr(0, contentOpfFilePath.find_last_of('/') + 1);
|
|
||||||
|
|
||||||
// Ensure cache directory exists
|
|
||||||
setupCacheDir();
|
setupCacheDir();
|
||||||
|
|
||||||
Serial.printf("[%lu] [EBP] Cache path: %s\n", millis(), cachePath.c_str());
|
|
||||||
|
|
||||||
// Begin building cache - stream entries to disk immediately
|
// Begin building cache - stream entries to disk immediately
|
||||||
if (!spineTocCache->beginWrite()) {
|
if (!spineTocCache->beginWrite()) {
|
||||||
Serial.printf("[%lu] [EBP] Could not begin writing cache\n", millis());
|
Serial.printf("[%lu] [EBP] Could not begin writing cache\n", millis());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!parseContentOpf(true)) {
|
||||||
if (!parseContentOpf(contentOpfFilePath)) {
|
|
||||||
Serial.printf("[%lu] [EBP] Could not parse content.opf\n", millis());
|
Serial.printf("[%lu] [EBP] Could not parse content.opf\n", millis());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!parseTocNcxFile()) {
|
if (!parseTocNcxFile()) {
|
||||||
Serial.printf("[%lu] [EBP] Could not parse toc\n", millis());
|
Serial.printf("[%lu] [EBP] Could not parse toc\n", millis());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close the cache files
|
// Close the cache files
|
||||||
if (!spineTocCache->endWrite()) {
|
if (!spineTocCache->endWrite()) {
|
||||||
Serial.printf("[%lu] [EBP] Could not end writing cache\n", millis());
|
Serial.printf("[%lu] [EBP] Could not end writing cache\n", millis());
|
||||||
@ -230,7 +194,6 @@ bool Epub::load() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Serial.printf("[%lu] [EBP] Loaded ePub: %s\n", millis(), filepath.c_str());
|
Serial.printf("[%lu] [EBP] Loaded ePub: %s\n", millis(), filepath.c_str());
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -309,45 +272,9 @@ bool Epub::generateCoverBmp() const {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string normalisePath(const std::string& path) {
|
|
||||||
std::vector<std::string> components;
|
|
||||||
std::string component;
|
|
||||||
|
|
||||||
for (const auto c : path) {
|
|
||||||
if (c == '/') {
|
|
||||||
if (!component.empty()) {
|
|
||||||
if (component == "..") {
|
|
||||||
if (!components.empty()) {
|
|
||||||
components.pop_back();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
components.push_back(component);
|
|
||||||
}
|
|
||||||
component.clear();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
component += c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!component.empty()) {
|
|
||||||
components.push_back(component);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string result;
|
|
||||||
for (const auto& c : components) {
|
|
||||||
if (!result.empty()) {
|
|
||||||
result += "/";
|
|
||||||
}
|
|
||||||
result += c;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t* Epub::readItemContentsToBytes(const std::string& itemHref, size_t* size, const bool trailingNullByte) const {
|
uint8_t* Epub::readItemContentsToBytes(const std::string& itemHref, size_t* size, const bool trailingNullByte) const {
|
||||||
const ZipFile zip("/sd" + filepath);
|
const ZipFile zip("/sd" + filepath);
|
||||||
const std::string path = normalisePath(itemHref);
|
const std::string path = FsHelpers::normalisePath(itemHref);
|
||||||
|
|
||||||
const auto content = zip.readFileToMemory(path.c_str(), size, trailingNullByte);
|
const auto content = zip.readFileToMemory(path.c_str(), size, trailingNullByte);
|
||||||
if (!content) {
|
if (!content) {
|
||||||
@ -360,7 +287,7 @@ 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 {
|
bool Epub::readItemContentsToStream(const std::string& itemHref, Print& out, const size_t chunkSize) const {
|
||||||
const ZipFile zip("/sd" + filepath);
|
const ZipFile zip("/sd" + filepath);
|
||||||
const std::string path = normalisePath(itemHref);
|
const std::string path = FsHelpers::normalisePath(itemHref);
|
||||||
|
|
||||||
return zip.readFileToStream(path.c_str(), out, chunkSize);
|
return zip.readFileToStream(path.c_str(), out, chunkSize);
|
||||||
}
|
}
|
||||||
@ -371,7 +298,7 @@ bool Epub::getItemSize(const std::string& itemHref, size_t* size) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Epub::getItemSize(const ZipFile& zip, const std::string& itemHref, size_t* size) {
|
bool Epub::getItemSize(const ZipFile& zip, const std::string& itemHref, size_t* size) {
|
||||||
const std::string path = normalisePath(itemHref);
|
const std::string path = FsHelpers::normalisePath(itemHref);
|
||||||
return zip.getInflatedFileSize(path.c_str(), size);
|
return zip.getInflatedFileSize(path.c_str(), size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -387,10 +314,12 @@ size_t Epub::getCumulativeSpineItemSize(const int spineIndex) const {
|
|||||||
Serial.printf("[%lu] [EBP] getCumulativeSpineItemSize called but cache not loaded\n", millis());
|
Serial.printf("[%lu] [EBP] getCumulativeSpineItemSize called but cache not loaded\n", millis());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spineIndex < 0 || spineIndex >= spineTocCache->getSpineCount()) {
|
if (spineIndex < 0 || spineIndex >= spineTocCache->getSpineCount()) {
|
||||||
Serial.printf("[%lu] [EBP] getCumulativeSpineItemSize index:%d is out of range\n", millis(), spineIndex);
|
Serial.printf("[%lu] [EBP] getCumulativeSpineItemSize index:%d is out of range\n", millis(), spineIndex);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return spineTocCache->getSpineEntry(spineIndex).cumulativeSize;
|
return spineTocCache->getSpineEntry(spineIndex).cumulativeSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -399,6 +328,7 @@ std::string Epub::getSpineHref(const int spineIndex) const {
|
|||||||
Serial.printf("[%lu] [EBP] getSpineItem called but cache not loaded\n", millis());
|
Serial.printf("[%lu] [EBP] getSpineItem called but cache not loaded\n", millis());
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spineIndex < 0 || spineIndex >= spineTocCache->getSpineCount()) {
|
if (spineIndex < 0 || spineIndex >= spineTocCache->getSpineCount()) {
|
||||||
Serial.printf("[%lu] [EBP] getSpineItem index:%d is out of range\n", millis(), spineIndex);
|
Serial.printf("[%lu] [EBP] getSpineItem index:%d is out of range\n", millis(), spineIndex);
|
||||||
return spineTocCache->getSpineEntry(0).href;
|
return spineTocCache->getSpineEntry(0).href;
|
||||||
@ -425,6 +355,7 @@ int Epub::getTocItemsCount() const {
|
|||||||
if (!spineTocCache || !spineTocCache->isLoaded()) {
|
if (!spineTocCache || !spineTocCache->isLoaded()) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return spineTocCache->getTocCount();
|
return spineTocCache->getTocCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -434,6 +365,7 @@ int Epub::getSpineIndexForTocIndex(const int tocIndex) const {
|
|||||||
Serial.printf("[%lu] [EBP] getSpineIndexForTocIndex called but cache not loaded\n", millis());
|
Serial.printf("[%lu] [EBP] getSpineIndexForTocIndex called but cache not loaded\n", millis());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tocIndex < 0 || tocIndex >= spineTocCache->getTocCount()) {
|
if (tocIndex < 0 || tocIndex >= spineTocCache->getTocCount()) {
|
||||||
Serial.printf("[%lu] [EBP] getSpineIndexForTocIndex: tocIndex %d out of range\n", millis(), tocIndex);
|
Serial.printf("[%lu] [EBP] getSpineIndexForTocIndex: tocIndex %d out of range\n", millis(), tocIndex);
|
||||||
return 0;
|
return 0;
|
||||||
@ -444,6 +376,7 @@ int Epub::getSpineIndexForTocIndex(const int tocIndex) const {
|
|||||||
Serial.printf("[%lu] [EBP] Section not found for TOC index %d\n", millis(), tocIndex);
|
Serial.printf("[%lu] [EBP] Section not found for TOC index %d\n", millis(), tocIndex);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return spineIndex;
|
return spineIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -27,7 +27,7 @@ class Epub {
|
|||||||
std::unique_ptr<SpineTocCache> spineTocCache;
|
std::unique_ptr<SpineTocCache> spineTocCache;
|
||||||
|
|
||||||
bool findContentOpfFile(std::string* contentOpfFile) const;
|
bool findContentOpfFile(std::string* contentOpfFile) const;
|
||||||
bool parseContentOpf(const std::string& contentOpfFilePath);
|
bool parseContentOpf(bool useCache);
|
||||||
bool parseTocNcxFile() const;
|
bool parseTocNcxFile() const;
|
||||||
static bool getItemSize(const ZipFile& zip, const std::string& itemHref, size_t* size);
|
static bool getItemSize(const ZipFile& zip, const std::string& itemHref, size_t* size);
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
#include "FsHelpers.h"
|
#include "FsHelpers.h"
|
||||||
|
|
||||||
#include <SD.h>
|
#include <SD.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
bool FsHelpers::removeDir(const char* path) {
|
bool FsHelpers::removeDir(const char* path) {
|
||||||
// 1. Open the directory
|
// 1. Open the directory
|
||||||
@ -34,3 +35,39 @@ bool FsHelpers::removeDir(const char* path) {
|
|||||||
|
|
||||||
return SD.rmdir(path);
|
return SD.rmdir(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string FsHelpers::normalisePath(const std::string& path) {
|
||||||
|
std::vector<std::string> components;
|
||||||
|
std::string component;
|
||||||
|
|
||||||
|
for (const auto c : path) {
|
||||||
|
if (c == '/') {
|
||||||
|
if (!component.empty()) {
|
||||||
|
if (component == "..") {
|
||||||
|
if (!components.empty()) {
|
||||||
|
components.pop_back();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
components.push_back(component);
|
||||||
|
}
|
||||||
|
component.clear();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
component += c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!component.empty()) {
|
||||||
|
components.push_back(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string result;
|
||||||
|
for (const auto& c : components) {
|
||||||
|
if (!result.empty()) {
|
||||||
|
result += "/";
|
||||||
|
}
|
||||||
|
result += c;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
|
||||||
class FsHelpers {
|
class FsHelpers {
|
||||||
public:
|
public:
|
||||||
static bool removeDir(const char* path);
|
static bool removeDir(const char* path);
|
||||||
|
static std::string normalisePath(const std::string &path);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -7,45 +7,13 @@
|
|||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "FsHelpers.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
constexpr uint8_t SPINE_TOC_CACHE_VERSION = 1;
|
constexpr uint8_t SPINE_TOC_CACHE_VERSION = 1;
|
||||||
|
constexpr char spineTocMetaBinFile[] = "/spine_toc_meta.bin";
|
||||||
// TODO: Centralize this?
|
constexpr char spineBinFile[] = "/spine.bin";
|
||||||
std::string normalisePath(const std::string& path) {
|
constexpr char tocBinFile[] = "/toc.bin";
|
||||||
std::vector<std::string> components;
|
|
||||||
std::string component;
|
|
||||||
|
|
||||||
for (const auto c : path) {
|
|
||||||
if (c == '/') {
|
|
||||||
if (!component.empty()) {
|
|
||||||
if (component == "..") {
|
|
||||||
if (!components.empty()) {
|
|
||||||
components.pop_back();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
components.push_back(component);
|
|
||||||
}
|
|
||||||
component.clear();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
component += c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!component.empty()) {
|
|
||||||
components.push_back(component);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string result;
|
|
||||||
for (const auto& c : components) {
|
|
||||||
if (!result.empty()) {
|
|
||||||
result += "/";
|
|
||||||
}
|
|
||||||
result += c;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
bool SpineTocCache::beginWrite() {
|
bool SpineTocCache::beginWrite() {
|
||||||
@ -56,7 +24,7 @@ bool SpineTocCache::beginWrite() {
|
|||||||
Serial.printf("[%lu] [STC] Beginning write to cache path: %s\n", millis(), cachePath.c_str());
|
Serial.printf("[%lu] [STC] Beginning write to cache path: %s\n", millis(), cachePath.c_str());
|
||||||
|
|
||||||
// Open spine file for writing
|
// Open spine file for writing
|
||||||
const std::string spineFilePath = cachePath + "/spine.bin";
|
const std::string spineFilePath = cachePath + spineBinFile;
|
||||||
Serial.printf("[%lu] [STC] Opening spine file: %s\n", millis(), spineFilePath.c_str());
|
Serial.printf("[%lu] [STC] Opening spine file: %s\n", millis(), spineFilePath.c_str());
|
||||||
spineFile = SD.open(spineFilePath.c_str(), FILE_WRITE, true);
|
spineFile = SD.open(spineFilePath.c_str(), FILE_WRITE, true);
|
||||||
if (!spineFile) {
|
if (!spineFile) {
|
||||||
@ -65,7 +33,7 @@ bool SpineTocCache::beginWrite() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Open TOC file for writing
|
// Open TOC file for writing
|
||||||
const std::string tocFilePath = cachePath + "/toc.bin";
|
const std::string tocFilePath = cachePath + tocBinFile;
|
||||||
Serial.printf("[%lu] [STC] Opening toc file: %s\n", millis(), tocFilePath.c_str());
|
Serial.printf("[%lu] [STC] Opening toc file: %s\n", millis(), tocFilePath.c_str());
|
||||||
tocFile = SD.open(tocFilePath.c_str(), FILE_WRITE, true);
|
tocFile = SD.open(tocFilePath.c_str(), FILE_WRITE, true);
|
||||||
if (!tocFile) {
|
if (!tocFile) {
|
||||||
@ -78,24 +46,18 @@ bool SpineTocCache::beginWrite() {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpineTocCache::writeString(File& file, const std::string& s) const {
|
|
||||||
const auto len = static_cast<uint32_t>(s.size());
|
|
||||||
file.write(reinterpret_cast<const uint8_t*>(&len), sizeof(len));
|
|
||||||
file.write(reinterpret_cast<const uint8_t*>(s.data()), len);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SpineTocCache::writeSpineEntry(File& file, const SpineEntry& entry) const {
|
void SpineTocCache::writeSpineEntry(File& file, const SpineEntry& entry) const {
|
||||||
writeString(file, entry.href);
|
serialization::writeString(file, entry.href);
|
||||||
file.write(reinterpret_cast<const uint8_t*>(&entry.cumulativeSize), sizeof(entry.cumulativeSize));
|
serialization::writePod(file, entry.cumulativeSize);
|
||||||
file.write(reinterpret_cast<const uint8_t*>(&entry.tocIndex), sizeof(entry.tocIndex));
|
serialization::writePod(file, entry.tocIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpineTocCache::writeTocEntry(File& file, const TocEntry& entry) const {
|
void SpineTocCache::writeTocEntry(File& file, const TocEntry& entry) const {
|
||||||
writeString(file, entry.title);
|
serialization::writeString(file, entry.title);
|
||||||
writeString(file, entry.href);
|
serialization::writeString(file, entry.href);
|
||||||
writeString(file, entry.anchor);
|
serialization::writeString(file, entry.anchor);
|
||||||
file.write(&entry.level, 1);
|
serialization::writePod(file, entry.level);
|
||||||
file.write(reinterpret_cast<const uint8_t*>(&entry.spineIndex), sizeof(entry.spineIndex));
|
serialization::writePod(file, entry.spineIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpineTocCache::addSpineEntry(const std::string& href) {
|
void SpineTocCache::addSpineEntry(const std::string& href) {
|
||||||
@ -131,15 +93,15 @@ bool SpineTocCache::endWrite() {
|
|||||||
tocFile.close();
|
tocFile.close();
|
||||||
|
|
||||||
// Write metadata files with counts
|
// Write metadata files with counts
|
||||||
const auto spineMetaPath = cachePath + "/spine_meta.bin";
|
const auto spineTocMetaPath = cachePath + spineTocMetaBinFile;
|
||||||
File metaFile = SD.open(spineMetaPath.c_str(), FILE_WRITE, true);
|
File metaFile = SD.open(spineTocMetaPath.c_str(), FILE_WRITE, true);
|
||||||
if (!metaFile) {
|
if (!metaFile) {
|
||||||
Serial.printf("[%lu] [STC] Failed to write spine metadata\n", millis());
|
Serial.printf("[%lu] [STC] Failed to write spine metadata\n", millis());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
metaFile.write(&SPINE_TOC_CACHE_VERSION, 1);
|
serialization::writePod(metaFile, SPINE_TOC_CACHE_VERSION);
|
||||||
metaFile.write(reinterpret_cast<const uint8_t*>(&spineCount), sizeof(spineCount));
|
serialization::writePod(metaFile, spineCount);
|
||||||
metaFile.write(reinterpret_cast<const uint8_t*>(&tocCount), sizeof(tocCount));
|
serialization::writePod(metaFile, tocCount);
|
||||||
metaFile.close();
|
metaFile.close();
|
||||||
|
|
||||||
buildMode = false;
|
buildMode = false;
|
||||||
@ -147,28 +109,21 @@ bool SpineTocCache::endWrite() {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpineTocCache::readString(std::ifstream& is, std::string& s) const {
|
|
||||||
uint32_t len;
|
|
||||||
is.read(reinterpret_cast<char*>(&len), sizeof(len));
|
|
||||||
s.resize(len);
|
|
||||||
is.read(&s[0], len);
|
|
||||||
}
|
|
||||||
|
|
||||||
SpineTocCache::SpineEntry SpineTocCache::readSpineEntry(std::ifstream& is) const {
|
SpineTocCache::SpineEntry SpineTocCache::readSpineEntry(std::ifstream& is) const {
|
||||||
SpineEntry entry;
|
SpineEntry entry;
|
||||||
readString(is, entry.href);
|
serialization::readString(is, entry.href);
|
||||||
is.read(reinterpret_cast<char*>(&entry.cumulativeSize), sizeof(entry.cumulativeSize));
|
serialization::readPod(is, entry.cumulativeSize);
|
||||||
is.read(reinterpret_cast<char*>(&entry.tocIndex), sizeof(entry.tocIndex));
|
serialization::readPod(is, entry.tocIndex);
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
SpineTocCache::TocEntry SpineTocCache::readTocEntry(std::ifstream& is) const {
|
SpineTocCache::TocEntry SpineTocCache::readTocEntry(std::ifstream& is) const {
|
||||||
TocEntry entry;
|
TocEntry entry;
|
||||||
readString(is, entry.title);
|
serialization::readString(is, entry.title);
|
||||||
readString(is, entry.href);
|
serialization::readString(is, entry.href);
|
||||||
readString(is, entry.anchor);
|
serialization::readString(is, entry.anchor);
|
||||||
is.read(reinterpret_cast<char*>(&entry.level), 1);
|
serialization::readPod(is, entry.level);
|
||||||
is.read(reinterpret_cast<char*>(&entry.spineIndex), sizeof(entry.spineIndex));
|
serialization::readPod(is, entry.spineIndex);
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,7 +141,7 @@ bool SpineTocCache::updateMappingsAndSizes(const std::string& epubPath) const {
|
|||||||
|
|
||||||
// Read spine entries
|
// Read spine entries
|
||||||
{
|
{
|
||||||
const auto spineFilePath = "/sd" + cachePath + "/spine.bin";
|
const auto spineFilePath = "/sd" + cachePath + spineBinFile;
|
||||||
std::ifstream spineStream(spineFilePath.c_str(), std::ios::binary);
|
std::ifstream spineStream(spineFilePath.c_str(), std::ios::binary);
|
||||||
if (!spineStream) {
|
if (!spineStream) {
|
||||||
Serial.printf("[%lu] [STC] Failed to open spine file for reading\n", millis());
|
Serial.printf("[%lu] [STC] Failed to open spine file for reading\n", millis());
|
||||||
@ -201,7 +156,7 @@ bool SpineTocCache::updateMappingsAndSizes(const std::string& epubPath) const {
|
|||||||
|
|
||||||
// Read TOC entries
|
// Read TOC entries
|
||||||
{
|
{
|
||||||
const auto tocFilePath = "/sd" + cachePath + "/toc.bin";
|
const auto tocFilePath = "/sd" + cachePath + tocBinFile;
|
||||||
std::ifstream tocStream(tocFilePath.c_str(), std::ios::binary);
|
std::ifstream tocStream(tocFilePath.c_str(), std::ios::binary);
|
||||||
if (!tocStream) {
|
if (!tocStream) {
|
||||||
Serial.printf("[%lu] [STC] Failed to open toc file for reading\n", millis());
|
Serial.printf("[%lu] [STC] Failed to open toc file for reading\n", millis());
|
||||||
@ -220,7 +175,7 @@ bool SpineTocCache::updateMappingsAndSizes(const std::string& epubPath) const {
|
|||||||
|
|
||||||
for (int i = 0; i < spineCount; i++) {
|
for (int i = 0; i < spineCount; i++) {
|
||||||
size_t itemSize = 0;
|
size_t itemSize = 0;
|
||||||
const std::string path = normalisePath(spineEntries[i].href);
|
const std::string path = FsHelpers::normalisePath(spineEntries[i].href);
|
||||||
if (zip.getInflatedFileSize(path.c_str(), &itemSize)) {
|
if (zip.getInflatedFileSize(path.c_str(), &itemSize)) {
|
||||||
cumSize += itemSize;
|
cumSize += itemSize;
|
||||||
spineEntries[i].cumulativeSize = cumSize;
|
spineEntries[i].cumulativeSize = cumSize;
|
||||||
@ -231,21 +186,12 @@ bool SpineTocCache::updateMappingsAndSizes(const std::string& epubPath) const {
|
|||||||
|
|
||||||
Serial.printf("[%lu] [STC] Book size: %lu\n", millis(), cumSize);
|
Serial.printf("[%lu] [STC] Book size: %lu\n", millis(), cumSize);
|
||||||
|
|
||||||
// Compute spine → TOC mappings
|
// Compute spine <-> TOC mappings
|
||||||
for (int i = 0; i < spineCount; i++) {
|
for (int i = 0; i < spineCount; i++) {
|
||||||
for (int j = 0; j < tocCount; j++) {
|
for (int j = 0; j < tocCount; j++) {
|
||||||
if (tocEntries[j].href == spineEntries[i].href) {
|
if (tocEntries[j].href == spineEntries[i].href) {
|
||||||
spineEntries[i].tocIndex = static_cast<int16_t>(j);
|
spineEntries[i].tocIndex = static_cast<int16_t>(j);
|
||||||
break;
|
tocEntries[j].spineIndex = static_cast<int16_t>(i);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute TOC → spine mappings
|
|
||||||
for (int i = 0; i < tocCount; i++) {
|
|
||||||
for (int j = 0; j < spineCount; j++) {
|
|
||||||
if (spineEntries[j].href == tocEntries[i].href) {
|
|
||||||
tocEntries[i].spineIndex = static_cast<int16_t>(j);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -253,7 +199,7 @@ bool SpineTocCache::updateMappingsAndSizes(const std::string& epubPath) const {
|
|||||||
|
|
||||||
// Rewrite spine file with updated data
|
// Rewrite spine file with updated data
|
||||||
{
|
{
|
||||||
const auto spineFilePath = cachePath + "/spine.bin";
|
const auto spineFilePath = cachePath + spineBinFile;
|
||||||
File spineFile = SD.open(spineFilePath.c_str(), FILE_WRITE, true);
|
File spineFile = SD.open(spineFilePath.c_str(), FILE_WRITE, true);
|
||||||
if (!spineFile) {
|
if (!spineFile) {
|
||||||
Serial.printf("[%lu] [STC] Failed to reopen spine file for writing\n", millis());
|
Serial.printf("[%lu] [STC] Failed to reopen spine file for writing\n", millis());
|
||||||
@ -268,7 +214,7 @@ bool SpineTocCache::updateMappingsAndSizes(const std::string& epubPath) const {
|
|||||||
|
|
||||||
// Rewrite TOC file with updated data
|
// Rewrite TOC file with updated data
|
||||||
{
|
{
|
||||||
const auto tocFilePath = cachePath + "/toc.bin";
|
const auto tocFilePath = cachePath + tocBinFile;
|
||||||
File tocFile = SD.open(tocFilePath.c_str(), FILE_WRITE, true);
|
File tocFile = SD.open(tocFilePath.c_str(), FILE_WRITE, true);
|
||||||
if (!tocFile) {
|
if (!tocFile) {
|
||||||
Serial.printf("[%lu] [STC] Failed to reopen toc file for writing\n", millis());
|
Serial.printf("[%lu] [STC] Failed to reopen toc file for writing\n", millis());
|
||||||
@ -293,20 +239,20 @@ bool SpineTocCache::updateMappingsAndSizes(const std::string& epubPath) const {
|
|||||||
|
|
||||||
bool SpineTocCache::load() {
|
bool SpineTocCache::load() {
|
||||||
// Load metadata
|
// Load metadata
|
||||||
const auto metaPath = cachePath + "/spine_meta.bin";
|
const auto spineTocMetaPath = cachePath + spineTocMetaBinFile;
|
||||||
if (!SD.exists(metaPath.c_str())) {
|
if (!SD.exists(spineTocMetaPath.c_str())) {
|
||||||
Serial.printf("[%lu] [STC] Cache metadata does not exist: %s\n", millis(), metaPath.c_str());
|
Serial.printf("[%lu] [STC] Cache metadata does not exist: %s\n", millis(), spineTocMetaPath.c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
File metaFile = SD.open(metaPath.c_str(), FILE_READ);
|
File metaFile = SD.open(spineTocMetaPath.c_str(), FILE_READ);
|
||||||
if (!metaFile) {
|
if (!metaFile) {
|
||||||
Serial.printf("[%lu] [STC] Failed to open cache metadata\n", millis());
|
Serial.printf("[%lu] [STC] Failed to open cache metadata\n", millis());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t version;
|
uint8_t version;
|
||||||
metaFile.read(&version, 1);
|
serialization::readPod(metaFile, version);
|
||||||
if (version != SPINE_TOC_CACHE_VERSION) {
|
if (version != SPINE_TOC_CACHE_VERSION) {
|
||||||
Serial.printf("[%lu] [STC] Cache version mismatch: expected %d, got %d\n", millis(), SPINE_TOC_CACHE_VERSION,
|
Serial.printf("[%lu] [STC] Cache version mismatch: expected %d, got %d\n", millis(), SPINE_TOC_CACHE_VERSION,
|
||||||
version);
|
version);
|
||||||
@ -314,8 +260,9 @@ bool SpineTocCache::load() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
metaFile.read(reinterpret_cast<uint8_t*>(&spineCount), sizeof(spineCount));
|
serialization::readPod(metaFile, spineCount);
|
||||||
metaFile.read(reinterpret_cast<uint8_t*>(&tocCount), sizeof(tocCount));
|
serialization::readPod(metaFile, tocCount);
|
||||||
|
// TODO: Add LUT to back of meta file
|
||||||
metaFile.close();
|
metaFile.close();
|
||||||
|
|
||||||
loaded = true;
|
loaded = true;
|
||||||
@ -334,7 +281,7 @@ SpineTocCache::SpineEntry SpineTocCache::getSpineEntry(const int index) const {
|
|||||||
return SpineEntry();
|
return SpineEntry();
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto spineFilePath = "/sd" + cachePath + "/spine.bin";
|
const auto spineFilePath = "/sd" + cachePath + spineBinFile;
|
||||||
std::ifstream spineStream(spineFilePath.c_str(), std::ios::binary);
|
std::ifstream spineStream(spineFilePath.c_str(), std::ios::binary);
|
||||||
if (!spineStream) {
|
if (!spineStream) {
|
||||||
Serial.printf("[%lu] [STC] Failed to open spine file for reading entry\n", millis());
|
Serial.printf("[%lu] [STC] Failed to open spine file for reading entry\n", millis());
|
||||||
@ -363,7 +310,7 @@ SpineTocCache::TocEntry SpineTocCache::getTocEntry(const int index) const {
|
|||||||
return TocEntry();
|
return TocEntry();
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto tocFilePath = "/sd" + cachePath + "/toc.bin";
|
const auto tocFilePath = "/sd" + cachePath + tocBinFile;
|
||||||
std::ifstream tocStream(tocFilePath.c_str(), std::ios::binary);
|
std::ifstream tocStream(tocFilePath.c_str(), std::ios::binary);
|
||||||
if (!tocStream) {
|
if (!tocStream) {
|
||||||
Serial.printf("[%lu] [STC] Failed to open toc file for reading entry\n", millis());
|
Serial.printf("[%lu] [STC] Failed to open toc file for reading entry\n", millis());
|
||||||
|
|||||||
@ -44,8 +44,6 @@ class SpineTocCache {
|
|||||||
File spineFile;
|
File spineFile;
|
||||||
File tocFile;
|
File tocFile;
|
||||||
|
|
||||||
void writeString(File& file, const std::string& s) const;
|
|
||||||
void readString(std::ifstream& is, std::string& s) const;
|
|
||||||
void writeSpineEntry(File& file, const SpineEntry& entry) const;
|
void writeSpineEntry(File& file, const SpineEntry& entry) const;
|
||||||
void writeTocEntry(File& file, const TocEntry& entry) const;
|
void writeTocEntry(File& file, const TocEntry& entry) const;
|
||||||
SpineEntry readSpineEntry(std::ifstream& is) const;
|
SpineEntry readSpineEntry(std::ifstream& is) const;
|
||||||
|
|||||||
@ -149,21 +149,22 @@ void XMLCALL ContentOpfParser::startElement(void* userData, const XML_Char* name
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: This relies on spine appearing after item manifest
|
// NOTE: This relies on spine appearing after item manifest
|
||||||
if (self->state == IN_SPINE && (strcmp(name, "itemref") == 0 || strcmp(name, "opf:itemref") == 0)) {
|
// Only run the spine parsing if there's a cache to add it to
|
||||||
for (int i = 0; atts[i]; i += 2) {
|
if (self->cache) {
|
||||||
if (strcmp(atts[i], "idref") == 0) {
|
if (self->state == IN_SPINE && (strcmp(name, "itemref") == 0 || strcmp(name, "opf:itemref") == 0)) {
|
||||||
const std::string idref = atts[i + 1];
|
for (int i = 0; atts[i]; i += 2) {
|
||||||
// Resolve the idref to href using items map
|
if (strcmp(atts[i], "idref") == 0) {
|
||||||
if (self->items.count(idref) > 0) {
|
const std::string idref = atts[i + 1];
|
||||||
const std::string& href = self->items.at(idref);
|
// Resolve the idref to href using items map
|
||||||
if (self->cache) {
|
if (self->items.count(idref) > 0) {
|
||||||
|
const std::string& href = self->items.at(idref);
|
||||||
self->cache->addSpineEntry(href);
|
self->cache->addSpineEntry(href);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,17 +7,33 @@ static void writePod(std::ostream& os, const T& value) {
|
|||||||
os.write(reinterpret_cast<const char*>(&value), sizeof(T));
|
os.write(reinterpret_cast<const char*>(&value), sizeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static void writePod(File& file, const T& value) {
|
||||||
|
file.write(reinterpret_cast<const uint8_t*>(&value), sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static void readPod(std::istream& is, T& value) {
|
static void readPod(std::istream& is, T& value) {
|
||||||
is.read(reinterpret_cast<char*>(&value), sizeof(T));
|
is.read(reinterpret_cast<char*>(&value), sizeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static void readPod(File& file, T& value) {
|
||||||
|
file.read(reinterpret_cast<uint8_t*>(&value), sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
static void writeString(std::ostream& os, const std::string& s) {
|
static void writeString(std::ostream& os, const std::string& s) {
|
||||||
const uint32_t len = s.size();
|
const uint32_t len = s.size();
|
||||||
writePod(os, len);
|
writePod(os, len);
|
||||||
os.write(s.data(), len);
|
os.write(s.data(), len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void writeString(File& file, const std::string& s) {
|
||||||
|
const uint32_t len = s.size();
|
||||||
|
writePod(file, len);
|
||||||
|
file.write(reinterpret_cast<const uint8_t *>(s.data()), len);
|
||||||
|
}
|
||||||
|
|
||||||
static void readString(std::istream& is, std::string& s) {
|
static void readString(std::istream& is, std::string& s) {
|
||||||
uint32_t len;
|
uint32_t len;
|
||||||
readPod(is, len);
|
readPod(is, len);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user