|
2 | 2 |
|
3 | 3 | These formats describe the SD-card cache files under `/.crosspoint/epub_<hash>/`. |
4 | 4 | All POD fields are written in the ESP32 little-endian representation used by |
5 | | -`Serialization.h`; strings are length-prefixed UTF-8. |
| 5 | +`Serialization.h`; strings are length-prefixed UTF-8 unless a format notes a |
| 6 | +fixed-size char buffer. |
6 | 7 |
|
7 | 8 | ## `book.bin` |
8 | 9 |
|
@@ -88,6 +89,135 @@ if (parsedSize != fileSize) { |
88 | 89 | } |
89 | 90 | ``` |
90 | 91 |
|
| 92 | +## `reader_settings.bin` |
| 93 | + |
| 94 | +### Version 2 |
| 95 | + |
| 96 | +Each EPUB cache directory may contain `reader_settings.bin`. Missing files mean |
| 97 | +the book uses global Reader settings and the default auto-page-turn interval. |
| 98 | + |
| 99 | +Version 1 stored only: |
| 100 | + |
| 101 | +- `u8 version` |
| 102 | +- `u16 autoPageTurnSeconds` |
| 103 | + |
| 104 | +Version 2 stores flags before the full reader-settings snapshot. This lets the |
| 105 | +file preserve an auto-page-turn interval without forcing custom font/layout |
| 106 | +settings for the book. |
| 107 | + |
| 108 | +```c++ |
| 109 | +struct ReaderSettingsBin { |
| 110 | + u8 version; // 2 |
| 111 | + u8 flags; // bit 0 = custom reader settings, bit 1 = custom auto-page-turn interval |
| 112 | + u16 autoPageTurnSeconds; |
| 113 | + |
| 114 | + u8 fontFamily; |
| 115 | + u8 fontSize; |
| 116 | + u8 lineHeightPercent; |
| 117 | + u8 orientation; |
| 118 | + u8 screenMargin; |
| 119 | + u8 publisherPageNumbers; |
| 120 | + u8 paragraphAlignment; |
| 121 | + u8 embeddedStyle; |
| 122 | + u8 hyphenationEnabled; |
| 123 | + u8 textAntiAliasing; |
| 124 | + u8 readerDarkMode; |
| 125 | + u8 imageRendering; |
| 126 | + u8 extraParagraphSpacing; |
| 127 | + u8 forceParagraphIndents; |
| 128 | + u8 bionicReadingEnabled; |
| 129 | + u8 guideReadingEnabled; |
| 130 | + char sdFontFamilyName[64]; |
| 131 | +}; |
| 132 | +``` |
| 133 | +
|
| 134 | +## `/.crosspoint/clippings/<bookType>_<crc32(path)>.bin` |
| 135 | +
|
| 136 | +### Version 2 |
| 137 | +
|
| 138 | +Clipping files store the per-book EPUB clipping list used by the reader. A |
| 139 | +saved clipping is also what CrossInk renders as an in-reader highlight; there is |
| 140 | +no separate highlight file. The file lives in `/.crosspoint/clippings/` instead |
| 141 | +of the EPUB render-cache directory so clearing/rebuilding layout cache does not |
| 142 | +delete user clippings. |
| 143 | +
|
| 144 | +The current implementation only writes EPUB clipping files, so `bookType` is |
| 145 | +`epub`. The numeric suffix is `uzlib_crc32()` of the book's SD-card path, for |
| 146 | +example: |
| 147 | +
|
| 148 | +```text |
| 149 | +/.crosspoint/clippings/epub_1234567890.bin |
| 150 | +``` |
| 151 | + |
| 152 | +Binary layout: |
| 153 | + |
| 154 | +- `[0]` version (`2`) |
| 155 | +- `[1-2]` clipping count (`uint16_t` LE, maximum `64`) |
| 156 | +- book title (`String`) |
| 157 | +- book author (`String`) |
| 158 | +- book path (`String`) |
| 159 | +- repeated clipping records: |
| 160 | + - `spineIndex` (`uint16_t` LE) |
| 161 | + - `startPage` (`uint16_t` LE) |
| 162 | + - `endPage` (`uint16_t` LE) |
| 163 | + - `pageCount` (`uint16_t` LE, at least `1`) |
| 164 | + - `startWordIndex` (`uint16_t` LE) |
| 165 | + - `endWordIndex` (`uint16_t` LE) |
| 166 | + - `wordCount` (`uint16_t` LE) |
| 167 | + - `paragraphIndex` (`uint16_t` LE, `UINT16_MAX` when unavailable) |
| 168 | + - `timestamp` (`uint32_t` LE, seconds since firmware boot when saved) |
| 169 | + - `chapterTitle` (`char[48]`, null-terminated/truncated) |
| 170 | + - selected text (`String`, truncated to `512` bytes for the in-app store) |
| 171 | + |
| 172 | +CrossInk uses the stored spine/page/paragraph fields as anchors, then searches |
| 173 | +near that location for the stored clipping text after relayout. This is similar |
| 174 | +to keeping both a DOM position and a text quote in a web app: the numeric |
| 175 | +position gives a fast starting point, while the text makes jumps and highlights |
| 176 | +survive font, layout, or page-count changes when possible. |
| 177 | + |
| 178 | +Creating a clipping also appends a Kindle-style export entry to |
| 179 | +`/My Clippings.txt` on the SD-card root. That text export can keep up to `2000` |
| 180 | +bytes of the selected text and is append-only. Removing a clipping from the |
| 181 | +reader deletes or rewrites only the binary clipping file; it does not remove |
| 182 | +previous entries from `/My Clippings.txt`. |
| 183 | + |
| 184 | +When CrossInk moves an EPUB through its built-in move-to-Read flow, it rewrites |
| 185 | +the clipping file under the new path-derived name and removes the old one. If a |
| 186 | +book is renamed or moved outside CrossInk, the path hash changes, so the old |
| 187 | +clipping file may no longer be associated with the book until the file is moved |
| 188 | +back or the clipping store is migrated. |
| 189 | + |
| 190 | +## `stats_v5.bin` |
| 191 | + |
| 192 | +### Version 5 |
| 193 | + |
| 194 | +`stats_v5.bin` stores per-book reading statistics for stats schema version 5. |
| 195 | +Versioned filenames let firmware branches with different stats schemas keep |
| 196 | +their own per-book stats files without overwriting each other. Version 5 extends |
| 197 | +version 4 with a cached live reader book time-left estimate so Home and Reading |
| 198 | +Stats can show the same estimate the reader last computed. |
| 199 | + |
| 200 | +When `stats_v5.bin` is missing, CrossInk can read the previous versioned stats |
| 201 | +filename (`stats_v4.bin` for version 5, `stats_v5.bin` after a future version 6 |
| 202 | +bump) before falling back to legacy `stats.bin` files with compatible stats |
| 203 | +payloads. Future changes are always saved to the current versioned filename. |
| 204 | + |
| 205 | +Binary layout: |
| 206 | + |
| 207 | +- `[0]` version (`5`) |
| 208 | +- `[1-2]` `sessionCount` (`uint16_t` LE) |
| 209 | +- `[3-6]` `totalReadingSeconds` (`uint32_t` LE) |
| 210 | +- `[7-10]` `totalPagesTurned` (`uint32_t` LE) |
| 211 | +- `[11]` `isCompleted` (`uint8_t`) |
| 212 | +- `[12-13]` `avgSecondsPerForwardPage` (`uint16_t` LE) |
| 213 | +- `[14-15]` `paceSampleCount` (`uint16_t` LE) |
| 214 | +- `[16]` flags (`bit0=startDateManual`, `bit1=finishedDateManual`) |
| 215 | +- `[17-20]` `startDate` (`year uint16_t` LE, `month uint8_t`, `day uint8_t`) |
| 216 | +- `[21-24]` `finishedDate` (`year uint16_t` LE, `month uint8_t`, `day uint8_t`) |
| 217 | +- `[25-40]` `timeOfDaySeconds[4]` (`uint32_t` LE each) |
| 218 | +- `[41-68]` `dayOfWeekSeconds[7]` (`uint32_t` LE each) |
| 219 | +- `[69-72]` `estimatedTimeLeftSeconds` (`uint32_t` LE, `0` means unavailable) |
| 220 | + |
91 | 221 | ## `section.bin` |
92 | 222 |
|
93 | 223 | ### Version 40 |
|
0 commit comments