89 lines
3.0 KiB
Python
89 lines
3.0 KiB
Python
#!/usr/bin/env python3
|
|
"""extract_si4684_blob.py — convert Si4684 C arrays or copies to .bin files.
|
|
|
|
DigiRadio firmware — https://github.com/manvalan/DigiRadio
|
|
|
|
Copyright 2026 Michele Bigi
|
|
SPDX-License-Identifier: Apache-2.0
|
|
|
|
Usage:
|
|
# From a SigmaStudio-style C header (PE5PVB firmware.h, Si468xROM.h):
|
|
tools/extract_si4684_blob.py --header src/firmware.h --array firmware \\
|
|
--out Firmware/Si4684-Firmware/dab_firmware.bin
|
|
|
|
# Copy an existing Skyworks .bin from an eval-board folder:
|
|
tools/extract_si4684_blob.py --copy ~/si46xx_firmware/fm_radio_5_1_0.bin \\
|
|
--out Firmware/Si4684-Firmware/fm_firmware.bin
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import re
|
|
import shutil
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
|
|
def extract_c_array(header: Path, array_name: str) -> bytes:
|
|
text = header.read_text(encoding="utf-8", errors="replace")
|
|
pattern = rf"{re.escape(array_name)}\s*\[\s*\]\s*=\s*\{{(.*?)\}};"
|
|
match = re.search(pattern, text, re.S)
|
|
if not match:
|
|
raise SystemExit(f"array {array_name!r} not found in {header}")
|
|
values = re.findall(r"0x[0-9a-fA-F]+", match.group(1))
|
|
if not values:
|
|
raise SystemExit(f"array {array_name!r} is empty in {header}")
|
|
return bytes(int(v, 16) for v in values)
|
|
|
|
|
|
def extract_flash_region(dump: Path, offset: int, size: int) -> bytes:
|
|
data = dump.read_bytes()
|
|
end = offset + size
|
|
if end > len(data):
|
|
raise SystemExit(
|
|
f"flash dump too small: need {end} bytes, have {len(data)}"
|
|
)
|
|
return data[offset:end]
|
|
|
|
|
|
def main() -> int:
|
|
ap = argparse.ArgumentParser(description=__doc__)
|
|
ap.add_argument("--out", type=Path, required=True, help="output .bin path")
|
|
src = ap.add_mutually_exclusive_group(required=True)
|
|
src.add_argument("--header", type=Path, help="C header with byte array")
|
|
src.add_argument("--copy", type=Path, help="existing firmware .bin to copy")
|
|
src.add_argument("--flash-dump", type=Path, help="full SPI flash dump")
|
|
ap.add_argument("--array", help="array name when using --header")
|
|
ap.add_argument(
|
|
"--offset",
|
|
type=lambda s: int(s, 0),
|
|
default=0x106000,
|
|
help="byte offset for --flash-dump (default: FM region from dirb.tech)",
|
|
)
|
|
ap.add_argument(
|
|
"--size",
|
|
type=lambda s: int(s, 0),
|
|
default=0x81721,
|
|
help="byte length for --flash-dump (default: FM V5.1.0 size)",
|
|
)
|
|
args = ap.parse_args()
|
|
|
|
if args.header is not None:
|
|
if not args.array:
|
|
ap.error("--array is required with --header")
|
|
payload = extract_c_array(args.header, args.array)
|
|
elif args.copy is not None:
|
|
payload = args.copy.read_bytes()
|
|
else:
|
|
payload = extract_flash_region(args.flash_dump, args.offset, args.size)
|
|
|
|
args.out.parent.mkdir(parents=True, exist_ok=True)
|
|
args.out.write_bytes(payload)
|
|
print(f"wrote {args.out} ({len(payload)} bytes)")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|