"""Flat-ROM checksum for the Triumph Keihin twin family -- reverse-engineered and validated Aug 2026 (see ../WRITE_PATH.md for the full trace). Source: `com.tuneecu.l.Ac(int, boolean)` in the decompiled TuneECU app, called from MainActivity's map-info display ("Checksum : %04x", flagged "*No-OEM"/"Error" on mismatch). This is a 16-bit sum-of-words checksum over the calibration region of the flat ROM (0x50000-0x60000 for this ECU family -- the exact same base/end this project's own `table_map.py` already uses), stored in the last 2 bytes of that region. Validated against 6 real, unmodified, official TuneECU downloads: 20187/20188/20191/20192/20262/20313 -- all computed == stored, exact. One deliberate non-match, informative rather than a bug: the community SAI/O2-delete file (20188Map2009AIRBOXBONNY.hex) has the exact same stored checksum as its unmodified base (20188Map.hex) -- whoever built it edited a few bytes by hand and never recomputed the checksum. TuneECU's own code path for this looks like a *display/validity* check (flags "*No-OEM" / "Error" in the UI) rather than a proven hard ECU-side write gate -- that community file apparently worked for people despite the stale checksum, which is consistent with this being an app-side sanity indicator rather than (or in addition to) something the ECU's own bootloader independently verifies during the real flash transfer. That ECU-side question is NOT resolved by this -- see caveats below. """ from __future__ import annotations CAL_BASE = 0x50000 CAL_END = 0x60000 def compute(rom: bytes, base: int = CAL_BASE, end: int = CAL_END) -> int: """16-bit sum-of-words checksum over rom[base:end-2], byte-swapped read (matches `l.Ac()`'s `bArr[pos ^ 1] | (bArr[pos] << 8)` exactly).""" length = (end - base) - 2 total = 0 i = 0 while i < length: pos = base + i total += rom[pos ^ 1] | (rom[pos] << 8) i += 2 return total & 0xFFFF def stored(rom: bytes, end: int = CAL_END) -> int: return (rom[end - 2] << 8) | rom[end - 1] def patch(rom: bytearray, base: int = CAL_BASE, end: int = CAL_END) -> None: """Recompute and write the checksum into rom[end-2:end] in place.""" value = compute(bytes(rom), base, end) rom[end - 2] = (value >> 8) & 0xFF rom[end - 1] = value & 0xFF if __name__ == "__main__": import sys from reconstruct_rom import flat_rom for path in sys.argv[1:] or ["20187Map.hex"]: rom = flat_rom(open(path, "rb").read()) c, s = compute(rom), stored(rom) print(f"{path:40s} computed={c:04x} stored={s:04x} {'MATCH' if c == s else 'MISMATCH'}")