|
|
|
@@ -14,6 +14,24 @@ constexpr uint8_t SPINE_TOC_CACHE_VERSION = 1;
|
|
|
|
constexpr char spineTocMetaBinFile[] = "/spine_toc_meta.bin";
|
|
|
|
constexpr char spineTocMetaBinFile[] = "/spine_toc_meta.bin";
|
|
|
|
constexpr char spineBinFile[] = "/spine.bin";
|
|
|
|
constexpr char spineBinFile[] = "/spine.bin";
|
|
|
|
constexpr char tocBinFile[] = "/toc.bin";
|
|
|
|
constexpr char tocBinFile[] = "/toc.bin";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool openFileForRead(const std::string& path, File& file) {
|
|
|
|
|
|
|
|
file = SD.open(path.c_str(), FILE_READ);
|
|
|
|
|
|
|
|
if (!file) {
|
|
|
|
|
|
|
|
Serial.printf("[%lu] [STC] Failed to open file for reading: %s\n", millis(), path.c_str());
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool openFileForWrite(const std::string& path, File& file) {
|
|
|
|
|
|
|
|
file = SD.open(path.c_str(), FILE_WRITE, true);
|
|
|
|
|
|
|
|
if (!file) {
|
|
|
|
|
|
|
|
Serial.printf("[%lu] [STC] Failed to open spine file for writing: %s\n", millis(), path.c_str());
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
|
|
bool SpineTocCache::beginWrite() {
|
|
|
|
bool SpineTocCache::beginWrite() {
|
|
|
|
@@ -24,20 +42,12 @@ 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 + spineBinFile;
|
|
|
|
if (!openFileForWrite(cachePath + spineBinFile, spineFile)) {
|
|
|
|
Serial.printf("[%lu] [STC] Opening spine file: %s\n", millis(), spineFilePath.c_str());
|
|
|
|
|
|
|
|
spineFile = SD.open(spineFilePath.c_str(), FILE_WRITE, true);
|
|
|
|
|
|
|
|
if (!spineFile) {
|
|
|
|
|
|
|
|
Serial.printf("[%lu] [STC] Failed to open spine file for writing: %s\n", millis(), spineFilePath.c_str());
|
|
|
|
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Open TOC file for writing
|
|
|
|
// Open TOC file for writing
|
|
|
|
const std::string tocFilePath = cachePath + tocBinFile;
|
|
|
|
if (!openFileForWrite(cachePath + tocBinFile, tocFile)) {
|
|
|
|
Serial.printf("[%lu] [STC] Opening toc file: %s\n", millis(), tocFilePath.c_str());
|
|
|
|
|
|
|
|
tocFile = SD.open(tocFilePath.c_str(), FILE_WRITE, true);
|
|
|
|
|
|
|
|
if (!tocFile) {
|
|
|
|
|
|
|
|
Serial.printf("[%lu] [STC] Failed to open toc file for writing: %s\n", millis(), tocFilePath.c_str());
|
|
|
|
|
|
|
|
spineFile.close();
|
|
|
|
spineFile.close();
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -93,10 +103,8 @@ bool SpineTocCache::endWrite() {
|
|
|
|
tocFile.close();
|
|
|
|
tocFile.close();
|
|
|
|
|
|
|
|
|
|
|
|
// Write metadata files with counts
|
|
|
|
// Write metadata files with counts
|
|
|
|
const auto spineTocMetaPath = cachePath + spineTocMetaBinFile;
|
|
|
|
File metaFile;
|
|
|
|
File metaFile = SD.open(spineTocMetaPath.c_str(), FILE_WRITE, true);
|
|
|
|
if (!openFileForWrite(cachePath + spineTocMetaBinFile, metaFile)) {
|
|
|
|
if (!metaFile) {
|
|
|
|
|
|
|
|
Serial.printf("[%lu] [STC] Failed to write spine metadata\n", millis());
|
|
|
|
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
serialization::writePod(metaFile, SPINE_TOC_CACHE_VERSION);
|
|
|
|
serialization::writePod(metaFile, SPINE_TOC_CACHE_VERSION);
|
|
|
|
@@ -109,25 +117,25 @@ bool SpineTocCache::endWrite() {
|
|
|
|
return true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SpineTocCache::SpineEntry SpineTocCache::readSpineEntry(std::ifstream& is) const {
|
|
|
|
SpineTocCache::SpineEntry SpineTocCache::readSpineEntry(File& file) const {
|
|
|
|
SpineEntry entry;
|
|
|
|
SpineEntry entry;
|
|
|
|
serialization::readString(is, entry.href);
|
|
|
|
serialization::readString(file, entry.href);
|
|
|
|
serialization::readPod(is, entry.cumulativeSize);
|
|
|
|
serialization::readPod(file, entry.cumulativeSize);
|
|
|
|
serialization::readPod(is, entry.tocIndex);
|
|
|
|
serialization::readPod(file, entry.tocIndex);
|
|
|
|
return entry;
|
|
|
|
return entry;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SpineTocCache::TocEntry SpineTocCache::readTocEntry(std::ifstream& is) const {
|
|
|
|
SpineTocCache::TocEntry SpineTocCache::readTocEntry(File& file) const {
|
|
|
|
TocEntry entry;
|
|
|
|
TocEntry entry;
|
|
|
|
serialization::readString(is, entry.title);
|
|
|
|
serialization::readString(file, entry.title);
|
|
|
|
serialization::readString(is, entry.href);
|
|
|
|
serialization::readString(file, entry.href);
|
|
|
|
serialization::readString(is, entry.anchor);
|
|
|
|
serialization::readString(file, entry.anchor);
|
|
|
|
serialization::readPod(is, entry.level);
|
|
|
|
serialization::readPod(file, entry.level);
|
|
|
|
serialization::readPod(is, entry.spineIndex);
|
|
|
|
serialization::readPod(file, entry.spineIndex);
|
|
|
|
return entry;
|
|
|
|
return entry;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool SpineTocCache::updateMappingsAndSizes(const std::string& epubPath) const {
|
|
|
|
bool SpineTocCache::updateMappingsAndSizes(const std::string& epubPath) {
|
|
|
|
Serial.printf("[%lu] [STC] Computing mappings and sizes for %d spine, %d TOC entries\n", millis(), spineCount,
|
|
|
|
Serial.printf("[%lu] [STC] Computing mappings and sizes for %d spine, %d TOC entries\n", millis(), spineCount,
|
|
|
|
tocCount);
|
|
|
|
tocCount);
|
|
|
|
|
|
|
|
|
|
|
|
@@ -141,32 +149,24 @@ bool SpineTocCache::updateMappingsAndSizes(const std::string& epubPath) const {
|
|
|
|
|
|
|
|
|
|
|
|
// Read spine entries
|
|
|
|
// Read spine entries
|
|
|
|
{
|
|
|
|
{
|
|
|
|
const auto spineFilePath = "/sd" + cachePath + spineBinFile;
|
|
|
|
if (!openFileForRead(cachePath + spineBinFile, spineFile)) {
|
|
|
|
std::ifstream spineStream(spineFilePath.c_str(), std::ios::binary);
|
|
|
|
|
|
|
|
if (!spineStream) {
|
|
|
|
|
|
|
|
Serial.printf("[%lu] [STC] Failed to open spine file for reading\n", millis());
|
|
|
|
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < spineCount; i++) {
|
|
|
|
for (int i = 0; i < spineCount; i++) {
|
|
|
|
spineEntries.push_back(readSpineEntry(spineStream));
|
|
|
|
spineEntries.push_back(readSpineEntry(spineFile));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
spineStream.close();
|
|
|
|
spineFile.close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Read TOC entries
|
|
|
|
// Read TOC entries
|
|
|
|
{
|
|
|
|
{
|
|
|
|
const auto tocFilePath = "/sd" + cachePath + tocBinFile;
|
|
|
|
if (!openFileForRead(cachePath + tocBinFile, tocFile)) {
|
|
|
|
std::ifstream tocStream(tocFilePath.c_str(), std::ios::binary);
|
|
|
|
|
|
|
|
if (!tocStream) {
|
|
|
|
|
|
|
|
Serial.printf("[%lu] [STC] Failed to open toc file for reading\n", millis());
|
|
|
|
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < tocCount; i++) {
|
|
|
|
for (int i = 0; i < tocCount; i++) {
|
|
|
|
tocEntries.push_back(readTocEntry(tocStream));
|
|
|
|
tocEntries.push_back(readTocEntry(tocFile));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
tocStream.close();
|
|
|
|
tocFile.close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Compute cumulative sizes
|
|
|
|
// Compute cumulative sizes
|
|
|
|
@@ -199,13 +199,9 @@ bool SpineTocCache::updateMappingsAndSizes(const std::string& epubPath) const {
|
|
|
|
|
|
|
|
|
|
|
|
// Rewrite spine file with updated data
|
|
|
|
// Rewrite spine file with updated data
|
|
|
|
{
|
|
|
|
{
|
|
|
|
const auto spineFilePath = cachePath + spineBinFile;
|
|
|
|
if (!openFileForWrite(cachePath + spineBinFile, spineFile)) {
|
|
|
|
File spineFile = SD.open(spineFilePath.c_str(), FILE_WRITE, true);
|
|
|
|
|
|
|
|
if (!spineFile) {
|
|
|
|
|
|
|
|
Serial.printf("[%lu] [STC] Failed to reopen spine file for writing\n", millis());
|
|
|
|
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for (const auto& entry : spineEntries) {
|
|
|
|
for (const auto& entry : spineEntries) {
|
|
|
|
writeSpineEntry(spineFile, entry);
|
|
|
|
writeSpineEntry(spineFile, entry);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -214,13 +210,9 @@ bool SpineTocCache::updateMappingsAndSizes(const std::string& epubPath) const {
|
|
|
|
|
|
|
|
|
|
|
|
// Rewrite TOC file with updated data
|
|
|
|
// Rewrite TOC file with updated data
|
|
|
|
{
|
|
|
|
{
|
|
|
|
const auto tocFilePath = cachePath + tocBinFile;
|
|
|
|
if (!openFileForWrite(cachePath + tocBinFile, tocFile)) {
|
|
|
|
File tocFile = SD.open(tocFilePath.c_str(), FILE_WRITE, true);
|
|
|
|
|
|
|
|
if (!tocFile) {
|
|
|
|
|
|
|
|
Serial.printf("[%lu] [STC] Failed to reopen toc file for writing\n", millis());
|
|
|
|
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for (const auto& entry : tocEntries) {
|
|
|
|
for (const auto& entry : tocEntries) {
|
|
|
|
writeTocEntry(tocFile, entry);
|
|
|
|
writeTocEntry(tocFile, entry);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -239,15 +231,8 @@ bool SpineTocCache::updateMappingsAndSizes(const std::string& epubPath) const {
|
|
|
|
|
|
|
|
|
|
|
|
bool SpineTocCache::load() {
|
|
|
|
bool SpineTocCache::load() {
|
|
|
|
// Load metadata
|
|
|
|
// Load metadata
|
|
|
|
const auto spineTocMetaPath = cachePath + spineTocMetaBinFile;
|
|
|
|
File metaFile;
|
|
|
|
if (!SD.exists(spineTocMetaPath.c_str())) {
|
|
|
|
if (!openFileForRead(cachePath + spineTocMetaBinFile, metaFile)) {
|
|
|
|
Serial.printf("[%lu] [STC] Cache metadata does not exist: %s\n", millis(), spineTocMetaPath.c_str());
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
File metaFile = SD.open(spineTocMetaPath.c_str(), FILE_READ);
|
|
|
|
|
|
|
|
if (!metaFile) {
|
|
|
|
|
|
|
|
Serial.printf("[%lu] [STC] Failed to open cache metadata\n", millis());
|
|
|
|
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -270,61 +255,55 @@ bool SpineTocCache::load() {
|
|
|
|
return true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SpineTocCache::SpineEntry SpineTocCache::getSpineEntry(const int index) const {
|
|
|
|
SpineTocCache::SpineEntry SpineTocCache::getSpineEntry(const int index) {
|
|
|
|
if (!loaded) {
|
|
|
|
if (!loaded) {
|
|
|
|
Serial.printf("[%lu] [STC] getSpineEntry called but cache not loaded\n", millis());
|
|
|
|
Serial.printf("[%lu] [STC] getSpineEntry called but cache not loaded\n", millis());
|
|
|
|
return SpineEntry();
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (index < 0 || index >= static_cast<int>(spineCount)) {
|
|
|
|
if (index < 0 || index >= static_cast<int>(spineCount)) {
|
|
|
|
Serial.printf("[%lu] [STC] getSpineEntry index %d out of range\n", millis(), index);
|
|
|
|
Serial.printf("[%lu] [STC] getSpineEntry index %d out of range\n", millis(), index);
|
|
|
|
return SpineEntry();
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const auto spineFilePath = "/sd" + cachePath + spineBinFile;
|
|
|
|
if (!openFileForRead(cachePath + spineBinFile, spineFile)) {
|
|
|
|
std::ifstream spineStream(spineFilePath.c_str(), std::ios::binary);
|
|
|
|
return {};
|
|
|
|
if (!spineStream) {
|
|
|
|
|
|
|
|
Serial.printf("[%lu] [STC] Failed to open spine file for reading entry\n", millis());
|
|
|
|
|
|
|
|
return SpineEntry();
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Seek to the correct entry - need to read entries sequentially until we reach the index
|
|
|
|
// Seek to the correct entry - need to read entries sequentially until we reach the index
|
|
|
|
// TODO: This could/should be based on a look up table/fixed sizes
|
|
|
|
// TODO: This could/should be based on a look up table/fixed sizes
|
|
|
|
for (int i = 0; i < index; i++) {
|
|
|
|
for (int i = 0; i < index; i++) {
|
|
|
|
readSpineEntry(spineStream); // Skip entries
|
|
|
|
readSpineEntry(spineFile); // Skip entries
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
auto entry = readSpineEntry(spineStream);
|
|
|
|
auto entry = readSpineEntry(spineFile);
|
|
|
|
spineStream.close();
|
|
|
|
spineFile.close();
|
|
|
|
return entry;
|
|
|
|
return entry;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SpineTocCache::TocEntry SpineTocCache::getTocEntry(const int index) const {
|
|
|
|
SpineTocCache::TocEntry SpineTocCache::getTocEntry(const int index) {
|
|
|
|
if (!loaded) {
|
|
|
|
if (!loaded) {
|
|
|
|
Serial.printf("[%lu] [STC] getTocEntry called but cache not loaded\n", millis());
|
|
|
|
Serial.printf("[%lu] [STC] getTocEntry called but cache not loaded\n", millis());
|
|
|
|
return TocEntry();
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (index < 0 || index >= static_cast<int>(tocCount)) {
|
|
|
|
if (index < 0 || index >= static_cast<int>(tocCount)) {
|
|
|
|
Serial.printf("[%lu] [STC] getTocEntry index %d out of range\n", millis(), index);
|
|
|
|
Serial.printf("[%lu] [STC] getTocEntry index %d out of range\n", millis(), index);
|
|
|
|
return TocEntry();
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const auto tocFilePath = "/sd" + cachePath + tocBinFile;
|
|
|
|
if (!openFileForRead(cachePath + tocBinFile, tocFile)) {
|
|
|
|
std::ifstream tocStream(tocFilePath.c_str(), std::ios::binary);
|
|
|
|
return {};
|
|
|
|
if (!tocStream) {
|
|
|
|
|
|
|
|
Serial.printf("[%lu] [STC] Failed to open toc file for reading entry\n", millis());
|
|
|
|
|
|
|
|
return TocEntry();
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Seek to the correct entry - need to read entries sequentially until we reach the index
|
|
|
|
// Seek to the correct entry - need to read entries sequentially until we reach the index
|
|
|
|
// TODO: This could/should be based on a look up table/fixed sizes
|
|
|
|
// TODO: This could/should be based on a look up table/fixed sizes
|
|
|
|
for (int i = 0; i < index; i++) {
|
|
|
|
for (int i = 0; i < index; i++) {
|
|
|
|
readTocEntry(tocStream); // Skip entries
|
|
|
|
readTocEntry(tocFile); // Skip entries
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
auto entry = readTocEntry(tocStream);
|
|
|
|
auto entry = readTocEntry(tocFile);
|
|
|
|
tocStream.close();
|
|
|
|
tocFile.close();
|
|
|
|
return entry;
|
|
|
|
return entry;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|