#!/usr/bin/env python3 # -*- coding: utf-8 -*- """Generate project/inc/LocalSongNames.h from Doc/音色文件/0903/local_songs.csv. CSV columns: index,code,name index — preset index in 2.bin (0-based) code — ASCII code in bin preset name (e.g. HKTK from 1.HKTK) name — display name (Unicode); written as GBK byte escapes for the LCD font Run standalone or from pack_extflash_tone_0903.py after packing 2.bin. """ from __future__ import annotations import csv import sys from pathlib import Path ROOT = Path(__file__).resolve().parents[1] REPO = ROOT.parent.parent CSV_PATH = REPO / "Doc" / "音色文件" / "0903" / "local_songs.csv" OUT_HDR = ROOT / "project" / "inc" / "LocalSongNames.h" def gbk_c_string(text: str) -> str: raw = text.encode("gbk") return "".join(f"\\x{b:02X}" for b in raw) def load_rows(path: Path) -> list[dict]: if not path.is_file(): raise FileNotFoundError(f"missing {path}") with path.open("r", encoding="utf-8-sig", newline="") as f: reader = csv.DictReader(f) rows = list(reader) if not rows: raise SystemExit(f"{path}: no song rows") out = [] for i, row in enumerate(rows): try: idx = int((row.get("index") or "").strip()) except ValueError as e: raise SystemExit(f"{path}: bad index on row {i + 2}: {row!r}") from e code = (row.get("code") or "").strip() name = (row.get("name") or "").strip() if not code or not name: raise SystemExit(f"{path}: empty code/name on row {i + 2}") out.append({"index": idx, "code": code, "name": name}) out.sort(key=lambda r: r["index"]) for expect, row in enumerate(out): if row["index"] != expect: raise SystemExit( f"{path}: indices must be contiguous from 0; " f"expected {expect}, got {row['index']}" ) return out def write_header(rows: list[dict], path: Path) -> None: n = len(rows) name_lines = [] code_lines = [] for r in rows: esc = gbk_c_string(r["name"]) name_lines.append(f' "{esc}", /* {r["index"]}: {r["name"]} */') code_lines.append(f' "{r["code"]}", /* {r["index"]} */') body = f"""#ifndef __LOCAL_SONG_NAMES_H #define __LOCAL_SONG_NAMES_H /* Auto-generated by tools/gen_local_song_names.py from local_songs.csv — do not hand-edit. */ /* Display names are GBK for LCD_ShowMixedString; codes match 2.bin preset ASCII (e.g. HKTK). */ #define LOCAL_SONG_COUNT {n} static const char * const LocalSongNameGbk[LOCAL_SONG_COUNT] = {{ {chr(10).join(name_lines)} }}; static const char * const LocalSongCode[LOCAL_SONG_COUNT] = {{ {chr(10).join(code_lines)} }}; #endif /* __LOCAL_SONG_NAMES_H */ """ path.parent.mkdir(parents=True, exist_ok=True) path.write_text(body, encoding="utf-8", newline="\n") def main() -> int: rows = load_rows(CSV_PATH) write_header(rows, OUT_HDR) print(f"Wrote {OUT_HDR} ({len(rows)} song(s) from {CSV_PATH.name})") for r in rows: print(f" [{r['index']}] {r['code']} {r['name']}") return 0 if __name__ == "__main__": sys.exit(main())