ASAM MCD-2MC / ASAP2 (
.a2l) Text Specification & Grammatical Architecture
Calibration definitions in the BMW ecosystem span six distinct file formats originating from standard bodies (ASAM), proprietary toolmakers (Bosch, Siemens, EVC electronic, TunerPro), and open-source communities. This section provides the binary specifications, structural records, and parsing grammars for each filetype.
2.1 ASAM MCD-2MC / ASAP2 (.a2l) Text Specification & Grammatical Architecture#
The ASAM MCD-2MC standard (formerly known as ASAP2) is the official international automotive industry specification for ECU calibration descriptions. An .a2l file is an ASCII text database containing hierarchical keyword blocks delimited by /begin and /end tags.
┌──────────────────────────────────────────────────────────────────────────────────────────────────┐
│ ASAM MCD-2MC / ASAP2 (.A2L) HIERARCHICAL STRUCTURE │
├──────────────────────────────────────────────────────────────────────────────────────────────────┤
│ │
│ ASAP2_VERSION 1 61 │
│ /begin PROJECT BMW_ENGINE_PROJECT "BMW Calibration Database" │
│ /begin MODULE ENGINE_CONTROL_UNIT "Siemens / Bosch DME" │
│ /begin A2ML ... /end A2ML <-- AML Interface Grammar Description │
│ /begin MOD_PAR ... /end MOD_PAR <-- Module & Memory Parameters │
│ /begin MOD_COMMON ... /end MOD_COMMON <-- Endianness & Alignment Rules │
│ /begin RECORD_LAYOUT ... /end RECORD_LAYOUT <-- Byte Ordering & Bit Packing │
│ /begin COMPU_METHOD ... /end COMPU_METHOD <-- Rational Conversion Math Functions │
│ /begin COMPU_VTAB ... /end COMPU_VTAB <-- Discrete Value-to-Text Tables │
│ /begin AXIS_PTS ... /end AXIS_PTS <-- Shared Breakpoint Axes │
│ /begin CHARACTERISTIC ... /end CHARACTERISTIC <-- 1D Scalars, 2D Curves, 3D Maps │
│ /begin MEASUREMENT ... /end MEASUREMENT <-- Real-Time RAM Telemetry Variables │
│ /begin IF_DATA ... /end IF_DATA <-- CAN / CCP / KWP Protocol Metadata │
│ /end MODULE │
│ /end PROJECT │
└──────────────────────────────────────────────────────────────────────────────────────────────────┘
Key Block Specifications:#
MOD_COMMON(Module Common Settings): Defines global alignment and CPU endianness: ``text /begin MOD_COMMON "Default module configuration" BYTE_ORDER MSB_FIRST /* Big-Endian (Motorola): Siemens MS4x, MSS5x, ME7.2 */ ALIGNMENT_BYTE 1 ALIGNMENT_WORD 2 ALIGNMENT_LONG 4 ALIGNMENT_FLOAT32_IEEE 4 /end MOD_COMMON``RECORD_LAYOUT(Data Organization in Flash): Instructs calibration tools how bytes are sequenced in memory. For standard 16-bit 3D maps: ``text /begin RECORD_LAYOUT Standard_Map_16Bit FNC_VALUES 1 SWORD ROW_DIR DIRECT NO_AXIS_PTS_X 2 UWORD AXIS_PTS_X 3 SWORD INDEX_INCR DIRECT NO_AXIS_PTS_Y 4 UWORD AXIS_PTS_Y 5 SWORD INDEX_INCR DIRECT /end RECORD_LAYOUT``COMPU_METHOD(Mathematical Conversion Formulas): Defines conversion between raw binary integers (INT) and engineering physical units (PHYS). Supports two primary types:CHARACTERISTIC(Map / Curve / Scalar Definition): The central entity representing a tunable element: ``text /begin CHARACTERISTIC ip_iga_ron_98_pl__n__maf "Ignition advance at 98 RON part load" MAP /* Type: VALUE, CURVE, MAP, CUBOID, VAL_BLK */ 0x70480 /* Flash Memory Address */ Standard_Map_16Bit /* Record Layout Reference */ 0.0 /* Max Possible Deviation */ CM_Ignition_Angle /* Compu Method Reference */ -24.0 /* Lower Physical Limit */ 72.0 /* Upper Physical Limit */ /begin AXIS_DESCR COM_AXIS /* Shared axis reference or STD_AXIS inline */ n_engine /* Input Measurement Quantity */ CM_Engine_Speed 16 /* Number of X Breakpoints */ 0.0 6800.0 /* Min/Max Physical Axis Values */ AXIS_PTS_REF n_axis_pts /* Reference to shared axis */ /end AXIS_DESCR /begin AXIS_DESCR COM_AXIS load_cylinder CM_Engine_Load 12 /* Number of Y Breakpoints */ 0.05 0.95 AXIS_PTS_REF maf_axis_pts /end AXIS_DESCR /end CHARACTERISTIC``
2.2 Bosch & Siemens Proprietary Binary DAMOS (.dam) Reverse Engineering#
The .dam file is the internal proprietary compiled binary format generated by Bosch/Siemens calibration engineering toolchains (such as Bosch CC-Studio or Siemens INCA-precursor toolsets). Because .dam files are compiled binary representations of ASAP2 descriptions, they parse orders of magnitude faster than multi-megabyte .a2l text files.
┌──────────────────────────────────────────────────────────────────────────────────────────────────┐
│ PROPRIETARY BINARY DAMOS (.DAM) FILE STRUCTURE │
├──────────────────────────────────────────────────────────────────────────────────────────────────┤
│ │
│ 0x0000 - 0x003F (64 Bytes): DAMOS FILE HEADER │
│ • Magic Signature: 0x44 0x41 0x4D 0x4F 0x53 ("DAMOS") or 0x41 0x53 0x41 0x50 ("ASAP") │
│ • Format Version: 0x00020000 (v2.0), 0x00030000 (v3.0), 0x00040000 (v4.0) │
│ • Module Name: Null-terminated ASCII string (e.g. "MS430069", "EDC15C4") │
│ • Endianness Flag: 0x00 = Little-Endian, 0x01 = Big-Endian (Motorola) │
│ │
│ 0x0040 - 0x00FF (192 Bytes): TABLE OF CONTENTS & SECTION DIRECTORY │
│ • Offset to Symbol Table: Pointer to Character Dictionary │
│ • Offset to Axis Table: Pointer to Axis Descriptors │
│ • Offset to Conversion Formula Table: Pointer to Math Polynomials │
│ • Offset to String Table: Centralized String Heap (Names, Units, Comments) │
│ │
│ 0x0100 - EOF: DATA RECORDS │
│ • Record Structures (32-byte fixed header per symbol + variable length fields) │
└──────────────────────────────────────────────────────────────────────────────────────────────────┘
Binary Record Descriptor Struct (C-Representation):#
// Binary DAMOS v3.0 / v4.0 Record Descriptor
typedef struct {
uint32_t name_offset; // Offset into string table for Symbol Identifier
uint32_t comment_offset; // Offset into string table for Description
uint32_t address; // 32-bit absolute/relative flash memory address
uint16_t record_type; // 0x01=Scalar, 0x02=1D Curve, 0x04=2D Map, 0x08=Cuboid
uint16_t data_type; // 0x01=UBYTE, 0x02=SBYTE, 0x03=UWORD, 0x04=SWORD, 0x05=ULONG, 0x06=FLOAT32
uint16_t dim_x; // Number of X-axis elements (columns)
uint16_t dim_y; // Number of Y-axis elements (rows)
uint16_t dim_z; // Number of Z-axis elements (cuboid depth, 1 for 2D maps)
uint16_t compu_method_id; // Index into conversion formula table
uint32_t axis_x_ptr; // Memory address or record pointer to X-axis definition
uint32_t axis_y_ptr; // Memory address or record pointer to Y-axis definition
float min_phys; // Minimum valid physical engineering value
float max_phys; // Maximum valid physical engineering value
} DamosRecord_v3;
2.3 EVC WinOLS Project File (.ols) Binary Container Architecture#
The .ols format is the native binary project workspace format utilized by EVC WinOLS, the global standard software for professional automotive calibration and reverse engineering. An .ols file is not merely a definition; it is a complete container housing the original raw binary, modified binaries, version histories, client metadata, folder taxonomies, and 3D visual map parameters.
┌──────────────────────────────────────────────────────────────────────────────────────────────────┐
│ EVC WINOLS PROJECT (.OLS) CONTAINER ARCHITECTURE │
├──────────────────────────────────────────────────────────────────────────────────────────────────┤
│ │
│ [ OLS Container Header ] │
│ • Magic Header: "EVC_OLS_PROJECT" or 0x4F 0x4C 0x53 0x00 │
│ • Compression: Proprietary LZSS / ZLIB compression wrapper applied to sub-blocks │
│ │
│ [ Block 1: Binary Image Storage ] │
│ • Original Raw Binary (Full Flash Image, e.g. 512 KB, 1024 KB, 2048 KB) │
│ • Active Modified Binary (Tuned Version) │
│ • Undo/Redo Version Snapshots & Difference Delta Arrays │
│ │
│ [ Block 2: Project Metadata Descriptor ] │
│ • Vehicle Producer: "BMW", Series: "E46", Model: "330i", Engine: "M54B30" │
│ • ECU Producer: "Siemens", ECU Type: "MS43", Hardware Nr: "7518111", Software Nr: "430069" │
│ • Programmer Notes, Checksum Plugin GUID, Date Stamps │
│ │
│ [ Block 3: Map List & Calibration Catalog ] │
│ • Array of Map Descriptors (Detailed below) │
│ • Folder Hierarchy & Group Trees (e.g. "Injection", "Ignition", "VANOS", "Limiters") │
└──────────────────────────────────────────────────────────────────────────────────────────────────┘
WinOLS Map Descriptor Binary Header Layout:#
For every identified map, WinOLS serializes a binary map header containing:
MapID(4 Bytes): Unique integer handle within the project.Name(Null-terminated string): Symbol name or user-defined description.StartAddress(4 Bytes): Flash memory start offset.Columns (X)&Rows (Y)(2 Bytes each): Dimensions of the table.DataType(1 Byte): Bitwidth (8, 16, 32-bit), signedness, floating point.ViewMode(1 Byte): Default rendering mode (0x00=Hex,0x01=2D Graph,0x02=3D Surface,0x03=Text).Factor&Offset(8 Bytes Double): Linear scaling equation y = m \cdot x + c.AxisDescriptors(32 Bytes each): Start addresses of X and Y axes, inline vs. separate data, reciprocal factors.
2.4 EVC WinOLS Map Pack (.kp) Export/Import Specification#
A .kp (Map Pack) file is an isolated export of map definitions extracted from an .ols project without bundling the proprietary binary firmware.
- Text Format (
.kpv1.x / v2.x): Plaintext key-value blocks structured as: ``text [MAP] NAME=ip_iga_ron_98_pl__n__maf ADDR=0x70480 COLS=16 ROWS=12 TYPE=WORD SIGNED=YES FACTOR=0.375 OFFSET=-24.0 UNIT=deg X_ADDR=0x71FB0 X_FACTOR=1.0 X_OFFSET=0.0 X_UNIT=rpm Y_ADDR=0x71FE0 Y_FACTOR=0.001 Y_OFFSET=0.0 Y_UNIT=load FOLDER=Ignition\Part Load`` - Binary Format (
.kpv3.x): Encrypted/compressed binary stream protecting commercial tuner intellectual property.
2.5 TunerPro XML Definition (.xdf) Grammar & Math Engine#
TunerPro .xdf files are standardized XML definitions widely utilized in open-source tuning communities (including the ms4x.net project). An .xdf file describes tables, scalars, flags, and checksum verification routines.
<!-- Example TunerPro XDF Table Definition: MS43 Ignition Map -->
<XDFTABLE uniqueid="0x7048" flags="0x0">
<title>ip_iga_ron_98_pl__n__maf</title>
<description>Base ignition timing for 98 RON fuel (part load).</description>
<categorymem index="0" category="2" /> <!-- Category: Ignition -->
<XDFAXIS id="x" uniqueid="0x0">
<EMBEDDEDDATA mmedaddress="0x71FB0" mmedelementsizebits="16" mmedelementcount="16" mmedmajorstridebits="0" mmedminorstridebits="0" />
<units>RPM</units>
<indexcount>16</indexcount>
<datatype>0</datatype>
<unittype>0</unittype>
<DALINK index="0" />
<LABEL index="0" value="0.0" />
<MATH equation="X*1.0">
<VAR id="X" />
</MATH>
</XDFAXIS>
<XDFAXIS id="y" uniqueid="0x0">
<EMBEDDEDDATA mmedaddress="0x71FE0" mmedelementsizebits="16" mmedelementcount="12" mmedmajorstridebits="0" mmedminorstridebits="0" />
<units>mg/stroke</units>
<indexcount>12</indexcount>
<datatype>0</datatype>
<unittype>0</unittype>
<DALINK index="0" />
<LABEL index="0" value="0.0" />
<MATH equation="X*0.125">
<VAR id="X" />
</MATH>
</XDFAXIS>
<XDFAXIS id="z">
<EMBEDDEDDATA mmedaddress="0x70480" mmedelementsizebits="16" mmedelementcount="192" mmedmajorstridebits="0" mmedminorstridebits="0" />
<units>deg BTDC</units>
<decimalpl>2</decimalpl>
<min>-24.00</min>
<max>72.00</max>
<outputtype>1</outputtype>
<MATH equation="(X*0.375)-24.0">
<VAR id="X" />
</MATH>
</XDFAXIS>
</XDFTABLE>
Key TunerPro XDF Tags:#
<EMBEDDEDDATA>: Defines memory address (mmedaddress), element size in bits (16for words,8for bytes), element count, and signedness flags.<MATH equation="...">: Real-time algebraic parser executing user conversion math. Supports standard operators (+,-,*,/), power (^), parentheses, and bitwise masking (X & 0xFF).<XDFPATCH>: Stores raw binary hex replacement strings used for firmware modifications (e.g. EWS deletes, launch control hooks).
2.6 Python Universal Multi-Format DAMOS Parser & Extractor Implementation#
Below is an executable Python parser architecture capable of reading and extracting calibration maps across .a2l, .kp, and .xdf formats into a standardized data model:
import re
import xml.etree.ElementTree as ET
from dataclasses import dataclass, field
from typing import Dict, List, Optional
@dataclass
class CalibrationMap:
name: str
address: int
dim_x: int = 1
dim_y: int = 1
size_bytes: int = 2
signed: bool = False
factor: float = 1.0
offset: float = 0.0
unit: str = ""
category: str = "Uncategorized"
description: str = ""
x_axis_addr: Optional[int] = None
y_axis_addr: Optional[int] = None
class UniversalDamosParser:
# Universal parser translating A2L, KP, and XDF files into a unified calibration object model.
@staticmethod
def parse_xdf(xdf_path: str) -> Dict[str, CalibrationMap]:
tree = ET.parse(xdf_path)
root = tree.getroot()
maps = {}
base_offset = 0
# Extract BASEOFFSET if specified in XDF header
header = root.find("XDFHEADER")
if header is not None:
b_off = header.find("BASEOFFSET")
if b_off is not None and b_off.text:
base_offset = int(b_off.text, 0)
for table in root.findall("XDFTABLE"):
title_elem = table.find("title")
name = title_elem.text.strip() if title_elem is not None else "UNKNOWN"
z_axis = table.find("XDFAXIS[@id='z']")
if z_axis is None:
continue
embed = z_axis.find("EMBEDDEDDATA")
if embed is None:
continue
raw_addr = int(embed.attrib.get("mmedaddress", "0"), 0)
abs_addr = raw_addr + base_offset
bits = int(embed.attrib.get("mmedelementsizebits", "16"))
size_bytes = bits // 8
# Parse Math Equation
factor, offset = 1.0, 0.0
math_elem = z_axis.find("MATH")
if math_elem is not None and "equation" in math_elem.attrib:
eq = math_elem.attrib["equation"]
# Basic regex parsing for linear formula (X * factor) + offset
m_mult = re.search(r"X\s*\*\s*([\d\.]+)", eq)
if m_mult:
factor = float(m_mult.group(1))
m_sub = re.search(r"-\s*([\d\.]+)", eq)
if m_sub:
offset = -float(m_sub.group(1))
m_add = re.search(r"\+\s*([\d\.]+)", eq)
if m_add:
offset = float(m_add.group(1))
x_axis = table.find("XDFAXIS[@id='x']")
y_axis = table.find("XDFAXIS[@id='y']")
dim_x = (
int(x_axis.find("indexcount").text)
if x_axis is not None and x_axis.find("indexcount") is not None
else 1
)
dim_y = (
int(y_axis.find("indexcount").text)
if y_axis is not None and y_axis.find("indexcount") is not None
else 1
)
unit = ""
u_elem = z_axis.find("units")
if u_elem is not None and u_elem.text:
unit = u_elem.text.strip()
maps[name] = CalibrationMap(
name=name,
address=abs_addr,
dim_x=dim_x,
dim_y=dim_y,
size_bytes=size_bytes,
factor=factor,
offset=offset,
unit=unit,
)
return maps
@staticmethod
def parse_a2l_characteristics(a2l_path: str) -> Dict[str, CalibrationMap]:
maps = {}
with open(a2l_path, "r", encoding="latin-1", errors="ignore") as f:
content = f.read()
# Regex search for /begin CHARACTERISTIC ... /end CHARACTERISTIC
char_pattern = re.compile(
r"/begin\s+CHARACTERISTIC\s+([a-zA-Z0-9_]+)\s+"([^"]*)"\s+([a-zA-Z0-9_]+)\s+(0x[0-9a-fA-F]+|[0-9]+)",
re.MULTILINE,
)
for match in char_pattern.finditer(content):
name = match.group(1)
desc = match.group(2)
map_type = match.group(3)
addr_str = match.group(4)
addr = int(addr_str, 0)
maps[name] = CalibrationMap(
name=name, address=addr, description=desc, category=map_type
)
return maps