feat: M6.10–M6.19 批量收编 + M6.19 推卡验收

- M7 launcher 重写 5 期(虚拟化宫格/meow_theme 单一权威/横幅动画/自定义 PIN 键盘)
- OOM 通用化(cachegov 上收 SDK)、自动切歌根治、meowpanel jv_free 泄漏修复、Hello 滚动卡顿修复
- ncm 歌词 UI / 媒体通知封面 / 封面分辨率 640
- 5 bug 修复:ncm 后台化 UI 叠层+点击穿透、再进 ncm 桌面叠层、播放页新旧 UI 叠层抢刷脏区(meow_ui_gfx 恢复边沿整帧重画)、PIN 亮屏亮度跳(keys.c meow_panel_on 按 settings_brightness_clamped 恢复)、通知中心封面显歌单封面(cover.c save_cover_raw 加 is_song + 命中块守卫)
- 四 App 统一重链新 meow_ui.a 51caee4a;M6.19=0.8.19 固件 2d2331f2 推卡自刷验收全绿
This commit is contained in:
AskaEth committed 2026-09-03 12:16:01 +08:00
1 parent c2ee7626a4
commit 3d70a3dd2c
176 files changed
+15507 -597

No files matched your search

File diff suppressed because it is too large. Load diff
+59 -26
View File
@@ -1,6 +1,7 @@
/* rdenc.c — 文本编码检测 + GB18030→UTF-8 + UTF-8 工具(实现见 rdenc.h)。 */
/* rdenc.c — 文本编码检测 + GB18030/Big5→UTF-8 + UTF-8 工具(实现见 rdenc.h)。 */
#include "rdenc.h"
#include "gb18030_table.h"
#include "big5_table.h"
#include <stdlib.h>
#include <string.h>
@@ -39,6 +40,14 @@ static inline int gb_trail_ok(unsigned b)
{
return (b >= 0x40 && b <= 0x7E) || (b >= 0x80 && b <= 0xFE);
}
static inline int gb_trail_q(unsigned b)
{
return gb_trail_ok(b);
}
static inline int big5_trail_q(unsigned b)
{
return (b >= 0x40 && b <= 0x7E) || (b >= 0xA1 && b <= 0xFE);
}
static inline int gb_col(unsigned b)
{
/* 列 = b - 0x40 - (b >= 0x80);b 已在合法范围(不含 0x7F) */
@@ -128,16 +137,45 @@ int rd_gb18030_to_utf8(const uint8_t *in, size_t inlen, uint8_t *out, size_t out
return RD_ENC_OK;
}
/* ================= 编码检测 ================= */
static inline int gb_trail_q(unsigned b)
/* ================= Big5 双字节 → UTF-8 ================= */
int rd_big5_to_utf8(const uint8_t *in, size_t inlen, uint8_t *out, size_t outcap, size_t *outlen)
{
return (b >= 0x40 && b <= 0x7E) || (b >= 0x80 && b <= 0xFE);
}
static inline int big5_trail_q(unsigned b)
{
return (b >= 0x40 && b <= 0x7E) || (b >= 0xA1 && b <= 0xFE);
if (!in || !outlen) return RD_ENC_ERR_INVAL;
if (!out && outcap) return RD_ENC_ERR_INVAL;
size_t o = 0;
size_t i = 0;
while (i < inlen) {
uint8_t b = in[i];
if (b < 0x80) {
if (o + 1 > outcap) { *outlen = o + 1; return RD_ENC_ERR_BUFSZ; }
out[o++] = b; i++; continue;
}
if (b >= 0x81 && b <= 0xFE && i + 1 < inlen) {
uint8_t t = in[i + 1];
if (big5_trail_q(t)) {
uint32_t cp = big5_2byte[b - RD_B5_LEAD0][gb_col(t)];
uint8_t tmp[4];
int n;
if (cp == 0) { cp = 0xFFFD; }
n = enc_utf8(cp, tmp);
if (o + (size_t)n > outcap) { *outlen = o + (size_t)n; return RD_ENC_ERR_BUFSZ; }
memcpy(out + o, tmp, (size_t)n); o += (size_t)n;
i += 2; continue;
}
}
/* 非法首字节 / 尾字节不成对 → U+FFFD */
{
uint8_t tmp[4]; int n = enc_utf8(0xFFFD, tmp);
if (o + (size_t)n > outcap) { *outlen = o + (size_t)n; return RD_ENC_ERR_BUFSZ; }
memcpy(out + o, tmp, (size_t)n); o += (size_t)n;
i++; continue;
}
}
*outlen = o;
return RD_ENC_OK;
}
/* ================= 编码检测 ================= */
rd_enc_t rd_enc_detect(const uint8_t *buf, size_t len, rd_enc_stats *st)
{
if (st) { st->gb_pairs = st->big5_pairs = st->stray = 0; }
@@ -156,33 +194,27 @@ rd_enc_t rd_enc_detect(const uint8_t *buf, size_t len, rd_enc_stats *st)
return ascii ? RD_ENC_ASCII : RD_ENC_UTF8;
}
/* 3) 双字节区段统计(GB18030 兼容 GBK;Big5 尾字节不同)。
* GB 有效 = 码表 gb_2byte 查到非 0(GBK/GB18030 已映射码位);
* Big5 专属 = 结构上 Big5 合法、但对 GB 码表无效(Big5 独有码位)。
* 二者比较更可靠(结构上每个 GBK 双字节对也是合法 Big5 结构,无法区分)。 */
long gb = 0, big5 = 0, stray = 0;
/* 3) 双字节区段统计(GB18030 兼容 GBK)。
* GB 有效 = 码表 gb_2byte 查到非 0(GBK/GB18030 已映射码位);stray = 无法成对字节。
* R3 注:Big5 双字节区间是 GB18030 双字节区间的**严格子集**(Big5 尾字节
* 0x40..0x7E|0xA1..0xFE ⊂ GB 尾字节 0x40..0x7E|0x80..0xFE;常见 GBK 简体字尾字节大量
* 落在 0xA1..0xFE 与 Big5 重叠),故字节结构**无法可靠区分 Big5 与 GB18030**——盲判必然
* 误伤(误把 GBK 判成 Big5 或反之)。自动检测统一兜底 GB18030(简体最常见、误伤代价
* 最小);Big5 书由 UI 提供「编码切换」显式选中(对应正确解码走 rd_big5_to_utf8)。 */
long gb = 0, stray = 0;
for (size_t i = 0; i < len; ) {
uint8_t b = buf[i];
if (b < 0x80) { i++; continue; }
if (b >= 0x81 && b <= 0xFE && i + 1 < len) {
uint8_t t = buf[i + 1];
int gb_ok = gb_trail_q(t) &&
gb_2byte[b - RD_GB_LEAD0][gb_col(t)] != 0;
int b5_ok = big5_trail_q(t);
if (gb_ok) gb++;
if (b5_ok && !gb_ok) big5++; /* Big5 专属:GB 码表无效 */
if (gb_trail_q(t) && gb_2byte[b - RD_GB_LEAD0][gb_col(t)] != 0) gb++;
i += 2; continue;
}
stray++; i++;
}
if (st) { st->gb_pairs = gb; st->big5_pairs = big5; st->stray = stray; }
if (st) { st->gb_pairs = gb; st->big5_pairs = 0; st->stray = stray; }
/* 阈值(经验值):双字节对太少 → 乱码/不确定 → 兜底 GB18030(最常见中文编码)。
* 注(READER_ENGINE_NOTES §3.5 风险):Big5 与 GB18030 双字节区大量重叠,码表有效性
* 无法可靠区分(简体 GB2312 文本 trail 0xA1-0xFE 恰是 Big5 trail 区)。R1 双字节一律
* 按 GB18030 兜底;Big5 经此检测也按 GB18030 试(v1 不转),真实区分需用户手动切编码
* (列 R2)。 */
if (gb + big5 < (long)(len / 5)) return RD_ENC_GB18030;
if (gb < (long)(len / 5)) return RD_ENC_GB18030; /* 双字节对太少 → 不确定 → GB18030 */
return RD_ENC_GB18030;
}
@@ -231,7 +263,8 @@ int rd_to_utf8(rd_enc_t enc, const uint8_t *in, size_t inlen, uint8_t *out, size
memcpy(out, in + off, inlen - off); *outlen = inlen - off;
return RD_ENC_OK;
}
/* GB18030 / Big5(v1 按 GB18030 试) */
if (enc == RD_ENC_BIG5) return rd_big5_to_utf8(in, inlen, out, outcap, outlen);
/* GB18030 兜底 */
return rd_gb18030_to_utf8(in, inlen, out, outcap, outlen);
}
+15 -7
View File
@@ -1,14 +1,19 @@
/* rdenc.h — 阅读器文本编码检测 + GB18030→UTF-8 解码 + UTF-8 工具(纯 C,宿主可测)。
/* rdenc.h — 阅读器文本编码检测 + GB18030/Big5→UTF-8 解码 + UTF-8 工具(纯 C,宿主可测)。
*
* 对齐 devdocs/plan/READER_ENGINE_NOTES.md §3(zLibrary 轻量化:BOM + 严格 UTF-8 + 双字节区段统计,
* 不抄 N-gram 马尔可夫):
* - BOM 优先:FE FF=UTF-16BE、FF FE=UTF-16LE、EF BB BF=UTF-8;
* - 无 BOM → 严格 UTF-8 校验(合法即 UTF-8;ASCII 属 UTF-8 子集);
* - 非 UTF-8 → GB18030(GBK 超集) / Big5 双字节区段统计(阈值经验值,见 READER_ENGINE_NOTES §3.3);
* - **R1 只做 GB18030→UTF-8 双字节映射**(uint16[126][190] 码表),4 字节扩展段 → U+FFFD;
* Big5 检测出来也按 GB18030 试(v1 不转 Big5,正文会乱但可读,注明)。
* - 非 UTF-8 → GB18030(GBK 超集) 双字节映射(uint16[126][190] 码表);4 字节扩展段 → U+FFFD。
*
* 无 SDK 依赖;`#include "gb18030_table.h"`(生成物,见 tools/gen_gb18030_table.py)。
* **Big5(R3)**:Big5 双字节区间是 GB18030 双字节区间的**严格子集**(Big5 尾字节
* 0x40..0x7E|0xA1..0xFE ⊂ GB 尾字节 0x40..0x7E|0x80..0xFE,且常用 GBK 简体字尾字节
* 大量落在 0xA1..0xFE 与 Big5 重叠),故**字节结构无法自动区分 Big5 与 GB18030**——
* 盲判必然误伤。R3 方案:解码器支持 RD_ENC_BIG5(big5_2byte 码表),由 UI 提供编码
* 切换(Auto→GB18030/Big5/UTF-8/UTF-16LE/BE),用户对繁体乱码书手动选 Big5 即正确
* 渲染;自动检测默认 GB18030(简体最常见,误伤代价最小)。详见 READER_ENGINE_NOTES §3.5。
*
* 无 SDK 依赖;`#include "gb18030_table.h"` 与 `"big5_table.h"`(生成物,见 tools/)。
*/
#ifndef RDENC_H
#define RDENC_H
@@ -26,7 +31,7 @@ typedef enum {
RD_ENC_UTF16LE,
RD_ENC_UTF16BE,
RD_ENC_GB18030, /* GBK 超集(解码器按 2 字节码表) */
RD_ENC_BIG5, /* v1 检测但按 GB18030 解码 */
RD_ENC_BIG5, /* Big5 → UTF-8(R3:big5_2byte 码表;需显式选中,不自动检测) */
} rd_enc_t;
/* 检测统计(诊断 + 阈值用;值可为 0)。 */
@@ -49,6 +54,9 @@ int rd_utf8_validate(const uint8_t *buf, size_t len);
#define RD_ENC_ERR_BUFSZ -2 /* out 不足;*outlen 已填所需 */
int rd_gb18030_to_utf8(const uint8_t *in, size_t inlen, uint8_t *out, size_t outcap, size_t *outlen);
/* Big5 双字节 → UTF-8(与 GB 版同构;Big5 非法双字节 → U+FFFD)。成功 0;输出不足 RD_ENC_ERR_BUFSZ。 */
int rd_big5_to_utf8(const uint8_t *in, size_t inlen, uint8_t *out, size_t outcap, size_t *outlen);
/* UTF-8 → 码点数组(布局用;malloc 分配 *cps,调用方 free)。成功 0。 *n = 码点数。
* 非法序列按一个 U+FFFD 收(宽松);len 为输入字节长。 */
#define RD_UTF8_ERR_INVAL -1
@@ -59,7 +67,7 @@ int rd_utf8_to_cp(const uint8_t *utf8, size_t len, uint32_t **cps, size_t *n);
int rd_cp_to_utf8(uint32_t cp, uint8_t out[4]);
/* 通用:任意检测编码 → UTF-8(rdenc 已知的各编码)。UTF-16LE/BE 走简易 BOM 对齐 + 码点转换;
* Big5 v1 按 GB18030 双字节表转(会乱但可读,注明)。返回码同 rd_gb18030_to_utf8。 */
* Big5 走 rd_big5_to_utf8。返回码同 rd_gb18030_to_utf8。 */
int rd_to_utf8(rd_enc_t enc, const uint8_t *in, size_t inlen, uint8_t *out, size_t outcap, size_t *outlen);
#ifdef __cplusplus
+22 -1
View File
@@ -40,6 +40,8 @@ static int txt_open(rd_src *s)
if (!m) return -6;
s->impl = m;
if (rdtxt_probe(s->path, &m->info) != 0) { txt_free(s); return -1; }
/* R3:显式编码覆盖(Big5 书)——探测只取 file_size/path,编码改用覆盖值 */
if (s->enc_override_set) m->info.enc = s->enc_override;
/* 建段表:一次扫描(off,len) */
rdtxt_reader *r = NULL;
if (rdtxt_reader_open(s->path, &m->info, &r) != 0) { txt_free(s); return -2; }
@@ -105,7 +107,7 @@ static int is_ext(const char *path, const char *ext)
return strcasecmp(path + pl - el, ext) == 0; /* ext 含点(如 ".cbz") */
}
int rd_src_open(rd_src **out, const char *path)
static int src_open_common(rd_src **out, const char *path, const rd_enc_t *enc)
{
if (!out || !path) return -1;
/* 读前 64 字节做 magic 探测 */
@@ -127,6 +129,7 @@ int rd_src_open(rd_src **out, const char *path)
if (strlen(path) >= sizeof s->path) { free(s); return -2; }
strcpy(s->path, path);
s->fmt = pick->fmt;
if (enc) { s->enc_override = *enc; s->enc_override_set = 1; }
int rc = pick->open(s);
if (rc != 0) { s->vt = NULL; s->impl = NULL; rd_src_free(s); return rc; }
s->vt = pick->vt;
@@ -134,6 +137,16 @@ int rd_src_open(rd_src **out, const char *path)
return 0;
}
int rd_src_open(rd_src **out, const char *path)
{
return src_open_common(out, path, NULL);
}
int rd_src_open_with_enc(rd_src **out, const char *path, const rd_enc_t *enc)
{
return src_open_common(out, path, enc);
}
void rd_src_free(rd_src *s)
{
if (!s) return;
@@ -141,6 +154,14 @@ void rd_src_free(rd_src *s)
free(s);
}
rd_fmt_t rd_src_fmt(const rd_src *s) { return s ? s->fmt : RD_FMT_TXT; }
int rd_src_txt_enc(const rd_src *s, rd_enc_t *out)
{
if (!s || s->fmt != RD_FMT_TXT || !out) return -1;
txt_impl *m = (txt_impl *)s->impl;
if (!m) return -1;
*out = m->info.enc;
return 0;
}
const char *rd_src_title(const rd_src *s) { return (s && s->title[0]) ? s->title : (s ? s->path : ""); }
const char *rd_src_author(const rd_src *s) { return (s && s->author[0]) ? s->author : "佚名"; }
const char *rd_src_cover(const rd_src *s) { return (s && s->cover[0]) ? s->cover : NULL; }
+7
View File
@@ -13,6 +13,7 @@
#ifndef RDSRC_H
#define RDSRC_H
#include "rdenc.h"
#include <stddef.h>
#include <stdint.h>
@@ -38,9 +39,15 @@ typedef struct rd_toc_entry {
/* 打开(按扩展名/magic 探测格式)。0=成功;负值=不支持/打不开。 */
int rd_src_open(rd_src **out, const char *path);
/* 同 rd_src_open,但用 *enc 显式覆盖文本编码(TXT;ENC 为 NULL=自动探测)。
* 非 TXT 格式忽略覆盖。R3 编码切换用(Big5 书显式选中)。 */
int rd_src_open_with_enc(rd_src **out, const char *path, const rd_enc_t *enc);
void rd_src_free(rd_src *s);
rd_fmt_t rd_src_fmt(const rd_src *s);
/* TXT 有效文本编码(含覆盖);*out 填 rd_enc_t。非 TXT 返回 -1(XML/结构化固定 UTF-8 内部码)。 */
int rd_src_txt_enc(const rd_src *s, rd_enc_t *out);
const char *rd_src_title(const rd_src *s);
const char *rd_src_author(const rd_src *s);
const char *rd_src_cover(const rd_src *s); /* 封面 ref;无则 NULL */
+5
View File
@@ -4,6 +4,7 @@
#define RDSRC_INT_H
#include "rdsrc.h"
#include "rdenc.h"
#include <stddef.h>
#include <stdint.h>
@@ -29,6 +30,10 @@ struct rd_src {
char author[160];
char cover[320];
void *impl;
/* R3:文本编码显式覆盖(rd_src_open_with_enc)。enc_override_set=1 时 TXT loader 用该编码;
* 非 TXT 格式(XML/结构化)固定 UTF-8 内部编码,忽略覆盖。 */
rd_enc_t enc_override;
int enc_override_set;
};
/* 注册一个格式(供 rdsrc.c 调度)。 */