140 lines
6.9 KiB
Markdown
140 lines
6.9 KiB
Markdown
> 本文是当时的分析 / 交付存档,路径按当时的工作目录写。
|
||
> 本项目里的对应关系:tools 对应 _tools,inputs/target/vela_app.bin 对应 _x/vela_app.bin,
|
||
> inputs/source 对应 _s5,.work 对应 _out(产物),dist 对应 _out/faces。
|
||
|
||
# S5 图标包 → S4 eSIM 替换包
|
||
|
||
把 **Xiaomi Watch S5(q63, v4.101.020)** 的系统应用图标,写进 **Xiaomi Watch S4 eSIM(o62lte)**
|
||
的 `/dev/app` 分区。
|
||
|
||
## 文件
|
||
|
||
| 文件 | 说明 |
|
||
|---|---|
|
||
| `main.lua` | Lua 表盘入口(LVGL 界面 + dd 写入/校验状态机) |
|
||
| `payload.lua` | 负载清单:38 条记录(1 条超级块 canary + 37 个图标) |
|
||
| `payload/` | 逐条的图标字节,文件名与 `payload.lua` 对应 |
|
||
| `payload_rollback.lua` + `payload_rollback/` | 同样 38 条,内容为 S4 原厂字节(回滚用) |
|
||
| `flash.sh` / `rollback.sh` | **不需要表盘**:有 nsh shell 时直接跑这两条脚本 |
|
||
| `payload.json` / `manifest.json` | 机器可读的明细(偏移、长度、格式、来源) |
|
||
| `README.md` | 本文件 |
|
||
|
||
## 机制:走官方 OTA 同一条路
|
||
|
||
Lua 表盘无法"设置图标"。系统图标住在 `/dev/app` 这个 265 MB 的 ROMFS 分区镜像里
|
||
(`/resource/app/<应用>/launcher.bin`),必须**写入分区 + 重启**才会生效。
|
||
|
||
关键发现来自 [`docs-shell`](../../):设备侧有 `dd` / `cmp` / `md5` / `reboot`,
|
||
而 Lua 里 **`os.execute` 可用**(`io.popen` 不可用)。固件自带的 `ota.sh` 正是这么写分区的:
|
||
|
||
```sh
|
||
dd if=/ota/vela_app.bin of=/dev/app bs=32768 verify
|
||
```
|
||
|
||
所以本包对每条记录执行三步,全程只用官方存在的命令:
|
||
|
||
```sh
|
||
dd if=payload/NNN.bin of=/dev/app bs=4 seek=<offset/4> count=<length/4>
|
||
dd if=/dev/app of=/tmp/chk.bin bs=4 skip=<offset/4> count=<length/4>
|
||
cmp /tmp/chk.bin payload/NNN.bin # 退出码 0 = 一致
|
||
```
|
||
|
||
**为什么 `bs=4`**:所有目标偏移都是 512 对齐,且每条的长度都是 4 的倍数
|
||
(82956 / 21772 / 32 都能被 4 整除),所以 `bs=4` 正好写满目标区间、
|
||
**不会多写一个字节到相邻文件里**,也不会产生不完整的尾块。
|
||
|
||
最后 `reboot` 让只读挂载重新读取分区 —— 这就是"点一下、稍等片刻、手表重启、替换成功"
|
||
那条链路。
|
||
|
||
## 关于"打包方法"
|
||
|
||
我把 `docs-lua` 全部 68 个文件读完了:**里面没有表盘打包/安装的说明。** 它给出的是**运行时契约**:
|
||
|
||
| 事实 | 出处 |
|
||
|---|---|
|
||
| 内建表盘源码就在 `/watchface/lua/<名字>/`(circle, clearheart, complexzone, corona, daynight) | `lvgl/modules/fs.md` |
|
||
| 表盘资源 `/watchface/`(`resource.bin`、`aod`) | 同上 |
|
||
| **已安装表盘在 `/data/app/watchface/`(builtin / market)** | 同上 |
|
||
| 资源用绝对路径引用,如 `/watchface/lua/corona/image/sec.bin` | `lvgl/widgets/analogtime.md` |
|
||
| 入口:`init(style)` 返回根对象;全局 `pageOnPause` / `pageOnResume` | `activity/overview.md` |
|
||
| `/data` fatfs 可读写;`/tmp` tmpfs 可读写(重启清空);其余系统目录 romfs **只读** | `docs-shell/syntax-and-environment.md` |
|
||
| `pm install <包名\|rpk>` / `am start <包名>` 是**快应用**的包管理器 | `docs-shell/runtimes-and-media.md` |
|
||
|
||
也就是说:**Lua 表盘本身就是一个目录**(`.lua` + 资源),没有专用打包格式;
|
||
真正没被文档覆盖的是"怎么把这个目录推上手表、怎么在 `watchface.db` 里注册"。
|
||
这一步取决于你所用的工具(AstroBox / 表盘自定义工具等)。
|
||
|
||
**好消息是这一步对本项目不是必需的** —— 见下面两条替代路线。
|
||
|
||
## 三条路线(按推荐顺序)
|
||
|
||
### 路线 A:`flash.sh`(最简单,只要能拿到一次 shell)
|
||
|
||
把 `flash.sh`、`payload/` 复制到手表 `/data/s5icons/`,然后:
|
||
|
||
```sh
|
||
sh /data/s5icons/flash.sh
|
||
reboot
|
||
```
|
||
|
||
不需要表盘、不需要打包、不依赖表盘宿主权限。回滚用 `rollback.sh`。
|
||
|
||
### 路线 B:Lua 表盘(`main.lua`)
|
||
|
||
按你需要的形态提供。界面三个按钮 + 回滚:
|
||
|
||
1. **① 只读检查**(不写任何字节):`dd` 读出分区头,确认 magic 是 `-rom1fs-`、
|
||
卷名是 `app`;再逐条确认负载文件都在、长度正确。**只有检查通过,② 才解锁。**
|
||
2. **② 写入并校验**:逐条 `dd` 写入 → `dd` 读回 → `cmp` 比对,带进度条。
|
||
3. **③ 重启**:`sync` + `reboot`。
|
||
4. **回滚**:用 `payload_rollback` 走同一套流程。
|
||
|
||
分片执行(定时器每 40 ms 一条),界面不会卡死;每条失败都会计数并在结束时报告。
|
||
|
||
### 路线 C:直接刷整分区(最稳)
|
||
|
||
```
|
||
_out/s4_from_s5/vela_app_s5icons.bin 已改好图标的 vela_app.bin(265 MB)
|
||
```
|
||
|
||
用你已有的刷机通道写进 `/dev/app`。完全绕开设备侧脚本。
|
||
|
||
## 替换内容
|
||
|
||
S4 有 40 个 launcher 图标槽位:
|
||
|
||
- **37 个真正被改写**(字节有差异)
|
||
- **2 个字节完全相同,已从负载中剔除**:`voice_alexa`、`wxpay`
|
||
- **1 个 S4 独有、S5 没有**:`walkie_talkie`,保持原厂
|
||
- S5 独有的 12 个(`launcher_*` 第三方图标、`temperature`、`calorie_management` 等)按约定跳过
|
||
|
||
编码策略:S5 文件长度 == S4 槽位长度 → 原样拷贝;否则解码 S5 图标后按 **S4 槽位的色彩格式**
|
||
重新编码,确保长度逐字节相等。结果:**8090 条目录项、inode 表、superblock 逐位不变**。
|
||
|
||
验证过的等价性:把这些负载按偏移重放进原厂镜像,得到的文件与我逐字节验证过的
|
||
`vela_app_s5icons.bin` **完全相同(sha256 一致)**。
|
||
|
||
观感差异:10 个视觉上无变化,29 个有可见差异,多数是重新配色/描边
|
||
(例如 `barometer` 的底盘由蓝变白、`activities` 的渐变换了)。
|
||
|
||
## ⚠ 未验证项
|
||
|
||
我**无法在真机上测试**,以下三点请自行确认:
|
||
|
||
1. **`os.execute` 是否在表盘宿主里真的可用** —— `docs-lua/intro/overview.md` 标为 ✅,
|
||
但那是模拟器环境的结论;`docs-lua/intro/conventions.md` 也写明"接口通过逆向分析获得,
|
||
不同设备可用性不一致"。
|
||
2. **`SCRIPT_PATH` 是否注入、值是什么** —— 这是 LoXe 文档的约定,不在本仓库文档里。
|
||
`main.lua` 里已有兜底常量,若加载失败请改成实际路径。
|
||
3. **写入 `dd` 的权限** —— 表盘宿主进程是否有权写 `/dev/app`。`docs-shell` 只说明命令存在,
|
||
不代表当前进程有权限。
|
||
|
||
**第一次请只按「① 只读检查」**,看清输出再决定是否写入。或者直接走路线 A/C。
|
||
|
||
## 风险
|
||
|
||
- 往分区节点写数据是危险操作,写错位置可能导致无法开机。
|
||
- 系统 OTA 升级会整分区覆盖 `vela_app.bin`,图标会回到原厂(等于自动还原)。
|
||
- 修改系统资源分区可能影响保修;请自行确认设备所有权与当地法律。
|
||
- **动手前请确认你能刷回原厂 `vela_app.bin`**(路线 C 那份镜像,或官方固件包里的同名文件)。
|