feat: Extract author from XTC/XTCH files (#563)
## Summary * Extract author from XTC/XTCH files ## Additional Context * Based on updated details in https://gist.github.com/CrazyCoder/b125f26d6987c0620058249f59f1327d --- ### 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:
parent
e2ca0e94ca
commit
5e24895f6d
@ -7,7 +7,6 @@
|
||||
|
||||
#include "Xtc.h"
|
||||
|
||||
#include <FsHelpers.h>
|
||||
#include <HardwareSerial.h>
|
||||
#include <SDCardManager.h>
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -56,6 +56,7 @@ class Xtc {
|
||||
|
||||
// Metadata
|
||||
std::string getTitle() const;
|
||||
std::string getAuthor() const;
|
||||
bool hasChapters() const;
|
||||
const std::vector<xtc::ChapterInfo>& getChapters() const;
|
||||
|
||||
|
||||
@ -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<uint8_t*>(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());
|
||||
|
||||
@ -67,8 +67,9 @@ class XtcParser {
|
||||
std::function<void(const uint8_t* data, size_t size, size_t offset)> 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<ChapterInfo>& getChapters() const { return m_chapters; }
|
||||
@ -86,6 +87,7 @@ class XtcParser {
|
||||
std::vector<PageInfo> m_pageTable;
|
||||
std::vector<ChapterInfo> 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();
|
||||
};
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user