#!/usr/bin/env python3
"""Maakt de 50kmh-flashbestanden uit de Mac-werkbestanden (2-10-2026).
Gebruik:
  python3 patch50.py mainware  <in> <out>   in = patched-mainware-1.9.3.pack (195860 B)
                                          of  x3-update-...pack (283492 B, mainware-lid word eruit getrokken)
  python3 patch50.py motorware <in> <out>   in = motorware-gain70-v2.bin (61720 B)
Abort zonder schrijven zodra iets niet exact klopt."""
import struct, sys

def stm32_crc(data):
    crc = 0xFFFFFFFF
    for i in range(0, len(data) - len(data) % 4, 4):
        crc ^= struct.unpack_from("<I", data, i)[0]
        for _ in range(32):
            crc = ((crc << 1) ^ 0x04C11DB7) & 0xFFFFFFFF if crc & 0x80000000 else (crc << 1) & 0xFFFFFFFF
    return crc

def ware_crc(blob):
    b = bytearray(blob)
    b[8:12] = b"\xff" * 4
    b[12:16] = b"\xff" * 4
    b += b"\xff" * ((-len(b)) % 4)
    return stm32_crc(bytes(b))

def fix(blob, off, old, new):
    if blob[off:off + len(old)] != old:
        sys.exit(f"ABORT @0x{off:X}: verwacht {old.hex()}, vond {bytes(blob[off:off+len(old)]).hex()}")
    blob[off:off + len(new)] = new

def main():
    if len(sys.argv) != 4 or sys.argv[1] not in ("mainware", "motorware"):
        sys.exit(__doc__)
    kind, src, dst = sys.argv[1], sys.argv[2], sys.argv[3]
    d = bytearray(open(src, "rb").read())
    if kind == "mainware":
        if len(d) == 283492:
            off, tsz = struct.unpack_from("<II", d, 4)
            if d[:4] != b"PACK" or (off, tsz) != (0x452E4, 0x80):
                sys.exit("ABORT: x3-pack ziet er anders uit dan verwacht")
            w = bytearray(d[0x1561C:0x1561C + 0x2FCC8])
        elif len(d) == 195860 and d[:4] == b"PACK":
            w = bytearray(d[0x0C:0x0C + 0x2FCC8])
        else:
            sys.exit("ABORT: onbekende bron voor mainware (lengte/magic)")
        if ware_crc(w) == 0x7C20DC01:
            sys.exit("ABORT: dit is al de 50kmh-versie")
        if ware_crc(w) != 0x119F64BC:
            sys.exit("ABORT: lid-CRC is niet de bekende basis 0x119f64bc")
        fix(w, 0x2F966, b"\x7c", b"\xf4")
        fix(w, 0x2F9DE, b"\x7c", b"\xf4")
        struct.pack_into("<I", w, 8, ware_crc(w))
        got = struct.unpack_from("<I", w, 8)[0]
        if got != 0x7C20DC01:
            sys.exit(f"ABORT mainware: lid-CRC {got:#010x} <> 0x7c20dc01")
        print(f"mainware-50kmh lid OK, CRC {got:#010x}")
        hdr = b"PACK" + struct.pack("<II", 0x2FCD4, 0x40)
        tab = b"mainware.bin".ljust(0x38, b"\0") + struct.pack("<II", 0x0C, len(w))
        out = hdr + bytes(w) + tab
        if len(out) != 195860:
            sys.exit("ABORT: output-lengte klopt niet")
    else:
        if struct.unpack_from("<I", d, 0)[0] != 0xAA55AA55 or len(d) != 61720:
            sys.exit("ABORT: geen 55AA-magic of verkeerde lengte")
        if ware_crc(d) == 0x94D27850:
            sys.exit("ABORT: dit is al de v4-versie")
        if ware_crc(d) != 0x0193A8C5:
            sys.exit("ABORT: lid-CRC is niet de bekende v2-basis 0x0193a8c5")
        for off, old, new in [(0x05, b"\x19", b"\x1c"), (0x27C, b"\x19", b"\x1c"),
                              (0x289, b"\x19", b"\x1c"), (0x1299, b"\x28", b"\x3c"),
                              (0x1F1C, b"\x8c", b"\x5c"), (0x79B4, b"\x0c", b"\x32")]:
            fix(d, off, old, new)
        struct.pack_into("<I", d, 8, ware_crc(d))
        got = struct.unpack_from("<I", d, 8)[0]
        if got != 0x94D27850:
            sys.exit(f"ABORT motorware: lid-CRC {got:#010x} <> 0x94d27850")
        print(f"motorware-v4 lid OK, CRC {got:#010x}")
        out = bytes(d)
    open(dst, "wb").write(out)
    print(f"geschreven: {dst} ({len(out)} B)")

main()
