diff --git a/lib/Xtc/Xtc.cpp b/lib/Xtc/Xtc.cpp index c79421d7..7850d934 100644 --- a/lib/Xtc/Xtc.cpp +++ b/lib/Xtc/Xtc.cpp @@ -7,7 +7,6 @@ #include "Xtc.h" -#include #include #include @@ -87,6 +86,15 @@ std::string Xtc::getTitle() const { return filepath.substr(lastSlash, lastDot - lastSlash); } +std::string Xtc::getAuthor() const { + if (!loaded || !parser) { + return ""; + } + + // Try to get author from XTC metadata + return parser->getAuthor(); +} + bool Xtc::hasChapters() const { if (!loaded || !parser) { return false; diff --git a/lib/Xtc/Xtc.h b/lib/Xtc/Xtc.h index 7413ef47..c8d9a040 100644 --- a/lib/Xtc/Xtc.h +++ b/lib/Xtc/Xtc.h @@ -56,6 +56,7 @@ class Xtc { // Metadata std::string getTitle() const; + std::string getAuthor() const; bool hasChapters() const; const std::vector& getChapters() const; diff --git a/lib/Xtc/Xtc/XtcParser.cpp b/lib/Xtc/Xtc/XtcParser.cpp index c33e7193..8db3dead 100644 --- a/lib/Xtc/Xtc/XtcParser.cpp +++ b/lib/Xtc/Xtc/XtcParser.cpp @@ -47,8 +47,21 @@ XtcError XtcParser::open(const char* filepath) { return m_lastError; } - // Read title if available - readTitle(); + // Read title & author if available + if (m_header.hasMetadata) { + m_lastError = readTitle(); + if (m_lastError != XtcError::OK) { + Serial.printf("[%lu] [XTC] Failed to read title: %s\n", millis(), errorToString(m_lastError)); + m_file.close(); + return m_lastError; + } + m_lastError = readAuthor(); + if (m_lastError != XtcError::OK) { + Serial.printf("[%lu] [XTC] Failed to read author: %s\n", millis(), errorToString(m_lastError)); + m_file.close(); + return m_lastError; + } + } // Read page table m_lastError = readPageTable(); @@ -124,24 +137,34 @@ XtcError XtcParser::readHeader() { } XtcError XtcParser::readTitle() { - // Title is usually at offset 0x38 (56) for 88-byte headers - // Read title as null-terminated UTF-8 string - if (m_header.titleOffset == 0) { - m_header.titleOffset = 0x38; // Default offset - } - - if (!m_file.seek(m_header.titleOffset)) { + constexpr auto titleOffset = 0x38; + if (!m_file.seek(titleOffset)) { return XtcError::READ_ERROR; } char titleBuf[128] = {0}; - m_file.read(reinterpret_cast(titleBuf), sizeof(titleBuf) - 1); + m_file.read(titleBuf, sizeof(titleBuf) - 1); m_title = titleBuf; Serial.printf("[%lu] [XTC] Title: %s\n", millis(), m_title.c_str()); return XtcError::OK; } +XtcError XtcParser::readAuthor() { + // Read author as null-terminated UTF-8 string with max length 64, directly following title + constexpr auto authorOffset = 0xB8; + if (!m_file.seek(authorOffset)) { + return XtcError::READ_ERROR; + } + + char authorBuf[64] = {0}; + m_file.read(authorBuf, sizeof(authorBuf) - 1); + m_author = authorBuf; + + Serial.printf("[%lu] [XTC] Author: %s\n", millis(), m_author.c_str()); + return XtcError::OK; +} + XtcError XtcParser::readPageTable() { if (m_header.pageTableOffset == 0) { Serial.printf("[%lu] [XTC] Page table offset is 0, cannot read\n", millis()); diff --git a/lib/Xtc/Xtc/XtcParser.h b/lib/Xtc/Xtc/XtcParser.h index 2d2b780e..b0033542 100644 --- a/lib/Xtc/Xtc/XtcParser.h +++ b/lib/Xtc/Xtc/XtcParser.h @@ -67,8 +67,9 @@ class XtcParser { std::function callback, size_t chunkSize = 1024); - // Get title from metadata + // Get title/author from metadata std::string getTitle() const { return m_title; } + std::string getAuthor() const { return m_author; } bool hasChapters() const { return m_hasChapters; } const std::vector& getChapters() const { return m_chapters; } @@ -86,6 +87,7 @@ class XtcParser { std::vector m_pageTable; std::vector m_chapters; std::string m_title; + std::string m_author; uint16_t m_defaultWidth; uint16_t m_defaultHeight; uint8_t m_bitDepth; // 1 = XTC/XTG (1-bit), 2 = XTCH/XTH (2-bit) @@ -96,6 +98,7 @@ class XtcParser { XtcError readHeader(); XtcError readPageTable(); XtcError readTitle(); + XtcError readAuthor(); XtcError readChapters(); }; diff --git a/lib/Xtc/Xtc/XtcTypes.h b/lib/Xtc/Xtc/XtcTypes.h index 08f9c00b..773c7ad5 100644 --- a/lib/Xtc/Xtc/XtcTypes.h +++ b/lib/Xtc/Xtc/XtcTypes.h @@ -38,14 +38,16 @@ struct XtcHeader { uint8_t versionMajor; // 0x04: Format version major (typically 1) (together with minor = 1.0) uint8_t versionMinor; // 0x05: Format version minor (typically 0) uint16_t pageCount; // 0x06: Total page count - uint32_t flags; // 0x08: Flags/reserved - uint32_t headerSize; // 0x0C: Size of header section (typically 88) - uint32_t reserved1; // 0x10: Reserved - uint32_t tocOffset; // 0x14: TOC offset (0 if unused) - 4 bytes, not 8! + uint8_t readDirection; // 0x08: Reading direction (0-2) + uint8_t hasMetadata; // 0x09: Has metadata (0-1) + uint8_t hasThumbnails; // 0x0A: Has thumbnails (0-1) + uint8_t hasChapters; // 0x0B: Has chapters (0-1) + uint32_t currentPage; // 0x0C: Current page (1-based) (0-65535) + uint64_t metadataOffset; // 0x10: Metadata offset (0 if unused) uint64_t pageTableOffset; // 0x18: Page table offset uint64_t dataOffset; // 0x20: First page data offset - uint64_t reserved2; // 0x28: Reserved - uint32_t titleOffset; // 0x30: Title string offset + uint64_t thumbOffset; // 0x28: Thumbnail offset + uint32_t chapterOffset; // 0x30: Chapter data offset uint32_t padding; // 0x34: Padding to 56 bytes }; #pragma pack(pop) diff --git a/src/activities/home/HomeActivity.cpp b/src/activities/home/HomeActivity.cpp index 3389e80d..58b29505 100644 --- a/src/activities/home/HomeActivity.cpp +++ b/src/activities/home/HomeActivity.cpp @@ -71,6 +71,9 @@ void HomeActivity::onEnter() { if (!xtc.getTitle().empty()) { lastBookTitle = std::string(xtc.getTitle()); } + if (!xtc.getAuthor().empty()) { + lastBookAuthor = std::string(xtc.getAuthor()); + } // Try to generate thumbnail image for Continue Reading card if (xtc.generateThumbBmp()) { coverBmpPath = xtc.getThumbBmpPath();