mirror of
https://github.com/crosspoint-reader/crosspoint-reader.git
synced 2026-04-29 10:26:52 -07:00
Opening XTC files with a high page count (e.g. *The Magic Mountain* at 4,187 pages) causes an immediate `abort()` crash and reboot loop. The device becomes unusable until the book is removed from the SD card. **Crash log:** ``` abort() was called at 0x4214a5fb on core 0 ``` ### Root cause During `XtcParser::open()`, the parser calls `m_pageTable.resize(pageCount)` to load the entire page table into RAM. Each `PageInfo` entry is 16 bytes, so: - 4,187 pages x 16 bytes = **66,992 bytes (~65KB)** as a single contiguous heap allocation On the ESP32-C3 with ~380KB total RAM (no PSRAM), this allocation fails after firmware, fonts, and the activity system are already loaded. Because the firmware is compiled with `-fno-exceptions`, the failed `new` inside `std::vector::resize()` calls `abort()` instead of throwing. This affects any XTC file with roughly 3,000+ pages, depending on heap state at the time of loading. ## Solution Replace the bulk page table allocation with on-demand reads from the SD card. Instead of loading all page table entries into a vector at file open, we now: 1. Read only the **first** page table entry at open time (to get default page dimensions) 2. Read a **single** 16-byte entry from the SD card each time a page is loaded This reduces page table memory usage from `pageCount * 16` bytes to **zero bytes**, regardless of how many pages the file contains. ### Changes | File | What changed | |------|-------------| | `XtcParser.h` | Removed `std::vector<PageInfo> m_pageTable`. Added `readPageTableEntry()` for on-demand reads. | | `XtcParser.cpp` | Replaced `readPageTable()` with `readFirstPageInfo()`. Updated `getPageInfo()`, `loadPage()`, and `loadPageStreaming()` to seek and read individual entries from the file. | ## Trade-offs ### Performance Each page turn now requires one additional SD card seek + 16-byte read to look up the page table entry before reading the page data itself. - SD card sequential read latency: ~0.1-0.5ms for a 16-byte read - E-ink full display refresh: ~1,000-2,000ms I personally can't see any performance difference while reading and the trade off of not boot looping seems to make this well worth it. ### Memory | Metric | Before | After | |--------|--------|-------| | Page table RAM (4,187 pages) | ~65KB | 0 bytes | | Page table RAM (1,000 pages) | ~16KB | 0 bytes | | Page table RAM (max 65,535 pages) | ~1MB (impossible) | 0 bytes |
# XTC/XTCH Library XTC ebook format support for CrossPoint Reader. ## Supported Formats | Format | Extension | Description | |--------|-----------|----------------------------------------------| | XTC | `.xtc` | Container with XTG pages (1-bit monochrome) | | XTCH | `.xtch` | Container with XTH pages (2-bit grayscale) | ## Format Overview XTC/XTCH are container formats designed for ESP32 e-paper displays. They store pre-rendered bitmap pages optimized for the XTeink X4 e-reader (480x800 resolution). ### Container Structure (XTC/XTCH) - 56-byte header with metadata offsets - Optional metadata (title, author, etc.) - Page index table (16 bytes per page) - Page data (XTG or XTH format) ### Page Formats #### XTG (1-bit monochrome) - Row-major storage, 8 pixels per byte - MSB first (bit 7 = leftmost pixel) - 0 = Black, 1 = White #### XTH (2-bit grayscale) - Two bit planes stored sequentially - Column-major order (right to left) - 8 vertical pixels per byte - Grayscale: 0=White, 1=Dark Grey, 2=Light Grey, 3=Black ## Reference Original format info: <https://gist.github.com/CrazyCoder/b125f26d6987c0620058249f59f1327d>