Skip to content

← 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.

StructureSizePackingUsed by
irsdk_header112 bytes4Live and IBT
irsdk_varHeader144 bytes1Live and IBT
irsdk_diskSubHeader32 bytes4IBT only
irsdk_varBuf16 bytes4Live and IBT headers
ConcernIBT fileLive shared memory
OrganizationTightly packed regionsFixed reserved-capacity regions
TelemetrySequential recordsRotating buffer ring
Buffer selectionvarBuf[0]varBuf[curBuf]
ConcurrencyImmutable mapped fileProducer can overwrite during a read
Row countsessionRecordCountUnbounded; 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 numVars variable headers.
  • Read only the first bufLen bytes 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:

OffsetSizeRegion
0112irsdk_header
11232irsdk_diskSubHeader
14438,016264 × irsdk_varHeader
38,16026,463session info
64,623127,655,110123,937 × 1,030-byte records
127,719,733end of file

IBT readers use varBuf[0].bufOffset. curBuf is a live-ring field and should not be used to select IBT records.

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 →

irsdk_varHeader fields
OffsetSizeFieldNative typeMeaning
04typeint32 enumElement type ID, 0 through 5
44offsetint32Offset from the start of every telemetry row
84countint32Element count; greater than 1 denotes an array
121countAsTimeboolTreat the value as time when true
133padbyte[3]Padding before text fields
1632namechar[32]Null-terminated variable name
4864descchar[64]Null-terminated description
11232unitchar[32]Null-terminated unit

Variable types

IDEnumElement sizeInterpretation
0irsdk_char1 bytesRaw byte when count = 1; otherwise a null-terminated string
1irsdk_bool1 bytesBoolean or Boolean array
2irsdk_int4 bytesSigned 32-bit integer or array
3irsdk_bitField4 bytes32-bit packed flags
4irsdk_float4 bytesIEEE-754 single-precision value or array
5irsdk_double8 bytesIEEE-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 →

irsdk_diskSubHeader fields
OffsetSizeFieldNative typeMeaning
1128sessionStartDateint64Unix epoch seconds
1208sessionStartTimedoubleSeconds since sessionStartDate
1288sessionEndTimedoubleSeconds since sessionStartDate
1364sessionLapCountint32Recorded session lap count
1404sessionRecordCountint32Telemetry 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:

  1. Map the variable type ID to its element size.
  2. Compute valueLength = count × elementSize.
  3. Reject the descriptor when offset + valueLength > bufLen.
  4. 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:

  1. Reread the header and validate curBuf against min(numBuf, 4).
  2. Select varBuf[curBuf] and save its tickCount.
  3. Issue a memory barrier, then copy bufLen bytes from bufOffset.
  4. Issue another barrier and reread that slot's tickCountBegin.
  5. Accept the row only when the saved tickCount equals the new tickCountBegin.
Tick semantics: tickCountBegin changes before a write and tickCount after 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_char with count = 1 is 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 numVars and bufLen, and 1 ≤ numBuf ≤ 4.
  • Variable headers: Verify varHeaderOffset + numVars × 144 remains 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)