ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
sh_book.c
Go to the documentation of this file.
1
28#include <string.h>
29
30#include "epub.h"
31#include "ra8_gfx.h"
32#include "reflow_image.h"
33#include "sh_app.h"
34
36typedef enum : uint32_t {
37 k_sh_xhtml_cap = 256U * 1024U,
38 k_sh_cover_cap = 512U * 1024U,
39 k_sh_arena_cap = 64U * 1024U,
40 k_sh_col_white = 0xFFFFFFU,
42 k_sh_luma_g = 150U,
45 k_sh_r5_sh = 11U,
47 k_sh_r5_mask = 0x1FU,
48 k_sh_g6_mask = 0x3FU,
49 k_sh_5to8 = 3U,
50 k_sh_6to8 = 2U,
52
54
55[[gnu::section(".sdram_data"), gnu::aligned(8)]] static uint8_t s_xhtml[k_sh_xhtml_cap];
56[[gnu::section(".sdram_data"), gnu::aligned(8)]] static uint8_t s_cover[k_sh_cover_cap];
57[[gnu::section(".sdram_data"), gnu::aligned(8)]] static uint8_t s_arena[k_sh_arena_cap];
58[[gnu::section(".sdram_data"),
59 gnu::aligned(8)]] static uint16_t s_thumb_rgb[k_sh_thumb_w * k_sh_thumb_h];
60
61/* ---- XHTML -> plain text -------------------------------------------------- */
62
64static bool sh_xhtml_is_block(const char* tag, size_t n)
65{
66 static const char* const k_block[] = {
67 "p", "br", "div", "h1", "h2", "h3", "h4", "h5",
68 "h6", "li", "ul", "ol", "tr", "hr", "pre", "section",
69 "blockquote", "article", "header", "footer", "figure", "figcaption"};
70 for (size_t i = 0U; i < (sizeof(k_block) / sizeof(k_block[0])); ++i) {
71 if ((strlen(k_block[i]) == n) && (strncmp(tag, k_block[i], n) == 0)) {
72 return true;
73 }
74 }
75 return false;
76}
77
79static bool sh_xhtml_is_suppress(const char* tag, size_t n)
80{
81 static const char* const k_skip[] = {"head", "title", "style", "script"};
82 for (size_t i = 0U; i < (sizeof(k_skip) / sizeof(k_skip[0])); ++i) {
83 if ((strlen(k_skip[i]) == n) && (strncmp(tag, k_skip[i], n) == 0)) {
84 return true;
85 }
86 }
87 return false;
88}
89
91typedef enum : uint32_t {
92 k_u8_1max = 0x80U,
93 k_u8_2max = 0x800U,
94 k_u8_cont = 0x80U,
95 k_u8_2lead = 0xC0U,
96 k_u8_3lead = 0xE0U,
97 k_u8_data = 0x3FU,
98 k_u8_sh6 = 6U,
99 k_u8_sh12 = 12U,
100 k_ent_dec = 10U,
101 k_ent_hex = 16U,
102 k_ent_a10 = 10U,
103} sh_u8enc_t;
104
106static void sh_emit_cp(char* out, size_t cap, size_t* pos, uint32_t cp)
107{
108 if ((cp < k_u8_1max) && (*pos < cap)) {
109 out[(*pos)++] = (char)cp;
110 } else if ((cp < k_u8_2max) && (*pos + 1U < cap)) {
111 out[(*pos)++] = (char)(k_u8_2lead | (cp >> k_u8_sh6));
112 out[(*pos)++] = (char)(k_u8_cont | (cp & k_u8_data));
113 } else if (*pos + 2U < cap) {
114 out[(*pos)++] = (char)(k_u8_3lead | (cp >> k_u8_sh12));
115 out[(*pos)++] = (char)(k_u8_cont | ((cp >> k_u8_sh6) & k_u8_data));
116 out[(*pos)++] = (char)(k_u8_cont | (cp & k_u8_data));
117 }
118}
119
121static uint32_t sh_digit(char c, uint32_t base)
122{
123 if ((c >= '0') && (c <= '9')) {
124 return (uint32_t)(c - '0');
125 }
126 if ((base == k_ent_hex) && (c >= 'a') && (c <= 'f')) {
127 return (uint32_t)(c - 'a') + k_ent_a10;
128 }
129 if ((base == k_ent_hex) && (c >= 'A') && (c <= 'F')) {
130 return (uint32_t)(c - 'A') + k_ent_a10;
131 }
132 return UINT32_MAX;
133}
134
136static bool sh_parse_numeric(const char* s, size_t n, size_t* consumed, uint32_t* cp)
137{
138 if ((n < 4U) || (s[1] != '#')) {
139 return false;
140 }
141 const bool hex = (s[2] == 'x') || (s[2] == 'X');
142 const uint32_t base = hex ? k_ent_hex : k_ent_dec;
143 size_t j = hex ? 3U : 2U;
144 uint32_t v = 0U;
145 size_t dig = 0U;
146 while ((j < n) && (s[j] != ';')) {
147 const uint32_t d = sh_digit(s[j], base);
148 if (d == UINT32_MAX) {
149 return false;
150 }
151 v = (v * base) + d;
152 ++j;
153 ++dig;
154 }
155 if ((dig == 0U) || (j >= n) || (s[j] != ';')) {
156 return false;
157 }
158 *cp = v;
159 *consumed = j + 1U;
160 return true;
161}
162
164static bool sh_decode_entity(const char* s, size_t n, size_t* consumed, uint32_t* cp)
165{
166 static const struct {
167 const char* name;
168 uint32_t cp;
169 } k_named[] = {{"&amp;", '&'}, {"&lt;", '<'}, {"&gt;", '>'}, {"&quot;", '"'}, {"&apos;", '\''}};
170 for (size_t i = 0U; i < (sizeof(k_named) / sizeof(k_named[0])); ++i) {
171 const size_t ln = strlen(k_named[i].name);
172 if ((n >= ln) && (strncmp(s, k_named[i].name, ln) == 0)) {
173 *cp = k_named[i].cp;
174 *consumed = ln;
175 return true;
176 }
177 }
178 return sh_parse_numeric(s, n, consumed, cp);
179}
180
185typedef struct {
186 size_t pos;
187 bool at_break;
189} sh_strip_t;
190
192static void sh_strip_break(char* out, sh_strip_t* st)
193{
194 while ((st->pos > 0U) && (out[st->pos - 1U] == ' ')) {
195 st->pos--;
196 }
197 st->at_break = true;
198 if ((st->pos > 0U) && (out[st->pos - 1U] != '\n')) {
199 out[st->pos++] = '\n';
200 }
201}
202
204static void sh_strip_text(char* out, size_t cap, sh_strip_t* st, uint32_t cp)
205{
206 const bool ws = (cp == ' ') || (cp == '\t') || (cp == '\n') || (cp == '\r');
207 if (ws) {
208 if (!st->at_break && (st->pos < cap)) {
209 out[st->pos++] = ' ';
210 st->at_break = true;
211 }
212 return;
213 }
214 sh_emit_cp(out, cap, &st->pos, cp);
215 st->at_break = false;
216}
217
219static size_t sh_strip_tag(const uint8_t* x, size_t n, size_t i, char* out, sh_strip_t* st)
220{
221 size_t j = i + 1U;
222 const bool close = (j < n) && (x[j] == '/');
223 if (close) {
224 ++j;
225 }
226 const size_t ts = j;
227 while ((j < n) && (((x[j] >= 'a') && (x[j] <= 'z')) || ((x[j] >= 'A') && (x[j] <= 'Z')) ||
228 ((x[j] >= '0') && (x[j] <= '9')))) {
229 ++j;
230 }
231 const size_t tn = j - ts;
232 if (sh_xhtml_is_suppress((const char*)&x[ts], tn)) {
233 st->suppress += close ? -1 : 1;
234 if (st->suppress < 0) {
235 st->suppress = 0;
236 }
237 } else if ((st->suppress == 0) && sh_xhtml_is_block((const char*)&x[ts], tn)) {
238 sh_strip_break(out, st);
239 }
240 while ((i < n) && (x[i] != '>')) {
241 ++i;
242 }
243 return (i < n) ? (i + 1U) : n;
244}
245
247static size_t sh_strip_xhtml(const uint8_t* x, size_t n, char* out, size_t cap)
248{
249 sh_strip_t st = {.pos = 0U, .at_break = true, .suppress = 0};
250 size_t i = 0U;
251 uint32_t itr = 0U;
252 while ((i < n) && (st.pos < cap) && (itr < (uint32_t)k_sh_xhtml_cap)) {
253 ++itr;
254 if (x[i] == '<') {
255 i = sh_strip_tag(x, n, i, out, &st);
256 continue;
257 }
258 if (st.suppress > 0) {
259 ++i;
260 continue;
261 }
262 if (x[i] == '&') {
263 uint32_t cp = 0U;
264 size_t adv = 0U;
265 if (sh_decode_entity((const char*)&x[i], n - i, &adv, &cp)) {
266 sh_strip_text(out, cap, &st, cp);
267 i += adv;
268 continue;
269 }
270 }
271 sh_strip_text(out, cap, &st, x[i]);
272 ++i;
273 }
274 return st.pos;
275}
276
277/* ---- cover decode --------------------------------------------------------- */
278
280static uint8_t sh_luma565(uint16_t px)
281{
282 const uint32_t r = (((uint32_t)px >> k_sh_r5_sh) & k_sh_r5_mask) << k_sh_5to8;
283 const uint32_t g = (((uint32_t)px >> k_sh_g6_sh) & k_sh_g6_mask) << k_sh_6to8;
284 const uint32_t b = ((uint32_t)px & k_sh_r5_mask) << k_sh_5to8;
285 return (uint8_t)(((r * k_sh_luma_r) + (g * k_sh_luma_g) + (b * k_sh_luma_b)) >> k_sh_luma_sh);
286}
287
289static bool sh_epub_thumb(uint16_t idx)
290{
291 size_t got = 0U;
292 if ((epub_get_cover_image(&s_epub, s_cover, sizeof s_cover, &got) != k_ra8_ok) || (got == 0U)) {
293 return false;
294 }
295 /* Decode into an off-screen RGB565 scratch by rebinding ra8_gfx, read back to
296 * gray8, then rebind to the live framebuffer. */
298 (uint16_t)k_sh_thumb_w,
299 (uint16_t)k_sh_thumb_h,
301 (void)ra8_gfx_clear((uint32_t)k_sh_col_white);
302 ra8_img_arena_t arena = {.base = s_arena, .cap = sizeof s_arena, .offset = 0U, .live = 0U};
303 int32_t w = 0;
304 int32_t h = 0;
305 const ra8_err_t err = ra8_img_decode_blit(&arena,
306 s_cover,
307 got,
308 0,
309 0,
310 (int32_t)k_sh_thumb_w,
311 (int32_t)k_sh_thumb_h,
312 &w,
313 &h);
314 if (err == k_ra8_ok) {
315 for (int32_t y = 0; y < h; ++y) {
316 for (int32_t x = 0; x < w; ++x) {
317 g_sh.thumb[idx][(y * w) + x] = sh_luma565(s_thumb_rgb[(y * (int32_t)k_sh_thumb_w) + x]);
318 }
319 }
320 g_sh.thumb_w[idx] = (uint16_t)w;
321 g_sh.thumb_h[idx] = (uint16_t)h;
322 }
323 (void)
325 return err == k_ra8_ok;
326}
327
328bool sh_book_decode_thumb(uint16_t idx)
329{
330 g_sh.thumb_w[idx] = 0U;
331 g_sh.thumb_h[idx] = 0U;
332 if (g_sh.open_fmt == k_sh_fmt_epub) {
333 return sh_epub_thumb(idx);
334 }
335 if (g_sh.book_src.vm != nullptr) {
336 sh_decode_cover(idx, &g_sh.book_src);
337 }
338 return g_sh.thumb_w[idx] > 0U;
339}
340
341void sh_book_cover_fullscreen(int32_t x, int32_t y, int32_t w, int32_t h)
342{
343 if (g_sh.open_fmt == k_sh_fmt_epub) {
344 size_t got = 0U;
345 if ((epub_get_cover_image(&s_epub, s_cover, sizeof s_cover, &got) == k_ra8_ok) && (got > 0U)) {
346 ra8_img_arena_t arena = {.base = s_arena, .cap = sizeof s_arena, .offset = 0U, .live = 0U};
347 int32_t dw = 0;
348 int32_t dh = 0;
349 (void)ra8_img_decode_blit(&arena, s_cover, got, x, y, w, h, &dw, &dh);
350 }
351 return;
352 }
353 if (g_sh.book_src.vm == nullptr) {
354 return;
355 }
356 const uint32_t cover = g_sh.book_src.hdr.cover_image_index;
357 if (cover != k_book_nil) {
358 (void)sh_image_blit_cover(&g_sh.book_src, cover, x, y, w, h, nullptr, nullptr);
359 }
360}
361
362/* ---- metadata / chapters / TOC ------------------------------------------- */
363
365static void sh_copy_str(char* dst, size_t cap, const char* src)
366{
367 (void)strncpy(dst, (src != nullptr) ? src : "", cap - 1U);
368 dst[cap - 1U] = '\0';
369}
370
379static void sh_src_string(const book_src_t* src, uint32_t off, char* out, size_t cap)
380{
381 out[0] = '\0';
382 if (cap < 2U) {
383 return;
384 }
385 const uint64_t pool_end = (uint64_t)src->hdr.string_off + src->hdr.string_size;
386 const uint64_t abs = (uint64_t)src->hdr.string_off + off;
387 if (abs >= pool_end) {
388 return;
389 }
390 uint64_t n = pool_end - abs;
391 if (n > (uint64_t)(cap - 1U)) {
392 n = (uint64_t)(cap - 1U);
393 }
394 if (book_src_read(src, (uint32_t)abs, out, (uint32_t)n) != k_ra8_ok) {
395 out[0] = '\0';
396 return;
397 }
398 out[n] = '\0';
399}
400
402static void sh_book_capture_meta(uint16_t idx)
403{
404 sh_entry_t* e = &g_sh.entry[idx];
405 if (g_sh.open_fmt == k_sh_fmt_epub) {
406 epub_metadata_t m = {};
407 if (epub_get_metadata(&s_epub, &m) == k_ra8_ok) {
408 sh_copy_str(e->title, sizeof e->title, m.title);
409 sh_copy_str(e->author, sizeof e->author, m.author);
410 }
411 } else if (g_sh.book_src.vm != nullptr) {
412 sh_src_string(&g_sh.book_src, g_sh.book_src.hdr.title_off, e->title, sizeof e->title);
413 sh_src_string(&g_sh.book_src, g_sh.book_src.hdr.author_off, e->author, sizeof e->author);
414 }
415 (void)sh_book_decode_thumb(idx);
416}
417
419static bool sh_book_open_rabook(const sh_entry_t* e)
420{
421 if (e->from_sd) {
422 uint32_t len = 0U;
423 if (!sh_sd_book_open(e->sd_name, &len)) {
424 return false;
425 }
426 return sh_paged_open(sh_sd_book_read, nullptr, (uint64_t)len);
427 }
428 return sh_paged_open_mram(e->blob, e->blob_len);
429}
430
431bool sh_book_open(uint16_t idx)
432{
433 if (idx >= g_sh.book_count) {
434 return false;
435 }
436 if (s_epub.in_use != 0U) {
438 }
440 sh_comic_close(); /* release any open comic before the next book binds */
441 sh_entry_t* const e = &g_sh.entry[idx];
442
443 if (sh_fmt_is_comic(e->fmt)) {
444 /* CBZ / CBR route through sh_comic.c (image pages), not the text screens. */
445 if (!sh_comic_open(idx)) {
446 return false;
447 }
448 g_sh.open_fmt = e->fmt;
449 g_sh.chapter_count = 0U; /* comics paginate by image page, not chapters */
450 return true;
451 }
452
453 if (e->fmt == k_sh_fmt_epub) {
454 if (!sh_sd_open_epub(e->sd_name, &s_epub)) {
455 return false;
456 }
457 uint16_t n = 0U;
460 return false;
461 }
462 g_sh.open_fmt = k_sh_fmt_epub;
463 g_sh.chapter_count = n;
464 } else {
465 /* Always demand-paged through the chunked container -- no resident/paged
466 * size threshold, one code path for every book size (#204/#205). */
467 if (!sh_book_open_rabook(e)) {
469 return false;
470 }
471 g_sh.open_fmt = k_sh_fmt_rabook;
472 g_sh.chapter_count = g_sh.book_src.hdr.chapter_count;
473 }
474 if (e->from_sd && (g_sh.thumb_w[idx] == 0U)) {
476 }
477 return true;
478}
479
480ra8_err_t sh_book_chapter_text(uint32_t chapter, char* out, size_t cap, size_t* out_len)
481{
482 if (g_sh.open_fmt == k_sh_fmt_epub) {
483 size_t got = 0U;
484 const ra8_err_t err =
485 epub_load_chapter(&s_epub, (uint16_t)chapter, s_xhtml, sizeof s_xhtml, &got);
486 if (err != k_ra8_ok) {
487 return err;
488 }
489 *out_len = sh_strip_xhtml(s_xhtml, got, out, cap);
490 return k_ra8_ok;
491 }
492 return book_chapter_text_src(&g_sh.book_src, chapter, out, cap, out_len);
493}
494
496static bool sh_epub_label(uint32_t chapter, char* out, size_t cap)
497{
498 uint16_t tc = 0U;
499 if (epub_get_toc_count(&s_epub, &tc) != k_ra8_ok) {
500 return false;
501 }
502 for (uint16_t i = 0U; i < tc; ++i) {
503 uint16_t mapped = UINT16_MAX;
504 if ((epub_toc_entry_to_chapter(&s_epub, i, &mapped) == k_ra8_ok) &&
505 ((uint32_t)mapped == chapter)) {
506 epub_toc_entry_t entry = {};
507 if ((epub_get_toc_entry(&s_epub, i, &entry) == k_ra8_ok) && (entry.title[0] != '\0')) {
508 sh_copy_str(out, cap, entry.title);
509 return true;
510 }
511 }
512 }
513 return false;
514}
515
517static bool sh_rabook_label(uint32_t chapter, char* out, size_t cap)
518{
519 const book_src_t* src = &g_sh.book_src;
520 if (src->vm == nullptr) {
521 return false;
522 }
523 if (chapter >= src->hdr.chapter_count) {
524 return false;
525 }
526 book_chapter_t ch = {};
527 const uint64_t off =
528 (uint64_t)src->hdr.chapter_off + ((uint64_t)chapter * sizeof(book_chapter_t));
529 if (book_src_read(src, (uint32_t)off, &ch, (uint32_t)sizeof ch) != k_ra8_ok) {
530 return false;
531 }
532 sh_src_string(src, ch.title_off, out, cap);
533 return out[0] != '\0';
534}
535
536void sh_book_chapter_label(uint32_t chapter, char* out, size_t cap)
537{
538 if (g_sh.open_fmt == k_sh_fmt_rabook) {
539 if (sh_rabook_label(chapter, out, cap)) {
540 return;
541 }
542 } else if (sh_epub_label(chapter, out, cap)) {
543 return;
544 }
545 size_t p = 0U;
546 out[p++] = 'C';
547 out[p++] = 'h';
548 out[p++] = '.';
549 out[p++] = ' ';
550 p = sh_fmt_uint(out, p, chapter + 1U);
551 out[p] = '\0';
552}
@ k_book_nil
Absent index / "applies to all chapters".
Definition book.h:129
ra8_err_t book_src_read(const book_src_t *src, uint32_t off, void *dst, uint32_t len)
Copy a byte range out of a book source (resident memcpy or paged fault).
Definition book_paged.c:141
ra8_err_t book_chapter_text_src(const book_src_t *src, uint32_t chapter_idx, char *out, size_t cap, size_t *out_len)
Extract one chapter's plain text from a book source (resident or paged).
Definition book_paged.c:681
static uint8_t s_arena[k_c6_cam_arena_bytes]
Definition c6_cam_app.c:43
EPUB (.epub) reader and chapter iterator for ra8-firmware.
ra8_err_t epub_toc_entry_to_chapter(const epub_book_t *book, uint16_t toc_idx, uint16_t *out_chapter_idx)
Resolve a TOC entry to the spine chapter index it points into.
ra8_err_t epub_get_cover_image(epub_book_t *book, uint8_t *out_buf, size_t max_len, size_t *got_len)
Copy the raw bytes of the cover image into the caller's buffer.
ra8_err_t epub_get_metadata(const epub_book_t *book, epub_metadata_t *out_meta)
Return Dublin Core metadata strings.
ra8_err_t epub_load_chapter(epub_book_t *book, uint16_t idx, uint8_t *out_xhtml, size_t max_len, size_t *got_len)
Extract the raw XHTML bytes of one chapter into the caller's buffer.
ra8_err_t epub_get_toc_entry(const epub_book_t *book, uint16_t idx, epub_toc_entry_t *out_entry)
Copy one table-of-contents entry into the caller's struct.
ra8_err_t epub_get_chapter_count(const epub_book_t *book, uint16_t *out_count)
Report how many chapters the spine contains.
ra8_err_t epub_get_toc_count(const epub_book_t *book, uint16_t *out_count)
Report how many table-of-contents entries the book exposes.
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
int strncmp(const char *s1, const char *s2, size_t n)
Compare two strings up to a specified length.
char * strncpy(char *dst, const char *src, size_t n)
Copy bounded string to destination buffer.
size_t strlen(const char *s)
Calculate string length.
int abs(int j)
Compute absolute value of integer.
Software 2D graphics primitives layered on top of a caller-ownedframebuffer (DRW / D/AVE 2D / GLCDC r...
@ k_ra8_gfx_format_rgb565
16-bit RGB565, little-endian in memory.
Definition ra8_gfx.h:40
ra8_err_t ra8_gfx_clear(uint32_t color)
Fill the bound framebuffer's clip region with a single colour.
ra8_err_t ra8_gfx_init(void *fb, uint16_t width, uint16_t height, ra8_gfx_format_t format)
Bind ra8_gfx to a caller-owned framebuffer.
Zero-heap raster image decode + scale + blit for reflow (#106).
ra8_err_t ra8_img_decode_blit(ra8_img_arena_t *arena, const uint8_t *bytes, size_t len, int32_t dst_x, int32_t dst_y, int32_t box_w, int32_t box_h, int32_t *out_w, int32_t *out_h)
Decode bytes and blit it, scaled to fit, into the bound framebuffer.
Shared contract for the ereader_shelf multi-screen e-reader.
void sh_decode_cover(uint16_t idx, const book_src_t *src)
Decode the open rabook source's 4bpp cover into thumbnail slot idx.
Definition sh_shelf.c:37
bool sh_paged_open(ra8_vsource_read_fn file_read, void *file_ctx, uint64_t file_len)
Open a chunked .rabook container as the demand-paged book source.
Definition sh_paged.c:223
bool sh_paged_open_mram(const uint8_t *blob, uint32_t blob_len)
Open a memory-mapped (baked MRAM) RBKC container as the paged source.
Definition sh_paged.c:267
bool sh_sd_open_epub(const char *name, void *out_book)
Stream-open SD file name as an EPUB via epub_open_streamed_fs.
Definition sh_sd.c:233
ra8_err_t sh_image_blit_cover(const book_src_t *src, uint32_t img_idx, int32_t dst_x, int32_t dst_y, int32_t box_w, int32_t box_h, int32_t *out_w, int32_t *out_h)
Decode + aspect-fit a 4bpp image straight into the framebuffer box.
Definition sh_image.c:228
bool sh_sd_book_open(const char *name, uint32_t *out_len)
Open SD file name and hold it as the paged book backing.
Definition sh_sd.c:150
void sh_comic_close(void)
Close any open comic and release its backing (idempotent).
Definition sh_comic.c:205
@ k_sh_thumb_h
Shelf cover-thumbnail box height.
Definition sh_app.h:56
@ k_sh_fb_h
Panel height in pixels.
Definition sh_app.h:47
@ k_sh_thumb_w
Shelf cover-thumbnail box width.
Definition sh_app.h:55
@ k_sh_fb_w
Panel width in pixels.
Definition sh_app.h:46
void sh_sd_close_epub(void *book)
Close a streamed EPUB opened by sh_sd_open_epub and its source file.
Definition sh_sd.c:244
size_t sh_fmt_uint(char *dst, size_t pos, uint32_t v)
Append unsigned v as decimal into dst at pos; returns new pos.
Definition sh_util.c:52
sh_state_t g_sh
The single whole-app state instance (defined in main.c).
Definition main.c:64
uint16_t * sh_fb_pixels(void)
The live RGB565 framebuffer ra8_gfx scans (defined in main.c).
Definition main.c:180
ra8_err_t sh_sd_book_read(void *ctx, uint64_t offset, uint8_t *buf, uint32_t len)
ra8_vsource_read_fn over the held-open SD book file (seek + read).
Definition sh_sd.c:176
void sh_paged_close(void)
Unbind the paged book source and release its SD backing (idempotent).
Definition sh_paged.c:279
bool sh_comic_open(uint16_t idx)
Open shelf entry idx as a comic archive (CBZ / CBR) for page reading.
Definition sh_comic.c:174
static void sh_book_capture_meta(uint16_t idx)
Populate SD entry idx title/author/cover from the just-opened book.
Definition sh_book.c:402
static void sh_copy_str(char *dst, size_t cap, const char *src)
Copy src into dst (cap bytes) NUL-terminated.
Definition sh_book.c:365
void sh_book_cover_fullscreen(int32_t x, int32_t y, int32_t w, int32_t h)
Draw the open book's cover full-size, aspect-fit into the box.
Definition sh_book.c:341
static epub_book_t s_epub
The open EPUB (in_use when active).
Definition sh_book.c:53
static void sh_src_string(const book_src_t *src, uint32_t off, char *out, size_t cap)
Copy the string-pool entry at pool offset off out of the paged source into out (cap bytes,...
Definition sh_book.c:379
static bool sh_epub_label(uint32_t chapter, char *out, size_t cap)
Reverse-lookup the EPUB TOC label that targets spine chapter.
Definition sh_book.c:496
static bool sh_epub_thumb(uint16_t idx)
Decode the open EPUB's cover into gray8 thumbnail slot idx.
Definition sh_book.c:289
bool sh_book_open(uint16_t idx)
Open shelf entry idx by its format; sets g_sh.open_fmt + chapter count, and (for SD books on first op...
Definition sh_book.c:431
void sh_book_chapter_label(uint32_t chapter, char *out, size_t cap)
Resolve a chapter's TOC label (or "Chapter N") into out.
Definition sh_book.c:536
static void sh_strip_text(char *out, size_t cap, sh_strip_t *st, uint32_t cp)
Emit one decoded code point as collapsed body text.
Definition sh_book.c:204
bool sh_book_decode_thumb(uint16_t idx)
Decode the open book's cover into thumbnail slot idx; returns ok.
Definition sh_book.c:328
static uint8_t sh_luma565(uint16_t px)
Rec.601 luma of an RGB565 pixel (0..255).
Definition sh_book.c:280
static uint32_t sh_digit(char c, uint32_t base)
Value of hex/decimal digit c in base base, or UINT32_MAX if invalid.
Definition sh_book.c:121
ra8_err_t sh_book_chapter_text(uint32_t chapter, char *out, size_t cap, size_t *out_len)
Extract one chapter's plain text from the open book (either backend).
Definition sh_book.c:480
static bool sh_decode_entity(const char *s, size_t n, size_t *consumed, uint32_t *cp)
Decode the &..; entity at s; sets cp + consumed; false if invalid.
Definition sh_book.c:164
static bool sh_xhtml_is_suppress(const char *tag, size_t n)
Is tag an element whose text content must be suppressed entirely?
Definition sh_book.c:79
static uint8_t s_cover[k_sh_cover_cap]
Definition sh_book.c:56
static void sh_emit_cp(char *out, size_t cap, size_t *pos, uint32_t cp)
Append code point cp to out as UTF-8 (the reader folds to ASCII).
Definition sh_book.c:106
sh_u8enc_t
UTF-8 encode boundaries / lead-byte prefixes.
Definition sh_book.c:91
@ k_ent_hex
Hex numeric-entity base.
Definition sh_book.c:101
@ k_ent_a10
Hex 'a'/'A' digit value.
Definition sh_book.c:102
@ k_u8_sh12
Two continuation shift.
Definition sh_book.c:99
@ k_u8_2max
Below: 2 bytes.
Definition sh_book.c:93
@ k_u8_data
Continuation payload mask.
Definition sh_book.c:97
@ k_ent_dec
Decimal numeric-entity base.
Definition sh_book.c:100
@ k_u8_cont
Continuation prefix.
Definition sh_book.c:94
@ k_u8_2lead
2-byte lead prefix.
Definition sh_book.c:95
@ k_u8_sh6
One continuation shift.
Definition sh_book.c:98
@ k_u8_1max
Code points below: 1 byte.
Definition sh_book.c:92
@ k_u8_3lead
3-byte lead prefix.
Definition sh_book.c:96
static size_t sh_strip_tag(const uint8_t *x, size_t n, size_t i, char *out, sh_strip_t *st)
Handle one <...> tag: break/suppress bookkeeping; returns index past >.
Definition sh_book.c:219
static bool sh_book_open_rabook(const sh_entry_t *e)
Bind shelf entry e's RBKC container as the demand-paged source.
Definition sh_book.c:419
static size_t sh_strip_xhtml(const uint8_t *x, size_t n, char *out, size_t cap)
Strip XHTML x[0,n) to collapsed plain text in out; returns length.
Definition sh_book.c:247
sh_book_const_t
Backend buffer + format constants.
Definition sh_book.c:36
@ k_sh_cover_cap
Compressed cover image bytes.
Definition sh_book.c:38
@ k_sh_r5_mask
5-bit channel mask.
Definition sh_book.c:47
@ k_sh_luma_r
Rec.601 red weight (/256).
Definition sh_book.c:41
@ k_sh_r5_sh
RGB565 red shift.
Definition sh_book.c:45
@ k_sh_col_white
Thumbnail letterbox fill.
Definition sh_book.c:40
@ k_sh_arena_cap
stb_image decode bump arena.
Definition sh_book.c:39
@ k_sh_xhtml_cap
One chapter's XHTML scratch.
Definition sh_book.c:37
@ k_sh_5to8
5-bit -> 8-bit shift.
Definition sh_book.c:49
@ k_sh_g6_mask
6-bit channel mask.
Definition sh_book.c:48
@ k_sh_luma_sh
Rec.601 divide shift (/256).
Definition sh_book.c:44
@ k_sh_g6_sh
RGB565 green shift.
Definition sh_book.c:46
@ k_sh_luma_g
Rec.601 green weight.
Definition sh_book.c:42
@ k_sh_6to8
6-bit -> 8-bit shift.
Definition sh_book.c:50
@ k_sh_luma_b
Rec.601 blue weight.
Definition sh_book.c:43
static void sh_strip_break(char *out, sh_strip_t *st)
Emit a paragraph break: trim trailing spaces, collapse runs of ' '.
Definition sh_book.c:192
static bool sh_rabook_label(uint32_t chapter, char *out, size_t cap)
Paged rabook TOC label: read the chapter record, then its string.
Definition sh_book.c:517
static uint8_t s_xhtml[k_sh_xhtml_cap]
Definition sh_book.c:55
static bool sh_parse_numeric(const char *s, size_t n, size_t *consumed, uint32_t *cp)
Parse a numeric entity body #[x]NNN; at s; sets cp + consumed.
Definition sh_book.c:136
static uint16_t s_thumb_rgb[k_sh_thumb_w *k_sh_thumb_h]
Definition sh_book.c:59
static bool sh_xhtml_is_block(const char *tag, size_t n)
Is tag (length n) a block element that should break the line?
Definition sh_book.c:64
@ k_sh_fmt_epub
EPUB parsed on-device by epub.
Definition sh_classify.h:39
@ k_sh_fmt_rabook
book RBKC container (demand-paged chunks).
Definition sh_classify.h:38
static bool sh_fmt_is_comic(sh_book_fmt_t fmt)
True if fmt is a comic-archive container (CBZ, CBR, or CBT).
Definition sh_classify.h:59
One spine document (a renderable chapter) plus its TOC label.
Definition book.h:280
uint32_t title_off
String-pool offset of the TOC label ("" if none).
Definition book.h:281
uint32_t chapter_count
Number of spine chapters.
Definition book.h:256
uint32_t string_off
Offset to the string pool.
Definition book.h:266
uint32_t string_size
String-pool length in bytes.
Definition book.h:267
uint32_t chapter_off
Offset to the chapter table.
Definition book.h:257
A book data source: resident (zero-copy) or paged (demand-fetched).
Definition book_paged.h:69
book_header_t hdr
Resident copy of the blob header.
Definition book_paged.h:75
ra8_vmem_t * vm
Paged page cache, or NULL in resident mode.
Definition book_paged.h:71
Opened EPUB book.
Definition epub.h:286
Dublin Core metadata bundle returned by epub_get_metadata().
Definition epub.h:370
const char * author
NUL-terminated creator; "" if absent.
Definition epub.h:372
const char * title
NUL-terminated title; "" if absent.
Definition epub.h:371
One titled entry in the table of contents.
Definition epub.h:142
char title[k_epub_meta_len]
Navigation label text ("" if none).
Definition epub.h:143
Caller-owned bump arena backing a single image decode.
One shelf book, sourced from MRAM (baked) or the SD card, in either the .rabook or ....
Definition sh_app.h:141
bool from_sd
true: read sd_name from SD; false: use blob.
Definition sh_app.h:142
const uint8_t * blob
Baked RBKC bytes (MRAM), or NULL when SD.
Definition sh_app.h:144
uint32_t blob_len
Baked length, or SD file size in bytes.
Definition sh_app.h:145
char sd_name[k_sh_name_cap]
SD file name, e.g.
Definition sh_app.h:149
sh_book_fmt_t fmt
Container format (rabook / epub).
Definition sh_app.h:143
char title[k_sh_title_cap]
Display title.
Definition sh_app.h:150
char author[k_sh_author_cap]
Display author.
Definition sh_app.h:151
Mutable state threaded through the XHTML strip.
Definition sh_book.c:185
bool at_break
Suppress a leading space (post-break).
Definition sh_book.c:187
int suppress
>0 inside head/title/style/script.
Definition sh_book.c:188
size_t pos
Bytes written to out.
Definition sh_book.c:186