feat(tuku): CBZ 档案册集成——档案页源/书库列册/解压缓存
- logic/reader/archive_source.c:档案册页源实现 M6 mr_book 契约 (mz_open/mz_count/mz_entry_at/mz_read_alloc),条目自然序 + 图片后缀 + 路径安全(`..`/绝对路径/反斜杠/隐藏段/内嵌 NUL)+ 单页尺寸上限过滤; meta 按需解压只读图片头(不落缓存,结果按页记忆);mr_book_open_auto 统一目录册/档案册开册,翻页与条漫两套消费代码零改动。 - logic/reader/page_cache.c/h:解压页落 MeowData/<pkg>/cache,LRU 64 页或 32MiB,启动清 mrpage_* 孤儿,写入 .part + rename;与缩略图缓存同目录、 前缀隔离;互斥量保护,解码/缩略图线程可并发。 - logic/library.c/h:扫描直接子文件里的 .cbz/.zip 成册(行=文件名+页数, 点入直接阅读);档案探测由调用方注入(logic 不依赖 zip 容器),不注入时 与 M7 行为逐字一致;册间衔接兄弟表=含图目录∪含页档案册,同列自然序。 条目构造/副标题/角标/兄弟判据拆成小函数,便于 M13 书库重写拼接。 - ui/library:档案册行 + 封面缩略图(解压档内首图)、扫描注入探测、缓存 目录统一由 page_cache 推导(一份规则)。 - ui/reader:open_book 走 mr_book_open_auto;跨册衔接纳入档案册。 - build_tuku.sh 补 zip/archive/cache 源与 sysroot zlib 头(zip.c include <zlib.h>);run_tests.sh 纳入 zip+cbz 用例;run_webtoon_tests.sh 的宿主 集成构建补新源与 -lz;.gitignore 同步测试产物。 - test/test_cbz.c(267 项断言)+ make_cbz_samples.py 真实 cbz 夹具: 列档/自然序/解压字节 CRC/路径安全/坏档与 zip 炸弹容错/LRU 与 32MiB 上限/ 启动清孤儿/续读角标/封面首图。
This commit is contained in:
1 parent
19620a7957
commit
562cc44931
16 files changed
+2330
-108
No files matched your search
@@ -146,16 +146,56 @@ int mr_dir_book_init(mr_dir_book *db, const char *dir, int maxdim,
|
||||
|
||||
/* ============================================================ */
|
||||
/* 图片头探测(不解码像素) */
|
||||
/* ============================================================ */
|
||||
/* ------------------------------------------------------------
|
||||
* 同一套解析同时服务「文件」(目录册,流式读不占内存)和「内存缓冲」
|
||||
* (档案册:page_cache 解压后直接探测,不必再落盘读文件)。
|
||||
* ============================================================ */
|
||||
typedef struct probe_src {
|
||||
const unsigned char *mem; /* 非 NULL = 内存源 */
|
||||
size_t len, pos;
|
||||
FILE *f; /* mem == NULL 时用 */
|
||||
} probe_src;
|
||||
|
||||
static int ps_read(probe_src *s, void *buf, size_t n)
|
||||
{
|
||||
if (s->mem) {
|
||||
if (n > s->len - s->pos) return 0;
|
||||
memcpy(buf, s->mem + s->pos, n);
|
||||
s->pos += n;
|
||||
return 1;
|
||||
}
|
||||
return fread(buf, 1, n, s->f) == n;
|
||||
}
|
||||
|
||||
static int ps_getc(probe_src *s)
|
||||
{
|
||||
if (s->mem) {
|
||||
if (s->pos >= s->len) return EOF;
|
||||
return s->mem[s->pos++];
|
||||
}
|
||||
return fgetc(s->f);
|
||||
}
|
||||
|
||||
static int ps_skip(probe_src *s, long n)
|
||||
{
|
||||
if (n < 0) return 0;
|
||||
if (s->mem) {
|
||||
if ((size_t)n > s->len - s->pos) return 0;
|
||||
s->pos += (size_t)n;
|
||||
return 1;
|
||||
}
|
||||
return fseek(s->f, n, SEEK_CUR) == 0;
|
||||
}
|
||||
|
||||
static unsigned probe_be32(const unsigned char *p)
|
||||
{
|
||||
return ((unsigned)p[0] << 24) | ((unsigned)p[1] << 16) | ((unsigned)p[2] << 8) | p[3];
|
||||
}
|
||||
|
||||
static int probe_png(FILE *f, int *w, int *h)
|
||||
static int probe_png(probe_src *s, int *w, int *h)
|
||||
{
|
||||
unsigned char hdr[24];
|
||||
if (fread(hdr, 1, sizeof hdr, f) != sizeof hdr) return 0;
|
||||
if (!ps_read(s, hdr, sizeof hdr)) return 0;
|
||||
static const unsigned char sig[8] = { 0x89, 'P', 'N', 'G', 0x0D, 0x0A, 0x1A, 0x0A };
|
||||
if (memcmp(hdr, sig, 8) != 0 || memcmp(hdr + 12, "IHDR", 4) != 0) return 0;
|
||||
unsigned ww = probe_be32(hdr + 16), hh = probe_be32(hdr + 20);
|
||||
@@ -165,27 +205,27 @@ static int probe_png(FILE *f, int *w, int *h)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int probe_jpeg(FILE *f, int *w, int *h)
|
||||
static int probe_jpeg(probe_src *s, int *w, int *h)
|
||||
{
|
||||
unsigned char b[2];
|
||||
if (fread(b, 1, 2, f) != 2 || b[0] != 0xFF || b[1] != 0xD8) return 0;
|
||||
if (!ps_read(s, b, 2) || b[0] != 0xFF || b[1] != 0xD8) return 0;
|
||||
for (;;) {
|
||||
int c = fgetc(f);
|
||||
int c = ps_getc(s);
|
||||
if (c == EOF) return 0;
|
||||
if (c != 0xFF) continue;
|
||||
int m = fgetc(f);
|
||||
while (m == 0xFF) m = fgetc(f); /* 填充字节 */
|
||||
int m = ps_getc(s);
|
||||
while (m == 0xFF) m = ps_getc(s); /* 填充字节 */
|
||||
if (m == EOF) return 0;
|
||||
if (m == 0xD8 || m == 0x01 || (m >= 0xD0 && m <= 0xD7)) continue; /* 无长度段 */
|
||||
if (m == 0xD9 || m == 0xDA) return 0; /* EOI/SOS 前没等到 SOF */
|
||||
unsigned char lb[2];
|
||||
if (fread(lb, 1, 2, f) != 2) return 0;
|
||||
if (!ps_read(s, lb, 2)) return 0;
|
||||
int len = ((int)lb[0] << 8) | lb[1];
|
||||
if (len < 2) return 0;
|
||||
int is_sof = (m >= 0xC0 && m <= 0xCF) && m != 0xC4 && m != 0xC8 && m != 0xCC;
|
||||
if (is_sof) {
|
||||
unsigned char so[5];
|
||||
if (len < 7 || fread(so, 1, 5, f) != 5) return 0;
|
||||
if (len < 7 || !ps_read(s, so, 5)) return 0;
|
||||
int hh = ((int)so[1] << 8) | so[2];
|
||||
int ww = ((int)so[3] << 8) | so[4];
|
||||
if (ww <= 0 || hh <= 0) return 0;
|
||||
@@ -193,14 +233,14 @@ static int probe_jpeg(FILE *f, int *w, int *h)
|
||||
*h = hh;
|
||||
return 1;
|
||||
}
|
||||
if (fseek(f, len - 2, SEEK_CUR) != 0) return 0;
|
||||
if (!ps_skip(s, len - 2)) return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int probe_bmp(FILE *f, int *w, int *h)
|
||||
static int probe_bmp(probe_src *s, int *w, int *h)
|
||||
{
|
||||
unsigned char hdr[26];
|
||||
if (fread(hdr, 1, sizeof hdr, f) != sizeof hdr) return 0;
|
||||
if (!ps_read(s, hdr, sizeof hdr)) return 0;
|
||||
if (hdr[0] != 'B' || hdr[1] != 'M') return 0;
|
||||
int ww = (int)((unsigned)hdr[18] | ((unsigned)hdr[19] << 8) |
|
||||
((unsigned)hdr[20] << 16) | ((unsigned)hdr[21] << 24));
|
||||
@@ -213,6 +253,32 @@ static int probe_bmp(FILE *f, int *w, int *h)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int probe_any(probe_src *s, int *w, int *h)
|
||||
{
|
||||
for (int pass = 0; pass < 3; pass++) {
|
||||
s->pos = 0;
|
||||
if (s->f) rewind(s->f);
|
||||
int ok = (pass == 0) ? probe_png(s, w, h)
|
||||
: (pass == 1) ? probe_jpeg(s, w, h)
|
||||
: probe_bmp(s, w, h);
|
||||
if (ok) return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mr_probe_size_mem(const void *buf, size_t len, int *w, int *h)
|
||||
{
|
||||
if (w) *w = 0;
|
||||
if (h) *h = 0;
|
||||
if (!buf || len < 8 || !w || !h) return 0;
|
||||
probe_src s = { (const unsigned char *)buf, len, 0, NULL };
|
||||
if (!probe_any(&s, w, h) || *w <= 0 || *h <= 0) {
|
||||
*w = *h = 0;
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int mr_probe_size(const char *path, int *w, int *h)
|
||||
{
|
||||
if (w) *w = 0;
|
||||
@@ -220,9 +286,8 @@ int mr_probe_size(const char *path, int *w, int *h)
|
||||
if (!path || !path[0] || !w || !h) return 0;
|
||||
FILE *f = fopen(path, "rb");
|
||||
if (!f) return 0;
|
||||
int ok = probe_png(f, w, h);
|
||||
if (!ok) { rewind(f); ok = probe_jpeg(f, w, h); }
|
||||
if (!ok) { rewind(f); ok = probe_bmp(f, w, h); }
|
||||
probe_src s = { NULL, 0, 0, f };
|
||||
int ok = probe_any(&s, w, h);
|
||||
fclose(f);
|
||||
if (ok && (*w <= 0 || *h <= 0)) { *w = *h = 0; return 0; }
|
||||
return ok;
|
||||
|
||||
Reference in new issue
Block a user