#!/usr/bin/env python3 # Author: cirakg & claude ai # Summary: Generate source/codes/QrTables.mc (QR error-correction block structure) from segno's published constants. """QR block structure for versions 1-40 and levels L, M, Q, H (ISO/IEC 18004 table 9), taken from segno so it isn't typed by hand. Each entry is [ec codewords per block, blocks in group 1, data codewords per group-1 block, blocks in group 2, data codewords per group-2 block]. gen_qr_tables.py [--out source/codes/QrTables.mc] """ from __future__ import annotations import argparse from pathlib import Path from typing import Any, cast import segno.consts as c LEVELS = [c.ERROR_LEVEL_L, c.ERROR_LEVEL_M, c.ERROR_LEVEL_Q, c.ERROR_LEVEL_H] # segno.consts.ECC: version -> level -> tuple of EC(num_blocks, num_total, num_data); untyped in segno. ECC = cast(dict[int, dict[int, tuple[Any, ...]]], c.ECC) def table() -> list[list[int]]: rows = [] for version in range(1, 41): for level in LEVELS: groups = ECC[version][level] ec = groups[0].num_total - groups[0].num_data g1 = groups[0] g2 = groups[1] if len(groups) > 1 else None assert all(g.num_total - g.num_data == ec for g in groups) rows.append([ec, g1.num_blocks, g1.num_data, g2.num_blocks if g2 else 0, g2.num_data if g2 else 0]) return rows def alignment() -> list[list[int]]: """Alignment pattern centre coordinates per version (ISO/IEC 18004 annex E).""" # segno starts at version 2; version 1 has no alignment pattern. return [[]] + [list(c.ALIGNMENT_POS[v - 2]) for v in range(2, 41)] def render(rows: list[list[int]]) -> str: lines = [] for version in range(1, 41): cells = ", ".join(str(n) for level in range(4) for n in rows[(version - 1) * 4 + level]) lines.append(f" {cells}{'' if version == 40 else ','} // version {version}: L, M, Q, H") return ( "// Author: cirakg & claude ai\n" "// Summary: QR error-correction block structure per version and level (generated from segno; do not edit).\n" "//\n" "// Derived from Segno (BSD-3-Clause). See THIRD_PARTY_NOTICES.md and LICENSES/segno.txt.\n" "// Generated by tools/gen_qr_tables.py. For version v (1-40) and level e (0 L, 1 M, 2 Q, 3 H), the five\n" "// numbers at index ((v - 1) * 4 + e) * 5 are: EC codewords per block, group-1 blocks, data codewords\n" "// per group-1 block, group-2 blocks, data codewords per group-2 block (ISO/IEC 18004 table 9).\n\n" "import Toybox.Lang;\n\nmodule QrTables {\n const BLOCKS = [\n" + "\n".join(lines) + "\n ];\n\n // Alignment pattern centres (row and column coordinates) per version, from version 1.\n" + " const ALIGNMENT = [\n" + ",\n".join(f" {pos}" for pos in alignment()) + "\n ];\n}\n" ) def main(argv: list[str] | None = None) -> int: parser = argparse.ArgumentParser(description=__doc__.splitlines()[0]) parser.add_argument("--out", type=Path, default=Path("source/codes/QrTables.mc")) args = parser.parse_args(argv) args.out.write_text(render(table()), encoding="utf-8") print(f"wrote the QR block table to {args.out}") return 0 if __name__ == "__main__": raise SystemExit(main())