100 lines
3.1 KiB
Python
100 lines
3.1 KiB
Python
#!/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())
|