# -*- coding: utf-8 -*- """Build K1 release zip: MCU .bin + tone .res + SoundWalkerIAP + docs. No RTT flashing. Version stays Firmware Version[] (currently 0.2.0). """ 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" MAP_TXT = REPO / "Doc" / "音色文件" / "0903" / "FLASH_MAP.txt" IAP_DIR = REPO / "升级" / "MCU主控升级" OUT_ROOT = REPO / "tools" / "out" FW_VER = "0.2.0" TONE_RES_VER = "0903" 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 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)") 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}") # date + commit only (no minutes, no dirty tag) 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) mcu_name = f"YNGJ-GT1-M_MCU_v{FW_VER}_{stamp}.bin" res_name = f"extflash_toneRes_v{TONE_RES_VER}_{stamp}.res" mcu_dst = pkg / mcu_name res_dst = pkg / res_name shutil.copy2(EXE_BIN, mcu_dst) shutil.copy2(TONE_BIN, res_dst) shutil.copy2(iap, pkg / "SoundWalkerIAP.exe") shutil.copy2(guide, pkg / "升级步骤.docx") if MAP_TXT.is_file(): shutil.copy2(MAP_TXT, pkg / "FLASH_MAP.txt") # flat convenience copies shutil.copy2(res_dst, OUT_ROOT / "extflash_tone_0903.res") shutil.copy2(mcu_dst, OUT_ROOT / mcu_name) readme = pkg / "README.txt" readme.write_text( "\n".join( [ f"K1 release {base}", f"Built {datetime.now().strftime('%Y-%m-%d %H:%M:%S %z')}", f"Git HEAD {commit}", f"Git subject {subject}", f"FW version {FW_VER} (settings UI Version[])", f"Tone res toneRes_v{TONE_RES_VER} (no in-bin version; tag in filename)", "", "Contents:", f" {mcu_name}", f" size={mcu_dst.stat().st_size} sha256={sha256(mcu_dst)}", f" flash with SoundWalkerIAP.exe (USB IAP)", f" {res_name}", f" size={res_dst.stat().st_size} sha256={sha256(res_dst)}", " W25Q128 layout: logo@0 + Charg@0xCB70 + 1.bin@0x1B8F0 + 2.bin + 3.bin", " Must pair with MCU v0.2.0 (address map changed).", " SoundWalkerIAP.exe", " 升级步骤.docx", " FLASH_MAP.txt", "", "Notes:", " - Boot logo uses UI0902 full-screen asset; packed logo.bin only pads 0x0..0xCB70.", " - AutoBand legacy 0x9EB5F not remapped in this release.", " - Flash MCU firmware AND tone .res together.", "", ] ), 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()