## Summary * **What is the goal of this PR?** Add support for XTC (XTeink X4 native) ebook format, which contains pre-rendered 480x800 1-bit bitmap pages optimized for e-ink displays. * **What changes are included?** - New `lib/Xtc/` library with XtcParser for reading XTC files - XtcReaderActivity for displaying XTC pages on e-ink display - XTC file detection in FileSelectionActivity - Cover BMP generation from first XTC page - Correct XTG page header structure (22 bytes) and bit polarity handling ## Additional Context - XTC files contain pre-rendered bitmap pages with embedded status bar (page numbers, progress %) - XTG page header: 22 bytes (magic + dimensions + reserved fields + bitmap size) - Bit polarity: 0 = black, 1 = white - No runtime text rendering needed - pages display directly on e-ink - Faster page display compared to EPUB since no parsing/rendering required - Memory efficient: loads one page at a time (48KB per page) - Tested with XTC files generated from https://x4converter.rho.sh/ - Verified correct page alignment and color rendering - Please report any issues if you test with XTC files from other sources. --------- Co-authored-by: Dave Allie <dave@daveallie.com>
97 lines
2.4 KiB
C++
97 lines
2.4 KiB
C++
/**
|
|
* XtcParser.h
|
|
*
|
|
* XTC file parsing and page data extraction
|
|
* XTC ebook support for CrossPoint Reader
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <SD.h>
|
|
|
|
#include <functional>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "XtcTypes.h"
|
|
|
|
namespace xtc {
|
|
|
|
/**
|
|
* XTC File Parser
|
|
*
|
|
* Reads XTC files from SD card and extracts page data.
|
|
* Designed for ESP32-C3's limited RAM (~380KB) using streaming.
|
|
*/
|
|
class XtcParser {
|
|
public:
|
|
XtcParser();
|
|
~XtcParser();
|
|
|
|
// File open/close
|
|
XtcError open(const char* filepath);
|
|
void close();
|
|
bool isOpen() const { return m_isOpen; }
|
|
|
|
// Header information access
|
|
const XtcHeader& getHeader() const { return m_header; }
|
|
uint16_t getPageCount() const { return m_header.pageCount; }
|
|
uint16_t getWidth() const { return m_defaultWidth; }
|
|
uint16_t getHeight() const { return m_defaultHeight; }
|
|
uint8_t getBitDepth() const { return m_bitDepth; } // 1 = XTC/XTG, 2 = XTCH/XTH
|
|
|
|
// Page information
|
|
bool getPageInfo(uint32_t pageIndex, PageInfo& info) const;
|
|
|
|
/**
|
|
* Load page bitmap (raw 1-bit data, skipping XTG header)
|
|
*
|
|
* @param pageIndex Page index (0-based)
|
|
* @param buffer Output buffer (caller allocated)
|
|
* @param bufferSize Buffer size
|
|
* @return Number of bytes read on success, 0 on failure
|
|
*/
|
|
size_t loadPage(uint32_t pageIndex, uint8_t* buffer, size_t bufferSize);
|
|
|
|
/**
|
|
* Streaming page load
|
|
* Memory-efficient method that reads page data in chunks.
|
|
*
|
|
* @param pageIndex Page index
|
|
* @param callback Callback function to receive data chunks
|
|
* @param chunkSize Chunk size (default: 1024 bytes)
|
|
* @return Error code
|
|
*/
|
|
XtcError loadPageStreaming(uint32_t pageIndex,
|
|
std::function<void(const uint8_t* data, size_t size, size_t offset)> callback,
|
|
size_t chunkSize = 1024);
|
|
|
|
// Get title from metadata
|
|
std::string getTitle() const { return m_title; }
|
|
|
|
// Validation
|
|
static bool isValidXtcFile(const char* filepath);
|
|
|
|
// Error information
|
|
XtcError getLastError() const { return m_lastError; }
|
|
|
|
private:
|
|
File m_file;
|
|
bool m_isOpen;
|
|
XtcHeader m_header;
|
|
std::vector<PageInfo> m_pageTable;
|
|
std::string m_title;
|
|
uint16_t m_defaultWidth;
|
|
uint16_t m_defaultHeight;
|
|
uint8_t m_bitDepth; // 1 = XTC/XTG (1-bit), 2 = XTCH/XTH (2-bit)
|
|
XtcError m_lastError;
|
|
|
|
// Internal helper functions
|
|
XtcError readHeader();
|
|
XtcError readPageTable();
|
|
XtcError readTitle();
|
|
};
|
|
|
|
} // namespace xtc
|