167 lines
6.6 KiB
Python
167 lines
6.6 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""Pack external W25Q128 tone/logo image for K1 (0903).
|
|
|
|
Layout (absolute W25Q128 offsets):
|
|
0x00000000 logo.bin (legacy pad; boot UI uses UI0902 full-screen logo)
|
|
0x0000CB70 Charg.bin (160x190 RGB565 charging art)
|
|
0x0001B8F0 1.bin 普通/专业 31 rhythms
|
|
0x0009D07D 2.bin 海阔天空
|
|
0x000A8607 3.bin 万能模式
|
|
|
|
Outputs:
|
|
tools/out/extflash_tone_0903.bin
|
|
project/inc/ExtFlash_Tone_Addr.h
|
|
Doc/音色文件/0903/FLASH_MAP.txt
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
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_HDR = ROOT / "project" / "inc" / "ExtFlash_Tone_Addr.h"
|
|
OUT_MAP = REPO / "Doc" / "音色文件" / "0903" / "FLASH_MAP.txt"
|
|
|
|
TONE_DIR = REPO / "Doc" / "音色文件" / "0903"
|
|
|
|
|
|
def find_ziliao() -> Path:
|
|
for p in REPO.iterdir():
|
|
if p.is_dir() and (p / "logo.bin").is_file() and (p / "Charg.bin").is_file():
|
|
return p
|
|
raise FileNotFoundError("资料/logo.bin + Charg.bin not found under repo")
|
|
|
|
|
|
def main() -> None:
|
|
ziliao = find_ziliao()
|
|
parts = [
|
|
("LOGO", ziliao / "logo.bin", 0x00000000, "legacy pad; boot uses UI0902_BOOT_LOGO"),
|
|
("CHARGING", ziliao / "Charg.bin", 0x0000CB70, "160x190 RGB565"),
|
|
("BIN1_RHYTHM", TONE_DIR / "1.bin", 0x0001B8F0, "普通/专业 31 条节奏"),
|
|
("BIN2_SONG_HAITIAN", TONE_DIR / "2.bin", None, "本地曲目 海阔天空"),
|
|
("BIN3_UNIVERSAL", TONE_DIR / "3.bin", None, "万能模式"),
|
|
]
|
|
|
|
blobs = []
|
|
cursor = 0
|
|
rows = []
|
|
for name, path, force_off, note in parts:
|
|
data = path.read_bytes()
|
|
if force_off is not None:
|
|
if cursor > force_off:
|
|
raise SystemExit(f"{name}: cursor 0x{cursor:X} past forced 0x{force_off:X}")
|
|
if cursor < force_off:
|
|
pad = force_off - cursor
|
|
blobs.append(b"\xFF" * pad)
|
|
cursor = force_off
|
|
rows.append((f"(pad)", force_off - pad, pad, "gap fill 0xFF"))
|
|
off = cursor
|
|
blobs.append(data)
|
|
cursor += len(data)
|
|
rows.append((name, off, len(data), f"{note} <- {path.name}"))
|
|
|
|
OUT_DIR.mkdir(parents=True, exist_ok=True)
|
|
packed = b"".join(blobs)
|
|
OUT_BIN.write_bytes(packed)
|
|
|
|
# named lookup
|
|
by_name = {r[0]: r for r in rows if not r[0].startswith("(")}
|
|
|
|
hdr = f"""#ifndef __EXTFLASH_TONE_ADDR_H
|
|
#define __EXTFLASH_TONE_ADDR_H
|
|
|
|
/* 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_MAJOR 0
|
|
#define EXTFLASH_TONE_RES_VER_MINOR 9
|
|
#define EXTFLASH_TONE_RES_VER_PATCH 3
|
|
|
|
#define EXTFLASH_LOGO_ADDR 0x{by_name['LOGO'][1]:08X}UL
|
|
#define EXTFLASH_LOGO_SIZE {by_name['LOGO'][2]}UL
|
|
|
|
#define EXTFLASH_CHARGING_ADDR 0x{by_name['CHARGING'][1]:08X}UL
|
|
#define EXTFLASH_CHARGING_SIZE {by_name['CHARGING'][2]}UL
|
|
#define EXTFLASH_CHARGING_W 160
|
|
#define EXTFLASH_CHARGING_H 190
|
|
|
|
#define EXTFLASH_BIN1_RHYTHM_ADDR 0x{by_name['BIN1_RHYTHM'][1]:08X}UL
|
|
#define EXTFLASH_BIN1_RHYTHM_SIZE {by_name['BIN1_RHYTHM'][2]}UL
|
|
|
|
#define EXTFLASH_BIN2_SONG_HAITIAN_ADDR 0x{by_name['BIN2_SONG_HAITIAN'][1]:08X}UL
|
|
#define EXTFLASH_BIN2_SONG_HAITIAN_SIZE {by_name['BIN2_SONG_HAITIAN'][2]}UL
|
|
|
|
#define EXTFLASH_BIN3_UNIVERSAL_ADDR 0x{by_name['BIN3_UNIVERSAL'][1]:08X}UL
|
|
#define EXTFLASH_BIN3_UNIVERSAL_SIZE {by_name['BIN3_UNIVERSAL'][2]}UL
|
|
|
|
/* Convenience aliases used by UI ADDRESS */
|
|
#define FLASH_ADDR_MODE_NORMAL EXTFLASH_BIN1_RHYTHM_ADDR /* 普通 */
|
|
#define FLASH_ADDR_MODE_EXPERT EXTFLASH_BIN1_RHYTHM_ADDR /* 专业 */
|
|
#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 */
|
|
#define FLASH_ADDR_AUTOBAND_LEGACY 0x0009EB5FUL
|
|
|
|
#define EXTFLASH_TONE_PACK_END 0x{cursor:08X}UL
|
|
#define EXTFLASH_TONE_PACK_SIZE {cursor}UL
|
|
#define UI0902_RES_BASE_SAFE_GAP (0x00100000UL - EXTFLASH_TONE_PACK_END)
|
|
|
|
#endif
|
|
"""
|
|
OUT_HDR.write_text(hdr, encoding="utf-8", newline="\n")
|
|
|
|
map_lines = [
|
|
"K1 external Flash map — tone pack 0903",
|
|
f"Packed file: Code/.../tools/out/{OUT_BIN.name} ({len(packed)} bytes)",
|
|
f"Ends at 0x{cursor:X}; UI0902_RES_BASE=0x00100000; free gap={0x100000 - cursor} bytes",
|
|
"",
|
|
f"{'Name':<22} {'Offset':>10} {'Size':>10} Note",
|
|
"-" * 72,
|
|
]
|
|
for name, off, size, note in rows:
|
|
map_lines.append(f"{name:<22} 0x{off:08X} {size:10d} {note}")
|
|
map_lines += [
|
|
"",
|
|
"Firmware ADDRESS mapping:",
|
|
" 普通/专业 -> FLASH_ADDR_MODE_NORMAL/EXPERT (1.bin)",
|
|
" 海阔天空 -> FLASH_ADDR_SONG_HAITIAN (2.bin)",
|
|
" 万能 -> FLASH_ADDR_MODE_UNIVERSAL (3.bin)",
|
|
" AutoBand -> FLASH_ADDR_AUTOBAND_LEGACY 0x9EB5F (unchanged; overlaps 2.bin region — do not enable until remapped)",
|
|
" Boot logo -> UI0902_BOOT_LOGO_ADDR (full-screen); packed logo.bin only pads 0x0..0xCB70",
|
|
" Charging -> EXTFLASH_CHARGING_ADDR",
|
|
"",
|
|
"Add a local song:",
|
|
" 1. Pack the new preset into 2.bin",
|
|
" 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",
|
|
]
|
|
OUT_MAP.write_text("\n".join(map_lines) + "\n", encoding="utf-8", newline="\n")
|
|
|
|
print(f"Wrote {OUT_BIN} ({len(packed)} bytes)")
|
|
print(f"Wrote {OUT_HDR}")
|
|
print(f"Wrote {OUT_MAP}")
|
|
for name, off, size, note in rows:
|
|
print(f" 0x{off:08X} {size:8d} {name} {note}")
|
|
if cursor > 0x00100000:
|
|
raise SystemExit("ERROR: pack overflows into UI0902_RES_BASE")
|
|
print(f"OK: {0x00100000 - cursor} bytes free before UI0902 @ 0x00100000")
|
|
|
|
# Keep LocalSongNames.h in sync with Doc/.../local_songs.csv when packing tones.
|
|
gen = ROOT / "tools" / "gen_local_song_names.py"
|
|
r = subprocess.run([sys.executable, str(gen)], check=False)
|
|
if r.returncode != 0:
|
|
raise SystemExit(f"gen_local_song_names.py failed ({r.returncode})")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|