Files
o62lte-customising-plan/s5e-icons/README.md
T
2026-10-02 21:34:56 +08:00

145 lines
11 KiB
Markdown
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.
# S5 图标包 → Xiaomi Watch S5 eSIM 46mm(图标修改器 · Lua 表盘)
把 **Xiaomi Watch S5 41mm(q63, v4.101.020)的 OS4 图标包**写进 **Xiaomi Watch S5 eSIM 46mm(p62lte, v3.112.035)** 的 `/dev/app` 与 `/dev/health` 两个分区。
- **153 条**负载:47 个 launcher 图标(app 33 + health 14)+ **44 个系统 UI 素材**(8 个锁屏/通知/免打扰小图标 + 22 张控制中心磁贴 + **14 张压力 App 小表情**)+ **160 张对讲机素材**(`walkie_talkie/**`)→ 全部等长覆盖,inode 表与后面所有偏移一个字节都不动
- **长宽比不一致的素材一律不替换**(保持原厂):源图与槽位长宽比相差 >0.5% 就跳过 —— 拉伸会把方块变长方形、裁切会切掉按钮两端。所以 `walkie_talkie/image/stop.bin`(176×96 vs 144×96)与 `walkie_talkie/guidance/finish_guidance.bin`(176×96 vs 160×96)**保持原厂**(本机逐字节确认)
- **对讲机那一组是 I8+RLE 压缩槽位**(固件里 `flags=0x08`,长度不是 4 的倍数)→ `iconpack.encode_into_slot()`:量化(颜色数从 256 逐级往下试)→ RLE → 用 `ctrl=0` 的 2 字节空指令补齐到原 `csize`,做到逐字节等长;设备端写入这类记录自动退化成 `dd bs=1`
- 界面按 **480×480** 重做(设计稿在 `tools/s5e_pages.py`,本机用真 MiSans 渲染 + 真实像素判圆屏切边,7 页 0 越界)
- **压力 App 小表情也搬了 14 张**(`pressure/icon/`):4 档情绪 48px(relax/mild/mid/severe)+ 32px 三张 + `press_grade1..4` 等级徽标 + `presure_icon` + 两张表情底版(`pressure_eomji_bg` / `_widbg`);96px 那四张两边本来就一样(自动跳过)。没搬的:整屏背景(`pressure_bg`/`success_bg`/`success_arc`/`press_widget_bg*`/`press_chart_bg`)、`reminder.bin`、`pressure/measure/*` 动画帧(体积大)
- **系统 UI 素材整套一起搬**:8 个锁屏/通知/免打扰小图标 + 22 张控制中心磁贴(q63 磁贴是 116×116 浅色主题,缩放到 S5 eSIM 的 128×128)。白盘放在深色控制中心上的观感已在 S4 eSIM 上实测,效果可以
- 只读预检查 + 每条写入前后对版/回读校验 + 一键回滚
---
## 1. 交付物与安装
| 文件 | 说明 |
|---|---|
| `dist/S5eIcons.face` | **主交付物** 10,852,268 B(sha256 50da912734a5db48…),表盘 id `462150101`,b5=0(与固件自带表盘一致) |
| `dist/S5eIcons_toolchain.face` | 官方工具链原样产物(b5=10);上面那个装不上时换这个 |
| `.work/s5e_from_os4/vela_app_os4.bin` | 重建后得到的整分区镜像(可直接刷机,绕开设备侧脚本) |
| `.work/s5e_from_os4/vela_health_os4.bin` | 同上(health 分区) |
| `docs/说明.md` | 完整说明:固件事实、47 条偏移表、验证结果、待实测项 |
| `docs/s5e_before_after.png` | 47 个 launcher 图标:原厂 → 替换后 对照图 |
| `docs/s5e_sysicons.png` | 44 个系统 UI 素材(22 控制中心 + 14 压力表情 + 8 其他):原厂 → 从 S5 41mm 搬过来的 对照图 |
| `docs/s5e_pressure.png` | 14 张压力 App 小表情:原厂 → q63 对照图 |
| `docs/s5e_ui_all.png` | 480×480 七个页面预览 |
装法与普通 Lua 表盘相同(`resource.bin` 推到 `/data/app/watchface/market/<id>/`)。装好后在表盘上操作:
```
首页 →「快速帮助」→「环境预检查」 只读,一个字节都不写
首页 →「开始替换」→ 确认 → 自动写入 153 条 →「重启手表」
快速帮助 →「回滚修改」→ 确认 → 写回原厂图标
```
输出怎么读(「正在替换」页):每 10 条一行 `> n/153 成 x 失 y`;结束时 `> 成功 x / 失败 y(共 153)`、必要时 `> 版本不符跳过 n 条`,以及 `> app/health 分区分条统计`。
**判断成功的唯一标准是 `失败 0`。**不是 0 就先别重启,点「回滚修改」。
---
## 2. 目录
```
main.lua 表盘源码(含 __UIIMG__ / __PAY__ / __RB__ 占位符,构建时回填)
payload/ payload.bin = MAGIC + 91 条顺序拼接;payload.lua = 逐条偏移表
payload_rollback/ 原厂字节(回滚用)
ui_images/ 界面素材(LVGL v9 .bin,8 张)
assets/ 设计素材 PNG(Mdi 图标、返回箭头、QQ、封面)
assets/override/ 自制图覆盖(activities.png = 跑道图标,优先级高于固件源)
tools/ 全部脚本(见下表)
dist/ 交付的 .face
docs/ 说明与预览图
inputs/ 要你自己放固件分区镜像(见第 4 节,不进仓库)
vendor/ 放第三方 LuaDevTemplate-main(不进仓库)
.work/ 构建工作区(产物,可删)
```
| 脚本 | 作用 |
|---|---|
| `tools/paths.py` | 所有路径的唯一入口(inputs / vendor / .work / dist,支持环境变量覆盖) |
| `tools/s5e_payload.py` | 从两个分区镜像生成 91 条负载 + 回滚 + 整分区镜像(自带正反两个证明) |
| `tools/s5e_ui_images.py` | 设计素材 PNG → LVGL .bin + 圆形 logo |
| `tools/s5e_pages.py` | 480×480 页面规格(唯一的设计稿来源) |
| `tools/s5e_render_ui.py` | 用真 MiSans 渲染 7 页预览 |
| `tools/s5e_check_clip.py` | 用真实渲染像素判圆屏是否切边 |
| `tools/s5e_build.py` | 两遍构建 → `dist/S5eIcons.face`(自动回填偏移 + 0 漂移校验) |
| `tools/s5e_verify.py` | **独立校验**:从 .face 里反解 Lua 表与偏移,再与固件镜像逐字节比对 |
| `tools/romfs.py` `tools/lvgl_bin.py` `tools/iconpack.py` `tools/render_ui.py` `tools/lua_balance.py` | 共用底层(ROMFS 解析 / LVGL 图片编解码 / UI 渲染 / Lua 块平衡) |
---
## 3. 目标固件的事实(从固件包里查出来的)
| 项 | 结论 | 证据 |
|---|---|---|
| 机型 | Xiaomi Watch S5 eSIM 46mm | `vela_ap.bin` / `vela_cp_image.bin` 里的机型字符串 |
| 屏幕 | 480×480 | 自带表盘素材 `bigsun.bin`、`citymage1.bin` 都是 I8 480×480(231,436 B);`complexzone.lua` 建 480×480 根对象 |
| Lua 宿主 | 有,Lua 5.4 | `vela_ap.bin` 里 `Lua 5.4`、`SCRIPT_PATH`、`watchface.db`、`luavgl`,以及同样的 7 个 C 模块 `luaopen_activity/dataman/miwear/navigator/screen/topic/vibrator` |
| 自带 Lua 表盘 | 7 个 | outdooradv / daynight / corona / complexzone / clearheart / circle / asymmetryzone |
| 图标槽位 | `/dev/app` 34 个 + `/dev/health` 15 个 | ROMFS `app` / `health` 卷里的 `*/launcher.bin` |
| 图标格式 | 全部 144×144 ARGB8888 = 82,956 B | 逐个解码确认;这台**没有 I8 槽位**(S4 eSIM 有 6 个),不存在量化损失 |
| 原厂画法 | 内容 136×136 居中(4px 透明边);`launcher_*` 138@(3,3);`debug` / `lua` / `voice_alexa` / `sports_game` 满幅 144 | 逐像素量 alpha 边界 |
## 4. 替换内容
- 能对上 **47** 个槽位(app 33 + health 14);OS4 里没有对应的 2 个槽位**保持原厂**:`walkie_talkie`、`sports_game`
- 其中 **8 个**槽位的 OS4 图标与原厂逐字节相同(debug / esimsms / interconnect / lua / sports / sports_course / sports_record / sports_training),照样写入,保证整套一致
- **`activities` 用自制图覆盖**:`assets/override/activities.png`(112×112 红/绿/青同心环,即 S4 项目里那张跑道图标)优先于 q63 固件源。裁剪 = 取实心 bbox(100×100) → 放大到目标槽位画法(136×136,LANCZOS + 锐化) → 居中粘回 (4,4);实测成品外径与原厂图标**完全对齐**(都是 (4,4)-(139,139))
- **`voice_aivs`(小爱同学)同样用自制图覆盖**:`assets/override/voice_aivs.png`(用户给的 144×144 PNG,圆形白盘直径 140)→ 裁到实心 → 缩放到槽位画法 136×136 → 居中;编码后 82,956 B 与原槽位等长
- **尺寸归一化**:把 OS4 的满幅 144 图按目标槽位的画法缩放(多数 136、`launcher_*` 138)并居中到同一原点 —— 这样每条负载长度与原槽位完全相等
- 负载 4.41 MB(替换 + 回滚各一份),.face 共 8.98 MB
---
## 5. 重建
### 5.1 需要自己准备的输入(放进 `inputs/`,不进仓库)
| 路径 | 是什么 | 大小 / sha256 前 16 位 |
|---|---|---|
| `inputs/target/vela_app.bin` | 目标固件(p62lte)app 分区 | 103,043,072 / 9b41c5f5eb17c112 |
| `inputs/target/vela_health.bin` | 目标固件 health 分区 | 206,924,800 / 489752c8a3fb24ff |
| `inputs/target/vela_font.bin` | 目标固件 font 分区(可选:渲染预览时从里面取 MiSans) | 43,379,712 / 16cb803ac1bd74c5 |
| `inputs/source/vela_app.bin` | 图标来源(S5 41mm q63)app 分区 | 206,000,128 / 161a8f7a5f0bd8af |
| `inputs/source/vela_health.bin` | 图标来源 health 分区 | 233,939,968 / 12a3bd2f8b62e969 |
| `inputs/fonts/MiSans-Medium.ttf`、`MiSans-Semibold.ttf` | 可选;不提供就自动从 `inputs/target/vela_font.bin` 里取 | — |
| `vendor/LuaDevTemplate-main/` | 官方模板(里面有 `Compiler.exe`) | — |
固件分区镜像就是官方 OTA 包(ZIP)里的 `vela_*.bin` 条目,解压出来即可。
### 5.2 步骤
```powershell
# 需要 Python 3 + Pillow
python tools/s5e_payload.py # 负载 + 整分区镜像(自己证明 正放=改后镜像 / 反放=原厂镜像)
python tools/s5e_ui_images.py # 界面素材
python tools/s5e_render_ui.py # 480 预览(可选)
python tools/s5e_check_clip.py # 圆屏切边检查(可选)
python tools/s5e_build.py # 两遍构建 -> dist/S5eIcons.face
python tools/s5e_verify.py # 独立校验
```
只想改界面/坐标:改 `main.lua` 与 `tools/s5e_pages.py`,然后跑 `s5e_render_ui.py` + `s5e_check_clip.py` + `s5e_build.py` + `s5e_verify.py`。
---
## 6. 已知边界(踩过的坑)
0. **对讲机素材默认不打包**(ICON_WALKIE=1 可打开);160 张 PNG 仍在 ssets/walkie_talkie/。
1. **偏移锁固件版本**:换固件要重跑 `s5e_payload.py` + 构建。用错版本时表盘会判「版本不符」整分区跳过,**不会把字节写进别人的文件里**。
2. **Compiler.exe 的设备表里没有 480 档**(枚举 DeviceType 1..1500 只有 17 个已知值,圆表且预览 326×326 的只有 362 / 462)→ 这里用 462 + 326×326 预览。预览只是表盘列表里的缩略图。
3. **表盘宿主能不能写 `/dev/app`、`/dev/health` 未在真机验证**(S4 eSIM 上写 `/dev/app` 是实测通过的,同源平台)。所以界面里第一步是只读的「环境预检查」。
4. **负载文件必须原样连续放在 .face 里**,偏移才等于 `文件基址 + 魔数长度 + 相对偏移`;`s5e_build.py` 会断言这一点。魔数长度**不要手写常量**(曾经把 15 写成 16,全部偏移差 1 字节)。
5. **独立校验不是摆设**:`tools/s5e_verify.py` 从 .face 里的 Lua 表反解偏移再与固件镜像比对,本项目开发时它抓出两个真 bug —— 魔数长度差 1;Lua 表里存的是 `/dev/app` 而不是运行时期望的 `app` 键(会让 dd 拼成 `if=nil`)。
6. 负载写入走 `/tmp` 中转(resource.bin 内的偏移不保证 4 字节对齐,不能直接 `dd skip`)。
## 7. 第三方与风险
- 本仓库不含固件、OTA 包、字体;`.face` 由第三方 `Compiler.exe` 编译(见 [../README.md](../README.md))
- 往分区节点写数据是危险操作:写错偏移可能损坏资源分区。表盘自带版本校验与回滚,但请自行确认设备所有权并承担后果
- 官方 OTA 升级会整分区覆盖,图标会回到原厂(等于自动还原)