# Bosch checksums (TriCore ME17/MED17/EDC17)

After editing a calibration byte, the block's checksums break and the ECU rejects the file
(or silently runs a backup). You must recompute them. This is solved — here's how.

## Block structure

A Bosch TriCore image is a set of blocks, each with its own checksum descriptor. medc17
scans and lists them. Typical ME17.9.11 (1540096-byte dump, calibration from 0x20000):

| Block | Location | Notes |
|---|---|---|
| Absolute constants #0 | 0x00000 | code |
| Customer block | 0x08000 | code |
| Tuning protection (OTP) | 0x14000 | code |
| Startup block | 0x18000 | code |
| **Dataset #0** | **0x20000–0x3FF03** | **calibration — most feature/tune edits live here** |
| Application software #0 | 0x40000–0xFFF03 | code + embedded calibration (its own ADD16) |

Markers: `FADECAFE` = checksum init seed; `CAFEAFFE` = ADD16 target; `DEADBEEF` = epilog
marker (do NOT overwrite). The adjust dword ("dCSAdjust") is a free dword the algo tweaks
so the target holds.

## Algorithms (in `tools/bosch_checksum.py`)

- **CRC32**: reflected poly `0xEDB88320`, init `0xFADECAFE`, dataset target `0x35015001`.
- **ADD16** (`SB_ADD16_ALGO_E`): sum 16-bit little-endian words over the region, but the
  **LAST word is shifted `<< 16`** before adding. Target `0xCAFEAFFE`. Because it's linear,
  the adjust dword = `(target - baseline_with_adjust_zeroed) & 0xFFFFFFFF`, written LE.
- **GF(2) CRC solve**: when the CRC adjust dword is NOT the last bytes of the region (an
  epilog like DEADBEEF follows it), you can't append a fix — solve the 4 bytes via 32
  single-bit CRC probes + Gaussian elimination. `solve_crc32_adjust()` does this.

ME17.9.11 GTD-B46* dataset defaults (confirm per-ECU with medc17's scan — they vary):
- ADD16: region `0x20230–0x203B7`, adjust at `0x203B4`, target `0xCAFEAFFE`.
- CRC32: region `0x20000–0x3FEFF`, adjust at `0x3FEFC`, target `0x35015001`.

## Procedure

```bash
# one-shot: edit bytes + fix ME17.9.11 dataset checksums
python tools/bosch_checksum.py in.bin out.bin 2024E 8007
# then ALWAYS independently validate:
python tools/medc17/main.py out.bin      # expect "Validated N/N checksums", all VALID
```
For other ECUs, import the functions and pass that ECU's regions (get them from medc17's
block scan). Fix ADD16 first, then CRC32 (CRC region usually contains the ADD16 area).

## Gotchas

- **Never use medc17 `--correct`** — WIP "variant dataset correction" rewrites ~132 bytes
  (0x3FE7C–0x3FEFF) and corrupts the file. Use medc17 only to *validate*.
- **Tuner non-standard epilog**: some tuned files put DEADBEEF at `0x3FEFC` and dCSAdjust at
  `0x3FEF8` (shifted 4 bytes vs stock's `0x3FF00` / `0x3FEFC`). medc17 won't parse the dataset
  block until you normalize to stock layout: set the block_end field `@0x2000C` to the stock
  value, put the CRC adjust at the region's last dword `0x3FEFC`, DEADBEEF at `0x3FF00`. After
  normalizing, medc17 validates 10/10. PCMflash verifies by descriptor (region CRC) and is
  lenient on marker position, but its *correction* needs the standard layout — so normalize.
- The ECU verifies the real sum by descriptor (cs_start/cs_end, direct map 0x80000000→file0),
  NOT by the block_end field. Get the region right and it'll pass.
- Validate on a KNOWN-GOOD stock file first (trust-gate): if medc17 says a factory dump is
  10/10 VALID, your algorithm/regions are correct for that ECU.
