#!/usr/bin/env python3
"""
v5: тот же v3, но cruise byte = 8007 (i30 hardware variant), не 800E (Rio 1.4).
Минимальное изменение — 2 байта в калибровке + пересчёт CRC32 + ADD16 adjust.
"""
import os, hashlib

V3  = os.path.expanduser("~/Разработки/GTD-B46CQS3-C000_TE2_CRUISE_v3.bin")
OUT = os.path.expanduser("~/Разработки/GTD-B46CQS3-C000_TE2_CRUISE_v5.bin")

# CRC tables
def make_crc_table():
    POLY = 0xEDB88320
    tbl = []
    for i in range(256):
        c = i
        for _ in range(8):
            c = (c>>1)^POLY if c&1 else c>>1
        tbl.append(c)
    return tbl
CRC_TBL = make_crc_table()

def crc32(data, init=0xFADECAFE):
    c = init
    for b in data:
        c = CRC_TBL[(c^b)&0xFF] ^ (c>>8)
    return c & 0xFFFFFFFF

def add16(data, init=0xFADECAFE):
    """medc17 SB_ADD16_ALGO_E: sum 16-bit LE words, last word shifted << 16."""
    s = init
    n = len(data)
    pos = 0
    while pos <= n - 4:
        w = data[pos] | (data[pos+1] << 8)
        s = (s + w) & 0xFFFFFFFF
        pos += 2
    w = data[pos] | (data[pos+1] << 8)
    s = (s + (w << 16)) & 0xFFFFFFFF
    return s

# Load v3
with open(V3, "rb") as f:
    d = bytearray(f.read())
print(f"Base v3: {len(d)} bytes")
print(f"  Cruise byte before: {bytes(d[0x2024E:0x20250]).hex()}")

# Change 0x2024E from 800E -> 8007
d[0x2024E:0x20250] = bytes.fromhex("8007")
print(f"  Cruise byte after:  {bytes(d[0x2024E:0x20250]).hex()}")

# === Recompute ADD16 (region 0x20230..0x203B7, adjust at 0x203B4) ===
ADD_A, ADD_B, ADD_ADJ = 0x20230, 0x203B7, 0x203B4
ADD_TARGET = 0xCAFEAFFE
d[ADD_ADJ:ADD_ADJ+4] = b"\x00\x00\x00\x00"
baseline = add16(bytes(d[ADD_A:ADD_B+1]))
needed = (ADD_TARGET - baseline) & 0xFFFFFFFF
d[ADD_ADJ:ADD_ADJ+4] = needed.to_bytes(4, "little")
check = add16(bytes(d[ADD_A:ADD_B+1]))
print(f"  ADD16: 0x{check:08X} (target 0xCAFEAFFE) {'OK' if check==ADD_TARGET else 'FAIL'}")

# === Recompute CRC32 (region 0x20000..0x3FEFF, adjust at 0x3FEFC) ===
CRC_A, CRC_B, CRC_ADJ = 0x20000, 0x3FEFF, 0x3FEFC
CRC_TARGET = 0x35015001
d[CRC_ADJ:CRC_ADJ+4] = b"\x00\x00\x00\x00"
baseline = crc32(bytes(d[CRC_A:CRC_B+1]))
delta = baseline ^ CRC_TARGET
# GF(2) matrix solve
A = []
for bit in range(32):
    test = bytearray(d)
    test[CRC_ADJ + bit//8] ^= (1 << (bit%8))
    A.append(baseline ^ crc32(bytes(test[CRC_A:CRC_B+1])))
rows = [0]*32
for j in range(32):
    for i in range(32):
        if (A[j]>>i)&1:
            rows[i] |= (1<<j)
M = [(rows[i], (delta>>i)&1) for i in range(32)]
for col in range(32):
    piv = -1
    for r in range(col,32):
        if (M[r][0]>>col)&1: piv=r; break
    if piv<0: continue
    M[col], M[piv] = M[piv], M[col]
    for r in range(32):
        if r != col and (M[r][0]>>col)&1:
            M[r] = (M[r][0]^M[col][0], M[r][1]^M[col][1])
x = 0
for col in range(32):
    if M[col][1]: x |= (1<<col)
d[CRC_ADJ:CRC_ADJ+4] = x.to_bytes(4, "little")
crc_check = crc32(bytes(d[CRC_A:CRC_B+1]))
print(f"  CRC32: 0x{crc_check:08X} (target 0x35015001) {'OK' if crc_check==CRC_TARGET else 'FAIL'}")
print(f"  DEADBEEF @ 0x3FF00: {bytes(d[0x3FF00:0x3FF04]).hex()}")

with open(OUT, "wb") as f:
    f.write(bytes(d))
print(f"\nWrote {OUT}")
print(f"SHA256: {hashlib.sha256(bytes(d)).hexdigest()}")

# Diff v3->v5
v3 = open(V3, "rb").read()
diffs = [i for i in range(len(v3)) if v3[i] != d[i]]
print(f"\nv3 → v5 changed bytes: {len(diffs)}")
for off in diffs:
    print(f"  0x{off:05X}: {v3[off]:02X} -> {d[off]:02X}")
