release: 0.2.10 — tone pack 0914 (BIN3 after 2.bin), autoclose label 关闭

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
yuquanjun 2026-09-14 19:37:57 +08:00
parent 7fa2f5f1c4
commit f8aeb4fb7c
7 changed files with 175 additions and 106 deletions

View File

@ -101,7 +101,7 @@ int16_t MIC_VOL_MAP[11]= {-9000,-7000,-5000,-3000,-1000,1000,3000,5000,7000,9000
uint8_t TOUCH_AREA_NUM = (sizeof(Touch_Areas)/sizeof(Touch_AreaTypeDef));
const uint8_t Version[3] = {0,2,9}; /* major.minor.patch — 0.2.9: Finger bass PC=33; HIL harness + tm1617/adc inject hooks */
const uint8_t Version[3] = {0,2,10}; /* major.minor.patch — 0.2.10: tone 0914 (BIN3 after 2.bin); autoclose label 关闭 */
uint8_t Led = 8;
bool PressFlag = 0;

View File

@ -1321,9 +1321,9 @@ void Draw_System_Item_List(const LEBEL_UI *UI, GUI_SWITCH *Group)
WHITE, c_top, c_bot, value_sz);
}else
{
/* 手册「不自动」;字库无「不」,界面用稿面「关机」 */
/* 手册「不自动」;字库无「不」,界面显示「关闭」 */
LCD_ShowChinese_AutoAlign_Gradient(val_x1 + UI0902_TRI_L_W, val_x2 - UI0902_TRI_R_W, text_y,
(const uint8_t*)"\xB9\xD8\xBB\xFA",
(const uint8_t*)"\xB9\xD8\xB1\xD5",
LCD_ALIGN_CENTER, WHITE, c_top, c_bot, value_sz);
}
UI_DrawTriRight((uint16_t)(val_x2 - UI0902_TRI_R_W), chev_cy);

View File

@ -4,10 +4,10 @@
/* Auto-generated by tools/pack_extflash_tone_0903.py — do not hand-edit. */
/* W25Q128 absolute offsets. Boot logo display uses UI0902_BOOT_LOGO_ADDR. */
/* Tone resource pack has no in-bin version field; release tag = EXTFLASH_TONE_RES_VER. */
#define EXTFLASH_TONE_RES_VER "0903"
#define EXTFLASH_TONE_RES_VER "0914"
#define EXTFLASH_TONE_RES_VER_MAJOR 0
#define EXTFLASH_TONE_RES_VER_MINOR 9
#define EXTFLASH_TONE_RES_VER_PATCH 3
#define EXTFLASH_TONE_RES_VER_PATCH 14
#define EXTFLASH_LOGO_ADDR 0x00000000UL
#define EXTFLASH_LOGO_SIZE 52080UL
@ -18,13 +18,13 @@
#define EXTFLASH_CHARGING_H 190
#define EXTFLASH_BIN1_RHYTHM_ADDR 0x0001B8F0UL
#define EXTFLASH_BIN1_RHYTHM_SIZE 530273UL
#define EXTFLASH_BIN1_RHYTHM_SIZE 509151UL
#define EXTFLASH_BIN2_SONG_HAITIAN_ADDR 0x0009D07DUL
#define EXTFLASH_BIN2_SONG_HAITIAN_SIZE 41263UL
#define EXTFLASH_BIN2_SONG_HAITIAN_SIZE 41914UL
#define EXTFLASH_BIN3_UNIVERSAL_ADDR 0x000A71ACUL
#define EXTFLASH_BIN3_UNIVERSAL_SIZE 31854UL
#define EXTFLASH_BIN3_UNIVERSAL_ADDR 0x000A7437UL
#define EXTFLASH_BIN3_UNIVERSAL_SIZE 33815UL
/* Convenience aliases used by UI ADDRESS */
#define FLASH_ADDR_MODE_NORMAL EXTFLASH_BIN1_RHYTHM_ADDR /* 普通 */
@ -32,11 +32,11 @@
#define FLASH_ADDR_SONG_HAITIAN EXTFLASH_BIN2_SONG_HAITIAN_ADDR
#define FLASH_ADDR_MODE_UNIVERSAL EXTFLASH_BIN3_UNIVERSAL_ADDR /* 万能 */
/* Legacy AutoBand bank — not repacked in 0903; left unchanged in firmware */
/* Legacy AutoBand bank — not repacked; may overlap 2.bin — do not enable until remapped */
#define FLASH_ADDR_AUTOBAND_LEGACY 0x0009EB5FUL
#define EXTFLASH_TONE_PACK_END 0x000AEE1AUL
#define EXTFLASH_TONE_PACK_SIZE 716314UL
#define EXTFLASH_TONE_PACK_END 0x000AF84EUL
#define EXTFLASH_TONE_PACK_SIZE 718926UL
#define UI0902_RES_BASE_SAFE_GAP (0x00100000UL - EXTFLASH_TONE_PACK_END)
#endif

View File

@ -1,16 +1,16 @@
/* Auto-generated by tools/gen_git_user_fw_ver.py — do not edit.
* git HEAD (dirty): 6da7d4d62737bd294dc42a3265e25b789b33e08c
* git HEAD (dirty): 7fa2f5f1c43ce40fcabb0b847dedd43618ac0875
* branch: develop develop
* 01 0C wire: develop_6da7d4*
* 01 0C wire: develop_7fa2f5*
*/
#ifndef GIT_USER_FW_VER_H
#define GIT_USER_FW_VER_H
#define GIT_COMMIT_ID_FULL "6da7d4d62737bd294dc42a3265e25b789b33e08c"
#define GIT_COMMIT_ID_FULL "7fa2f5f1c43ce40fcabb0b847dedd43618ac0875"
#define GIT_BRANCH_NAME "develop"
#define GIT_COMMIT_SHORT6 "6da7d4"
#define GIT_COMMIT_SHORT6 "7fa2f5"
#define GIT_DIRTY (1)
#define GIT_BUILD_ID "develop_6da7d4*"
#define GIT_BUILD_ID "develop_7fa2f5*"
#define GIT_BUILD_ID_LEN 15u
#endif /* GIT_USER_FW_VER_H */

View File

@ -17,6 +17,8 @@ from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
REPO = ROOT.parent.parent
CSV_PATH = REPO / "Doc" / "音色文件" / "0914" / "local_songs.csv"
if not CSV_PATH.is_file():
CSV_PATH = REPO / "Doc" / "音色文件" / "0903" / "local_songs.csv"
OUT_HDR = ROOT / "project" / "inc" / "LocalSongNames.h"

View File

@ -1,20 +1,19 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Pack external W25Q128 tone/logo image for K1 (0903).
"""Pack external W25Q128 tone/logo image for K1 (0914).
Layout (absolute W25Q128 offsets BIN2/BIN3 are FIXED so FW addresses stay stable):
Layout (absolute W25Q128 offsets):
0x00000000 logo.bin (legacy pad; boot UI uses UI0902 full-screen logo)
0x0000CB70 Charg.bin (legacy pad; charge UI uses UI0902_CHARGE_SCREEN full-screen)
0x0000CB70 Charg.bin (legacy pad; charge UI uses UI0902_CHARGE_SCREEN)
0x0001B8F0 1.bin 普通/专业 31 rhythms
0x0009D07D 2.bin 本地曲目变长尾部 0xFF 填到 BIN3<=41KB
0x000A71AC 3.bin 万能模式固定勿随 2.bin 长度漂移
0x0009D07D 2.bin 本地曲目变长<=41KB
after 2.bin 3.bin 万能模式 2.bin 长度后移写入 ExtFlash_Tone_Addr.h
Outputs:
tools/out/extflash_tone_0903.bin
tools/out/extflash_tone_0903.res (same bytes, .res alias)
tools/out/extflash_ALL_tone0903_ui0902.res (tone + pad + ui0902, if ui pack present)
tools/out/extflash_tone_0914.bin / .res
tools/out/extflash_ALL_tone0914_ui0902.res
project/inc/ExtFlash_Tone_Addr.h
Doc/音色文件/0903/FLASH_MAP.txt
Doc/音色文件/0914/FLASH_MAP.txt
"""
from __future__ import annotations
@ -26,25 +25,28 @@ from pathlib import Path
ROOT = Path(__file__).resolve().parents[1] # firmware project root
REPO = ROOT.parent.parent # 一诺国际吉他
OUT_DIR = ROOT / "tools" / "out"
OUT_BIN = OUT_DIR / "extflash_tone_0903.bin"
OUT_RES = OUT_DIR / "extflash_tone_0903.res"
OUT_ALL_RES = OUT_DIR / "extflash_ALL_tone0903_ui0902.res"
TONE_TAG = "0914"
OUT_BIN = OUT_DIR / f"extflash_tone_{TONE_TAG}.bin"
OUT_RES = OUT_DIR / f"extflash_tone_{TONE_TAG}.res"
OUT_ALL_RES = OUT_DIR / f"extflash_ALL_tone{TONE_TAG}_ui0902.res"
# Keep legacy alias names for older tools that still look for 0903
OUT_BIN_LEGACY = OUT_DIR / "extflash_tone_0903.bin"
OUT_RES_LEGACY = OUT_DIR / "extflash_tone_0903.res"
OUT_ALL_LEGACY = OUT_DIR / "extflash_ALL_tone0903_ui0902.res"
OUT_HDR = ROOT / "project" / "inc" / "ExtFlash_Tone_Addr.h"
OUT_MAP = REPO / "Doc" / "音色文件" / "0903" / "FLASH_MAP.txt"
OUT_MAP = REPO / "Doc" / "音色文件" / TONE_TAG / "FLASH_MAP.txt"
UI0902_BIN = OUT_DIR / "ui0902_res.bin"
TONE_DIR = REPO / "Doc" / "音色文件" / "0903"
TONE_DIR = REPO / "Doc" / "音色文件" / TONE_TAG
# Fixed map — must match project/inc/ExtFlash_Tone_Addr.h consumed by firmware.
OFF_LOGO = 0x00000000
OFF_CHARGING = 0x0000CB70
OFF_BIN1 = 0x0001B8F0
OFF_BIN2 = 0x0009D07D
OFF_BIN3 = 0x000A71AC # FIXED: never place 3.bin by concatenating after variable 2.bin
# BIN3 placed immediately after 2.bin (computed at pack time)
UI0902_RES_BASE = 0x00100000
DAB_MAGIC = b"\xABDAB"
MAX_SONG_BIN_BYTES = 41 * 1024 # 曲目文件(2.bin)硬上限 41KB
BIN2_SLOT_BYTES = OFF_BIN3 - OFF_BIN2 # 41263; must keep BIN3 fixed
def find_ziliao() -> Path:
@ -76,28 +78,31 @@ def require_region_equals(blob: bytes, off: int, src: bytes, label: str) -> None
def main() -> None:
if not TONE_DIR.is_dir():
raise SystemExit(f"missing tone dir: {TONE_DIR}")
ziliao = find_ziliao()
bin1_path = TONE_DIR / "1.bin"
bin2_path = TONE_DIR / "2.bin"
bin3_path = TONE_DIR / "3.bin"
parts = [
("LOGO", ziliao / "logo.bin", OFF_LOGO, "legacy pad; boot uses UI0902_BOOT_LOGO"),
("CHARGING", ziliao / "Charg.bin", OFF_CHARGING, "legacy pad; UI uses UI0902_CHARGE_SCREEN"),
("BIN1_RHYTHM", bin1_path, OFF_BIN1, "普通/专业 31 条节奏"),
("BIN2_SONG_HAITIAN", bin2_path, OFF_BIN2, "本地曲目"),
("BIN3_UNIVERSAL", bin3_path, OFF_BIN3, "万能模式(固定偏移)"),
]
for p in (bin1_path, bin2_path, bin3_path):
if not p.is_file():
raise SystemExit(f"missing {p}")
bin2_data = bin2_path.read_bytes()
if len(bin2_data) > MAX_SONG_BIN_BYTES:
raise SystemExit(
f"2.bin (曲目) too large: {len(bin2_data)} bytes > {MAX_SONG_BIN_BYTES} (41KB)"
)
if len(bin2_data) > BIN2_SLOT_BYTES:
raise SystemExit(
f"2.bin too large for fixed BIN3 slot: {len(bin2_data)} > {BIN2_SLOT_BYTES} "
f"(would shift 3.bin past 0x{OFF_BIN3:X})"
)
off_bin3 = OFF_BIN2 + len(bin2_data)
parts = [
("LOGO", ziliao / "logo.bin", OFF_LOGO, "legacy pad; boot uses UI0902_BOOT_LOGO"),
("CHARGING", ziliao / "Charg.bin", OFF_CHARGING, "legacy pad; UI uses UI0902_CHARGE_SCREEN"),
("BIN1_RHYTHM", bin1_path, OFF_BIN1, "普通/专业 31 条节奏"),
("BIN2_SONG_HAITIAN", bin2_path, OFF_BIN2, "本地曲目"),
("BIN3_UNIVERSAL", bin3_path, off_bin3, "万能模式(紧跟 2.bin 之后)"),
]
blobs: list[bytes] = []
cursor = 0
@ -107,7 +112,7 @@ def main() -> None:
if cursor > force_off:
raise SystemExit(
f"{name}: cursor 0x{cursor:X} past forced 0x{force_off:X} "
f"(previous blob too large; enlarge next fixed gap or shrink prior file)"
f"(previous blob too large)"
)
if cursor < force_off:
pad = force_off - cursor
@ -122,25 +127,33 @@ def main() -> None:
OUT_DIR.mkdir(parents=True, exist_ok=True)
packed = b"".join(blobs)
OUT_BIN.write_bytes(packed)
OUT_RES.write_bytes(packed) # 总音色 .bin 同步为 .res
OUT_RES.write_bytes(packed)
OUT_BIN_LEGACY.write_bytes(packed)
OUT_RES_LEGACY.write_bytes(packed)
by_name = {r[0]: r for r in rows if not r[0].startswith("(")}
bin1_data = bin1_path.read_bytes()
bin3_data = bin3_path.read_bytes()
# Hard integrity gates
dab_ok(packed, OFF_BIN1, expect_cnt=31)
dab_ok(packed, OFF_BIN2, expect_cnt=1)
dab_ok(packed, OFF_BIN3, expect_cnt=3)
dab_ok(packed, off_bin3, expect_cnt=3)
if by_name["BIN1_RHYTHM"][1] != OFF_BIN1:
raise SystemExit(f"BIN1 placed @0x{by_name['BIN1_RHYTHM'][1]:X} want 0x{OFF_BIN1:X}")
if by_name["BIN2_SONG_HAITIAN"][1] != OFF_BIN2:
raise SystemExit(f"BIN2 placed @0x{by_name['BIN2_SONG_HAITIAN'][1]:X} want 0x{OFF_BIN2:X}")
if by_name["BIN3_UNIVERSAL"][1] != OFF_BIN3:
raise SystemExit(f"BIN3 placed @0x{by_name['BIN3_UNIVERSAL'][1]:X} want 0x{OFF_BIN3:X}")
if by_name["BIN3_UNIVERSAL"][1] != off_bin3:
raise SystemExit(f"BIN3 placed @0x{by_name['BIN3_UNIVERSAL'][1]:X} want 0x{off_bin3:X}")
require_region_equals(packed, OFF_LOGO, (ziliao / "logo.bin").read_bytes(), "LOGO")
require_region_equals(packed, OFF_CHARGING, (ziliao / "Charg.bin").read_bytes(), "CHARGING")
require_region_equals(packed, OFF_BIN1, bin1_data, "BIN1/1.bin")
require_region_equals(packed, OFF_BIN2, bin2_data, "BIN2/2.bin")
require_region_equals(packed, OFF_BIN3, bin3_data, "BIN3/3.bin")
require_region_equals(packed, off_bin3, bin3_data, "BIN3/3.bin")
if cursor > UI0902_RES_BASE:
raise SystemExit(
f"ERROR: tone pack end 0x{cursor:X} overflows UI0902 @ 0x{UI0902_RES_BASE:X}"
)
hdr = f"""#ifndef __EXTFLASH_TONE_ADDR_H
#define __EXTFLASH_TONE_ADDR_H
@ -148,10 +161,10 @@ def main() -> None:
/* Auto-generated by tools/pack_extflash_tone_0903.py do not hand-edit. */
/* W25Q128 absolute offsets. Boot logo display uses UI0902_BOOT_LOGO_ADDR. */
/* Tone resource pack has no in-bin version field; release tag = EXTFLASH_TONE_RES_VER. */
#define EXTFLASH_TONE_RES_VER "0903"
#define EXTFLASH_TONE_RES_VER "{TONE_TAG}"
#define EXTFLASH_TONE_RES_VER_MAJOR 0
#define EXTFLASH_TONE_RES_VER_MINOR 9
#define EXTFLASH_TONE_RES_VER_PATCH 3
#define EXTFLASH_TONE_RES_VER_PATCH 14
#define EXTFLASH_LOGO_ADDR 0x{by_name['LOGO'][1]:08X}UL
#define EXTFLASH_LOGO_SIZE {by_name['LOGO'][2]}UL
@ -167,7 +180,7 @@ def main() -> None:
#define EXTFLASH_BIN2_SONG_HAITIAN_ADDR 0x{OFF_BIN2:08X}UL
#define EXTFLASH_BIN2_SONG_HAITIAN_SIZE {by_name['BIN2_SONG_HAITIAN'][2]}UL
#define EXTFLASH_BIN3_UNIVERSAL_ADDR 0x{OFF_BIN3:08X}UL
#define EXTFLASH_BIN3_UNIVERSAL_ADDR 0x{off_bin3:08X}UL
#define EXTFLASH_BIN3_UNIVERSAL_SIZE {by_name['BIN3_UNIVERSAL'][2]}UL
/* Convenience aliases used by UI ADDRESS */
@ -176,7 +189,7 @@ def main() -> None:
#define FLASH_ADDR_SONG_HAITIAN EXTFLASH_BIN2_SONG_HAITIAN_ADDR
#define FLASH_ADDR_MODE_UNIVERSAL EXTFLASH_BIN3_UNIVERSAL_ADDR /* 万能 */
/* Legacy AutoBand bank not repacked in 0903; left unchanged in firmware */
/* Legacy AutoBand bank not repacked; may overlap 2.bin do not enable until remapped */
#define FLASH_ADDR_AUTOBAND_LEGACY 0x0009EB5FUL
#define EXTFLASH_TONE_PACK_END 0x{cursor:08X}UL
@ -188,12 +201,13 @@ def main() -> None:
OUT_HDR.write_text(hdr, encoding="utf-8", newline="\n")
map_lines = [
"K1 external Flash map — tone pack 0903",
f"K1 external Flash map — tone pack {TONE_TAG}",
f"Packed file: Code/.../tools/out/{OUT_BIN.name} ({len(packed)} bytes)",
f"Also: {OUT_RES.name}; ALL.res when ui0902_res.bin present",
f"Ends at 0x{cursor:X}; UI0902_RES_BASE=0x00100000; free gap={0x100000 - cursor} bytes",
f"Ends at 0x{cursor:X}; UI0902_RES_BASE=0x00100000; free gap={UI0902_RES_BASE - cursor} bytes",
"",
"NOTE: BIN3 is FIXED at 0x000A71AC. 2.bin must be <= 41KB and <= slot 41263 bytes.",
f"NOTE: BIN3 follows 2.bin @ 0x{off_bin3:X} (2.bin grew past old fixed 0xA71AC).",
" 2.bin must be <= 41KB; LOGO/CHARG/BIN1/BIN2 offsets remain fixed.",
"",
f"{'Name':<22} {'Offset':>10} {'Size':>10} Note",
"-" * 72,
@ -205,17 +219,16 @@ def main() -> None:
"Firmware ADDRESS mapping:",
" 普通/专业 -> FLASH_ADDR_MODE_NORMAL/EXPERT (1.bin @ 0x1B8F0)",
" 本地曲目 -> FLASH_ADDR_SONG_HAITIAN (2.bin @ 0x9D07D, max 41KB)",
" 万能 -> FLASH_ADDR_MODE_UNIVERSAL (3.bin @ 0xA71AC FIXED)",
" AutoBand -> FLASH_ADDR_AUTOBAND_LEGACY 0x9EB5F (unchanged; overlaps 2.bin region — do not enable until remapped)",
f" 万能 -> FLASH_ADDR_MODE_UNIVERSAL (3.bin @ 0x{off_bin3:X})",
" Boot logo -> UI0902_BOOT_LOGO_ADDR (full-screen); packed logo.bin only pads 0x0..0xCB70",
" Charging -> EXTFLASH_CHARGING_ADDR",
" Charging -> EXTFLASH_CHARGING_ADDR (legacy pad) / UI0902_CHARGE_SCREEN",
"",
"Add a local song:",
" 1. Pack the new preset into 2.bin (must stay <= 41KB / 41263 slot)",
" 2. Append a row to local_songs.csv (index,code,name)",
" 3. python tools/pack_extflash_tone_0903.py (also regenerates LocalSongNames.h)",
" 4. Rebuild firmware and flash MCU + ExtFlash ALL.res",
"Rebuild after pack:",
" 1. python tools/pack_extflash_tone_0903.py",
" 2. Rebuild MCU (ExtFlash_Tone_Addr.h updated)",
" 3. Flash MCU + ExtFlash ALL.res",
]
OUT_MAP.parent.mkdir(parents=True, exist_ok=True)
OUT_MAP.write_text("\n".join(map_lines) + "\n", encoding="utf-8", newline="\n")
print(f"Wrote {OUT_BIN} ({len(packed)} bytes)")
@ -224,32 +237,42 @@ def main() -> None:
print(f"Wrote {OUT_MAP}")
for name, off, size, note in rows:
print(f" 0x{off:08X} {size:8d} {name} {note}")
if cursor > UI0902_RES_BASE:
raise SystemExit("ERROR: pack overflows into UI0902_RES_BASE")
print(f"OK: {UI0902_RES_BASE - cursor} bytes free before UI0902 @ 0x{UI0902_RES_BASE:X}")
print("OK: BIN1/BIN2/BIN3 present at fixed addresses and match source files")
print(f"OK: 2.bin size {len(bin2_data)} <= 41KB ({MAX_SONG_BIN_BYTES})")
print("OK: LOGO/CHARG/BIN1/BIN2/BIN3 present and match source files")
print(f"OK: 2.bin size {len(bin2_data)} <= 41KB; BIN3 @ 0x{off_bin3:X}")
if UI0902_BIN.is_file():
ui = UI0902_BIN.read_bytes()
all_res = packed + (b"\xFF" * (UI0902_RES_BASE - len(packed))) + ui
require_region_equals(all_res, OFF_BIN1, bin1_data, "ALL.res BIN1")
require_region_equals(all_res, OFF_BIN2, bin2_data, "ALL.res BIN2")
require_region_equals(all_res, OFF_BIN3, bin3_data, "ALL.res BIN3")
require_region_equals(all_res, off_bin3, bin3_data, "ALL.res BIN3")
dab_ok(all_res, OFF_BIN1, 31)
dab_ok(all_res, OFF_BIN2, 1)
dab_ok(all_res, OFF_BIN3, 3)
dab_ok(all_res, off_bin3, 3)
# UI0902 magic/presence: non-empty and starts within expected region
if len(ui) < 1024:
raise SystemExit(f"UI0902 pack suspiciously small: {len(ui)}")
OUT_ALL_RES.write_bytes(all_res)
# Mirror under repo tools/out for publish/consumers
OUT_ALL_LEGACY.write_bytes(all_res)
repo_out = REPO / "tools" / "out"
repo_out.mkdir(parents=True, exist_ok=True)
(repo_out / OUT_ALL_RES.name).write_bytes(all_res)
(repo_out / OUT_ALL_LEGACY.name).write_bytes(all_res)
(repo_out / OUT_RES.name).write_bytes(packed)
print(f"Wrote {OUT_ALL_RES} ({len(all_res)} bytes) — verified 1/2/3.bin")
(repo_out / OUT_RES_LEGACY.name).write_bytes(packed)
print(f"Wrote {OUT_ALL_RES} ({len(all_res)} bytes) — verified logo/charg/1/2/3 + UI0902")
else:
print(f"WARN: {UI0902_BIN} missing; skipped ALL.res")
# Prefer 0914 CSV; fall back to 0903
csv_0914 = TONE_DIR / "local_songs.csv"
gen = ROOT / "tools" / "gen_local_song_names.py"
if not csv_0914.is_file():
src = REPO / "Doc" / "音色文件" / "0903" / "local_songs.csv"
if src.is_file():
csv_0914.write_bytes(src.read_bytes())
print(f"Copied {src.name} -> {csv_0914}")
r = subprocess.run([sys.executable, str(gen)], check=False)
if r.returncode != 0:
raise SystemExit(f"gen_local_song_names.py failed ({r.returncode})")

View File

@ -11,6 +11,7 @@ Boot must also be updated so USB IAP wait screen can show UI0902_FLASH_MODE.
from __future__ import annotations
import hashlib
import re
import shutil
import subprocess
import zipfile
@ -29,14 +30,15 @@ BOOT_BIN = (
/ "Exe"
/ "AT32F403ARCT7_BOOT.bin"
)
TONE_BIN = PROJ / "tools" / "out" / "extflash_tone_0903.bin"
TONE_RES_VER = "0914"
TONE_BIN = PROJ / "tools" / "out" / f"extflash_tone_{TONE_RES_VER}.bin"
UI0902_BIN = PROJ / "tools" / "out" / "ui0902_res.bin"
MAP_TXT = REPO / "Doc" / "音色文件" / "0903" / "FLASH_MAP.txt"
MAP_TXT = REPO / "Doc" / "音色文件" / TONE_RES_VER / "FLASH_MAP.txt"
TONE_HDR = PROJ / "project" / "inc" / "ExtFlash_Tone_Addr.h"
IAP_DIR = REPO / "升级" / "MCU主控升级"
OUT_ROOT = REPO / "tools" / "out"
FW_VER = "0.2.9"
TONE_RES_VER = "0903"
FW_VER = "0.2.10"
UI0902_RES_BASE = 0x00100000
BOOT_FLASH_ADDR = 0x08000000
APP_FLASH_ADDR = 0x08008000
@ -69,17 +71,46 @@ def build_combined_extflash(tone: bytes, ui: bytes) -> bytes:
return tone + (b"\xFF" * pad) + ui
def verify_tone_layout(tone: bytes, check_sources: bool = True) -> None:
"""Refuse to ship ALL.res if 1/2/3.bin missing, shifted, or content mismatch."""
dab = b"\xABDAB"
sources = {
"BIN1": (0x0001B8F0, 31, REPO / "Doc" / "音色文件" / "0903" / "1.bin"),
"BIN2": (0x0009D07D, 1, REPO / "Doc" / "音色文件" / "0903" / "2.bin"),
"BIN3": (0x000A71AC, 3, REPO / "Doc" / "音色文件" / "0903" / "3.bin"),
def read_tone_addrs() -> dict[str, int]:
text = TONE_HDR.read_text(encoding="utf-8", errors="replace")
def grab(name: str) -> int:
m = re.search(rf"#define\s+{name}\s+0x([0-9A-Fa-f]+)UL", text)
if not m:
raise SystemExit(f"missing {name} in {TONE_HDR}")
return int(m.group(1), 16)
return {
"LOGO": grab("EXTFLASH_LOGO_ADDR"),
"CHARGING": grab("EXTFLASH_CHARGING_ADDR"),
"BIN1": grab("EXTFLASH_BIN1_RHYTHM_ADDR"),
"BIN2": grab("EXTFLASH_BIN2_SONG_HAITIAN_ADDR"),
"BIN3": grab("EXTFLASH_BIN3_UNIVERSAL_ADDR"),
}
for name, (off, want_cnt, src_path) in sources.items():
def verify_tone_layout(tone: bytes, check_sources: bool = True) -> None:
"""Refuse to ship ALL.res if logo/charg/1/2/3.bin missing, shifted, or mismatch."""
dab = b"\xABDAB"
addrs = read_tone_addrs()
tone_dir = REPO / "Doc" / "音色文件" / TONE_RES_VER
ziliao = None
for p in REPO.iterdir():
if p.is_dir() and (p / "logo.bin").is_file() and (p / "Charg.bin").is_file():
ziliao = p
break
if ziliao is None:
raise SystemExit("logo.bin/Charg.bin not found under repo")
sources = [
("LOGO", addrs["LOGO"], None, ziliao / "logo.bin"),
("CHARGING", addrs["CHARGING"], None, ziliao / "Charg.bin"),
("BIN1", addrs["BIN1"], 31, tone_dir / "1.bin"),
("BIN2", addrs["BIN2"], 1, tone_dir / "2.bin"),
("BIN3", addrs["BIN3"], 3, tone_dir / "3.bin"),
]
for name, off, want_cnt, src_path in sources:
if off + 16 > len(tone):
raise SystemExit(f"verify {name}: tone pack too short for 0x{off:X}")
if want_cnt is not None:
magic = tone[off : off + 4]
cnt = int.from_bytes(tone[off + 12 : off + 16], "little")
if magic != dab or cnt != want_cnt:
@ -93,7 +124,10 @@ def verify_tone_layout(tone: bytes, check_sources: bool = True) -> None:
raise SystemExit(f"verify BIN2: source 2.bin {len(src)} > 41KB")
if tone[off : off + len(src)] != src:
raise SystemExit(f"verify {name}: content @0x{off:X} != {src_path}")
print("verify tone layout: BIN1/BIN2/BIN3 addresses + content OK")
print(
"verify tone layout: LOGO/CHARG/BIN1/BIN2/BIN3 OK "
f"(BIN3@0x{addrs['BIN3']:X})"
)
def main() -> None:
@ -102,9 +136,17 @@ def main() -> None:
if not EXE_BIN.is_file():
raise SystemExit(f"missing MCU bin: {EXE_BIN}")
if not TONE_BIN.is_file():
# fallback legacy alias written by packer
legacy = PROJ / "tools" / "out" / "extflash_tone_0903.bin"
if legacy.is_file():
global TONE_BIN
TONE_BIN = legacy
else:
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)")
if not TONE_HDR.is_file():
raise SystemExit(f"missing {TONE_HDR}")
iap = IAP_DIR / "SoundWalkerIAP.exe"
guide = IAP_DIR / "升级步骤.docx"
@ -126,12 +168,15 @@ def main() -> None:
tone_data = TONE_BIN.read_bytes()
verify_tone_layout(tone_data)
ui_data = UI0902_BIN.read_bytes()
if len(ui_data) < 1024:
raise SystemExit(f"UI0902 pack too small: {len(ui_data)}")
combined = build_combined_extflash(tone_data, ui_data)
verify_tone_layout(combined) # same offsets in ALL.res
verify_tone_layout(combined)
addrs = read_tone_addrs()
boot_name = f"AT32F403ARCT7_BOOT_v{FW_VER}_{stamp}.bin"
mcu_name = f"YNGJ-GT1-M_MCU_v{FW_VER}_{stamp}.bin"
comb_name = f"extflash_ALL_tone0903_ui0902_{stamp}.res"
comb_name = f"extflash_ALL_tone{TONE_RES_VER}_ui0902_{stamp}.res"
boot_dst = pkg / boot_name
mcu_dst = pkg / mcu_name
@ -147,9 +192,8 @@ def main() -> None:
shutil.copy2(boot_dst, OUT_ROOT / boot_name)
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)
shutil.copy2(comb_dst, OUT_ROOT / f"extflash_ALL_tone{TONE_RES_VER}_ui0902.res")
(OUT_ROOT / f"extflash_tone_{TONE_RES_VER}.res").write_bytes(tone_data)
(OUT_ROOT / "extflash_ui0902.res").write_bytes(ui_data)
readme = pkg / "README.txt"
@ -176,7 +220,8 @@ def main() -> None:
"",
"外部 Flash 分区:",
" 0x00000000 音色/充电图 (toneRes)",
" logo pad + Charg@0xCB70 + 1.bin@0x1B8F0 + 2.bin@0x9D07D + 3.bin@0xA71AC(FIXED)",
f" logo@0x{addrs['LOGO']:X} + Charg@0x{addrs['CHARGING']:X} + "
f"1.bin@0x{addrs['BIN1']:X} + 2.bin@0x{addrs['BIN2']:X} + 3.bin@0x{addrs['BIN3']:X}",
" 0x00100000 UI0902 图片 (模式选择、开机 Logo、充电、烧录模式、设置/调音台等)",
" 0x001D2000 UI0902_FLASH_MODE 烧录模式全屏图Boot 读取)",
"",
@ -189,9 +234,8 @@ def main() -> None:
"",
"说明:",
" - Boot 进入 USB IAP 时显示 UI0902_FLASH_MODE资源未烧录时白字黑底兜底。",
" - 烧录模式图来源K1标准界面图 0904/烧录模式.png。",
" - 开机全屏 Logo、关机充电全屏画面在 UI0902不在音色包里的 logo.bin/Charg.bin 占位。",
" - AutoBand 0x9EB5F 本版未重排",
f" - 本包音色来源 Doc/音色文件/{TONE_RES_VER}/BIN3 随 2.bin 长度后移",
"",
"附件: SoundWalkerIAP.exe / 升级步骤.docx / FLASH_MAP.txt",
"",