ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
comic.c
Go to the documentation of this file.
1
25#include "comic.h"
26
27#include <string.h>
28
29#include "comic_internal.h"
30#include "ra8_attributes.h"
31#include "ra8_check.h"
32
34static const char* const s_tag_comic = "comic";
35
43typedef enum : uint16_t {
44 k_zip_b0 = 0x50U,
45 k_zip_b1 = 0x4BU,
46 k_zip_lfh_b2 = 0x03U,
47 k_zip_lfh_b3 = 0x04U,
48 k_zip_eocd_b2 = 0x05U,
49 k_zip_eocd_b3 = 0x06U,
52
58typedef enum : uint8_t {
64
79RA8_INTERNAL static uint8_t internal_lower(uint8_t c)
80{
81 const uint8_t up =
82 (uint8_t)(((uint8_t)(c - (uint8_t)k_ascii_upper_a) < (uint8_t)k_ascii_alpha_n) ? 1U : 0U);
83 return (uint8_t)(c | (uint8_t)(up << (uint8_t)k_ascii_case_sh));
84}
85
103RA8_INTERNAL static bool internal_ends_with_ci(const char* name, uint16_t len, const char* ext)
104{
105 size_t elen = 0U;
106 for (uint8_t i = 0U; i < (uint8_t)k_comic_ext_max; ++i) { /* bound: fixed max ext */
107 if (ext[i] == '\0') {
108 break;
109 }
110 elen = (size_t)i + 1U;
111 }
112 if ((size_t)len < elen) {
113 return false;
114 }
115 const char* tail = &name[(size_t)len - elen];
116 for (size_t i = 0U; i < elen; ++i) { /* bound: elen <= k_comic_ext_max */
117 if (internal_lower((uint8_t)tail[i]) != internal_lower((uint8_t)ext[i])) {
118 return false;
119 }
120 }
121 return true;
122}
123
140RA8_INTERNAL static bool internal_is_image_ext(const char* name, uint16_t len)
141{
142 static const char* const k_exts[] = {".jpg", ".jpeg", ".png", ".gif", ".bmp"};
143 for (uint8_t i = 0U; i < (uint8_t)(sizeof(k_exts) / sizeof(k_exts[0]));
144 ++i) { /* bound: fixed table */
145 if (internal_ends_with_ci(name, len, k_exts[i])) {
146 return true;
147 }
148 }
149 return false;
150}
151
152RA8_PRIV bool priv_comic_is_page_name(const char* name, uint16_t len)
153{
154 if (name == nullptr) {
155 return false;
156 }
157 if (len == 0U) {
158 return false;
159 }
160 size_t base = 0U;
161 for (size_t i = 0U; i < (size_t)len; ++i) { /* bound: len */
162 if (name[i] == '/') {
163 base = i + 1U;
164 }
165 if (name[i] == '\\') {
166 base = i + 1U;
167 }
168 }
169 if (base < (size_t)len) {
170 if (name[base] == '.') {
171 return false; /* hidden entry or macOS AppleDouble resource fork */
172 }
173 }
174 return internal_is_image_ext(name, len);
175}
176
178 const char* name,
179 uint16_t name_len,
180 uint64_t raw_size,
181 uint8_t extractable,
182 uint8_t method,
183 uint32_t zip_index,
184 uint64_t data_off,
185 uint64_t pack_size)
186{
187 RA8_CHECK_NULL_PTR(c, s_tag_comic, "add: null c");
188 RA8_CHECK_NULL_PTR(name, s_tag_comic, "add: null name");
189 if (c->page_count >= c->page_cap) {
191 }
192 if (((uint64_t)c->names_len + (uint64_t)name_len) > (uint64_t)c->names_cap) {
194 }
195 comic_page_t* p = &c->pages[c->page_count];
196 *p = (comic_page_t){};
197 p->name_off = c->names_len;
198 p->name_len = name_len;
199 p->extractable = extractable;
200 p->rar_method = method;
201 p->zip_index = zip_index;
202 p->data_off = data_off;
203 p->pack_size = pack_size;
204 p->raw_size = raw_size;
205 if (name_len > 0U) {
206 (void)memcpy(&c->names[c->names_len], name, (size_t)name_len);
207 }
208 c->names_len += (uint32_t)name_len;
209 c->page_count += 1U;
210 return k_ra8_ok;
211}
212
230RA8_INTERNAL static int32_t
231internal_name_cmp(const char* a, uint16_t alen, const char* b, uint16_t blen)
232{
233 const uint16_t m = (alen < blen) ? alen : blen;
234 int c = 0;
235 for (uint16_t i = 0U; (i < m) && (c == 0); ++i) {
236 c = (int)(uint8_t)a[i] - (int)(uint8_t)b[i];
237 }
238 if (c != 0) {
239 return (c < 0) ? -1 : 1;
240 }
241 if (alen < blen) {
242 return -1;
243 }
244 if (alen > blen) {
245 return 1;
246 }
247 return 0;
248}
249
264{
265 for (uint32_t i = 1U; i < c->page_count; ++i) { /* bound: page_count <= page_cap */
266 const comic_page_t key = c->pages[i];
267 const char* const kn = &c->names[key.name_off];
268 uint32_t j = i;
269 while (j > 0U) { /* bound: j strictly decreases toward 0 */
270 const comic_page_t prev = c->pages[j - 1U];
271 if (internal_name_cmp(&c->names[prev.name_off], prev.name_len, kn, key.name_len) <= 0) {
272 break;
273 }
274 c->pages[j] = prev;
275 j -= 1U;
276 }
277 c->pages[j] = key;
278 }
279}
280
296RA8_INTERNAL static bool internal_is_zip(const uint8_t* s)
297{
298 if (s[0] != (uint8_t)k_zip_b0) {
299 return false;
300 }
301 if (s[1] != (uint8_t)k_zip_b1) {
302 return false;
303 }
304 if (s[2] == (uint8_t)k_zip_lfh_b2) {
305 return s[3] == (uint8_t)k_zip_lfh_b3;
306 }
307 if (s[2] == (uint8_t)k_zip_eocd_b2) {
308 return s[3] == (uint8_t)k_zip_eocd_b3;
309 }
310 return false;
311}
312
332 comic_read_fn read,
333 const comic_page_t* pages,
334 const char* names)
335{
336 RA8_CHECK_NULL_PTR(c, s_tag_comic, "open: null c");
337 RA8_CHECK_NULL_PTR(read, s_tag_comic, "open: null read");
338 RA8_CHECK_NULL_PTR(pages, s_tag_comic, "open: null pages");
339 RA8_CHECK_NULL_PTR(names, s_tag_comic, "open: null names");
340 return k_ra8_ok;
341}
342
362{
363 uint8_t sig[k_comic_magic_len] = {};
364 const size_t got = c->read(c->ctx, 0U, sig, sizeof(sig));
365 if (got < (size_t)k_comic_sig_min) {
367 }
368 if (internal_is_zip(sig)) {
370 c->stream.read = c->read;
371 c->stream.ctx = c->ctx;
372 c->stream.size = c->size;
373 return priv_comic_cbz_open(c);
374 }
375 const ra8_err_t rerr = ra8_rar_open(&c->rar, c->read, c->ctx, c->size);
376 if (rerr == k_ra8_ok) {
378 return priv_comic_cbr_open(c);
379 }
380 /* tar has no leading magic bytes; the walker's open probes block 0
381 * (ustar magic + checksum), so a non-tar file is rejected cheaply. */
382 const ra8_err_t terr = unarch_tar_open(&c->tar, c->read, c->ctx, c->size, nullptr);
383 if (terr == k_ra8_ok) {
385 return priv_comic_cbt_open(c);
386 }
388}
389
391 comic_read_fn read,
392 void* ctx,
393 uint64_t size,
394 comic_page_t* pages,
395 uint32_t page_cap,
396 char* names,
397 uint32_t names_cap)
398{
399 const ra8_err_t nerr = internal_open_reject_null(c, read, pages, names);
400 if (nerr != k_ra8_ok) {
401 return nerr;
402 }
403 *c = (comic_t){};
404 if (size == 0U) {
406 }
407 if (page_cap == 0U) {
409 }
410 if (names_cap == 0U) {
412 }
413 c->read = read;
414 c->ctx = ctx;
415 c->size = size;
416 c->pages = pages;
417 c->page_cap = page_cap;
418 c->names = names;
419 c->names_cap = names_cap;
420
421 const ra8_err_t err = internal_open_detect(c);
422 if (err != k_ra8_ok) {
423 (void)comic_close(c);
424 return err;
425 }
426 if (c->page_count == 0U) {
427 (void)comic_close(c);
428 return k_ra8_err_not_found;
429 }
430 internal_sort(c);
431 return k_ra8_ok;
432}
433
435 uint32_t page,
436 char* name_buf,
437 uint16_t name_cap,
438 uint16_t* out_name_len,
439 uint64_t* out_raw_size,
440 uint8_t* out_extractable)
441{
442 RA8_CHECK_NULL_PTR(c, s_tag_comic, "info: null c");
443 if (c->kind == k_comic_kind_none) {
445 }
446 if (page >= c->page_count) {
448 }
449 const comic_page_t* p = &c->pages[page];
450 if (name_buf != nullptr) {
451 const uint16_t want = (p->name_len > name_cap) ? name_cap : p->name_len;
452 if (want > 0U) {
453 (void)memcpy(name_buf, &c->names[p->name_off], (size_t)want);
454 }
455 if (out_name_len != nullptr) {
456 *out_name_len = want;
457 }
458 } else if (out_name_len != nullptr) {
459 *out_name_len = p->name_len;
460 } else {
461 /* The caller did not request a page name or its length. */
462 }
463 if (out_raw_size != nullptr) {
464 *out_raw_size = p->raw_size;
465 }
466 if (out_extractable != nullptr) {
467 *out_extractable = p->extractable;
468 }
469 return k_ra8_ok;
470}
471
472ra8_err_t comic_page_read(comic_t* c, uint32_t page, uint8_t* buf, size_t cap, size_t* got)
473{
474 RA8_CHECK_NULL_PTR(c, s_tag_comic, "read: null c");
475 RA8_CHECK_NULL_PTR(buf, s_tag_comic, "read: null buf");
476 RA8_CHECK_NULL_PTR(got, s_tag_comic, "read: null got");
477 *got = 0U;
478 if (c->kind == k_comic_kind_none) {
480 }
481 if (page >= c->page_count) {
483 }
484 const comic_page_t* p = &c->pages[page];
485 if (c->kind == k_comic_kind_cbz) {
486 return priv_comic_cbz_extract(c, p, buf, cap, got);
487 }
488 if (c->kind == k_comic_kind_cbt) {
489 return priv_comic_cbt_extract(c, p, buf, cap, got);
490 }
491 return priv_comic_cbr_extract(c, p, buf, cap, got);
492}
493
495{
496 RA8_CHECK_NULL_PTR(c, s_tag_comic, "close: null c");
497 if (c->kind == k_comic_kind_cbz) {
498 (void)priv_comic_cbz_close(c);
499 }
501 c->page_count = 0U;
502 c->zip_active = 0U;
503 c->tar.live = false; /* the tar walker holds no resources to release */
504 return k_ra8_ok;
505}
ra8_err_t comic_page_read(comic_t *c, uint32_t page, uint8_t *buf, size_t cap, size_t *got)
Extract one page's encoded image bytes for the decoder.
Definition comic.c:472
static const char *const s_tag_comic
Log tag for comic-facade diagnostics.
Definition comic.c:34
ra8_err_t comic_close(comic_t *c)
Release an open comic's backend resources.
Definition comic.c:494
comic_magic_t
ZIP magic bytes and the minimum magic-read length.
Definition comic.c:43
@ k_comic_sig_min
Bytes required to test the ZIP magic.
Definition comic.c:50
@ k_zip_b1
'K'.
Definition comic.c:45
@ k_zip_eocd_b2
Empty-archive EOCD 3rd byte.
Definition comic.c:48
@ k_zip_b0
'P'.
Definition comic.c:44
@ k_zip_eocd_b3
Empty-archive EOCD 4th byte.
Definition comic.c:49
@ k_zip_lfh_b2
Local-file-header 3rd byte.
Definition comic.c:46
@ k_zip_lfh_b3
Local-file-header 4th byte.
Definition comic.c:47
static ra8_err_t internal_open_reject_null(const comic_t *c, comic_read_fn read, const comic_page_t *pages, const char *names)
Reject any NULL required argument to comic_open.
Definition comic.c:331
bool priv_comic_is_page_name(const char *name, uint16_t len)
Whether an archive entry name is a decodable comic page image.
Definition comic.c:152
ra8_err_t comic_open(comic_t *c, comic_read_fn read, void *ctx, uint64_t size, comic_page_t *pages, uint32_t page_cap, char *names, uint32_t names_cap)
Open a comic archive (CBZ or CBR) and build its sorted page index.
Definition comic.c:390
static int32_t internal_name_cmp(const char *a, uint16_t alen, const char *b, uint16_t blen)
Lexicographic compare of two entry names (byte order, length tiebreak).
Definition comic.c:231
static void internal_sort(comic_t *c)
Insertion-sort the page index by entry name (reading order).
Definition comic.c:263
ra8_err_t priv_comic_page_add(comic_t *c, const char *name, uint16_t name_len, uint64_t raw_size, uint8_t extractable, uint8_t method, uint32_t zip_index, uint64_t data_off, uint64_t pack_size)
Append one image member to the resident page index.
Definition comic.c:177
ra8_err_t comic_page_info(const comic_t *c, uint32_t page, char *name_buf, uint16_t name_cap, uint16_t *out_name_len, uint64_t *out_raw_size, uint8_t *out_extractable)
Read one page's manifest entry: name, encoded length, decodability.
Definition comic.c:434
static bool internal_ends_with_ci(const char *name, uint16_t len, const char *ext)
Case-insensitive test that name ends with ext.
Definition comic.c:103
comic_ascii_t
Constants for branchless ASCII case folding.
Definition comic.c:58
@ k_ascii_alpha_n
Letters in the alphabet.
Definition comic.c:60
@ k_ascii_upper_a
'A'.
Definition comic.c:59
@ k_comic_ext_max
Longest extension the filter compares.
Definition comic.c:62
@ k_ascii_case_sh
0x20 == 1 << 5 (upper->lower bit).
Definition comic.c:61
static bool internal_is_image_ext(const char *name, uint16_t len)
Whether name ends in a decodable image extension.
Definition comic.c:140
static uint8_t internal_lower(uint8_t c)
ASCII-lowercase one byte, branchlessly (letters only).
Definition comic.c:79
static bool internal_is_zip(const uint8_t *s)
Whether the leading bytes are a ZIP local-file-header or empty EOCD.
Definition comic.c:296
static ra8_err_t internal_open_detect(comic_t *c)
Detect the container and run its backend enumeration.
Definition comic.c:361
Unified demand-paged reader for comic-book archives – CBZ (ZIP) and CBR (RAR).
size_t(* comic_read_fn)(void *ctx, uint64_t offset, void *buf, size_t len)
Seek+read backing over the comic-archive bytes.
Definition comic.h:95
@ k_comic_kind_cbz
ZIP of images (.cbz).
Definition comic.h:106
@ k_comic_kind_cbt
tar of images (.cbt).
Definition comic.h:108
@ k_comic_kind_cbr
RAR of images (.cbr).
Definition comic.h:107
@ k_comic_kind_none
Not opened / unrecognised.
Definition comic.h:105
@ k_comic_magic_len
Bytes read to detect the container magic.
Definition comic.h:117
ra8_err_t priv_comic_cbr_extract(comic_t *c, const comic_page_t *p, uint8_t *buf, size_t cap, size_t *got)
Extract one CBR page's encoded image (STORE copy or RAR5 decode).
Definition comic_cbr.c:150
ra8_err_t priv_comic_cbr_open(comic_t *c)
Open the CBR (RAR) backend: walk the archive + build the page index.
Definition comic_cbr.c:111
ra8_err_t priv_comic_cbt_open(comic_t *c)
Open the CBT (tar) backend: walk the archive + build the page index.
Definition comic_cbt.c:87
ra8_err_t priv_comic_cbt_extract(comic_t *c, const comic_page_t *p, uint8_t *buf, size_t cap, size_t *got)
Extract one CBT page's encoded image (verbatim tar member copy).
Definition comic_cbt.c:116
ra8_err_t priv_comic_cbz_extract(comic_t *c, const comic_page_t *p, uint8_t *buf, size_t cap, size_t *got)
Extract one CBZ page's encoded image through the streaming ZIP reader.
Definition comic_cbz.c:243
ra8_err_t priv_comic_cbz_close(comic_t *c)
Tear down the CBZ backend's miniz reader.
Definition comic_cbz.c:264
ra8_err_t priv_comic_cbz_open(comic_t *c)
Open the CBZ (ZIP) backend: init the streaming miniz reader + page index.
Definition comic_cbz.c:204
Cross-TU seams between the comic facade and its CBZ / CBR backends.
Annotation-attribute framework macros for ra8-firmware.
#define RA8_PRIV
Module-private helper: shared across TUs but only inside one library.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Validation and Error-Checking Macros for ra8-firmware.
#define RA8_CHECK_NULL_PTR(ptr, tag, message)
Reject nullptr pointer, returning k_ra8_err_null_ptr.
Definition ra8_check.h:243
@ k_ra8_err_not_supported
Requested feature not compiled in, not wired, or not supported by this MCU variant.
Definition ra8_err.h:180
@ k_ra8_err_out_of_range
Sensor or peripheral output out of valid range.
Definition ra8_err.h:337
@ k_ra8_err_invalid_state
Module in wrong state for requested operation.
Definition ra8_err.h:161
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ k_ra8_err_not_found
Requested item not found (lookup / search missed).
Definition ra8_err.h:173
@ k_ra8_err_invalid_size
Invalid size parameter (too large, too small, or misaligned).
Definition ra8_err.h:167
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
void * memcpy(void *dst, const void *src, size_t n)
Copy memory area between non-overlapping regions.
ra8_err_t ra8_rar_open(ra8_rar_t *rar, ra8_rar_read_fn read, void *ctx, uint64_t size)
Detect a RAR archive's generation and locate its first block.
Definition ra8_rar.c:659
One entry in the resident, sorted page index.
Definition comic.h:140
uint64_t raw_size
Encoded image length in bytes (decoder input).
Definition comic.h:148
uint64_t data_off
CBR: absolute offset of the member data area.
Definition comic.h:146
uint64_t pack_size
CBR: packed member size in bytes.
Definition comic.h:147
uint8_t extractable
1 if this reader can decode the page's bytes.
Definition comic.h:143
uint32_t zip_index
CBZ: miniz central-directory index.
Definition comic.h:145
uint16_t name_len
Page name length in bytes.
Definition comic.h:142
uint32_t name_off
Offset of the page name in the name arena.
Definition comic.h:141
uint8_t rar_method
CBR: ra8_rar_method_t (0 = store); 0 for CBZ.
Definition comic.h:144
uint64_t size
Archive length in bytes.
Definition comic.h:162
void * ctx
Backing context.
Definition comic.h:161
comic_read_fn read
Backing reader (mirrors comic_t::read).
Definition comic.h:160
One open comic archive: backing, kind, page index, and backend state.
Definition comic.h:179
unarch_tar_t tar
CBT tar walker state.
Definition comic.h:192
uint32_t page_count
Populated page count.
Definition comic.h:185
uint8_t zip_active
1 = miniz reader is initialised (CBZ).
Definition comic.h:194
uint32_t names_len
Bytes used in names.
Definition comic.h:188
comic_read_fn read
Byte reader over the archive.
Definition comic.h:180
void * ctx
Context for read.
Definition comic.h:181
comic_kind_t kind
Detected container kind.
Definition comic.h:193
comic_stream_t stream
CBZ miniz I/O descriptor (stable addr).
Definition comic.h:189
char * names
Caller name arena.
Definition comic.h:186
ra8_rar_t rar
CBR walker state.
Definition comic.h:190
uint32_t page_cap
Capacity of pages in entries.
Definition comic.h:184
uint64_t size
Archive length in bytes.
Definition comic.h:182
comic_page_t * pages
Caller page-index array.
Definition comic.h:183
uint32_t names_cap
Capacity of names in bytes.
Definition comic.h:187
bool live
Open succeeded and not superseded.
Definition unarch_tar.h:102
ra8_err_t unarch_tar_open(unarch_tar_t *t, unarch_read_fn read, void *ctx, uint64_t size, const ra8_decomp_limits_t *limits)
Bind a walker to an archive and validate its first header block.
Definition unarch_tar.c:74