87 lines
4.4 KiB
C
87 lines
4.4 KiB
C
/* zip.h — 纯 C zip 容器读取器(只依赖 libz 的 inflate/crc32 + stdio)。
|
||
*
|
||
* 定位:tuku 漫画阅读器的 CBZ/zip 容器层——开包、列目录、按需解压单条目。
|
||
* 不依赖 meow_ui.h / SDK / tuku 其它逻辑,宿主可裸测(见 tuku/test/test_zip.c)。
|
||
*
|
||
* 用法:
|
||
* mz_archive *z = NULL;
|
||
* int rc = mz_open("book.cbz", &z);
|
||
* if (rc != MZ_OK) { fprintf(stderr, "%s\n", mz_strerror(rc)); return; }
|
||
* for (int i = 0; i < mz_count(z); i++) {
|
||
* const mz_entry *e = mz_entry_at(z, i);
|
||
* if (mz_entry_is_dir(e) || !is_image_name(e->name)) continue;
|
||
* size_t n = 0;
|
||
* void *px = NULL;
|
||
* if (mz_read_alloc(z, e, &px, &n) == MZ_OK) { decode(px, n); free(px); }
|
||
* }
|
||
* mz_close(z);
|
||
*
|
||
* 线程约束:单个 mz_archive 不是线程安全的(共享 FILE* 位置与条目表只读);
|
||
* 多线程请各自 mz_open 自己的 archive,或由调用方加锁。模块内无全局可变状态。
|
||
*
|
||
* 内存:mz_open 只把 central directory 的条目元数据(条目表 + 名字副本)读进内存,
|
||
* 不展开条目数据。mz_read 解压到调用方缓冲(缓冲需 >= 该条目 uncomp_size);
|
||
* mz_read_alloc 只为单个条目分配一次。
|
||
*
|
||
* 安全:所有偏移/长度都按文件实际大小校验;越界、截断、坏 magic、坏 CRC 一律
|
||
* 返回错误码而不崩溃。条目名按原始字节透传(通常是 UTF-8,不做规范化/解码)。
|
||
* 名字可能是 ".."、绝对路径或反斜杠——调用方若把它落到文件系统,必须自行拒绝
|
||
* 越界路径。加密、zip64、分卷压缩包明确返回 MZ_ERR_UNSUPPORTED。
|
||
*
|
||
* 错误码均为负值;MZ_OK 为 0。mz_strerror 对所有已知码返回静态字符串。
|
||
*/
|
||
#ifndef TUKU_LOGIC_ZIP_ZIP_H
|
||
#define TUKU_LOGIC_ZIP_ZIP_H
|
||
|
||
#include <stddef.h>
|
||
#include <stdint.h>
|
||
|
||
enum {
|
||
MZ_OK = 0,
|
||
MZ_ERR_IO = -1, /* 文件读写失败(打开/seek/read) */
|
||
MZ_ERR_FORMAT = -2, /* 结构非法:坏 magic、central directory 不一致、流损坏 */
|
||
MZ_ERR_TRUNCATED = -3, /* 声明的偏移/长度越过文件尾(含局部头越界) */
|
||
MZ_ERR_UNSUPPORTED = -4, /* 加密 / zip64 / 分卷 / 非 stored-deflate 压缩方式 */
|
||
MZ_ERR_BUF = -5, /* 调用方缓冲小于条目 uncomp_size */
|
||
MZ_ERR_MEM = -6, /* 内存分配失败 */
|
||
MZ_ERR_ARG = -7, /* 空指针 / 越界 index 等调用错误 */
|
||
MZ_ERR_CRC = -8 /* 解压结果 CRC-32 与 central directory 不符 */
|
||
};
|
||
|
||
typedef struct mz_entry {
|
||
const char *name; /* NUL 结尾;内容为 zip 内原始字节,生命周期同 archive */
|
||
uint32_t name_len; /* 原始名字字节数(不含结尾 NUL,name 中可能有内嵌 NUL) */
|
||
uint16_t method; /* 0=stored, 8=deflate;其它值在 mz_read 时返回 UNSUPPORTED */
|
||
uint16_t flags; /* zip general purpose flags(bit0=加密,bit11=UTF-8 名) */
|
||
uint32_t crc; /* 条目内容 CRC-32 */
|
||
uint64_t comp_size; /* 压缩后字节数(archive 内) */
|
||
uint64_t uncomp_size; /* 原始字节数 */
|
||
uint64_t local_offset; /* local file header 在 archive 内的偏移 */
|
||
} mz_entry;
|
||
|
||
typedef struct mz_archive mz_archive;
|
||
|
||
/* 打开 zip。成功时 *out 为新 archive(mz_close 释放);失败时 *out == NULL。 */
|
||
int mz_open(const char *path, mz_archive **out);
|
||
void mz_close(mz_archive *z);
|
||
|
||
int mz_count(const mz_archive *z);
|
||
const mz_entry *mz_entry_at(const mz_archive *z, int index); /* 越界返回 NULL */
|
||
const mz_entry *mz_find(const mz_archive *z, const char *name); /* 精确 strcmp,找不到 NULL */
|
||
|
||
/* 目录条目(名字以 '/' 结尾)。注意 zip 常见但没有强制要求目录条目存在。 */
|
||
int mz_entry_is_dir(const mz_entry *e);
|
||
|
||
/* 解压单条目到 buf。buf_size 必须 >= e->uncomp_size,否则 MZ_ERR_BUF。
|
||
* 成功时 *out_size = uncomp_size(可为 0)。out_size 可为 NULL。
|
||
* 校验 CRC 与声明尺寸;失败时 buf 内容不可用。 */
|
||
int mz_read(mz_archive *z, const mz_entry *e, void *buf, size_t buf_size, size_t *out_size);
|
||
|
||
/* mz_read 的便捷版:malloc 恰好 uncomp_size 字节(0 长度条目分配 1 字节)。
|
||
* 成功时 *out_buf 由调用方 free。 */
|
||
int mz_read_alloc(mz_archive *z, const mz_entry *e, void **out_buf, size_t *out_size);
|
||
|
||
const char *mz_strerror(int err);
|
||
|
||
#endif /* TUKU_LOGIC_ZIP_ZIP_H */
|