增添 S5 支持
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 219 KiB After Width: | Height: | Size: 219 KiB |
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
+32
-11
@@ -557,7 +557,14 @@ local function buildHome()
|
||||
local p = newPage()
|
||||
if not p then return nil end
|
||||
clockLabel(p)
|
||||
pageTitle(p, ROLE == 'restore' and 'S4PAnim还原' or 'S4PAnim', 159, 158)
|
||||
-- 盘内标题 = 显示名(不再用内部代号 S4PAnim);x 按真 MiSans 30px 量出的宽度居中:
|
||||
-- 'S4e Pt.2' = 123 -> x = (466-123)/2 = 171.5 -> 172
|
||||
-- 'S4e Pt.2 · 还原' = 220 -> x = (466-220)/2 = 123
|
||||
if ROLE == 'restore' then
|
||||
pageTitle(p, 'S4e Pt.2 · 还原', 123, 222)
|
||||
else
|
||||
pageTitle(p, 'S4e Pt.2', 172, 125)
|
||||
end
|
||||
listItem(p, 85, 'ic_help', '快速帮助', function() show(pages.help) end)
|
||||
listItem(p, 181, 'ic_replace', ROLE == 'restore' and '开始还原' or '开始替换', function()
|
||||
pendingVariant = 'replace'
|
||||
@@ -577,22 +584,30 @@ local function buildHelp()
|
||||
setClickable(t)
|
||||
bind(t, function() show(pages.home) end)
|
||||
|
||||
listItem(p, 85, 'ic_check', '环境预检查', function() doCheck() end)
|
||||
desc1 = lab(p, { x = 65, y = 176, w = 338, h = 72,
|
||||
-- 竖排 4 个元素的 y:76 / 166 / 236 / 326。
|
||||
-- 原来 85 / 176 / 256 / 344 时,末尾那句 2 行块的**第 2 行**(y 377..410)已经越过圆边
|
||||
-- (仓库外 check_clip_real.py 实测:replace 盘 229 越界像素,restore 盘 23 像素),
|
||||
-- 因为整页内容 88+66+88+66=308px,而 466 圆在 x=65 处最多只给到 y<=394。
|
||||
-- 4 个元素整体上移 9~22px 后:整页 replace/restore 都是 0 越界、0 溢框(最远像素 228.2 < 233)。
|
||||
listItem(p, 76, 'ic_check', '环境预检查', function() doCheck() end)
|
||||
desc1 = lab(p, { x = 65, y = 166, w = 338, h = 72,
|
||||
text = '检查原始资源状态、分区可写情况等。',
|
||||
text_color = C_GRAY, font_size = 24, text_font = F_BODY })
|
||||
|
||||
-- 拆盘之后:本盘只装一份负载,「回滚/恢复」由另一份盘负责
|
||||
listItem(p, 256, 'ic_rollback', ROLE == 'restore' and '恢复修改' or '回滚修改', function()
|
||||
listItem(p, 236, 'ic_rollback', ROLE == 'restore' and '恢复修改' or '回滚修改', function()
|
||||
if ROLE == 'restore' then
|
||||
setDesc1('本盘是还原盘。要重新换回动画,请安装 S4PAnim.face。', C_GRAY)
|
||||
setDesc1('本盘是还原盘。要重新换回动画,请安装 S4e Pt.2。', C_GRAY)
|
||||
else
|
||||
setDesc1('本盘只负责替换。要还原原厂动画,请安装 S4PAnimRestore.face。', C_GRAY)
|
||||
setDesc1('本盘只负责替换。要还原原厂动画,请安装 S4e Pt.2 · 还原。', C_GRAY)
|
||||
end
|
||||
end)
|
||||
lab(p, { x = 65, y = 344, w = 334, h = 72,
|
||||
-- 换行写死成 2 行:24px 行高 33 -> 2 行 66 才放得进 h=72;
|
||||
-- 原来 w=334 时这句会被折成 3 行(第 3 行整个在圆外),所以框宽 334 -> 366
|
||||
-- (x=65 不动;实测行宽 360/306)。
|
||||
lab(p, { x = 65, y = 326, w = 366, h = 72,
|
||||
text = ROLE == 'restore' and '把压力检测动画写回原厂样式。'
|
||||
or '本盘不含回滚数据(体积减半),还原请装 S4PAnimRestore.face。',
|
||||
or '本盘不含回滚数据(体积减半),\n还原请装 S4e Pt.2 · 还原。',
|
||||
text_color = C_GRAY, font_size = 24, text_font = F_BODY })
|
||||
return p
|
||||
end
|
||||
@@ -652,9 +667,15 @@ local function buildAbout()
|
||||
if IMG.logo then
|
||||
pcall(function() p:Image { src = IMG.logo, x = 194, y = 108 } end)
|
||||
end
|
||||
-- "OS4 Icons" 28px 量得 138px -> 居中 x = 66 + (334-138)/2 = 164
|
||||
lab(p, { x = 164, y = 192, w = 138, h = 38, text = ROLE == 'restore' and 'S4PAnim还原' or 'S4PAnim',
|
||||
text_color = C_WHITE, font_size = 28, text_font = F_ITEM })
|
||||
-- 28px 量得:'S4e Pt.2' = 113 -> 居中 x = 233 - 56.5 = 176.5 -> 176
|
||||
-- 'S4e Pt.2 · 还原' = 203 -> x = 233 - 101.5 = 131.5 -> 132
|
||||
if ROLE == 'restore' then
|
||||
lab(p, { x = 132, y = 192, w = 205, h = 38, text = 'S4e Pt.2 · 还原',
|
||||
text_color = C_WHITE, font_size = 28, text_font = F_ITEM })
|
||||
else
|
||||
lab(p, { x = 176, y = 192, w = 115, h = 38, text = 'S4e Pt.2',
|
||||
text_color = C_WHITE, font_size = 28, text_font = F_ITEM })
|
||||
end
|
||||
-- "1.0.0" 20px 量得 45px -> 居中 x = 66 + (334-45)/2 = 210
|
||||
lab(p, { x = 210, y = 227, w = 45, h = 27, text = '1.0.0',
|
||||
text_color = C_GRAY, font_size = 20, text_font = F_VER })
|
||||
|
||||
@@ -38,6 +38,13 @@ def prepare(name):
|
||||
|
||||
shutil.copyfile(os.path.join(ov, 'watchface', 'fprj', name + '.fprj'),
|
||||
os.path.join(dst, 'watchface', 'fprj', name + '.fprj'))
|
||||
# projectName 现在就是「手表上显示的表盘名」(见 make_overlays.py 的 DISPLAY_NAME)。
|
||||
# 官方 scripts/build_face.ps1 先跑 sync_watchface_config.ps1:它按 projectName 找
|
||||
# watchface/fprj/<projectName>.fprj,找不到就把目录里**唯一**那份 .fprj 改名成它,
|
||||
# 并把该 fprj 的 <Screen Title> 覆盖成 projectName —— 所以这条流水线里真正进 .face 的
|
||||
# 显示名就是这个 projectName。注意 fprj 目录里必须**只有一份** .fprj:Compiler.exe 会
|
||||
# 按目录里每份 .fprj 把资源各打包一遍,多放一份副本会让 .face 直接翻倍。
|
||||
disp = cfg['projectName']
|
||||
shutil.copyfile(os.path.join(ov, 'watchface', 'fprj', 'images', 'preview.png'),
|
||||
os.path.join(dst, 'watchface', 'fprj', 'images', 'preview.png'))
|
||||
shutil.copyfile(os.path.join(ov, 'watchface', 'fprj', 'app', 'lua', 'main.lua'),
|
||||
@@ -52,18 +59,54 @@ def prepare(name):
|
||||
shutil.rmtree(tgt)
|
||||
shutil.copytree(os.path.join(ovimg, d), tgt)
|
||||
print(' %s: app/images/%s/ -> %d files' % (name, d, len(os.listdir(tgt))))
|
||||
return dst
|
||||
return dst, cfg['projectName']
|
||||
|
||||
|
||||
def build(dst):
|
||||
def workspace_fprj(dst):
|
||||
"""把工作区里的 .fprj 归一成 <name>.fprj,并返回它的路径。
|
||||
|
||||
我们拷进去时是 <name>.fprj,但 vendor 的 build_face.ps1 会先跑
|
||||
sync_watchface_config.ps1:它按 config 的 projectName(现在是**显示名** S4e Pt.2)找
|
||||
<projectName>.fprj,找不到就把目录里**唯一**那份改名过去(<Screen Title> 也被改写成
|
||||
projectName)。编完这里再改回 <name>.fprj —— 工作区里始终只有一份 .fprj(放两份会被
|
||||
Compiler.exe 并成双倍资源),文件名保持项目标识不变,下游 finalize_faces.compile_raw /
|
||||
gen_ui_lua 仍能按 fprj/<name>.fprj 找到它。
|
||||
多于一份 / 一份都没有都直接报错,不静默放过。
|
||||
"""
|
||||
name = os.path.basename(dst)
|
||||
d = os.path.join(dst, 'watchface', 'fprj')
|
||||
fs = sorted(f for f in os.listdir(d) if f.endswith('.fprj'))
|
||||
if len(fs) != 1:
|
||||
raise SystemExit('%s: watchface/fprj 下的 .fprj 应恰好 1 份,实际:%s' % (name, fs))
|
||||
want = os.path.join(d, name + '.fprj')
|
||||
if fs[0] != name + '.fprj':
|
||||
os.replace(os.path.join(d, fs[0]), want)
|
||||
return want
|
||||
|
||||
|
||||
def build(dst, name, disp):
|
||||
ps = os.path.join(dst, 'scripts', 'build_face.ps1')
|
||||
r = subprocess.run(['powershell', '-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', ps],
|
||||
# 显式指定产物名:build_face.ps1 默认按 projectName(现在 = 显示名,可能带空格/点)命名产物,
|
||||
# 这里仍然用 NAME,保证 bin/<NAME>.face、dist/<NAME>.face 这些标识路径不变。
|
||||
r = subprocess.run(['powershell', '-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', ps,
|
||||
'-FaceName', name + '.face'],
|
||||
capture_output=True, text=True, errors='replace')
|
||||
# sync 已经把 .fprj 改名成 <显示名>.fprj(里面的 <Screen Title> 也写成了显示名);
|
||||
# 这里改回 NAME,好让下游 finalize_faces.compile_raw 仍按 fprj/<NAME>.fprj 找它。
|
||||
fprj = workspace_fprj(dst)
|
||||
print(' fprj 归一:%s' % os.path.relpath(fprj, dst))
|
||||
for l in [x for x in (r.stdout or '').splitlines() if x.strip()][-6:]:
|
||||
print(' |', l)
|
||||
if r.returncode != 0:
|
||||
print(' !! build failed, rc=', r.returncode)
|
||||
return r.returncode == 0
|
||||
return False
|
||||
# 官方脚本按 projectName 命名产物(bin/<显示名>.face);-FaceName 已经把它压成 NAME,
|
||||
# 万一没生效,这里再兜一次(内容一样,只差文件名)。
|
||||
face = os.path.join(dst, 'bin', name + '.face')
|
||||
alt = os.path.join(dst, 'bin', disp + '.face')
|
||||
if disp != name and os.path.exists(alt) and not os.path.exists(face):
|
||||
os.replace(alt, face)
|
||||
return True
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
@@ -72,8 +115,8 @@ if __name__ == '__main__':
|
||||
if only and name != only:
|
||||
continue
|
||||
print('== %s (id=%s) ==' % (name, TARGETS[name][0]))
|
||||
dst = prepare(name)
|
||||
build(dst)
|
||||
dst, disp = prepare(name)
|
||||
build(dst, name, disp)
|
||||
face = os.path.join(dst, 'bin', name + '.face')
|
||||
if os.path.exists(face):
|
||||
print(' OK %s %d bytes' % (face, os.path.getsize(face)))
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
体积原因:本盘各 ~16 MB,不再额外复制工具链变体(要的话去掉下面的 SKIP_TOOLCHAIN 即可)
|
||||
"""
|
||||
import os, sys, shutil, subprocess, hashlib
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
from paths import WORK, DIST
|
||||
from build_faces import workspace_fprj # 工作区 .fprj 归一(vendor 会把它改名成显示名)
|
||||
|
||||
BUILD = os.path.join(WORK, 'build')
|
||||
TARGETS = {'S4PAnim': '362150102', 'S4PAnimRestore': '362150102'}
|
||||
@@ -19,7 +21,9 @@ def compile_raw(name):
|
||||
if not os.path.exists(exe):
|
||||
print(' %s: 跳过(没有构建树,先跑 build_faces.py %s)' % (name, name))
|
||||
return None
|
||||
fprj = os.path.join(w, 'watchface', 'fprj', name + '.fprj')
|
||||
# vendor 的 sync_watchface_config.ps1 会把工作区 .fprj 改名成显示名(S4e Pt.2);
|
||||
# 这里取工作区**唯一**那份 .fprj 并归一回 <name>.fprj,再交给 Compiler.exe 裸编译。
|
||||
fprj = workspace_fprj(w)
|
||||
out = os.path.join(WORK, 'raw_' + name)
|
||||
if os.path.exists(out):
|
||||
shutil.rmtree(out)
|
||||
|
||||
@@ -12,6 +12,7 @@ rb 表里放的是**另一侧字节前 64 B 的指纹**(只给版本校验用
|
||||
"""
|
||||
import os, re, sys, json, subprocess
|
||||
from paths import PROJ, WORK
|
||||
from build_faces import workspace_fprj # 工作区 .fprj 归一(vendor 会把它改名成显示名)
|
||||
|
||||
ROLE = (sys.argv[1] if len(sys.argv) > 1 else 'replace').lower()
|
||||
NAME = 'S4PAnim' if ROLE == 'replace' else 'S4PAnimRestore'
|
||||
@@ -62,6 +63,10 @@ def main():
|
||||
print('第 1 遍:用占位符编一次,量出资源在 resource.bin 里的位置')
|
||||
run('make_overlays.py', ROLE)
|
||||
run('build_faces.py', NAME)
|
||||
# vendor 的 build_face.ps1 会把工作区 .fprj 改名成 projectName(= 显示名);
|
||||
# 编完立刻取**唯一**那份并归一回 <NAME>.fprj,后面的 finalize_faces 才有得可读。
|
||||
print(' fprj 归一:%s' % os.path.relpath(
|
||||
workspace_fprj(os.path.join(WORK, 'build', NAME)), WORK))
|
||||
face = open(FACE, 'rb').read()
|
||||
print(' face %d bytes' % len(face))
|
||||
|
||||
@@ -97,6 +102,10 @@ def main():
|
||||
open(RESOLVED, 'w', encoding='utf-8', newline='\n').write(resolved)
|
||||
print('第 2 遍:回填后的 Lua(%d bytes),重新编译' % len(resolved))
|
||||
run('build_faces.py', NAME)
|
||||
# 同上:finalize_faces.compile_raw 是按 fprj/<NAME>.fprj 路径找工作区 fprj 的,
|
||||
# vendor 刚把它改名成显示名,这里必须归一(并断言只有一份)后再往下走。
|
||||
print(' fprj 归一:%s' % os.path.relpath(
|
||||
workspace_fprj(os.path.join(WORK, 'build', NAME)), WORK))
|
||||
print(run('finalize_faces.py', NAME).strip())
|
||||
|
||||
face2 = open(FACE, 'rb').read()
|
||||
|
||||
@@ -5,6 +5,10 @@ from paths import PROJ, WORK
|
||||
|
||||
ROLE = (sys.argv[1] if len(sys.argv) > 1 else 'replace').lower()
|
||||
NAME = 'S4PAnim' if ROLE == 'replace' else 'S4PAnimRestore'
|
||||
# 手表上显示的表盘名:replace / restore 两张盘共用同一个名字(写进 .fprj 的 <Screen Title>,
|
||||
# 同时就是 watchface.config.json 的 projectName)。只是「显示名」—— NAME、.fprj 文件名、
|
||||
# dist/<NAME>.face、.work/build/<NAME>、表盘 id 这些路径/标识全部不变。
|
||||
DISPLAY_NAME = 'S4e Pt.2'
|
||||
OUT = os.path.join(WORK, 'overlay')
|
||||
ASSETS = os.path.join(PROJ, 'assets')
|
||||
|
||||
@@ -49,12 +53,12 @@ def build(name, role):
|
||||
os.makedirs(os.path.join(fprj, 'images'), exist_ok=True)
|
||||
os.makedirs(os.path.join(fprj, 'app', 'lua'), exist_ok=True)
|
||||
|
||||
cfg = {"projectName": name, "watchfaceId": WF_ID, "power_consumption": "3",
|
||||
cfg = {"projectName": DISPLAY_NAME, "watchfaceId": WF_ID, "power_consumption": "3",
|
||||
"resourceBin": {"lvglVersion": 9, "colorFormat": "I8", "compress": "NONE",
|
||||
"input": "watchface/fprj/images/preview.png", "name": "preview"}}
|
||||
json.dump(cfg, open(os.path.join(base, 'watchface.config.json'), 'w', encoding='utf-8'),
|
||||
indent=4, ensure_ascii=False)
|
||||
write_fprj(os.path.join(fprj, f'{name}.fprj'), name)
|
||||
write_fprj(os.path.join(fprj, f'{name}.fprj'), DISPLAY_NAME)
|
||||
make_preview(os.path.join(fprj, 'images', 'preview.png'))
|
||||
shutil.copyfile(os.path.join(PROJ, 'main.lua'),
|
||||
os.path.join(fprj, 'app', 'lua', 'main.lua'))
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user