Files
samplez/tunie/research/reference-maps
uhryniuk f830f8937d Log real-world road test results and correct/extend the CO-trim theory
New research/ROAD_TEST_LOG.md: the 2026-09-15 flash of
20262-arrow-delete-lowthrottle-composed.hex was a major real-world
success (stalling fully resolved, decel backfire much reduced but not
eliminated). Captures the live diagnostic reasoning that followed:
correcting an overly-lean-leaning framing mid-session (residual decel pop
is more likely a rich-unburned-mixture-with-no-SAI-assist problem, not a
lean one), a structural hypothesis that the Idle Fuel Trim (CO) curve
likely applies whenever the throttle is closed at any RPM rather than
only at a literal stop (only 2 of its 32 points are currently corrected,
and the untouched middle range lines up with the residual pop's RPM
window), and the reasoning for rejecting airbox-baffle removal as a fix
for decel popping specifically (wrong mechanism -- closed throttle isn't
airbox-limited -- and risks worsening the separately-flagged, still
unaddressed main-VE-table leanness gap).

Also updates TABLES.md with the CO-trim applicability open question, and
brings TUNING_GUIDE.md's "local research assets" section up to date (it
still said maps were gitignored/unflashable, both stale).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FP2GaxS9HkUdL5sLBnjKje
2026-09-16 09:07:35 -05:00
..

Reference stock maps (real, from TuneECU's server)

Four genuine factory Bonneville calibrations, downloaded from https://www.tuneecu.fr/Maps/Triumph/Bonneville/<n>Map.hex (the endpoint the TuneECU app itself uses; found in the decompile at MainActivity.java:7407).

File Map Fits
20187Map.hex 20187 Bonneville, production silencers, mechanical odo
20188Map.hex 20188 Bonneville, aftermarket silencers, mechanical odo
20191Map.hex 20191 production, up to VIN 739050, E25
20192Map.hex 20192 aftermarket, up to VIN 739050, E25

These are the candidate stock maps for the 2010 T100 (mechanical odometer). Once we dump the bike, its ROM should correspond to one of these.

What they validated

  • Header format is correct. All four satisfy the reversed magic: little-endian u32 of bytes[0:4] & 0xFF00FFE0 == 0x18001360, i.e. byte0=0x67, byte1=0x13, byte3=0x18. This confirms the format reversing in ../../viewer/FORMAT.md and the little-endian correction.
  • c.b is the table directory. m.uc() reassembles a map by copying tables at the c.b (offset,length) pairs — independent confirmation of the geometry.

What they revealed (the new blocker)

The map body is encrypted. Evidence:

  • Uniform entropy ~7.91 bits/byte across the whole body (8.0 = random).
  • Sibling maps that should differ only in fueling (20187 vs 20188) share just 0.1% of bytes, with no equal run ≥16 bytes.
  • A low-entropy footer (last ~5 KB, H≈3.0) that holds structured key/signature material — matching p8(), which reads its key from the file tail (length-42, length-35, …), derives it via m.Sb(), and unpacks via m.uc().

So the table offsets in mapdefs.json describe the decrypted map, and can't be validated against these files until the map decryption is reversed. The signature lookup (sc() at header offset 20) does not match our clean s.a directory for these files — consistent with the real directory key living in the encrypted/footer region, not the header.

DECRYPTION SOLVED (decode_map.py)

The distribution format is decrypted by l.dc() — a self-synchronising CBC-style XOR stream cipher seeded by the (plaintext) 4-byte header. NOT AES; the earlier m.uc/m.Sb path is for raw ROM dumps, not these downloads. p8() calls l.dc() on any map that isn't a raw-ROM size.

Reversed, ported, and verified:

  • decode_map.py decodes all four maps; encode(decode(x)) == x (round-trip).
  • Decoded 20187 contains the plaintext strings Bonneville / Production silencers / Mechanical odometer at 0x1E/0x29/0x3E — matching the catalogue.
  • Entropy drops 7.99 → ~6.1 bit/byte.
  • Directory lookup now validates on the decoded map: signature at offset 20 = 0x0187CA84, which matches s.a record #541 (field[0]=25676420). So the whole chain works: decode → signature@20 → s.a directory → c.a/c.b.

Decoded files: *.dec.bin.

SOLVED: flat ROM reconstruction + table location (reconstruct_rom.py)

The decoded map is a packed container, which is why a naive byte-diff of two decoded maps showed 96% difference — the packed chunks are misaligned. Each decoded map carries its own unpack directory (from MainActivity.p8): base = le16(dec[28]); a 0x6F66 marker at base+31; a count at base+33; then count (le32 dest_offset, le32 length) entries, followed by the packed data copied verbatim to flat_rom[dest:dest+len]. This yields a 384 KB (0x60000) flat ROM — the real ECU address space.

In the flat ROM, 20187 (production) vs 20188 (aftermarket) differ by only 1.4% (down from 96%), localised to 26 regions. That is the actual production→ aftermarket calibration change, not noise.

Table pointers are in fe = c.a[Qd*48] (Qd from the directory lookup). Table address = fe[39] (base 0x50000) + fe[k]. VERIFIED: fe[11]=0x6990 → 0x56990 is a main fuel table — a smooth 20-wide VE surface, and the aftermarket map is richer in 284/320 cells (mean +323), exactly as an aftermarket-exhaust tune should be.

The two big high-entropy diff regions (0x55599, 0x56990) are the two fuel tables; the small isolated diffs (e.g. single bytes at 0x50C13, 0x534AD, 0x59759) are prime SAI / O2 / lambda flag candidates — now findable because the flat-ROM diff is localised.

Still to finish: map the remaining fe[k] pointers to named tables (small- throttle fuel, AFR, ignition-by-gear, idle, limiters) and confirm each table's dimensions/axes/scaling from the fe metadata (or cross-check with an XDF). The hard part — decrypt, reconstruct, locate — is done and validated.

Provenance (sha256)

cd02cd2a…306c99  20187Map.hex
bed451a9…e1ebe4  20188Map.hex
7436b0f2…79ca3a6  20191Map.hex
f9b9c60d…ffa10f  20192Map.hex