K1Guitar/tools/publish_k1_release.py

165 lines
6.1 KiB
Python
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.

# -*- coding: utf-8 -*-
"""Build K1 release zip: MCU .bin + ExtFlash resources + SoundWalkerIAP + docs.
External Flash has TWO regions that must both be present on a blank/erased chip:
0x00000000 tone pack (logo pad + Charg + 1/2/3.bin) -> *.tone.res / combined
0x00100000 UI0902 bitmaps (mode rows, boot logo, ...) -> *.ui0902.res / combined
Prefer flashing the combined *.extflash.res @ 0x0 for colleague upgrades.
"""
from __future__ import annotations
import hashlib
import shutil
import subprocess
import zipfile
from datetime import datetime
from pathlib import Path
REPO = Path(r"C:\Users\qjyu\Documents\SoundWalker\一诺国际吉他")
PROJ = REPO / "Code" / "YNGJ-GT1-M - AT32F403ARCT7"
EXE_BIN = PROJ / "project" / "IAR_V7.4" / "YNGJ-GT1-M" / "Exe" / "YNGJ-GT1-M.bin"
TONE_BIN = PROJ / "tools" / "out" / "extflash_tone_0903.bin"
UI0902_BIN = PROJ / "tools" / "out" / "ui0902_res.bin"
MAP_TXT = REPO / "Doc" / "音色文件" / "0903" / "FLASH_MAP.txt"
IAP_DIR = REPO / "升级" / "MCU主控升级"
OUT_ROOT = REPO / "tools" / "out"
FW_VER = "0.2.6"
TONE_RES_VER = "0903"
UI0902_RES_BASE = 0x00100000
def git_info(cwd: Path) -> tuple[str, str]:
def run(args: list[str]) -> str:
r = subprocess.run(
args, cwd=str(cwd), capture_output=True, text=True, encoding="utf-8", errors="replace"
)
return (r.stdout or "").strip()
commit = run(["git", "rev-parse", "--short=7", "HEAD"]) or "nogit"
subject = run(["git", "log", "-1", "--pretty=%s"]) or ""
return commit, subject
def sha256(path: Path) -> str:
h = hashlib.sha256()
with path.open("rb") as f:
for chunk in iter(lambda: f.read(1 << 20), b""):
h.update(chunk)
return h.hexdigest()
def build_combined_extflash(tone: bytes, ui: bytes) -> bytes:
if len(tone) > UI0902_RES_BASE:
raise SystemExit(f"tone pack {len(tone)} overflows into UI0902 @ 0x{UI0902_RES_BASE:X}")
pad = UI0902_RES_BASE - len(tone)
return tone + (b"\xFF" * pad) + ui
def main() -> None:
if not EXE_BIN.is_file():
raise SystemExit(f"missing MCU bin: {EXE_BIN}")
if not TONE_BIN.is_file():
raise SystemExit(f"missing tone pack: {TONE_BIN} (run pack_extflash_tone_0903.py)")
if not UI0902_BIN.is_file():
raise SystemExit(f"missing UI0902 pack: {UI0902_BIN} (run gen_ui0902_assets.py)")
iap = IAP_DIR / "SoundWalkerIAP.exe"
guide = IAP_DIR / "升级步骤.docx"
if not iap.is_file():
raise SystemExit(f"missing IAP tool: {iap}")
if not guide.is_file():
raise SystemExit(f"missing guide: {guide}")
stamp = datetime.now().strftime("%Y%m%d")
commit, subject = git_info(PROJ)
base = f"K1_MCU_v{FW_VER}_toneRes_v{TONE_RES_VER}_{stamp}_{commit}"
OUT_ROOT.mkdir(parents=True, exist_ok=True)
pkg = OUT_ROOT / base
if pkg.exists():
shutil.rmtree(pkg)
pkg.mkdir(parents=True)
tone_data = TONE_BIN.read_bytes()
ui_data = UI0902_BIN.read_bytes()
combined = build_combined_extflash(tone_data, ui_data)
mcu_name = f"YNGJ-GT1-M_MCU_v{FW_VER}_{stamp}.bin"
comb_name = f"extflash_ALL_tone0903_ui0902_{stamp}.res"
mcu_dst = pkg / mcu_name
comb_dst = pkg / comb_name
shutil.copy2(EXE_BIN, mcu_dst)
comb_dst.write_bytes(combined)
shutil.copy2(iap, pkg / "SoundWalkerIAP.exe")
shutil.copy2(guide, pkg / "升级步骤.docx")
if MAP_TXT.is_file():
shutil.copy2(MAP_TXT, pkg / "FLASH_MAP.txt")
shutil.copy2(mcu_dst, OUT_ROOT / mcu_name)
shutil.copy2(comb_dst, OUT_ROOT / "extflash_ALL_tone0903_ui0902.res")
# Keep build intermediates under tools/out for local rebuilds; not shipped in package.
(OUT_ROOT / "extflash_tone_0903.res").write_bytes(tone_data)
(OUT_ROOT / "extflash_ui0902.res").write_bytes(ui_data)
readme = pkg / "README.txt"
readme.write_text(
"\n".join(
[
f"K1 release {base}",
f"Built {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}",
f"Git HEAD {commit}",
f"Git subject {subject}",
f"FW version {FW_VER}",
"",
"==== 整机升级请刷这两项 ====",
f" 1) {mcu_name} -> MCU APP",
f" 2) {comb_name} -> 外部 Flash 从 0x0 起整包",
" (= 音色区 + 填充 + UI0902 图片区)",
"",
"建议步骤:",
" - 先整片擦除外部 Flash再刷上述 ALL .res @ 0x00000000",
" - 勿只刷音色区否则模式选择页会花屏UI 图在 0x100000",
"",
"外部 Flash 分区:",
" 0x00000000 音色/充电图 (toneRes)",
" logo pad + Charg@0xCB70 + 1.bin@0x1B8F0 + 2.bin(HKTK/2s) + 3.bin",
" 0x00100000 UI0902 图片 (模式选择、开机 Logo、充电、烧录模式、设置/调音台等)",
"",
f" {comb_name} size={comb_dst.stat().st_size} @ 0x00000000",
f" sha256={sha256(comb_dst)}",
"",
f"MCU: {mcu_name} size={mcu_dst.stat().st_size} sha256={sha256(mcu_dst)}",
"",
"说明:",
" - 开机全屏 Logo、关机充电全屏画面在 UI0902不在音色包里的 logo.bin/Charg.bin 占位。",
" - AutoBand 0x9EB5F 本版未重排。",
"",
"附件: SoundWalkerIAP.exe / 升级步骤.docx / FLASH_MAP.txt",
"",
]
),
encoding="utf-8",
newline="\n",
)
zip_path = OUT_ROOT / f"{base}.zip"
if zip_path.exists():
zip_path.unlink()
with zipfile.ZipFile(zip_path, "w", compression=zipfile.ZIP_DEFLATED) as zf:
for f in sorted(pkg.rglob("*")):
if f.is_file():
zf.write(f, arcname=f"{base}/{f.relative_to(pkg).as_posix()}")
print(f"PKG {pkg}")
print(f"ZIP {zip_path} ({zip_path.stat().st_size} bytes)")
for f in sorted(pkg.iterdir()):
print(f" {f.name} {f.stat().st_size}")
if __name__ == "__main__":
main()