@hotlap.ai/telemetry 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,418 @@
1
+ // @bun
2
+ import {
3
+ $parse
4
+ } from "./index-hxw291vy.js";
5
+ import {
6
+ getLatestVarBuffer,
7
+ parseHeader,
8
+ parseVarHeaders,
9
+ readString,
10
+ readVarValue
11
+ } from "./index-c5jf6rjt.js";
12
+ import {
13
+ BroadcastMsg,
14
+ CameraState,
15
+ ChatCommandMode,
16
+ FFBCommandMode,
17
+ PitCommandMode,
18
+ ReloadTexturesMode,
19
+ RpyPosMode,
20
+ RpySrchMode,
21
+ RpyStateMode,
22
+ SIM_STATUS_URL,
23
+ StatusField,
24
+ TelemCommandMode,
25
+ VideoCaptureMode
26
+ } from "./index-wb6ebd0j.js";
27
+ import {
28
+ __require
29
+ } from "./index-ftpvnye9.js";
30
+
31
+ // src/live.ts
32
+ var kernel32 = null;
33
+ var user32 = null;
34
+ function loadKernel32() {
35
+ const { dlopen, FFIType, suffix } = __require("bun:ffi");
36
+ return dlopen(`kernel32.${suffix}`, {
37
+ OpenEventW: {
38
+ args: [FFIType.u32, FFIType.bool, FFIType.ptr],
39
+ returns: FFIType.ptr
40
+ },
41
+ OpenFileMappingW: {
42
+ args: [FFIType.u32, FFIType.bool, FFIType.ptr],
43
+ returns: FFIType.ptr
44
+ },
45
+ MapViewOfFile: {
46
+ args: [FFIType.ptr, FFIType.u32, FFIType.u32, FFIType.u32, FFIType.u32],
47
+ returns: FFIType.ptr
48
+ },
49
+ UnmapViewOfFile: {
50
+ args: [FFIType.ptr],
51
+ returns: FFIType.bool
52
+ },
53
+ CloseHandle: {
54
+ args: [FFIType.ptr],
55
+ returns: FFIType.bool
56
+ },
57
+ WaitForSingleObject: {
58
+ args: [FFIType.ptr, FFIType.u32],
59
+ returns: FFIType.u32
60
+ }
61
+ });
62
+ }
63
+ function loadUser32() {
64
+ const { dlopen, FFIType, suffix } = __require("bun:ffi");
65
+ return dlopen(`user32.${suffix}`, {
66
+ RegisterWindowMessageW: {
67
+ args: [FFIType.ptr],
68
+ returns: FFIType.u32
69
+ },
70
+ SendNotifyMessageW: {
71
+ args: [FFIType.ptr, FFIType.u32, FFIType.u32, FFIType.u32],
72
+ returns: FFIType.bool
73
+ }
74
+ });
75
+ }
76
+ var FILE_MAP_READ = 4;
77
+ var SYNCHRONIZE = 1048576;
78
+ var WAIT_OBJECT_0 = 0;
79
+ var HWND_BROADCAST = 65535;
80
+ var DATA_VALID_EVENT_NAME = "Local\\IRSDKDataValidEvent";
81
+ var MEMMAPFILE = "Local\\IRSDKMemMapFileName";
82
+ var MEMMAPFILESIZE = 1164 * 1024;
83
+ var BROADCASTMSGNAME = "IRSDK_BROADCASTMSG";
84
+ function toWideString(str) {
85
+ const buf = new Uint8Array((str.length + 1) * 2);
86
+ for (let i = 0;i < str.length; i++) {
87
+ const code = str.charCodeAt(i);
88
+ buf[i * 2] = code & 255;
89
+ buf[i * 2 + 1] = code >> 8 & 255;
90
+ }
91
+ return buf;
92
+ }
93
+ function ensureFFILoaded() {
94
+ if (process.platform !== "win32") {
95
+ return false;
96
+ }
97
+ try {
98
+ if (!kernel32)
99
+ kernel32 = loadKernel32();
100
+ if (!user32)
101
+ user32 = loadUser32();
102
+ return true;
103
+ } catch {
104
+ return false;
105
+ }
106
+ }
107
+
108
+ class IRSDK {
109
+ parseYamlAsync;
110
+ sharedMemPtr = null;
111
+ sharedMemBuffer = null;
112
+ sharedMemView = null;
113
+ fileMappingHandle = null;
114
+ dataValidEvent = null;
115
+ header = null;
116
+ varHeaders = null;
117
+ varHeadersNames = null;
118
+ sessionInfoCache = new Map;
119
+ broadcastMsgId = null;
120
+ lastSessionInfoUpdate = 0;
121
+ workaroundConnectedState = 0;
122
+ frozenBuffer = null;
123
+ frozenView = null;
124
+ frozenBufOffset = 0;
125
+ testMode = false;
126
+ isInitialized = false;
127
+ constructor(options = {}) {
128
+ this.parseYamlAsync = options.parseYamlAsync ?? false;
129
+ }
130
+ get isConnected() {
131
+ if (!this.header)
132
+ return false;
133
+ if (this.header.status === StatusField.STATUS_CONNECTED) {
134
+ this.workaroundConnectedState = 0;
135
+ }
136
+ if (this.workaroundConnectedState === 0 && this.header.status !== StatusField.STATUS_CONNECTED) {
137
+ this.workaroundConnectedState = 1;
138
+ }
139
+ if (this.workaroundConnectedState === 1 && this.get("SessionNum") === null) {
140
+ this.workaroundConnectedState = 2;
141
+ }
142
+ if (this.workaroundConnectedState === 2 && this.get("SessionNum") !== null) {
143
+ this.workaroundConnectedState = 3;
144
+ }
145
+ return (this.dataValidEvent !== null || this.testMode) && (this.header.status === StatusField.STATUS_CONNECTED || this.workaroundConnectedState === 3);
146
+ }
147
+ get sessionInfoUpdate() {
148
+ return this.header?.sessionInfoUpdate ?? 0;
149
+ }
150
+ get varHeaderNames() {
151
+ if (!this.varHeaders)
152
+ return null;
153
+ if (!this.varHeadersNames) {
154
+ this.varHeadersNames = Array.from(this.varHeaders.keys());
155
+ }
156
+ return this.varHeadersNames;
157
+ }
158
+ async startup(testFile, dumpTo) {
159
+ if (testFile) {
160
+ return this.startupFromFile(testFile, dumpTo);
161
+ }
162
+ if (!ensureFFILoaded() || !kernel32) {
163
+ console.error("FFI not available - live telemetry requires Windows");
164
+ return false;
165
+ }
166
+ const simRunning = await this.checkSimStatus();
167
+ if (!simRunning)
168
+ return false;
169
+ const { ptr, toArrayBuffer } = __require("bun:ffi");
170
+ const eventNameWide = toWideString(DATA_VALID_EVENT_NAME);
171
+ this.dataValidEvent = kernel32.symbols.OpenEventW(SYNCHRONIZE, false, ptr(eventNameWide));
172
+ if (!this.waitValidDataEvent()) {
173
+ this.dataValidEvent = null;
174
+ return false;
175
+ }
176
+ const fileNameWide = toWideString(MEMMAPFILE);
177
+ this.fileMappingHandle = kernel32.symbols.OpenFileMappingW(FILE_MAP_READ, false, ptr(fileNameWide));
178
+ if (!this.fileMappingHandle)
179
+ return false;
180
+ this.sharedMemPtr = kernel32.symbols.MapViewOfFile(this.fileMappingHandle, FILE_MAP_READ, 0, 0, MEMMAPFILESIZE);
181
+ if (!this.sharedMemPtr) {
182
+ kernel32.symbols.CloseHandle(this.fileMappingHandle);
183
+ this.fileMappingHandle = null;
184
+ return false;
185
+ }
186
+ const arrayBuffer = toArrayBuffer(this.sharedMemPtr, 0, MEMMAPFILESIZE);
187
+ this.sharedMemBuffer = new Uint8Array(arrayBuffer);
188
+ this.sharedMemView = new DataView(arrayBuffer);
189
+ if (this.sharedMemView) {
190
+ if (dumpTo) {
191
+ await Bun.write(dumpTo, this.sharedMemBuffer);
192
+ }
193
+ this.header = parseHeader(this.sharedMemView);
194
+ this.isInitialized = this.header.version >= 1 && this.header.varBuf.length > 0;
195
+ if (this.isInitialized) {
196
+ this.varHeaders = parseVarHeaders(this.sharedMemView, this.sharedMemBuffer, this.header);
197
+ }
198
+ }
199
+ return this.isInitialized;
200
+ }
201
+ async startupFromFile(testFile, dumpTo) {
202
+ this.testMode = true;
203
+ const file = Bun.file(testFile);
204
+ const arrayBuffer = await file.arrayBuffer();
205
+ this.sharedMemBuffer = new Uint8Array(arrayBuffer);
206
+ this.sharedMemView = new DataView(arrayBuffer);
207
+ if (dumpTo) {
208
+ await Bun.write(dumpTo, this.sharedMemBuffer);
209
+ }
210
+ this.header = parseHeader(this.sharedMemView);
211
+ this.isInitialized = this.header.version >= 1 && this.header.varBuf.length > 0;
212
+ if (this.isInitialized) {
213
+ this.varHeaders = parseVarHeaders(this.sharedMemView, this.sharedMemBuffer, this.header);
214
+ }
215
+ return this.isInitialized;
216
+ }
217
+ shutdown() {
218
+ this.isInitialized = false;
219
+ this.lastSessionInfoUpdate = 0;
220
+ if (kernel32) {
221
+ if (this.sharedMemPtr) {
222
+ kernel32.symbols.UnmapViewOfFile(this.sharedMemPtr);
223
+ this.sharedMemPtr = null;
224
+ }
225
+ if (this.fileMappingHandle) {
226
+ kernel32.symbols.CloseHandle(this.fileMappingHandle);
227
+ this.fileMappingHandle = null;
228
+ }
229
+ if (this.dataValidEvent) {
230
+ kernel32.symbols.CloseHandle(this.dataValidEvent);
231
+ this.dataValidEvent = null;
232
+ }
233
+ }
234
+ this.sharedMemBuffer = null;
235
+ this.sharedMemView = null;
236
+ this.header = null;
237
+ this.varHeaders = null;
238
+ this.varHeadersNames = null;
239
+ this.sessionInfoCache.clear();
240
+ this.broadcastMsgId = null;
241
+ this.frozenBuffer = null;
242
+ this.frozenView = null;
243
+ this.testMode = false;
244
+ }
245
+ get(key) {
246
+ if (!this.header || !this.varHeaders)
247
+ return null;
248
+ const varHeader = this.varHeaders.get(key);
249
+ if (!varHeader) {
250
+ return this.getSessionInfo(key);
251
+ }
252
+ const view = this.frozenView ?? this.sharedMemView;
253
+ if (!view)
254
+ return null;
255
+ const varBuf = this.frozenView ? { bufOffset: this.frozenBufOffset } : getLatestVarBuffer(this.header.varBuf);
256
+ const offset = varBuf.bufOffset + varHeader.offset;
257
+ return readVarValue(view, offset, varHeader.type, varHeader.count);
258
+ }
259
+ freezeVarBufferLatest() {
260
+ this.unfreezeVarBufferLatest();
261
+ if (!this.header || !this.sharedMemBuffer)
262
+ return;
263
+ this.waitValidDataEvent();
264
+ const varBuf = this.header.varBuf.reduce((a, b) => a.tickCount > b.tickCount ? a : b);
265
+ this.frozenBufOffset = 0;
266
+ this.frozenBuffer = this.sharedMemBuffer.slice(varBuf.bufOffset, varBuf.bufOffset + this.header.bufLen);
267
+ this.frozenView = new DataView(this.frozenBuffer.buffer);
268
+ }
269
+ unfreezeVarBufferLatest() {
270
+ this.frozenBuffer = null;
271
+ this.frozenView = null;
272
+ this.frozenBufOffset = 0;
273
+ }
274
+ async checkSimStatus() {
275
+ try {
276
+ const response = await fetch(SIM_STATUS_URL);
277
+ const text = await response.text();
278
+ return text.includes("running:1");
279
+ } catch {
280
+ return false;
281
+ }
282
+ }
283
+ waitValidDataEvent() {
284
+ if (!kernel32)
285
+ return true;
286
+ if (this.dataValidEvent) {
287
+ return kernel32.symbols.WaitForSingleObject(this.dataValidEvent, 32) === WAIT_OBJECT_0;
288
+ }
289
+ return true;
290
+ }
291
+ getSessionInfo(key) {
292
+ if (!this.header || !this.sharedMemBuffer)
293
+ return null;
294
+ if (this.lastSessionInfoUpdate < this.header.sessionInfoUpdate) {
295
+ this.lastSessionInfoUpdate = this.header.sessionInfoUpdate;
296
+ for (const [k, v] of this.sessionInfoCache) {
297
+ if (v.data) {
298
+ this.sessionInfoCache.set(k, { ...v, data: null });
299
+ }
300
+ }
301
+ }
302
+ const cached = this.sessionInfoCache.get(key);
303
+ if (cached?.data) {
304
+ return cached.data;
305
+ }
306
+ const start = this.header.sessionInfoOffset;
307
+ const len = this.header.sessionInfoLen;
308
+ const raw = readString(this.sharedMemBuffer, start, len);
309
+ const sectionMatch = raw.match(new RegExp(`
310
+ ${key}:
311
+ ([\\s\\S]*?)(?=
312
+
313
+ |$)`));
314
+ if (!sectionMatch)
315
+ return null;
316
+ try {
317
+ const sectionYaml = `${key}:
318
+ ${sectionMatch[1]}`;
319
+ const cleaned = this.cleanYaml(sectionYaml);
320
+ const parsed = $parse(cleaned);
321
+ const result = parsed?.[key] ?? null;
322
+ this.sessionInfoCache.set(key, { data: result, update: this.lastSessionInfoUpdate });
323
+ return result;
324
+ } catch {
325
+ return null;
326
+ }
327
+ }
328
+ cleanYaml(yaml) {
329
+ let cleaned = yaml.replace(/[\x81\x8D\x8F\x90\x9D]/g, " ").replace(/[^\x09\x0A\x0D\x20-\x7E\xA0-\xFF]/g, "");
330
+ cleaned = cleaned.replace(/((?:DriverSetupName|UserName|TeamName|AbbrevName|Initials): )(.*)/g, (_, prefix, value) => {
331
+ const escaped = value.replace(/["\\]/g, "\\$&");
332
+ return `${prefix}"${escaped}"`;
333
+ });
334
+ cleaned = cleaned.replace(/(\w+: )(,.*)/g, '$1"$2"');
335
+ return cleaned;
336
+ }
337
+ getBroadcastMsgId() {
338
+ if (!user32)
339
+ return 0;
340
+ if (this.broadcastMsgId === null) {
341
+ const { ptr } = __require("bun:ffi");
342
+ const msgNameWide = toWideString(BROADCASTMSGNAME);
343
+ this.broadcastMsgId = user32.symbols.RegisterWindowMessageW(ptr(msgNameWide));
344
+ }
345
+ return this.broadcastMsgId;
346
+ }
347
+ broadcastMsg(broadcastType, var1 = 0, var2 = 0, var3 = 0) {
348
+ if (!user32)
349
+ return false;
350
+ return user32.symbols.SendNotifyMessageW(HWND_BROADCAST, this.getBroadcastMsgId(), (broadcastType | var1 << 16) >>> 0, (var2 | var3 << 16) >>> 0);
351
+ }
352
+ padCarNum(num) {
353
+ const numStr = String(num);
354
+ const numLen = numStr.length;
355
+ let zero = numLen - numStr.replace(/^0+/, "").length;
356
+ if (zero > 0 && numLen === zero)
357
+ zero--;
358
+ const numVal = parseInt(numStr, 10);
359
+ if (zero) {
360
+ const numPlace = numVal > 99 ? 3 : numVal > 9 ? 2 : 1;
361
+ return numVal + 1000 * (numPlace + zero);
362
+ }
363
+ return numVal;
364
+ }
365
+ camSwitchPos(position = 0, group = 1, camera = 0) {
366
+ return this.broadcastMsg(BroadcastMsg.CAM_SWITCH_POS, position, group, camera);
367
+ }
368
+ camSwitchNum(carNumber = "1", group = 1, camera = 0) {
369
+ return this.broadcastMsg(BroadcastMsg.CAM_SWITCH_NUM, this.padCarNum(carNumber), group, camera);
370
+ }
371
+ camSetState(cameraState = CameraState.CAM_TOOL_ACTIVE) {
372
+ return this.broadcastMsg(BroadcastMsg.CAM_SET_STATE, cameraState);
373
+ }
374
+ replaySetPlaySpeed(speed = 0, slowMotion = false) {
375
+ return this.broadcastMsg(BroadcastMsg.REPLAY_SET_PLAY_SPEED, speed, slowMotion ? 1 : 0);
376
+ }
377
+ replaySetPlayPosition(posMode = RpyPosMode.BEGIN, frameNum = 0) {
378
+ return this.broadcastMsg(BroadcastMsg.REPLAY_SET_PLAY_POSITION, posMode, frameNum);
379
+ }
380
+ replaySearch(searchMode = RpySrchMode.TO_START) {
381
+ return this.broadcastMsg(BroadcastMsg.REPLAY_SEARCH, searchMode);
382
+ }
383
+ replaySetState(stateMode = RpyStateMode.ERASE_TAPE) {
384
+ return this.broadcastMsg(BroadcastMsg.REPLAY_SET_STATE, stateMode);
385
+ }
386
+ reloadAllTextures() {
387
+ return this.broadcastMsg(BroadcastMsg.RELOAD_TEXTURES, ReloadTexturesMode.ALL);
388
+ }
389
+ reloadTexture(carIdx = 0) {
390
+ return this.broadcastMsg(BroadcastMsg.RELOAD_TEXTURES, ReloadTexturesMode.CAR_IDX, carIdx);
391
+ }
392
+ chatCommand(chatCommandMode = ChatCommandMode.BEGIN_CHAT) {
393
+ return this.broadcastMsg(BroadcastMsg.CHAT_COMMAND, chatCommandMode);
394
+ }
395
+ chatCommandMacro(macroNum = 0) {
396
+ return this.broadcastMsg(BroadcastMsg.CHAT_COMMAND, ChatCommandMode.MACRO, macroNum);
397
+ }
398
+ pitCommand(pitCommandMode = PitCommandMode.CLEAR, variable = 0) {
399
+ return this.broadcastMsg(BroadcastMsg.PIT_COMMAND, pitCommandMode, variable);
400
+ }
401
+ telemCommand(telemCommandMode = TelemCommandMode.STOP) {
402
+ return this.broadcastMsg(BroadcastMsg.TELEM_COMMAND, telemCommandMode);
403
+ }
404
+ ffbCommand(ffbCommandMode = FFBCommandMode.FFB_COMMAND_MAX_FORCE, value = 0) {
405
+ return this.broadcastMsg(BroadcastMsg.FFB_COMMAND, ffbCommandMode, Math.floor(value * 65536));
406
+ }
407
+ replaySearchSessionTime(sessionNum = 0, sessionTimeMs = 0) {
408
+ return this.broadcastMsg(BroadcastMsg.REPLAY_SEARCH_SESSION_TIME, sessionNum, sessionTimeMs);
409
+ }
410
+ videoCapture(videoCaptureMode = VideoCaptureMode.TRIGGER_SCREEN_SHOT) {
411
+ return this.broadcastMsg(BroadcastMsg.VIDEO_CAPTURE, videoCaptureMode);
412
+ }
413
+ }
414
+ function createIRSDK(options) {
415
+ return new IRSDK(options);
416
+ }
417
+
418
+ export { IRSDK, createIRSDK };
@@ -0,0 +1,293 @@
1
+ // @bun
2
+ import {
3
+ $parse
4
+ } from "./index-hxw291vy.js";
5
+ import {
6
+ parseDiskSubHeader,
7
+ parseHeader,
8
+ parseVarHeaders,
9
+ readString,
10
+ readVarValue,
11
+ readVarValueBatch
12
+ } from "./index-c5jf6rjt.js";
13
+ import {
14
+ DISK_SUB_HEADER_OFFSET
15
+ } from "./index-wb6ebd0j.js";
16
+
17
+ // src/ibt.ts
18
+ class IBT {
19
+ buffer = null;
20
+ view = null;
21
+ header = null;
22
+ diskHeader = null;
23
+ varHeaders = null;
24
+ sessionInfoCache = new Map;
25
+ rawSessionInfo = null;
26
+ filePath = null;
27
+ get fileName() {
28
+ return this.filePath;
29
+ }
30
+ get varHeaderBufferTick() {
31
+ return this.header?.varBuf[0]?.tickCount ?? null;
32
+ }
33
+ get varHeaderNames() {
34
+ if (!this.varHeaders)
35
+ return null;
36
+ return Array.from(this.varHeaders.keys());
37
+ }
38
+ get recordCount() {
39
+ return this.diskHeader?.sessionRecordCount ?? 0;
40
+ }
41
+ get sessionLapCount() {
42
+ return this.diskHeader?.sessionLapCount ?? 0;
43
+ }
44
+ get sessionStartTime() {
45
+ return this.diskHeader?.sessionStartTime ?? 0;
46
+ }
47
+ get sessionEndTime() {
48
+ return this.diskHeader?.sessionEndTime ?? 0;
49
+ }
50
+ get tickRate() {
51
+ return this.header?.tickRate ?? 60;
52
+ }
53
+ get bufLen() {
54
+ return this.header?.bufLen ?? 0;
55
+ }
56
+ async open(filePath) {
57
+ this.filePath = filePath;
58
+ const file = Bun.file(filePath);
59
+ const arrayBuffer = await file.arrayBuffer();
60
+ this.buffer = new Uint8Array(arrayBuffer);
61
+ this.view = new DataView(arrayBuffer);
62
+ this.header = parseHeader(this.view);
63
+ this.diskHeader = parseDiskSubHeader(this.view, DISK_SUB_HEADER_OFFSET);
64
+ this.varHeaders = parseVarHeaders(this.view, this.buffer, this.header);
65
+ this.sessionInfoCache.clear();
66
+ this.rawSessionInfo = null;
67
+ }
68
+ openSync(filePath) {
69
+ this.filePath = filePath;
70
+ const file = Bun.file(filePath);
71
+ const arrayBuffer = file.arrayBuffer();
72
+ if (arrayBuffer instanceof Promise) {
73
+ throw new Error("Sync file reading requires Bun.file().bytes() - use open() instead");
74
+ }
75
+ this.buffer = new Uint8Array(arrayBuffer);
76
+ this.view = new DataView(arrayBuffer);
77
+ this.header = parseHeader(this.view);
78
+ this.diskHeader = parseDiskSubHeader(this.view, DISK_SUB_HEADER_OFFSET);
79
+ this.varHeaders = parseVarHeaders(this.view, this.buffer, this.header);
80
+ this.sessionInfoCache.clear();
81
+ this.rawSessionInfo = null;
82
+ }
83
+ static async fromBuffer(buffer) {
84
+ const ibt = new IBT;
85
+ ibt.buffer = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);
86
+ ibt.view = new DataView(ibt.buffer.buffer, ibt.buffer.byteOffset, ibt.buffer.byteLength);
87
+ ibt.header = parseHeader(ibt.view);
88
+ ibt.diskHeader = parseDiskSubHeader(ibt.view, DISK_SUB_HEADER_OFFSET);
89
+ ibt.varHeaders = parseVarHeaders(ibt.view, ibt.buffer, ibt.header);
90
+ return ibt;
91
+ }
92
+ close() {
93
+ this.buffer = null;
94
+ this.view = null;
95
+ this.header = null;
96
+ this.diskHeader = null;
97
+ this.varHeaders = null;
98
+ this.sessionInfoCache.clear();
99
+ this.rawSessionInfo = null;
100
+ this.filePath = null;
101
+ }
102
+ getVarHeader(name) {
103
+ return this.varHeaders?.get(name);
104
+ }
105
+ get(index, key) {
106
+ if (!this.header || !this.view || !this.varHeaders || !this.diskHeader)
107
+ return null;
108
+ if (index < 0 || index >= this.diskHeader.sessionRecordCount)
109
+ return null;
110
+ const varHeader = this.varHeaders.get(key);
111
+ if (!varHeader)
112
+ return null;
113
+ const varBuf = this.header.varBuf[0];
114
+ if (!varBuf)
115
+ return null;
116
+ const varOffset = varHeader.offset + varBuf.bufOffset + index * this.header.bufLen;
117
+ return readVarValue(this.view, varOffset, varHeader.type, varHeader.count);
118
+ }
119
+ getAll(key) {
120
+ if (!this.header || !this.view || !this.varHeaders || !this.diskHeader)
121
+ return null;
122
+ const varHeader = this.varHeaders.get(key);
123
+ if (!varHeader)
124
+ return null;
125
+ const varBuf = this.header.varBuf[0];
126
+ if (!varBuf)
127
+ return null;
128
+ const baseOffset = varBuf.bufOffset;
129
+ const bufLen = this.header.bufLen;
130
+ const recordCount = this.diskHeader.sessionRecordCount;
131
+ const varOffset = varHeader.offset;
132
+ const isArray = varHeader.count > 1;
133
+ const results = new Array(recordCount);
134
+ for (let i = 0;i < recordCount; i++) {
135
+ const offset = baseOffset + i * bufLen + varOffset;
136
+ results[i] = readVarValue(this.view, offset, varHeader.type, varHeader.count);
137
+ }
138
+ return results;
139
+ }
140
+ getAllTyped(key) {
141
+ if (!this.header || !this.view || !this.varHeaders || !this.diskHeader)
142
+ return null;
143
+ const varHeader = this.varHeaders.get(key);
144
+ if (!varHeader || varHeader.count !== 1)
145
+ return null;
146
+ const varBuf = this.header.varBuf[0];
147
+ if (!varBuf)
148
+ return null;
149
+ const baseOffset = varBuf.bufOffset;
150
+ const bufLen = this.header.bufLen;
151
+ const recordCount = this.diskHeader.sessionRecordCount;
152
+ const varOffset = varHeader.offset;
153
+ let output;
154
+ switch (varHeader.type) {
155
+ case 4:
156
+ output = new Float32Array(recordCount);
157
+ break;
158
+ case 5:
159
+ output = new Float64Array(recordCount);
160
+ break;
161
+ case 2:
162
+ output = new Int32Array(recordCount);
163
+ break;
164
+ case 3:
165
+ output = new Uint32Array(recordCount);
166
+ break;
167
+ default:
168
+ return null;
169
+ }
170
+ readVarValueBatch(this.view, baseOffset, varOffset, varHeader.type, bufLen, recordCount, output);
171
+ return output;
172
+ }
173
+ getAllMultiple(keys) {
174
+ const results = new Map;
175
+ if (!this.header || !this.view || !this.varHeaders || !this.diskHeader)
176
+ return results;
177
+ const varBuf = this.header.varBuf[0];
178
+ if (!varBuf)
179
+ return results;
180
+ const baseOffset = varBuf.bufOffset;
181
+ const bufLen = this.header.bufLen;
182
+ const recordCount = this.diskHeader.sessionRecordCount;
183
+ const varInfos = [];
184
+ for (const key of keys) {
185
+ const varHeader = this.varHeaders.get(key);
186
+ if (varHeader) {
187
+ const data = new Array(recordCount);
188
+ varInfos.push({ key, varHeader, data });
189
+ results.set(key, data);
190
+ }
191
+ }
192
+ for (let i = 0;i < recordCount; i++) {
193
+ const recordOffset = baseOffset + i * bufLen;
194
+ for (const { varHeader, data } of varInfos) {
195
+ const offset = recordOffset + varHeader.offset;
196
+ data[i] = readVarValue(this.view, offset, varHeader.type, varHeader.count);
197
+ }
198
+ }
199
+ return results;
200
+ }
201
+ *iterate(keys) {
202
+ if (!this.header || !this.view || !this.varHeaders || !this.diskHeader)
203
+ return;
204
+ const varBuf = this.header.varBuf[0];
205
+ if (!varBuf)
206
+ return;
207
+ const baseOffset = varBuf.bufOffset;
208
+ const bufLen = this.header.bufLen;
209
+ const recordCount = this.diskHeader.sessionRecordCount;
210
+ const varInfos = [];
211
+ for (const key of keys) {
212
+ const varHeader = this.varHeaders.get(key);
213
+ if (varHeader) {
214
+ varInfos.push({ key, varHeader });
215
+ }
216
+ }
217
+ for (let i = 0;i < recordCount; i++) {
218
+ const recordOffset = baseOffset + i * bufLen;
219
+ const record = new Map;
220
+ for (const { key, varHeader } of varInfos) {
221
+ const offset = recordOffset + varHeader.offset;
222
+ record.set(key, readVarValue(this.view, offset, varHeader.type, varHeader.count));
223
+ }
224
+ yield record;
225
+ }
226
+ }
227
+ getRawSessionInfo() {
228
+ if (!this.header || !this.buffer)
229
+ return null;
230
+ if (this.rawSessionInfo === null) {
231
+ const start = this.header.sessionInfoOffset;
232
+ const len = this.header.sessionInfoLen;
233
+ this.rawSessionInfo = readString(this.buffer, start, len);
234
+ }
235
+ return this.rawSessionInfo;
236
+ }
237
+ getSessionInfo(key) {
238
+ const raw = this.getRawSessionInfo();
239
+ if (!raw)
240
+ return null;
241
+ if (!key) {
242
+ if (!this.sessionInfoCache.has("__full__")) {
243
+ try {
244
+ const cleaned = this.cleanYaml(raw);
245
+ this.sessionInfoCache.set("__full__", $parse(cleaned));
246
+ } catch {
247
+ return null;
248
+ }
249
+ }
250
+ return this.sessionInfoCache.get("__full__");
251
+ }
252
+ if (this.sessionInfoCache.has(key)) {
253
+ return this.sessionInfoCache.get(key);
254
+ }
255
+ const sectionMatch = raw.match(new RegExp(`
256
+ ${key}:
257
+ ([\\s\\S]*?)(?=
258
+
259
+ |$)`));
260
+ if (!sectionMatch)
261
+ return null;
262
+ try {
263
+ const sectionYaml = `${key}:
264
+ ${sectionMatch[1]}`;
265
+ const cleaned = this.cleanYaml(sectionYaml);
266
+ const parsed = $parse(cleaned);
267
+ const result = parsed?.[key] ?? null;
268
+ this.sessionInfoCache.set(key, result);
269
+ return result;
270
+ } catch {
271
+ return null;
272
+ }
273
+ }
274
+ cleanYaml(yaml) {
275
+ let cleaned = yaml.replace(/[\x81\x8D\x8F\x90\x9D]/g, " ").replace(/[^\x09\x0A\x0D\x20-\x7E\xA0-\xFF]/g, "");
276
+ cleaned = cleaned.replace(/((?:DriverSetupName|UserName|TeamName|AbbrevName|Initials): )(.*)/g, (_, prefix, value) => {
277
+ const escaped = value.replace(/["\\]/g, "\\$&");
278
+ return `${prefix}"${escaped}"`;
279
+ });
280
+ cleaned = cleaned.replace(/(\w+: )(,.*)/g, '$1"$2"');
281
+ return cleaned;
282
+ }
283
+ }
284
+ async function parseIbt(filePath) {
285
+ const ibt = new IBT;
286
+ await ibt.open(filePath);
287
+ return ibt;
288
+ }
289
+ async function parseIbtBuffer(buffer) {
290
+ return IBT.fromBuffer(buffer);
291
+ }
292
+
293
+ export { IBT, parseIbt, parseIbtBuffer };