mirror of
https://github.com/armel/uv-k1-k5v3-firmware-custom.git
synced 2026-10-02 11:08:20 +00:00
108 lines
4.6 KiB
Python
Executable File
108 lines
4.6 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# Shared builder for overlay-app read-only assets (served by api->asset_read).
|
|
#
|
|
# Each app has a gen_assets.py that declares its texts and data with this
|
|
# module, then calls Assets.main(): it writes the asset blob packed into the
|
|
# .app by pack_app.py, and a C header of offsets the app includes. The header
|
|
# is generated before compiling, so code and data can never drift apart.
|
|
#
|
|
# a = Assets("SNAKE")
|
|
# a.text("T_PAUSE", "PAUSE") # NUL-terminated string
|
|
# a.table("T_STATE", ["TX", "RX"]) # fixed-stride strings (+ _STRIDE)
|
|
# a.u16("SPR_HEAD", [0x6566, 0x0FD2]) # packed little-endian data
|
|
# a.main() # argv: <out.bin> <out.h>
|
|
#
|
|
# Texts come first, so their offsets stay small (cheap immediates in Thumb
|
|
# code). TEXT_MAX is the longest text including its NUL: the size of the
|
|
# app's shared text buffer, read in one asset_read by T().
|
|
import os, re, struct, sys
|
|
|
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
|
|
|
def _cdefine(header, name):
|
|
pat = re.compile(r"^\s*#define\s+" + re.escape(name) + r"\s+(0[xX][0-9a-fA-F]+|\d+)[uUlL]*\b")
|
|
with open(os.path.join(HERE, header)) as f:
|
|
for line in f:
|
|
m = pat.match(line)
|
|
if m:
|
|
return int(m.group(1), 0)
|
|
sys.exit(f"{header}: #define {name} not found")
|
|
|
|
ASSET_MAX = _cdefine("app_overlay.h", "APP_ASSET_MAX")
|
|
|
|
class Assets:
|
|
def __init__(self, guard):
|
|
self.guard = guard
|
|
self.texts = [] # (name, bytes)
|
|
self.data = [] # (name, bytes)
|
|
self.extra = [] # (name, value) plain constants
|
|
self.names = set()
|
|
|
|
def _name(self, name):
|
|
if name in self.names or not re.fullmatch(r"[A-Z][A-Z0-9_]*", name):
|
|
sys.exit(f"asset name {name!r} is invalid or duplicated")
|
|
self.names.add(name)
|
|
|
|
def text(self, name, s):
|
|
self._name(name)
|
|
self.texts.append((name, s.encode("ascii") + b"\x00"))
|
|
|
|
def table(self, name, strings, stride=None):
|
|
"""Fixed-stride string table: entry i is at name + i * name_STRIDE."""
|
|
self._name(name)
|
|
width = stride or max(len(s) for s in strings) + 1
|
|
if any(len(s) + 1 > width for s in strings):
|
|
sys.exit(f"{name}: an entry does not fit the {width} B stride")
|
|
blob = b"".join(s.encode("ascii").ljust(width, b"\x00") for s in strings)
|
|
self.texts.append((name, blob))
|
|
self.const(name + "_STRIDE", width)
|
|
|
|
def const(self, name, value):
|
|
self._name(name)
|
|
self.extra.append((name, value))
|
|
|
|
def raw(self, name, payload):
|
|
self._name(name)
|
|
self.data.append((name, bytes(payload)))
|
|
|
|
def u8(self, name, values): self.raw(name, struct.pack(f"<{len(values)}B", *values))
|
|
def i8(self, name, values): self.raw(name, struct.pack(f"<{len(values)}b", *values))
|
|
def u16(self, name, values): self.raw(name, struct.pack(f"<{len(values)}H", *values))
|
|
def i16(self, name, values): self.raw(name, struct.pack(f"<{len(values)}h", *values))
|
|
def u32(self, name, values): self.raw(name, struct.pack(f"<{len(values)}I", *values))
|
|
|
|
def build(self):
|
|
blob = bytearray()
|
|
lines = []
|
|
text_max = 1
|
|
strides = dict(self.extra)
|
|
for name, payload in self.texts:
|
|
lines.append((name, len(blob), len(payload)))
|
|
text_max = max(text_max, strides.get(name + "_STRIDE", len(payload)))
|
|
blob += payload
|
|
for name, payload in self.data: # asset_read copies bytes: no alignment needed
|
|
lines.append((name, len(blob), len(payload)))
|
|
blob += payload
|
|
if len(blob) > ASSET_MAX:
|
|
sys.exit(f"assets {len(blob)} B exceed the {ASSET_MAX} B asset area")
|
|
return bytes(blob), lines, text_max
|
|
|
|
def main(self):
|
|
if len(sys.argv) != 3:
|
|
sys.exit("usage: gen_assets.py <out.bin> <out.h>")
|
|
blob, lines, text_max = self.build()
|
|
with open(sys.argv[1], "wb") as f:
|
|
f.write(blob)
|
|
guard = self.guard.upper() + "_ASSETS_H"
|
|
out = [f"/* Generated by gen_assets.py - do not edit ({len(blob)} B of assets). */",
|
|
f"#ifndef {guard}", f"#define {guard}", "",
|
|
f"#define TEXT_MAX {text_max}u /* longest text incl. NUL */"]
|
|
for name, off, size in lines:
|
|
out.append(f"#define {name} {off}u")
|
|
out.append(f"#define {name}_LEN {size}u")
|
|
for name, value in self.extra:
|
|
out.append(f"#define {name} {value}u")
|
|
out += ["", f"#endif /* {guard} */", ""]
|
|
with open(sys.argv[2], "w") as f:
|
|
f.write("\n".join(out))
|