← iRacing Telemetry SDK for C# .NET
iRacing SDK (iRSDK) Data Format Reference
This page documents the binary layout used by iRacing's SDK (iRSDK) — the live shared memory and recorded IBT files it produces. It covers the common structures, region offsets, telemetry rows, and the checks a reader needs to perform.
All offsets are decimal byte offsets from the beginning of the map or file. Multi-byte numbers are little-endian on iRacing's supported Windows systems.
Overview
The live memory-mapped file and IBT files use the same core iRacing structures. A 112-byte irsdk_header supplies offsets and counts for the session YAML, variable headers,
and telemetry data. Each irsdk_varHeader describes one value in a packed telemetry
row.
| Structure | Size | Packing | Used by |
|---|---|---|---|
irsdk_header | 112 bytes | 4 | Live and IBT |
irsdk_varHeader | 144 bytes | 1 | Live and IBT |
irsdk_diskSubHeader | 32 bytes | 4 | IBT only |
irsdk_varBuf | 16 bytes | 4 | Live and IBT headers |
| Concern | IBT file | Live shared memory |
|---|---|---|
| Organization | Tightly packed regions | Fixed reserved-capacity regions |
| Telemetry | Sequential records | Rotating buffer ring |
| Buffer selection | varBuf[0] | varBuf[curBuf] |
| Concurrency | Immutable mapped file | Producer can overwrite during a read |
| Row count | sessionRecordCount | Unbounded; use tick counters |
Offset interpretation: Do not infer positions from declaration order. Use the offsets stored in the header and each variable header.
Live shared-memory layout
Open the Windows mapping Local\IRSDKMemMapFileName. The map reserves capacity for
session data, 4,096 variable headers, and three 24 KiB telemetry slots. These sizes are
capacities, not the amount of valid data.
0 +------------------------------------+ 112 B
| irsdk_header |
112 +------------------------------------+ 512 KiB reserved
| session info (YAML) |
| ... null padded ... |
524,400 +------------------------------------+ 576 KiB reserved
| irsdk_varHeader[numVars] |
| room for 4,096 entries |
1,114,224 +------------------------------------+ 24 KiB reserved
| telemetry buffer 0 |
1,138,800 +------------------------------------+ 24 KiB reserved
| telemetry buffer 1 |
1,163,376 +------------------------------------+ 24 KiB reserved
| telemetry buffer 2 |
1,187,952 +------------------------------------+ end of map- Read only the first
numVarsvariable headers. - Read only the first
bufLenbytes in a telemetry slot. - Stop session YAML at its first null byte.
- The region offsets remain stable during a session; their contents change.
IBT file layout
An IBT file adds a 32-byte disk subheader after the common header. Variable headers, session YAML, and equal-length telemetry records follow without inter-region alignment padding.
0 +------------------------------------+ 112 B
| irsdk_header |
112 +------------------------------------+ 32 B (IBT only)
| irsdk_diskSubHeader |
144 +------------------------------------+ numVars x 144 B
| irsdk_varHeader[numVars] |
sessionInfoOffset +------------------------------------+ sessionInfoLen B
| session info (YAML) |
varBuf[0].bufOffset +------------------------------------+ bufLen B
| record 0 |
+------------------------------------+ bufLen B
| record 1 |
+------------------------------------+
| ... |
+------------------------------------+ n = sessionRecordCount
| record n-1 |
end of file +------------------------------------+Offset formulas
varHeaderOffset = 144
sessionInfoOffset = 144 + numVars × 144
firstRecordOffset = sessionInfoOffset + sessionInfoLen
recordOffset(i) = varBuf[0].bufOffset + i × bufLen
expectedFileLength = firstRecordOffset + sessionRecordCount × bufLen Worked example
The following file has 264 variables, 26,463 bytes of session information, 123,937 records, and a row length of 1,030 bytes:
| Offset | Size | Region |
|---|---|---|
| 0 | 112 | irsdk_header |
| 112 | 32 | irsdk_diskSubHeader |
| 144 | 38,016 | 264 × irsdk_varHeader |
| 38,160 | 26,463 | session info |
| 64,623 | 127,655,110 | 123,937 × 1,030-byte records |
| 127,719,733 | end of file | |
IBT readers use varBuf[0].bufOffset. curBuf is a live-ring field and should
not be used to select IBT records.
irsdk_header
This 112-byte structure uses 4-byte packing and is common to both data sources.
Scroll table →
| Offset | Size | Field | Native type | Meaning |
|---|---|---|---|---|
| 0 | 4 | ver | int32 | API/header version; currently 2 |
| 4 | 4 | status | int32 flags | 0 = disconnected; bit 0 = connected |
| 8 | 4 | tickRate | int32 | Samples per second, commonly 60 or 360 |
| 12 | 4 | sessionInfoUpdate | int32 | Changes whenever session information changes |
| 16 | 4 | sessionInfoLen | int32 | Session information length or reserved capacity |
| 20 | 4 | sessionInfoOffset | int32 | Absolute offset to session YAML |
| 24 | 4 | numVars | int32 | Number of valid variable headers |
| 28 | 4 | varHeaderOffset | int32 | Absolute offset to the variable-header array |
| 32 | 4 | numBuf | int32 | Number of telemetry buffers; 1 through 4 |
| 36 | 4 | bufLen | int32 | Length of one packed telemetry row |
| 40 | 4 | curBufTickCount | int32 | Cached tick for detecting new live data |
| 44 | 1 | curBuf | uint8 | Latest completed live-buffer index |
| 45 | 3 | pad1 | byte[3] | Padding to offset 48 |
| 48 | 16 | varBuf1 | irsdk_varBuf | Buffer descriptor 0 |
| 64 | 16 | varBuf2 | irsdk_varBuf | Buffer descriptor 1 |
| 80 | 16 | varBuf3 | irsdk_varBuf | Buffer descriptor 2 |
| 96 | 16 | varBuf4 | irsdk_varBuf | Buffer descriptor 3 |
irsdk_varBuf
Four 16-byte buffer descriptors are embedded in the header. Live data normally uses three; IBT files use descriptor 0.
Scroll table →
| Offset | Size | Field | Native type | Meaning |
|---|---|---|---|---|
| 0 | 4 | tickCount | int32 | Updated after a row write completes |
| 4 | 4 | bufOffset | int32 | Absolute offset to this telemetry row |
| 8 | 4 | tickCountBegin | int32 | Updated before a row write begins |
| 12 | 4 | pad | int32 | Padding to 16 bytes |
irsdk_varHeader
Each telemetry variable has one 144-byte descriptor with 1-byte packing. Its offset is relative to the beginning of a telemetry row, not the beginning of the map
or file.
Scroll table →
| Offset | Size | Field | Native type | Meaning |
|---|---|---|---|---|
| 0 | 4 | type | int32 enum | Element type ID, 0 through 5 |
| 4 | 4 | offset | int32 | Offset from the start of every telemetry row |
| 8 | 4 | count | int32 | Element count; greater than 1 denotes an array |
| 12 | 1 | countAsTime | bool | Treat the value as time when true |
| 13 | 3 | pad | byte[3] | Padding before text fields |
| 16 | 32 | name | char[32] | Null-terminated variable name |
| 48 | 64 | desc | char[64] | Null-terminated description |
| 112 | 32 | unit | char[32] | Null-terminated unit |
Variable types
| ID | Enum | Element size | Interpretation |
|---|---|---|---|
| 0 | irsdk_char | 1 bytes | Raw byte when count = 1; otherwise a null-terminated string |
| 1 | irsdk_bool | 1 bytes | Boolean or Boolean array |
| 2 | irsdk_int | 4 bytes | Signed 32-bit integer or array |
| 3 | irsdk_bitField | 4 bytes | 32-bit packed flags |
| 4 | irsdk_float | 4 bytes | IEEE-754 single-precision value or array |
| 5 | irsdk_double | 8 bytes | IEEE-754 double-precision value or array |
Type ID 6, irsdk_ETCount, is a sentinel rather than a value type. A count greater than one denotes consecutive elements of the same type.
irsdk_diskSubHeader
This 32-byte, 4-byte-packed structure exists only in IBT files. It begins at absolute file offset 112, so the offsets in this table are absolute file offsets.
Scroll table →
| Offset | Size | Field | Native type | Meaning |
|---|---|---|---|---|
| 112 | 8 | sessionStartDate | int64 | Unix epoch seconds |
| 120 | 8 | sessionStartTime | double | Seconds since sessionStartDate |
| 128 | 8 | sessionEndTime | double | Seconds since sessionStartDate |
| 136 | 4 | sessionLapCount | int32 | Recorded session lap count |
| 140 | 4 | sessionRecordCount | int32 | Telemetry row count claimed by the file |
Reading a telemetry row
A row contains exactly bufLen bytes. Each variable's position comes from its own irsdk_varHeader.offset, not declaration order — a descriptor with offset = n and type = irsdk_float points 4 bytes starting at byte n of the row:
irsdk_varHeader - 144 bytes a row of bufLen bytes
+----------------------------+ +----------------------+
| type (irsdk_VarType)| 0 | SessionTime (double) |
| offset --------------------+---+ +----------------------+
| count (array length) | | 8 | SessionTick (int) |
| countAsTime | | +----------------------+
| name[32] "Speed" | | 12 | ... |
| desc[64] | | +----------------------+
| unit[32] "m/s" | +---> n | Speed (float) |
+----------------------------+ +----------------------+
| ... |
+----------------------+After the row has been copied into private memory, live and IBT decoding are identical:
- Map the variable type ID to its element size.
- Compute
valueLength = count × elementSize. - Reject the descriptor when
offset + valueLength > bufLen. - Read little-endian values beginning at
row[offset].
valueStart = rowStart + descriptor.offset
valueBytes = descriptor.count × elementSize(descriptor.type)
valid when descriptor.offset + valueBytes ≤ header.bufLen Do not apply native alignment rules between values. The descriptor offsets define the row
layout, including arrays such as the CarIdx* variables.
Reading live data without tearing
iRacing can overwrite a slot while another process is reading it. Copy a row into private memory and verify the tick fields before decoding it:
- Reread the header and validate
curBufagainstmin(numBuf, 4). - Select
varBuf[curBuf]and save itstickCount. - Issue a memory barrier, then copy
bufLenbytes frombufOffset. - Issue another barrier and reread that slot's
tickCountBegin. - Accept the row only when the saved
tickCountequals the newtickCountBegin.
Tick semantics:tickCountBeginchanges before a write andtickCountafter it. If the values do not match after the copy, discard the row or retry the read.
Tick gaps indicate overwritten samples. A lower tick than the previous sample usually means the reader lost synchronization, iRacing disconnected, or a new session started.
Strings and session YAML
Variable-header text
name[32], desc[64], and unit[32] are fixed-size, null-terminated
ANSI-compatible buffers.
Telemetry character fields
irsdk_charwithcount = 1is one raw byte.- With
count > 1, it is one null-terminated string, not an array of strings. - The SDK decodes telemetry character buffers as ISO-8859-1 (Latin-1).
Session information
Read no more than sessionInfoLen bytes beginning at sessionInfoOffset, then stop at the first null. Use UTF-8 when WeekendInfo declares Encoding: UTF8; otherwise use Latin-1 for older session data.
In live memory, sessionInfoLen describes reserved capacity rather than current text
length. In an IBT file it participates in the packed-region offset formula.
Reader validation
- Header: Require a sane version, positive
numVarsandbufLen, and1 ≤ numBuf ≤ 4. - Variable headers: Verify
varHeaderOffset + numVars × 144remains inside the source. - Session information: Verify its offset and declared extent before scanning for a null.
- Variables: Verify
offset + count × elementSize ≤ bufLen. - Live rows: Complete the tick-based torn-read check before decoding.
Truncated IBT files
Treat sessionRecordCount as a claim. A recording can end before every row is flushed,
so expose only complete rows that are present in the file:
availableRecords = floor((fileLength − firstRecordOffset) / bufLen)
usableRecords = min(sessionRecordCount, availableRecords)