ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
comic.c File Reference

Facade for the comic-archive reader: magic detect, page index, dispatch. More...

#include "comic.h"
#include <string.h>
#include "comic_internal.h"
#include "ra8_attributes.h"
#include "ra8_check.h"
Include dependency graph for comic.c:

Go to the source code of this file.

Enumerations

enum  comic_magic_t : uint16_t {
  k_zip_b0 = 0x50U ,
  k_zip_b1 = 0x4BU ,
  k_zip_lfh_b2 = 0x03U ,
  k_zip_lfh_b3 = 0x04U ,
  k_zip_eocd_b2 = 0x05U ,
  k_zip_eocd_b3 = 0x06U ,
  k_comic_sig_min = 4U
}
 ZIP magic bytes and the minimum magic-read length. More...
enum  comic_ascii_t : uint8_t {
  k_ascii_upper_a = 0x41U ,
  k_ascii_alpha_n = 26U ,
  k_ascii_case_sh = 5U ,
  k_comic_ext_max = 8U
}
 Constants for branchless ASCII case folding. More...

Functions

static uint8_t internal_lower (uint8_t c)
 ASCII-lowercase one byte, branchlessly (letters only).
static bool internal_ends_with_ci (const char *name, uint16_t len, const char *ext)
 Case-insensitive test that name ends with ext.
static bool internal_is_image_ext (const char *name, uint16_t len)
 Whether name ends in a decodable image extension.
bool priv_comic_is_page_name (const char *name, uint16_t len)
 Whether an archive entry name is a decodable comic page image.
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.
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).
static void internal_sort (comic_t *c)
 Insertion-sort the page index by entry name (reading order).
static bool internal_is_zip (const uint8_t *s)
 Whether the leading bytes are a ZIP local-file-header or empty EOCD.
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.
static ra8_err_t internal_open_detect (comic_t *c)
 Detect the container and run its backend enumeration.
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.
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.
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.
ra8_err_t comic_close (comic_t *c)
 Release an open comic's backend resources.

Variables

static const char *const s_tag_comic = "comic"
 Log tag for comic-facade diagnostics.

Detailed Description

Facade for the comic-archive reader: magic detect, page index, dispatch.

Tag
[Ring 4 / Domain] {World: NS}

The container-agnostic half of comic.h. comic_open reads the leading magic, routes a ZIP to the CBZ backend, a RAR to the CBR backend, or a tar (probed by header block – tar has no leading magic) to the CBT backend, and lets each backend append its image members to the shared page index through priv_comic_page_add; the index is then sorted by entry name so reading order is the sorted names. comic_page_read / comic_page_info / comic_close dispatch on the detected kind.

Every decision here is a single condition on purpose, so the facade carries no MC/DC obligation of its own.

Since
Version 0.1.0

Definition in file comic.c.

Enumeration Type Documentation

◆ comic_ascii_t

enum comic_ascii_t : uint8_t

Constants for branchless ASCII case folding.

Since
Version 0.1.0
Enumerator
k_ascii_upper_a 

'A'.

k_ascii_alpha_n 

Letters in the alphabet.

k_ascii_case_sh 

0x20 == 1 << 5 (upper->lower bit).

k_comic_ext_max 

Longest extension the filter compares.

Definition at line 58 of file comic.c.

◆ comic_magic_t

enum comic_magic_t : uint16_t

ZIP magic bytes and the minimum magic-read length.

The ZIP local-file-header and empty-archive (EOCD) signatures; a RAR is delegated to ra8_rar_open, which owns its own signature match.

Since
Version 0.1.0
Enumerator
k_zip_b0 

'P'.

k_zip_b1 

'K'.

k_zip_lfh_b2 

Local-file-header 3rd byte.

k_zip_lfh_b3 

Local-file-header 4th byte.

k_zip_eocd_b2 

Empty-archive EOCD 3rd byte.

k_zip_eocd_b3 

Empty-archive EOCD 4th byte.

k_comic_sig_min 

Bytes required to test the ZIP magic.

Definition at line 43 of file comic.c.

Function Documentation

◆ comic_close()

ra8_err_t comic_close ( comic_t * c)
nodiscard

Release an open comic's backend resources.

Ends the CBZ miniz reader (freeing its central directory through the per-object bounded arena); the CBR backend allocates nothing. Resets c to the unopened state. Idempotent after a failed open.

Parameters
[in,out]cComic from comic_open (non-NULL).
Returns
ra8_err_t Error code.
Return values
k_ra8_okResources released; c reset.
k_ra8_err_null_ptrc was NULL.
Precondition
c was populated by comic_open or zero-initialised.
c out-lives the call.
Postcondition
c->kind == k_comic_kind_none and c->zip_active == 0.
No further comic_page_read may be issued until re-opened.
Note
Not thread-safe.
See also
comic_open()
Since
Version 0.1.0

Definition at line 494 of file comic.c.

References k_comic_kind_cbz, k_comic_kind_none, k_ra8_ok, comic_t::kind, unarch_tar_t::live, comic_t::page_count, priv_comic_cbz_close(), RA8_CHECK_NULL_PTR, s_tag_comic, comic_t::tar, and comic_t::zip_active.

Referenced by cm_comic_tiled_selfcheck(), comic_open(), ra8_viewer_close(), ra8_viewer_open(), and sh_comic_close().

◆ comic_open()

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 )
nodiscard

Open a comic archive (CBZ or CBR) and build its sorted page index.

Reads the leading magic through read, detects a ZIP (.cbz) or a RAR (.cbr) container, enumerates its image members into pages (filtered to decodable image extensions, names copied into names), and sorts the index by entry name so page 0 is the first page. On success at least one page is present.

Parameters
[out]cReader to populate (caller-owned).
[in]readByte reader over the archive (non-NULL).
[in]ctxContext passed to read.
[in]sizeArchive length in bytes (> 0).
[in]pagesCaller page-index array (non-NULL).
[in]page_capCapacity of pages in entries (> 0).
[in]namesCaller name arena (non-NULL).
[in]names_capCapacity of names in bytes (> 0).
Returns
ra8_err_t Error code.
Return values
k_ra8_okComic opened; c bound with >= 1 page.
k_ra8_err_null_ptrA required pointer argument was NULL.
k_ra8_err_invalid_sizesize / a capacity is 0, a short magic read, or the page index / name arena was too small.
k_ra8_err_not_supportedThe bytes are neither a ZIP nor a RAR archive.
k_ra8_err_not_foundA valid archive with no decodable image pages.
k_ra8_err_*A backend (miniz / RAR) or reader error.
Precondition
read serves offsets [0, size) of the archive.
pages / names out-live c and every read from it.
Postcondition
On k_ra8_ok, comic_page_count(c) >= 1 and pages are name-sorted.
On any error c is left with kind == k_comic_kind_none.
Note
Not thread-safe. Call comic_close to release a CBZ's miniz reader.
See also
comic_page_read()
comic_close()
Since
Version 0.1.0

Definition at line 390 of file comic.c.

References comic_close(), comic_t::ctx, internal_open_detect(), internal_open_reject_null(), internal_sort(), k_ra8_err_invalid_size, k_ra8_err_not_found, k_ra8_ok, comic_t::names, comic_t::names_cap, comic_t::page_cap, comic_t::page_count, comic_t::pages, comic_t::read, and comic_t::size.

Referenced by cm_ct_open(), cm_open_comic(), comic_open_wrapped(), internal_open_unwrapped(), priv_viewer_open_comic(), and sh_comic_bind().

◆ comic_page_info()

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 )
nodiscard

Read one page's manifest entry: name, encoded length, decodability.

Pure index query – no archive I/O. Copies the page's entry name into name_buf (clamped to name_cap) and reports its encoded byte length and whether this reader can decode it (0 for a compressed CBR member).

Parameters
[in]cComic bound by comic_open (non-NULL).
[in]pagePage index (< comic_page_count(c)).
[out]name_bufBuffer for the entry name (may be NULL if name_cap 0).
[in]name_capCapacity of name_buf in bytes.
[out]out_name_lenReceives the copied name length (may be NULL).
[out]out_raw_sizeReceives the encoded image length (may be NULL).
[out]out_extractableReceives 1 if the page is decodable (may be NULL).
Returns
ra8_err_t Error code.
Return values
k_ra8_okOutputs populated from the index.
k_ra8_err_null_ptrc was NULL.
k_ra8_err_invalid_statec was never opened.
k_ra8_err_out_of_rangepage is at or past the page count.
Precondition
c was populated by comic_open.
name_buf holds name_cap writable bytes when name_cap > 0.
Postcondition
On k_ra8_ok every non-NULL output is populated from page page.
On any error no output is modified.
Note
Thread-safe: pure read of an immutable index.
See also
comic_page_read()
Since
Version 0.1.0

Definition at line 434 of file comic.c.

References comic_page_t::extractable, k_comic_kind_none, k_ra8_err_invalid_state, k_ra8_err_out_of_range, k_ra8_ok, comic_t::kind, memcpy(), comic_page_t::name_len, comic_page_t::name_off, comic_t::names, comic_t::page_count, comic_t::pages, RA8_CHECK_NULL_PTR, comic_page_t::raw_size, and s_tag_comic.

Referenced by internal_read_page().

◆ comic_page_read()

ra8_err_t comic_page_read ( comic_t * c,
uint32_t page,
uint8_t * buf,
size_t cap,
size_t * got )
nodiscard

Extract one page's encoded image bytes for the decoder.

Streams the page's encoded image (JPEG / PNG / GIF / BMP) into buf: for a CBZ, miniz inflates the ZIP entry (STORE or DEFLATE) through the streaming reader; for a CBR, a STORE member is copied and a RAR5-compressed member is inflated by the clean-room decompressor, both via the RAR walker. The result is ready to hand to ra8_img_decode_blit. One call is one page's worth of I/O – the demand-paged model.

Parameters
[in]cComic bound by comic_open (non-NULL).
[in]pagePage index (< comic_page_count(c)).
[out]bufDestination for the encoded image (non-NULL).
[in]capCapacity of buf in bytes; must be >= the page raw_size.
[out]gotReceives the bytes written (non-NULL).
Returns
ra8_err_t Error code.
Return values
k_ra8_okPage bytes written; *got == raw_size.
k_ra8_err_null_ptrA required pointer argument was NULL.
k_ra8_err_invalid_statec was never opened.
k_ra8_err_out_of_rangepage is at or past the page count.
k_ra8_err_not_supportedA CBR page packed with a RAR compressor.
k_ra8_err_no_memcap is smaller than the page raw_size.
k_ra8_err_*A backend extract / reader error.
Precondition
c was populated by comic_open.
buf holds at least cap writable bytes.
Postcondition
On k_ra8_ok, buf[0..*got) holds the page's encoded image.
On any error buf contents are unspecified and *got == 0.
Note
Not thread-safe: reuses the backend's decode state across calls.
See also
comic_page_info()
Since
Version 0.1.0

Definition at line 472 of file comic.c.

References k_comic_kind_cbt, k_comic_kind_cbz, k_comic_kind_none, k_ra8_err_invalid_state, k_ra8_err_out_of_range, comic_t::kind, comic_t::page_count, comic_t::pages, priv_comic_cbr_extract(), priv_comic_cbt_extract(), priv_comic_cbz_extract(), RA8_CHECK_NULL_PTR, and s_tag_comic.

Referenced by cm_ct_open(), cm_draw_page(), internal_read_page(), and sh_comic_blit_page().

◆ internal_ends_with_ci()

bool internal_ends_with_ci ( const char * name,
uint16_t len,
const char * ext )
static

Case-insensitive test that name ends with ext.

Compares the tail of name against ext byte-for-byte through internal_lower. A name shorter than ext cannot match.

Parameters
[in]nameEntry name bytes.
[in]lenLength of name.
[in]extNUL-terminated extension (e.g. ".png"), <= k_comic_ext_max.
Returns
Whether name ends with ext, case-insensitively.
Return values
trueThe suffix matches.
falseThe suffix differs or name is too short.
Precondition
name holds len readable bytes; ext is NUL-terminated.
ext is at most k_comic_ext_max bytes.
Postcondition
No state is modified (pure read).
The result depends only on the arguments.
Note
Thread-safe: pure read.
Since
Version 0.1.0

Definition at line 103 of file comic.c.

References internal_lower(), k_comic_ext_max, and RA8_INTERNAL.

Referenced by internal_is_image_ext().

◆ internal_is_image_ext()

bool internal_is_image_ext ( const char * name,
uint16_t len )
static

Whether name ends in a decodable image extension.

The pure suffix test over the stb_image set; the hidden/base-name policy is applied separately by priv_comic_is_page_name.

Parameters
[in]nameEntry name bytes.
[in]lenLength of name.
Returns
Whether name has a supported image extension.
Return values
trueA decodable image extension.
falseNot a decodable image.
Precondition
name holds len readable bytes.
len > 0.
Postcondition
No state is modified (pure read).
The result depends only on the arguments.
Note
Thread-safe: pure read.
Since
Version 0.1.0

Definition at line 140 of file comic.c.

References internal_ends_with_ci(), and RA8_INTERNAL.

Referenced by priv_comic_is_page_name().

◆ internal_is_zip()

bool internal_is_zip ( const uint8_t * s)
static

Whether the leading bytes are a ZIP local-file-header or empty EOCD.

Matches "PK" then the local-file-header (0x03 0x04) or empty-archive EOCD (0x05 0x06) pair; a RAR is detected separately via ra8_rar_open.

Parameters
[in]sAt least k_comic_sig_min readable bytes.
Returns
Whether s begins a ZIP archive.
Return values
trueA ZIP signature.
falseNot a ZIP signature.
Precondition
s addresses at least k_comic_sig_min bytes.
s is the archive start.
Postcondition
No state is modified (pure read).
The result depends only on the four leading bytes.
Note
Thread-safe: pure read.
Since
Version 0.1.0

Definition at line 296 of file comic.c.

References k_zip_b0, k_zip_b1, k_zip_eocd_b2, k_zip_eocd_b3, k_zip_lfh_b2, k_zip_lfh_b3, and RA8_INTERNAL.

Referenced by internal_open_detect().

◆ internal_lower()

uint8_t internal_lower ( uint8_t c)
static

ASCII-lowercase one byte, branchlessly (letters only).

Sets the case bit only for 'A'..'Z'; the unsigned-wrap range test is a single condition, so no other byte is altered.

Parameters
[in]cByte to fold.
Returns
c with the case bit set if it was an uppercase letter.
Return values
cFor any non-letter byte (unchanged).
Precondition
None.
None.
Postcondition
No state is modified (pure function).
Only 'A'..'Z' are altered.
Note
Thread-safe: pure.
Since
Version 0.1.0

Definition at line 79 of file comic.c.

References k_ascii_alpha_n, k_ascii_case_sh, k_ascii_upper_a, and RA8_INTERNAL.

Referenced by internal_ends_with_ci().

◆ internal_name_cmp()

int32_t internal_name_cmp ( const char * a,
uint16_t alen,
const char * b,
uint16_t blen )
static

Lexicographic compare of two entry names (byte order, length tiebreak).

Unsigned byte compare over the shorter length, then the shorter name sorts first – the total order the page index is sorted by.

Parameters
[in]aFirst name bytes.
[in]alenFirst name length.
[in]bSecond name bytes.
[in]blenSecond name length.
Returns
Negative, zero, or positive as a sorts before, equal to, or after b.
Return values
0The names are byte-identical and equal length.
Precondition
a / b hold alen / blen readable bytes.
Both length arguments are the true name lengths.
Postcondition
No state is modified (pure read).
The order is a total order over (bytes, length).
Note
Thread-safe: pure read.
Since
Version 0.1.0

Definition at line 231 of file comic.c.

Referenced by internal_sort().

◆ internal_open_detect()

ra8_err_t internal_open_detect ( comic_t * c)
static

Detect the container and run its backend enumeration.

Reads the magic, dispatches to the CBZ or CBR backend, and leaves the page index populated (unsorted). Split from comic_open so the entry point stays within the function-size budget.

Parameters
[in,out]cComic with backing + buffers already bound.
Returns
ra8_err_t status.
Return values
k_ra8_okA backend populated the index.
k_ra8_err_invalid_sizeA short magic read.
k_ra8_err_not_supportedNeither a ZIP nor a RAR archive.
k_ra8_err_*A backend error.
Precondition
c->read / c->size / buffers are bound.
c was zeroed then populated by the caller.
Postcondition
On k_ra8_ok, c->kind is set and the index is filled (unsorted).
On any error c->kind reflects the detection outcome.
Note
Not thread-safe.
Since
Version 0.1.0

Definition at line 361 of file comic.c.

References comic_stream_t::ctx, comic_t::ctx, internal_is_zip(), k_comic_kind_cbr, k_comic_kind_cbt, k_comic_kind_cbz, k_comic_magic_len, k_comic_sig_min, k_ra8_err_invalid_size, k_ra8_err_not_supported, k_ra8_ok, comic_t::kind, priv_comic_cbr_open(), priv_comic_cbt_open(), priv_comic_cbz_open(), RA8_INTERNAL, ra8_rar_open(), comic_t::rar, comic_stream_t::read, comic_t::read, comic_stream_t::size, comic_t::size, comic_t::stream, comic_t::tar, and unarch_tar_open().

Referenced by comic_open().

◆ internal_open_reject_null()

ra8_err_t internal_open_reject_null ( const comic_t * c,
comic_read_fn read,
const comic_page_t * pages,
const char * names )
static

Reject any NULL required argument to comic_open.

Runs the mandatory null guards (reader, page index, name arena) so the entry point stays within the function-size budget; scalars may be zero.

Parameters
[in]cReader-to-populate pointer.
[in]readArchive byte reader.
[in]pagesCaller page-index array.
[in]namesCaller name arena.
Returns
ra8_err_t status.
Return values
k_ra8_okEvery required argument is non-NULL.
k_ra8_err_null_ptrSome required argument was NULL.
Precondition
The caller forwards comic_open's pointer arguments unchanged.
No argument is dereferenced before this returns k_ra8_ok.
Postcondition
On k_ra8_ok each checked pointer is safe to dereference.
On any error the reason is logged against s_tag_comic.
Note
Thread-safe: reads only its pointer arguments.
Since
Version 0.1.0

Definition at line 331 of file comic.c.

References k_ra8_ok, RA8_CHECK_NULL_PTR, RA8_INTERNAL, and s_tag_comic.

Referenced by comic_open().

◆ internal_sort()

void internal_sort ( comic_t * c)
static

Insertion-sort the page index by entry name (reading order).

Stable insertion sort over the resident index; page-count-bounded, no recursion. The name arena is immutable, so only the small page records move.

Parameters
[in,out]cComic whose pages[0..page_count) is sorted in place.
Precondition
c->page_count <= c->page_cap and the name arena is populated.
Every page's name_off/name_len addresses the arena.
Postcondition
pages is non-decreasing under internal_name_cmp.
page_count is unchanged.
Note
Not thread-safe.
Since
Version 0.1.0

Definition at line 263 of file comic.c.

References internal_name_cmp(), comic_page_t::name_len, comic_page_t::name_off, comic_t::names, comic_t::page_count, comic_t::pages, and RA8_INTERNAL.

Referenced by comic_open().

◆ priv_comic_is_page_name()

bool priv_comic_is_page_name ( const char * name,
uint16_t len )

Whether an archive entry name is a decodable comic page image.

A page qualifies when its base name is not a dotfile (skips hidden entries and macOS __MACOSX/._* AppleDouble resource forks) and it ends in an image extension the on-device decoder (stb_image) handles: .jpg, .jpeg, .png, .gif, .bmp (case-insensitive). Both / and \\ are treated as path separators for the base-name test.

Parameters
[in]nameEntry name bytes (not NUL-terminated; len is authoritative).
[in]lenLength of name in bytes.
Returns
Whether name is a page image to index.
Return values
truename is a decodable, non-hidden page image.
falsename is hidden, a resource fork, or not a decodable image.
Precondition
name addresses at least len readable bytes (or len is 0).
len is the true entry-name length.
Postcondition
No state is modified (pure read).
The result depends only on name / len.
Note
Thread-safe: pure read.
Since
Version 0.1.0

Definition at line 152 of file comic.c.

References internal_is_image_ext(), and RA8_PRIV.

Referenced by internal_add_entry(), internal_add_member(), and internal_add_member().

◆ priv_comic_page_add()

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.

Copies name into the comic's name arena and fills the next comic_page_t slot with the backend bookkeeping. Rejects an index or arena overflow so no page is silently dropped.

Parameters
[in,out]cComic accumulating its page index.
[in]nameEntry name bytes.
[in]name_lenLength of name in bytes.
[in]raw_sizeEncoded image length in bytes.
[in]extractable1 if this reader can decode the page, else 0.
[in]methodCBR ra8_rar_method_t (0 = store); 0 for CBZ.
[in]zip_indexCBZ miniz central-directory index (0 for CBR).
[in]data_offCBR member data-area offset (0 for CBZ).
[in]pack_sizeCBR packed size (0 for CBZ).
Returns
ra8_err_t status.
Return values
k_ra8_okPage appended.
k_ra8_err_invalid_sizePage index or name arena is full.
Precondition
c has its pages / names buffers bound.
name addresses name_len readable bytes.
Postcondition
On k_ra8_ok, c->page_count and c->names_len advanced by one entry.
On any error the index and arena are unchanged.
Note
Not thread-safe.
Since
Version 0.1.0

Definition at line 177 of file comic.c.

References comic_page_t::data_off, comic_page_t::extractable, k_ra8_err_invalid_size, k_ra8_ok, memcpy(), comic_page_t::name_len, comic_page_t::name_off, comic_t::names, comic_t::names_cap, comic_t::names_len, comic_page_t::pack_size, comic_t::page_cap, comic_t::page_count, comic_t::pages, RA8_CHECK_NULL_PTR, RA8_PRIV, comic_page_t::rar_method, comic_page_t::raw_size, s_tag_comic, and comic_page_t::zip_index.

Referenced by internal_add_entry(), internal_add_member(), and internal_add_member().

Variable Documentation

◆ s_tag_comic

const char* const s_tag_comic = "comic"
static

Log tag for comic-facade diagnostics.

Definition at line 34 of file comic.c.

Referenced by comic_close(), comic_page_info(), comic_page_read(), internal_open_reject_null(), and priv_comic_page_add().