Files
MeowOS-For-R3P2/patch_v2/reader_app/app/shelf_model.h
T
AskaEth 409fd1edf7 feat(reader): M27 书架按目录分组(ui.shelf_group=folder|format,默认 format 不变)
- 新增 shelf_model.c/.h:扫描/行序列从 shelf.c 拆出(纯 C,宿主可测);shelf_book 加 group[96](相对 Books/ 的目录路径,根下空串),path 语义/内容完全不变(进度键仍为 rdprog_hash(path))
- folder 模式:目录段头(根下=未分类),未分类置顶、其余按目录名排序;组内沿用最近阅读优先/其次书名;空目录不产生段头
- format 模式逐行保持旧行为;新增 reader.conf ui.shelf_group,缺键默认 format
- 上限 256→2048(SHELF_MAX_ROWS=2N=4096,段头≤书数),超限置可读提示,不再静默截断
- 顶栏加「目录/格式」切换,设置页加「书架分组」;UTF-8 安全截断长中文目录名
- 单测 test/shelf_group_smoke.c(38 断言)+ 计时 test/shelf_scan_bench.c
- build_reader.sh:链 meow_ui.a 解析冒烟 stbi_*;无 qemu 时跳过执行而非失败
2026-11-09 21:55:33 +08:00

74 lines
3.8 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* app/shelf_model.h — 书架数据模型:扫描 Books/ + 生成行序列(纯 C,不依赖 SDK/设备)。
*
* 从 shelf.c 拆出,供 shelf.c(控件装配)、宿主单测(test/shelf_group_smoke.c)与
* 扫描计时(test/shelf_scan_bench.c)共用。
*
* 目录归属(M27):folder 模式按「相对 Books/ 的目录路径」分组,如 "轻小说"、"科幻/长篇";
* Books/ 根下的书 group="",显示段头 SHELF_GROUP_ROOT。分组键是相对路径(不是目录名),
* 故 Books/a/b 与 Books/b 不会混。
*
* 硬约束:path 永远是书的完整路径(打开书 + 进度键 rdprog_hash(path)),分组只写 group,
* 绝不改写 path;两个分组模式间切换不影响进度。
*/
#ifndef SHELF_MODEL_H
#define SHELF_MODEL_H
#include <stdint.h>
/* 书籍上限:真实场景 180 本(Books/轻小说),2048 给 11× 余量。
* 超限不再静默停扫:report->truncated=1,由 UI 出提示。 */
#define SHELF_MAX_BOOKS 2048
#define SHELF_GROUP_MAX 96 /* 目录字段容量(含 NUL):95 字节 ≈ 31 个汉字 */
/* 行数上限:每本书最多贡献 1 行书 + 1 行段头(段头数 ≤ 书数),故 2N 必够。 */
#define SHELF_MAX_ROWS (SHELF_MAX_BOOKS * 2)
#define SHELF_GROUP_ROOT "未分类" /* Books/ 根下书籍的段头 */
typedef struct shelf_book {
char path[400]; /* 完整路径:打开书 + 进度键;分组不得改写 */
char title[128];
char sub[160];
char group[SHELF_GROUP_MAX]; /* 相对 Books/ 的目录路径;根下="" */
int fmt; /* 0 txt,1 fb2,2 epub,3 mobi,4 cbz */
long size;
uint64_t ts; /* 最近阅读 unix;无=0 */
} shelf_book;
typedef struct shelf_row {
int section; /* 1=段头行 */
int fmt; /* 段头/书行格式;段头无 label 时显示格式名 */
int book; /* 书行=books 下标;段头=-1 */
const char *label; /* 段头标题;NULL=格式名(指向 books[].group 或静态串) */
} shelf_row;
/* 进度时间戳查询(生产=rdprog_load;测试/基线=NULL,全部 ts=0)。 */
typedef uint64_t (*shelf_ts_fn)(const char *path);
typedef struct shelf_scan_report {
int nbooks;
int truncated; /* 1=有书因 SHELF_MAX_BOOKS 上限被丢弃 */
int root_missing; /* 1=root 打不开(无卡/无 Books 目录) */
} shelf_scan_report;
/* 递归扫描 root(子目录深度 ≤2,与旧行为一致),按 ts 降序/书名升序排序后写 books(容量 cap)。
* 返回写入本数。目录为空不产生任何记录(段头由 build_rows 按书生成,天然无空段头)。 */
int shelf_scan_books(const char *root, shelf_book *books, int cap,
shelf_ts_fn ts_fn, shelf_scan_report *rep);
/* 生成行序列:
* folder_mode=0:按格式分段(TXT/FB2/EPUB/MOBI/CBZ,空格式不出段头)——与 M27 前逐行一致;
* folder_mode=1:先按目录分组,根目录组「未分类」置顶,其余组按目录名排序;
* 组内沿用 books[] 的全局顺序(最近阅读优先、其次书名)。
* 返回行数;超过 rowcap 时只写 rowcap 行并返回 rowcap。 */
int shelf_build_rows(const shelf_book *books, int nbooks, int folder_mode,
shelf_row *rows, int rowcap);
/* 工具(单测直接调用)。 */
int shelf_fmt_from_ext(const char *name);
const char *shelf_fmt_name(int fmt);
void shelf_fmt_size(char *out, int outsz, long size);
/* UTF-8 安全截断:最多写 cap-1 字节;若截断点落在多字节序列中间,整段丢弃该残缺字符。
* 返回写入字节数(不含 NUL)。 */
int shelf_copy_utf8(char *dst, int cap, const char *src);
#endif /* SHELF_MODEL_H */