Rolled back some stuff here like the package stuff

This will add support to view live data
This commit is contained in:
2024-10-27 22:48:32 +00:00
parent 62a38da3b3
commit 2bc756a2f0
14 changed files with 1500 additions and 765 deletions
+117
View File
@@ -0,0 +1,117 @@
package main
type Msg struct {
Cmd int
P1 int32
P2 int32
P3 int32
}
const (
SimStatusUrl string = "http://127.0.0.1:32034/get_sim_status?object=simStatus"
IRSDK_DATAVALIDEVENTNAME string = "Local\\IRSDKDataValidEvent"
IRSDK_MEMMAPFILENAME string = "Local\\IRSDKMemMapFileName"
IRSDK_BROADCASTMSGNAME string = "IRSDK_BROADCASTMSG"
fileMapSize uint32 = 1164 * 1024
connTimeout int64 = 30
stConnected int = 1
)
const (
BroadcastCamSwitchPos int = 0 // car position, group, camera
BroadcastCamSwitchNum int = 1 // driver #, group, camera
BroadcastCamSetState int = 2 // irsdk_CameraState, unused, unused
BroadcastReplaySetPlaySpeed int = 3 // speed, slowMotion, unused
BroadcastReplaySetPlayPosition int = 4 // irsdk_RpyPosMode, Frame Number (high, low)
BroadcastReplaySearch int = 5 // irsdk_RpySrchMode, unused, unused
BroadcastReplaySetState int = 6 // irsdk_RpyStateMode, unused, unused
BroadcastReloadTextures int = 7 // irsdk_ReloadTexturesMode, carIdx, unused
BroadcastChatComand int = 8 // irsdk_ChatCommandMode, subCommand, unused
BroadcastPitCommand int = 9 // irsdk_PitCommandMode, parameter
BroadcastTelemCommand int = 10 // irsdk_TelemCommandMode, unused, unused
BroadcastFFBCommand int = 11 // irsdk_FFBCommandMode, value (float, high, low)
BroadcastReplaySearchSessionTime int = 12 // sessionNum, sessionTimeMS (high, low)
BroadcastLast int = 13 // unused placeholder
)
const (
ChatCommandMacro int = 0 // pass in a number from 1-15 representing the chat macro to launch
ChatCommandBeginChat int = 1 // Open up a new chat window
ChatCommandReply int = 2 // Reply to last private chat
ChatCommandCancel int = 3 // Close chat window
)
// this only works when the driver is in the car
const (
PitCommandClear int = 0 // Clear all pit checkboxes
PitCommandWS int = 1 // Clean the winshield, using one tear off
PitCommandFuel int = 2 // Add fuel, optionally specify the amount to add in liters or pass '0' to use existing amount
PitCommandLF int = 3 // Change the left front tire, optionally specifying the pressure in KPa or pass '0' to use existing pressure
PitCommandRF int = 4 // right front
PitCommandLR int = 5 // left rear
PitCommandRR int = 6 // right rear
PitCommandClearTires int = 7 // Clear tire pit checkboxes
PitCommandFR int = 8 // Request a fast repair
PitCommandClearWS int = 9 // Uncheck Clean the winshield checkbox
PitCommandClearFR int = 10 // Uncheck request a fast repair
PitCommandClearFuel int = 11 // Uncheck add fuel
)
// You can call this any time, but telemtry only records when driver is in there car
const (
TelemCommandStop int = 0 // Turn telemetry recording off
TelemCommandStart int = 1 // Turn telemetry recording on
TelemCommandRestart int = 2 // Write current file to disk and start a new one
)
const (
RpyStateEraseTape int = 0 // clear any data in the replay tape
RpyStateLast int = 1 // unused place holder
)
const (
ReloadTexturesAll int = 0 // reload all textuers
ReloadTexturesCarIdx int = 1 // reload only textures for the specific carIdx
)
// Search replay tape for events
const (
RpySrchToStart int = 0
RpySrchToEnd int = 1
RpySrchPrevSession int = 2
RpySrchNextSession int = 3
RpySrchPrevLap int = 4
RpySrchNextLap int = 5
RpySrchPrevFrame int = 6
RpySrchNextFrame int = 7
RpySrchPrevIncident int = 8
RpySrchNextIncident int = 9
RpySrchLast int = 10 // unused placeholder
)
const (
RpyPosBegin int = 0
RpyPosCurrent int = 1
RpyPosEnd int = 2
RpyPosLast int = 3 // unused placeholder
)
// You can call this any time
const (
FFBCommandMaxForce int = 0 // Set the maximum force when mapping steering torque force to direct input units (float in Nm)
FFBCommandLast int = 1 // unused placeholder
)
// irsdk_BroadcastCamSwitchPos or irsdk_BroadcastCamSwitchNum camera focus defines
// pass these in for the first parameter to select the 'focus at' types in the camera system.
const (
csFocusAtIncident int = -3
csFocusAtLeader int = -2
csFocusAtExiting int = -1
csFocusAtDriver int = 0 // ctFocusAtDriver + car number...
)
// Camera positions
const (
// CamNose
)
+47 -47
View File
@@ -1,47 +1,47 @@
package ibtReader
import (
"bytes"
"encoding/binary"
"fmt"
)
const (
SubHeaderSize = 32 // SubHeaderSize is the size of the subheader
)
// DiskSubHeader represents the IBT sub headers
type DiskSubHeader struct {
StartDate int64 // StartDate represents the start date of the telemetry
StartTime float64 // StartTime of file relative to start of session
EndTime float64 // EndTime of file relative to start of session
LapCount int32 // LapCount represents the total number laps
RecordCount int32 // RecordCount holds the number of data frames
}
// parseTelemetrySubHeader will return a pointer to a DiskSubHeader variable
// or nil if an error occurs. In which case the error return value is more
// valuable
func parseTelemetrySubHeader(buf [SubHeaderSize]byte) (*DiskSubHeader, error) {
dst := DiskSubHeader{}
err := binary.Read(bytes.NewBuffer(buf[:]), binary.LittleEndian, &dst)
if err != nil {
return nil, err
}
return &dst, nil
}
// ToString renders a string showing the values of the struct
func (d *DiskSubHeader) ToString() string {
return fmt.Sprintf(
"StartDate: %13d (0x%04x)\n"+
"StartTime: %13f (0x%08x)\n"+
"EndTime: %13f (0x%08x)\n"+
"LapCount: %13d (0x%04x)\n"+
"RecordCount: %13d (0x%04x)\n",
d.StartDate, d.StartDate, d.StartTime, d.StartTime,
d.EndTime, d.EndTime, d.LapCount, d.LapCount,
d.RecordCount, d.RecordCount,
)
}
package main
import (
"bytes"
"encoding/binary"
"fmt"
)
const (
SubHeaderSize = 32 // SubHeaderSize is the size of the subheader
)
// DiskSubHeader represents the IBT sub headers
type DiskSubHeader struct {
StartDate int64 // StartDate represents the start date of the telemetry
StartTime float64 // StartTime of file relative to start of session
EndTime float64 // EndTime of file relative to start of session
LapCount int32 // LapCount represents the total number laps
RecordCount int32 // RecordCount holds the number of data frames
}
// parseTelemetrySubHeader will return a pointer to a DiskSubHeader variable
// or nil if an error occurs. In which case the error return value is more
// valuable
func parseTelemetrySubHeader(buf [SubHeaderSize]byte) (*DiskSubHeader, error) {
dst := DiskSubHeader{}
err := binary.Read(bytes.NewBuffer(buf[:]), binary.LittleEndian, &dst)
if err != nil {
return nil, err
}
return &dst, nil
}
// ToString renders a string showing the values of the struct
func (d *DiskSubHeader) ToString() string {
return fmt.Sprintf(
"StartDate: %13d (0x%04x)\n"+
"StartTime: %13f (0x%08x)\n"+
"EndTime: %13f (0x%08x)\n"+
"LapCount: %13d (0x%04x)\n"+
"RecordCount: %13d (0x%04x)\n",
d.StartDate, d.StartDate, d.StartTime, d.StartTime,
d.EndTime, d.EndTime, d.LapCount, d.LapCount,
d.RecordCount, d.RecordCount,
)
}
+5 -2
View File
@@ -1,5 +1,8 @@
module esilvalabs.org/ibtReader.git
module ibtReader
go 1.23.2
require gopkg.in/yaml.v3 v3.0.1
require (
golang.org/x/sys v0.26.0
gopkg.in/yaml.v3 v3.0.1
)
+2
View File
@@ -1,3 +1,5 @@
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+80 -80
View File
@@ -1,80 +1,80 @@
package ibtReader
import (
"bytes"
"encoding/binary"
"fmt"
)
const (
FileHeaderSize = 112 // FileHeaderSize is the size of the headers
HeaderSize = 4 // HeaderSize is the size of a single header
)
// TelemetryHeaders struct to hold an IBT file's headers
type TelemetryHeaders struct {
Version int32
// Status of 1 indicates a completed session and status of 0 a live session
Status int32
// TickRate indicates the frequency of writes (usually 60)
TickRate int32
// SessionInfoUpdate indicates the number of times the SessionInfo was
// updated. 0 for finished sessions and >1 for active sessions
SessionInfoUpdate int32
// SessionInfoLength is the length of the session info buffer
SessionInfoLength int32
// SessionInfoOffset is the offset of the session info in the buffer
SessionInfoOffset int32
// NumVars is the number of variables in each input
NumVars int32
// VarHeaderOffset is the offset of the VarHeader
VarHeaderOffset int32
// NumBuf will be 1 for static files and 3 for live telemetry files
NumBuf int32
// BufLen is the length for parsing VarHeader values
BufLen int32
// Padding
Padding [12]byte
// I still don't know what this is:
BufOffset int32
}
// ToString renders a string showing the values of the struct
func (th *TelemetryHeaders) ToString() string {
return fmt.Sprintf(
"Version: %5d (0x%04x)\n"+
"Status: %5d (0x%04x)\n"+
"TickRate: %5d (0x%04x)\n"+
"SIUpdate: %5d (0x%04x)\n"+
"SILength: %5d (0x%04x)\n"+
"SIOffset: %5d (0x%04x)\n"+
"NumVars: %5d (0x%04x)\n"+
"VarHeaderOffset: %5d (0x%04x)\n"+
"NumBuf: %5d (0x%04x)\n"+
"BufLen: %5d (0x%04x)\n"+
"BufOffset: %5d (0x%04x)\n",
th.Version, th.Version, th.Status, th.Status, th.TickRate, th.TickRate,
th.SessionInfoUpdate, th.SessionInfoUpdate,
th.SessionInfoLength, th.SessionInfoLength,
th.SessionInfoOffset, th.SessionInfoOffset,
th.NumVars, th.NumVars, th.VarHeaderOffset, th.VarHeaderOffset,
th.NumBuf, th.NumBuf, th.BufLen, th.BufLen, th.BufOffset, th.BufOffset,
)
}
// parseTelemetryHeader will read the IBT file headers from a correctly sized
// buffer.
// You need to pass a the first FILE_HEADER_SIZE bytes of the buffer
func parseTelemetryHeader(buf [FileHeaderSize]byte) (*TelemetryHeaders, error) {
if len(buf)%HeaderSize != 0 {
return nil, fmt.Errorf("buffer must be multiple of size: %d", HeaderSize)
}
dst := TelemetryHeaders{}
err := binary.Read(bytes.NewBuffer(buf[:]), binary.LittleEndian, &dst)
if err != nil {
return nil, fmt.Errorf("unable to unpack data: %v", err)
}
return &dst, nil
}
package main
import (
"bytes"
"encoding/binary"
"fmt"
)
const (
FileHeaderSize = 112 // FileHeaderSize is the size of the headers
HeaderSize = 4 // HeaderSize is the size of a single header
)
// TelemetryHeaders struct to hold an IBT file's headers
type TelemetryHeaders struct {
Version int32
// Status of 1 indicates a completed session and status of 0 a live session
Status int32
// TickRate indicates the frequency of writes (usually 60)
TickRate int32
// SessionInfoUpdate indicates the number of times the SessionInfo was
// updated. 0 for finished sessions and >1 for active sessions
SessionInfoUpdate int32
// SessionInfoLength is the length of the session info buffer
SessionInfoLength int32
// SessionInfoOffset is the offset of the session info in the buffer
SessionInfoOffset int32
// NumVars is the number of variables in each input
NumVars int32
// VarHeaderOffset is the offset of the VarHeader
VarHeaderOffset int32
// NumBuf will be 1 for static files and 3 for live telemetry files
NumBuf int32
// BufLen is the length for parsing VarHeader values
BufLen int32
// Padding
Padding [12]byte
// I still don't know what this is:
BufOffset int32
}
// ToString renders a string showing the values of the struct
func (th *TelemetryHeaders) ToString() string {
return fmt.Sprintf(
"Version: %5d (0x%04x)\n"+
"Status: %5d (0x%04x)\n"+
"TickRate: %5d (0x%04x)\n"+
"SIUpdate: %5d (0x%04x)\n"+
"SILength: %5d (0x%04x)\n"+
"SIOffset: %5d (0x%04x)\n"+
"NumVars: %5d (0x%04x)\n"+
"VarHeaderOffset: %5d (0x%04x)\n"+
"NumBuf: %5d (0x%04x)\n"+
"BufLen: %5d (0x%04x)\n"+
"BufOffset: %5d (0x%04x)\n",
th.Version, th.Version, th.Status, th.Status, th.TickRate, th.TickRate,
th.SessionInfoUpdate, th.SessionInfoUpdate,
th.SessionInfoLength, th.SessionInfoLength,
th.SessionInfoOffset, th.SessionInfoOffset,
th.NumVars, th.NumVars, th.VarHeaderOffset, th.VarHeaderOffset,
th.NumBuf, th.NumBuf, th.BufLen, th.BufLen, th.BufOffset, th.BufOffset,
)
}
// parseTelemetryHeader will read the IBT file headers from a correctly sized
// buffer.
// You need to pass a the first FILE_HEADER_SIZE bytes of the buffer
func parseTelemetryHeader(buf [FileHeaderSize]byte) (*TelemetryHeaders, error) {
if len(buf)%HeaderSize != 0 {
return nil, fmt.Errorf("buffer must be multiple of size: %d", HeaderSize)
}
dst := TelemetryHeaders{}
err := binary.Read(bytes.NewBuffer(buf[:]), binary.LittleEndian, &dst)
if err != nil {
return nil, fmt.Errorf("unable to unpack data: %v", err)
}
return &dst, nil
}
+154 -136
View File
@@ -1,136 +1,154 @@
// Package IbtReader is all you need for you iRacing telemetry parsing
package ibtReader
import (
"fmt"
"io"
)
const (
ibtFile = "./telemetryFiles/mx5_2016Okayama_full_2024_10_19_22_02_12.ibt"
)
// Reader is an interface to represent the readable data that can be either
// a .ibt file (or live data, hopefully)
type Reader interface {
io.Reader
io.ReaderAt
io.ReadCloser
}
// IBT struct will hold the relevant data for a given IBT file
type IBT struct {
File Reader // Source of the data
Headers *TelemetryHeaders // IBT file Headers
SubHeaders *DiskSubHeader // IBT file Sub Headers
SessionInfo *SessionInfoYAML // IBT file Session Info
Vars *TelemetryVars // Vars will hold the telemetry data
Tick int32 // Tick holds the cound of the reads
}
// Init serves to initialize and get a hold of a IBT struct
func Init(f Reader) (*IBT, error) {
// Read the header of the file
var err error
ibt := IBT{
File: f,
Vars: &TelemetryVars{},
}
// Read the file headers
var headerRaw [FileHeaderSize]byte
_, err = ibt.File.ReadAt(headerRaw[:], 0)
if err != nil {
return nil, fmt.Errorf("Failed to read headers from file: %v", err)
}
ibt.Headers, err = parseTelemetryHeader(headerRaw)
if err != nil {
return nil, fmt.Errorf("Unable to read headers from file: %v", err)
}
// Read the disk sub headers
var subheaderRaw [SubHeaderSize]byte
_, err = ibt.File.ReadAt(subheaderRaw[:], 112)
if err != nil {
return nil, fmt.Errorf("Failed to read disk subheaders from file: %v", err)
}
ibt.SubHeaders, err = parseTelemetrySubHeader(subheaderRaw)
if err != nil {
return nil, fmt.Errorf("Unable to parse disk subheaders from file: %v", err)
}
// Read session info string
sessionInfoStringRaw := make([]byte, ibt.Headers.SessionInfoLength)
_, err = ibt.File.ReadAt(sessionInfoStringRaw, int64(ibt.Headers.SessionInfoOffset))
if err != nil {
return nil, fmt.Errorf("Failed to read sessionInfoString from file: %v", err)
}
ibt.SessionInfo, err = parseSessionInfo(sessionInfoStringRaw)
if err != nil {
return nil, fmt.Errorf("Unable to parse SessionInfoString from file: %v", err)
}
// Read the telemetry vars info
err = ibt.readVariablerHeaders()
if err != nil {
return nil, fmt.Errorf("Unable to parser variable headers from file: %v", err)
}
return &ibt, nil
}
func msToKph(v float32) int {
return int((3600 * v) / 1000)
}
// func main() {
// fmt.Println("================== IBT FILE PARSER ==================")
//
// file, err := os.Open(ibtFile)
// if err != nil {
// log.Fatalf("Failed to open IBT file: %v", err)
// }
//
// ibt, err := Init(file)
// fmt.Printf("%s\n", ibt.Headers.ToString())
// fmt.Printf("%s\n", ibt.SubHeaders.ToString())
//
// // Display the human readable start date
// unixStartDate := time.Unix(ibt.SubHeaders.StartDate, 0)
// startDate := unixStartDate.Format("2006/01/02 15:04:05 -0700 MST")
// fmt.Println("StartDate:", startDate)
//
// // Display the human readable version of start time
// unixStartTime := time.Unix(ibt.SubHeaders.StartDate+int64(ibt.SubHeaders.StartTime), 0)
// startTime := unixStartTime.Format("2006/01/02 15:04:05 -0700 MST")
// fmt.Println("StartTime:", startTime)
//
// // Display the human readable version of end time
// unixEndTime := time.Unix(ibt.SubHeaders.StartDate+int64(ibt.SubHeaders.EndTime), 0)
// endTime := unixEndTime.Format("2006/01/02 15:04:05 -0700 MST")
// fmt.Println("EndTime: ", endTime)
//
// last := time.Now().UnixMilli()
// for {
// time.Sleep(time.Second / 60)
// res := ibt.Update()
//
// curTime := time.Now().UnixMilli()
//
// if curTime-last > 250 {
// fmt.Printf(" \r")
// if val, ok := ibt.Vars.Vars["Speed"]; ok {
// fmt.Printf("\r%d %d", ibt.Tick/60, msToKph(val.Value.(float32)))
// } else {
// fmt.Printf("\r%d %s", ibt.Tick/60, "KEY DOESN'T EXIST")
// }
// }
//
// if !res {
// fmt.Println("\nEnd of file found...")
// break
// }
// }
//
// fmt.Printf("%d\n", ibt.Tick)
// }
// Package IbtReader is all you need for you iRacing telemetry parsing
package main
import (
"fmt"
"io"
"log"
"time"
"ibtReader/sharedMem"
)
const (
ibtFile = "./telemetryFiles/mx5_2016Okayama_full_2024_10_19_22_02_12.ibt"
)
// Reader is an interface to represent the readable data that can be either
// a .ibt file (or live data, hopefully)
type Reader interface {
io.Reader
io.ReaderAt
io.ReadCloser
}
// IBT struct will hold the relevant data for a given IBT file
type IBT struct {
File Reader // Source of the data
Headers *TelemetryHeaders // IBT file Headers
SubHeaders *DiskSubHeader // IBT file Sub Headers
SessionInfo *SessionInfoYAML // IBT file Session Info
Vars *TelemetryVars // Vars will hold the telemetry data
Tick int32 // Tick holds the cound of the reads
LiveData bool
}
// Init serves to initialize and get a hold of a IBT struct
func Init(f Reader) (*IBT, error) {
// Read the header of the file
var err error
ibt := IBT{
File: f,
Vars: &TelemetryVars{},
LiveData: false,
}
if ibt.File == nil {
// User is requesting us to read live data - present in the mem map file
ibt.File, err = sharedMem.Open(IRSDK_MEMMAPFILENAME, fileMapSize)
if err != nil {
return nil, fmt.Errorf("Failed to open memory mapped file: %v", err)
}
ibt.LiveData = true
}
// Read the file headers
var headerRaw [FileHeaderSize]byte
_, err = ibt.File.ReadAt(headerRaw[:], 0)
if err != nil {
return nil, fmt.Errorf("Failed to read headers from file: %v", err)
}
ibt.Headers, err = parseTelemetryHeader(headerRaw)
if err != nil {
return nil, fmt.Errorf("Unable to read headers from file: %v", err)
}
// Read the disk sub headers
var subheaderRaw [SubHeaderSize]byte
_, err = ibt.File.ReadAt(subheaderRaw[:], 112)
if err != nil {
return nil, fmt.Errorf("Failed to read disk subheaders from file: %v", err)
}
ibt.SubHeaders, err = parseTelemetrySubHeader(subheaderRaw)
if err != nil {
return nil, fmt.Errorf("Unable to parse disk subheaders from file: %v", err)
}
// Read session info string
sessionInfoStringRaw := make([]byte, ibt.Headers.SessionInfoLength)
_, err = ibt.File.ReadAt(sessionInfoStringRaw, int64(ibt.Headers.SessionInfoOffset))
if err != nil {
return nil, fmt.Errorf("Failed to read sessionInfoString from file: %v", err)
}
ibt.SessionInfo, err = parseSessionInfo(sessionInfoStringRaw)
if err != nil {
return nil, fmt.Errorf("Unable to parse SessionInfoString from file: %v", err)
}
// Read the telemetry vars info
err = ibt.readVariablerHeaders()
if err != nil {
return nil, fmt.Errorf("Unable to parser variable headers from file: %v", err)
}
return &ibt, nil
}
func msToKph(v float32) int {
return int((3600 * v) / 1000)
}
func main() {
fmt.Println("================== IBT FILE PARSER ==================")
// file, err := os.Open(ibtFile)
// if err != nil {
// log.Fatalf("Failed to open IBT file: %v", err)
// }
ibt, err := Init(nil)
if err != nil {
log.Fatalf("Failed to create irsdk instance: %v", err)
}
fmt.Printf("%s\n", ibt.Headers.ToString())
fmt.Printf("%s\n", ibt.SubHeaders.ToString())
// Display the human readable start date
unixStartDate := time.Unix(ibt.SubHeaders.StartDate, 0)
startDate := unixStartDate.Format("2006/01/02 15:04:05 -0700 MST")
fmt.Println("StartDate:", startDate)
// Display the human readable version of start time
unixStartTime := time.Unix(ibt.SubHeaders.StartDate+int64(ibt.SubHeaders.StartTime), 0)
startTime := unixStartTime.Format("2006/01/02 15:04:05 -0700 MST")
fmt.Println("StartTime:", startTime)
// Display the human readable version of end time
unixEndTime := time.Unix(ibt.SubHeaders.StartDate+int64(ibt.SubHeaders.EndTime), 0)
endTime := unixEndTime.Format("2006/01/02 15:04:05 -0700 MST")
fmt.Println("EndTime: ", endTime)
last := time.Now().UnixMilli()
for {
time.Sleep(time.Second / 60)
res := ibt.Update()
curTime := time.Now().UnixMilli()
if curTime-last > 250 {
fmt.Printf(" \r")
if val, ok := ibt.Vars.Vars["Speed"]; ok {
fmt.Printf("\r%d %d", ibt.Tick/60, msToKph(val.Value.(float32)))
} else {
fmt.Printf("\r%d %s", ibt.Tick/60, "KEY DOESN'T EXIST")
}
}
if !res {
fmt.Println("\nEnd of file found...")
break
}
}
fmt.Printf("%d\n", ibt.Tick)
}
+323 -323
View File
@@ -1,323 +1,323 @@
package ibtReader
import (
"encoding/json"
"gopkg.in/yaml.v3"
)
// SessionInfoYAML is a string with session info in the IBT file
type SessionInfoYAML struct {
WeekendInfo struct {
TrackName string `yaml:"TrackName"`
TrackID int `yaml:"TrackID"`
TrackLength string `yaml:"TrackLength"`
TrackDisplayName string `yaml:"TrackDisplayName"`
TrackDisplayShortName string `yaml:"TrackDisplayShortName"`
TrackConfigName string `yaml:"TrackConfigName"`
TrackCity string `yaml:"TrackCity"`
TrackCountry string `yaml:"TrackCountry"`
TrackAltitude string `yaml:"TrackAltitude"`
TrackLatitude string `yaml:"TrackLatitude"`
TrackLongitude string `yaml:"TrackLongitude"`
TrackNorthOffset string `yaml:"TrackNorthOffset"`
TrackNumTurns int `yaml:"TrackNumTurns"`
TrackPitSpeedLimit string `yaml:"TrackPitSpeedLimit"`
TrackType string `yaml:"TrackType"`
TrackDirection string `yaml:"TrackDirection"`
TrackWeatherType string `yaml:"TrackWeatherType"`
TrackSkies string `yaml:"TrackSkies"`
TrackSurfaceTemp string `yaml:"TrackSurfaceTemp"`
TrackAirTemp string `yaml:"TrackAirTemp"`
TrackAirPressure string `yaml:"TrackAirPressure"`
TrackWindVel string `yaml:"TrackWindVel"`
TrackWindDir string `yaml:"TrackWindDir"`
TrackRelativeHumidity string `yaml:"TrackRelativeHumidity"`
TrackFogLevel string `yaml:"TrackFogLevel"`
TrackCleanup int `yaml:"TrackCleanup"`
TrackDynamicTrack int `yaml:"TrackDynamicTrack"`
TrackVersion string `yaml:"TrackVersion"`
SeriesID int `yaml:"SeriesID"`
SeasonID int `yaml:"SeasonID"`
SessionID int `yaml:"SessionID"`
SubSessionID int `yaml:"SubSessionID"`
LeagueID int `yaml:"LeagueID"`
Official int `yaml:"Official"`
RaceWeek int `yaml:"RaceWeek"`
EventType string `yaml:"EventType"`
Category string `yaml:"Category"`
SimMode string `yaml:"SimMode"`
TeamRacing int `yaml:"TeamRacing"`
MinDrivers int `yaml:"MinDrivers"`
MaxDrivers int `yaml:"MaxDrivers"`
DCRuleSet string `yaml:"DCRuleSet"`
QualifierMustStartRace int `yaml:"QualifierMustStartRace"`
NumCarClasses int `yaml:"NumCarClasses"`
NumCarTypes int `yaml:"NumCarTypes"`
HeatRacing int `yaml:"HeatRacing"`
BuildType string `yaml:"BuildType"`
BuildTarget string `yaml:"BuildTarget"`
BuildVersion string `yaml:"BuildVersion"`
WeekendOptions struct {
NumStarters int `yaml:"NumStarters"`
StartingGrid string `yaml:"StartingGrid"`
QualifyScoring string `yaml:"QualifyScoring"`
CourseCautions string `yaml:"CourseCautions"`
StandingStart int `yaml:"StandingStart"`
ShortParadeLap int `yaml:"ShortParadeLap"`
Restarts string `yaml:"Restarts"`
WeatherType string `yaml:"WeatherType"`
Skies string `yaml:"Skies"`
WindDirection string `yaml:"WindDirection"`
WindSpeed string `yaml:"WindSpeed"`
WeatherTemp string `yaml:"WeatherTemp"`
RelativeHumidity string `yaml:"RelativeHumidity"`
FogLevel string `yaml:"FogLevel"`
TimeOfDay string `yaml:"TimeOfDay"`
Date string `yaml:"Date"`
EarthRotationSpeedupFactor int `yaml:"EarthRotationSpeedupFactor"`
Unofficial int `yaml:"Unofficial"`
CommercialMode string `yaml:"CommercialMode"`
NightMode string `yaml:"NightMode"`
IsFixedSetup int `yaml:"IsFixedSetup"`
StrictLapsChecking string `yaml:"StrictLapsChecking"`
HasOpenRegistration int `yaml:"HasOpenRegistration"`
HardcoreLevel int `yaml:"HardcoreLevel"`
NumJokerLaps int `yaml:"NumJokerLaps"`
IncidentLimit string `yaml:"IncidentLimit"`
FastRepairsLimit string `yaml:"FastRepairsLimit"`
GreenWhiteCheckeredLimit int `yaml:"GreenWhiteCheckeredLimit"`
} `yaml:"WeekendOptions"`
TelemetryOptions struct {
TelemetryDiskFile string `yaml:"TelemetryDiskFile"`
} `yaml:"TelemetryOptions"`
} `yaml:"WeekendInfo"`
SessionInfo struct {
Sessions []struct {
SessionNum int `yaml:"SessionNum"`
SessionLaps string `yaml:"SessionLaps"`
SessionTime string `yaml:"SessionTime"`
SessionNumLapsToAvg int `yaml:"SessionNumLapsToAvg"`
SessionType string `yaml:"SessionType"`
SessionTrackRubberState string `yaml:"SessionTrackRubberState"`
SessionName string `yaml:"SessionName"`
SessionSubType interface{} `yaml:"SessionSubType"`
SessionSkipped int `yaml:"SessionSkipped"`
SessionRunGroupsUsed int `yaml:"SessionRunGroupsUsed"`
ResultsPositions interface{} `yaml:"ResultsPositions"`
ResultsFastestLap []struct {
CarIdx int `yaml:"CarIdx"`
FastestLap int `yaml:"FastestLap"`
FastestTime int `yaml:"FastestTime"`
} `yaml:"ResultsFastestLap"`
ResultsAverageLapTime int `yaml:"ResultsAverageLapTime"`
ResultsNumCautionFlags int `yaml:"ResultsNumCautionFlags"`
ResultsNumCautionLaps int `yaml:"ResultsNumCautionLaps"`
ResultsNumLeadChanges int `yaml:"ResultsNumLeadChanges"`
ResultsLapsComplete int `yaml:"ResultsLapsComplete"`
ResultsOfficial int `yaml:"ResultsOfficial"`
} `yaml:"Sessions"`
} `yaml:"SessionInfo"`
CameraInfo struct {
Groups []struct {
GroupNum int `yaml:"GroupNum"`
GroupName string `yaml:"GroupName"`
Cameras []struct {
CameraNum int `yaml:"CameraNum"`
CameraName string `yaml:"CameraName"`
} `yaml:"Cameras"`
IsScenic bool `yaml:"IsScenic,omitempty"`
} `yaml:"Groups"`
} `yaml:"CameraInfo"`
RadioInfo struct {
SelectedRadioNum int `yaml:"SelectedRadioNum"`
Radios []struct {
RadioNum int `yaml:"RadioNum"`
HopCount int `yaml:"HopCount"`
NumFrequencies int `yaml:"NumFrequencies"`
TunedToFrequencyNum int `yaml:"TunedToFrequencyNum"`
ScanningIsOn int `yaml:"ScanningIsOn"`
Frequencies []struct {
FrequencyNum int `yaml:"FrequencyNum"`
FrequencyName string `yaml:"FrequencyName"`
Priority int `yaml:"Priority"`
CarIdx int `yaml:"CarIdx"`
EntryIdx int `yaml:"EntryIdx"`
ClubID int `yaml:"ClubID"`
CanScan int `yaml:"CanScan"`
CanSquawk int `yaml:"CanSquawk"`
Muted int `yaml:"Muted"`
IsMutable int `yaml:"IsMutable"`
IsDeletable int `yaml:"IsDeletable"`
} `yaml:"Frequencies"`
} `yaml:"Radios"`
} `yaml:"RadioInfo"`
DriverInfo struct {
DriverCarIdx int `yaml:"DriverCarIdx"`
DriverUserID int `yaml:"DriverUserID"`
PaceCarIdx int `yaml:"PaceCarIdx"`
DriverHeadPosX float64 `yaml:"DriverHeadPosX"`
DriverHeadPosY float64 `yaml:"DriverHeadPosY"`
DriverHeadPosZ float64 `yaml:"DriverHeadPosZ"`
DriverCarIdleRPM float64 `yaml:"DriverCarIdleRPM"`
DriverCarRedLine float64 `yaml:"DriverCarRedLine"`
DriverCarEngCylinderCount int `yaml:"DriverCarEngCylinderCount"`
DriverCarFuelKgPerLtr float64 `yaml:"DriverCarFuelKgPerLtr"`
DriverCarFuelMaxLtr float64 `yaml:"DriverCarFuelMaxLtr"`
DriverCarMaxFuelPct float64 `yaml:"DriverCarMaxFuelPct"`
DriverCarGearNumForward int `yaml:"DriverCarGearNumForward"`
DriverCarGearNeutral int `yaml:"DriverCarGearNeutral"`
DriverCarGearReverse int `yaml:"DriverCarGearReverse"`
DriverCarSLFirstRPM float64 `yaml:"DriverCarSLFirstRPM"`
DriverCarSLShiftRPM float64 `yaml:"DriverCarSLShiftRPM"`
DriverCarSLLastRPM float64 `yaml:"DriverCarSLLastRPM"`
DriverCarSLBlinkRPM float64 `yaml:"DriverCarSLBlinkRPM"`
DriverCarVersion string `yaml:"DriverCarVersion"`
DriverPitTrkPct float64 `yaml:"DriverPitTrkPct"`
DriverCarEstLapTime float64 `yaml:"DriverCarEstLapTime"`
DriverSetupName string `yaml:"DriverSetupName"`
DriverSetupIsModified int `yaml:"DriverSetupIsModified"`
DriverSetupLoadTypeName string `yaml:"DriverSetupLoadTypeName"`
DriverSetupPassedTech int `yaml:"DriverSetupPassedTech"`
DriverIncidentCount int `yaml:"DriverIncidentCount"`
Drivers []Driver `yaml:"Drivers"`
} `yaml:"DriverInfo"`
SplitTimeInfo struct {
Sectors []struct {
SectorNum int `yaml:"SectorNum"`
SectorStartPct float64 `yaml:"SectorStartPct"`
} `yaml:"Sectors"`
} `yaml:"SplitTimeInfo"`
CarSetup struct {
UpdateCount int `yaml:"UpdateCount"`
TiresAero struct {
LeftFront struct {
StartingPressure string `yaml:"StartingPressure"`
LastHotPressure string `yaml:"LastHotPressure"`
LastTempsOMI string `yaml:"LastTempsOMI"`
TreadRemaining string `yaml:"TreadRemaining"`
} `yaml:"LeftFront"`
LeftRear struct {
StartingPressure string `yaml:"StartingPressure"`
LastHotPressure string `yaml:"LastHotPressure"`
LastTempsOMI string `yaml:"LastTempsOMI"`
TreadRemaining string `yaml:"TreadRemaining"`
} `yaml:"LeftRear"`
RightFront struct {
StartingPressure string `yaml:"StartingPressure"`
LastHotPressure string `yaml:"LastHotPressure"`
LastTempsIMO string `yaml:"LastTempsIMO"`
TreadRemaining string `yaml:"TreadRemaining"`
} `yaml:"RightFront"`
RightRear struct {
StartingPressure string `yaml:"StartingPressure"`
LastHotPressure string `yaml:"LastHotPressure"`
LastTempsIMO string `yaml:"LastTempsIMO"`
TreadRemaining string `yaml:"TreadRemaining"`
} `yaml:"RightRear"`
} `yaml:"TiresAero"`
Chassis struct {
Front struct {
ArbSetting int `yaml:"ArbSetting"`
ToeIn string `yaml:"ToeIn"`
FuelLevel string `yaml:"FuelLevel"`
CrossWeight string `yaml:"CrossWeight"`
} `yaml:"Front"`
LeftFront struct {
CornerWeight string `yaml:"CornerWeight"`
RideHeight string `yaml:"RideHeight"`
SpringPerchOffset string `yaml:"SpringPerchOffset"`
Camber string `yaml:"Camber"`
} `yaml:"LeftFront"`
LeftRear struct {
CornerWeight string `yaml:"CornerWeight"`
RideHeight string `yaml:"RideHeight"`
SpringPerchOffset string `yaml:"SpringPerchOffset"`
Camber string `yaml:"Camber"`
ToeIn string `yaml:"ToeIn"`
} `yaml:"LeftRear"`
InCarDials struct {
DisplayPage string `yaml:"DisplayPage"`
BrakePressureBias string `yaml:"BrakePressureBias"`
} `yaml:"InCarDials"`
RightFront struct {
CornerWeight string `yaml:"CornerWeight"`
RideHeight string `yaml:"RideHeight"`
SpringPerchOffset string `yaml:"SpringPerchOffset"`
Camber string `yaml:"Camber"`
} `yaml:"RightFront"`
RightRear struct {
CornerWeight string `yaml:"CornerWeight"`
RideHeight string `yaml:"RideHeight"`
SpringPerchOffset string `yaml:"SpringPerchOffset"`
Camber string `yaml:"Camber"`
ToeIn string `yaml:"ToeIn"`
} `yaml:"RightRear"`
Rear struct {
ArbSetting int `yaml:"ArbSetting"`
WingSetting int `yaml:"WingSetting"`
} `yaml:"Rear"`
} `yaml:"Chassis"`
} `yaml:"CarSetup"`
}
// Driver ...
type Driver struct {
CarIdx int `yaml:"CarIdx"`
UserName string `yaml:"UserName"`
AbbrevName string `yaml:"AbbrevName"`
Initials string `yaml:"Initials"`
UserID int `yaml:"UserID"`
TeamID int `yaml:"TeamID"`
TeamName string `yaml:"TeamName"`
CarNumber string `yaml:"CarNumber"`
CarNumberRaw int `yaml:"CarNumberRaw"`
CarPath string `yaml:"CarPath"`
CarClassID int `yaml:"CarClassID"`
CarID int `yaml:"CarID"`
CarIsPaceCar int `yaml:"CarIsPaceCar"`
CarIsAI int `yaml:"CarIsAI"`
CarScreenName string `yaml:"CarScreenName"`
CarScreenNameShort string `yaml:"CarScreenNameShort"`
CarClassShortName string `yaml:"CarClassShortName"`
CarClassRelSpeed int `yaml:"CarClassRelSpeed"`
CarClassLicenseLevel int `yaml:"CarClassLicenseLevel"`
CarClassMaxFuelPct string `yaml:"CarClassMaxFuelPct"`
CarClassWeightPenalty string `yaml:"CarClassWeightPenalty"`
CarClassPowerAdjust string `yaml:"CarClassPowerAdjust"`
CarClassDryTireSetLimit string `yaml:"CarClassDryTireSetLimit"`
CarClassColor int `yaml:"CarClassColor"`
CarClassEstLapTime float64 `yaml:"CarClassEstLapTime"`
IRating int `yaml:"IRating"`
LicLevel int `yaml:"LicLevel"`
LicSubLevel int `yaml:"LicSubLevel"`
LicString string `yaml:"LicString"`
LicColor string `yaml:"LicColor"`
IsSpectator int `yaml:"IsSpectator"`
CarDesignStr string `yaml:"CarDesignStr"`
HelmetDesignStr string `yaml:"HelmetDesignStr"`
SuitDesignStr string `yaml:"SuitDesignStr"`
CarNumberDesignStr string `yaml:"CarNumberDesignStr"`
CarSponsor1 int `yaml:"CarSponsor_1"`
CarSponsor2 int `yaml:"CarSponsor_2"`
CurDriverIncidentCount int `yaml:"CurDriverIncidentCount"`
TeamIncidentCount int `yaml:"TeamIncidentCount"`
}
// parseSessionInfo will parse the sessionInfo buffer into the SessionInfoYAML
// struct
func parseSessionInfo(data []byte) (*SessionInfoYAML, error) {
var sessionInfo SessionInfoYAML
err := yaml.Unmarshal(data, &sessionInfo)
if err != nil {
return nil, err
}
return &sessionInfo, nil
}
// ToString will return a readable string of the struct
func (s *SessionInfoYAML) ToString() string {
stringified, _ := json.MarshalIndent(s, "", " ")
return string(stringified)
}
package main
import (
"encoding/json"
"gopkg.in/yaml.v3"
)
// SessionInfoYAML is a string with session info in the IBT file
type SessionInfoYAML struct {
WeekendInfo struct {
TrackName string `yaml:"TrackName"`
TrackID int `yaml:"TrackID"`
TrackLength string `yaml:"TrackLength"`
TrackDisplayName string `yaml:"TrackDisplayName"`
TrackDisplayShortName string `yaml:"TrackDisplayShortName"`
TrackConfigName string `yaml:"TrackConfigName"`
TrackCity string `yaml:"TrackCity"`
TrackCountry string `yaml:"TrackCountry"`
TrackAltitude string `yaml:"TrackAltitude"`
TrackLatitude string `yaml:"TrackLatitude"`
TrackLongitude string `yaml:"TrackLongitude"`
TrackNorthOffset string `yaml:"TrackNorthOffset"`
TrackNumTurns int `yaml:"TrackNumTurns"`
TrackPitSpeedLimit string `yaml:"TrackPitSpeedLimit"`
TrackType string `yaml:"TrackType"`
TrackDirection string `yaml:"TrackDirection"`
TrackWeatherType string `yaml:"TrackWeatherType"`
TrackSkies string `yaml:"TrackSkies"`
TrackSurfaceTemp string `yaml:"TrackSurfaceTemp"`
TrackAirTemp string `yaml:"TrackAirTemp"`
TrackAirPressure string `yaml:"TrackAirPressure"`
TrackWindVel string `yaml:"TrackWindVel"`
TrackWindDir string `yaml:"TrackWindDir"`
TrackRelativeHumidity string `yaml:"TrackRelativeHumidity"`
TrackFogLevel string `yaml:"TrackFogLevel"`
TrackCleanup int `yaml:"TrackCleanup"`
TrackDynamicTrack int `yaml:"TrackDynamicTrack"`
TrackVersion string `yaml:"TrackVersion"`
SeriesID int `yaml:"SeriesID"`
SeasonID int `yaml:"SeasonID"`
SessionID int `yaml:"SessionID"`
SubSessionID int `yaml:"SubSessionID"`
LeagueID int `yaml:"LeagueID"`
Official int `yaml:"Official"`
RaceWeek int `yaml:"RaceWeek"`
EventType string `yaml:"EventType"`
Category string `yaml:"Category"`
SimMode string `yaml:"SimMode"`
TeamRacing int `yaml:"TeamRacing"`
MinDrivers int `yaml:"MinDrivers"`
MaxDrivers int `yaml:"MaxDrivers"`
DCRuleSet string `yaml:"DCRuleSet"`
QualifierMustStartRace int `yaml:"QualifierMustStartRace"`
NumCarClasses int `yaml:"NumCarClasses"`
NumCarTypes int `yaml:"NumCarTypes"`
HeatRacing int `yaml:"HeatRacing"`
BuildType string `yaml:"BuildType"`
BuildTarget string `yaml:"BuildTarget"`
BuildVersion string `yaml:"BuildVersion"`
WeekendOptions struct {
NumStarters int `yaml:"NumStarters"`
StartingGrid string `yaml:"StartingGrid"`
QualifyScoring string `yaml:"QualifyScoring"`
CourseCautions string `yaml:"CourseCautions"`
StandingStart int `yaml:"StandingStart"`
ShortParadeLap int `yaml:"ShortParadeLap"`
Restarts string `yaml:"Restarts"`
WeatherType string `yaml:"WeatherType"`
Skies string `yaml:"Skies"`
WindDirection string `yaml:"WindDirection"`
WindSpeed string `yaml:"WindSpeed"`
WeatherTemp string `yaml:"WeatherTemp"`
RelativeHumidity string `yaml:"RelativeHumidity"`
FogLevel string `yaml:"FogLevel"`
TimeOfDay string `yaml:"TimeOfDay"`
Date string `yaml:"Date"`
EarthRotationSpeedupFactor int `yaml:"EarthRotationSpeedupFactor"`
Unofficial int `yaml:"Unofficial"`
CommercialMode string `yaml:"CommercialMode"`
NightMode string `yaml:"NightMode"`
IsFixedSetup int `yaml:"IsFixedSetup"`
StrictLapsChecking string `yaml:"StrictLapsChecking"`
HasOpenRegistration int `yaml:"HasOpenRegistration"`
HardcoreLevel int `yaml:"HardcoreLevel"`
NumJokerLaps int `yaml:"NumJokerLaps"`
IncidentLimit string `yaml:"IncidentLimit"`
FastRepairsLimit string `yaml:"FastRepairsLimit"`
GreenWhiteCheckeredLimit int `yaml:"GreenWhiteCheckeredLimit"`
} `yaml:"WeekendOptions"`
TelemetryOptions struct {
TelemetryDiskFile string `yaml:"TelemetryDiskFile"`
} `yaml:"TelemetryOptions"`
} `yaml:"WeekendInfo"`
SessionInfo struct {
Sessions []struct {
SessionNum int `yaml:"SessionNum"`
SessionLaps string `yaml:"SessionLaps"`
SessionTime string `yaml:"SessionTime"`
SessionNumLapsToAvg int `yaml:"SessionNumLapsToAvg"`
SessionType string `yaml:"SessionType"`
SessionTrackRubberState string `yaml:"SessionTrackRubberState"`
SessionName string `yaml:"SessionName"`
SessionSubType interface{} `yaml:"SessionSubType"`
SessionSkipped int `yaml:"SessionSkipped"`
SessionRunGroupsUsed int `yaml:"SessionRunGroupsUsed"`
ResultsPositions interface{} `yaml:"ResultsPositions"`
ResultsFastestLap []struct {
CarIdx int `yaml:"CarIdx"`
FastestLap int `yaml:"FastestLap"`
FastestTime int `yaml:"FastestTime"`
} `yaml:"ResultsFastestLap"`
ResultsAverageLapTime int `yaml:"ResultsAverageLapTime"`
ResultsNumCautionFlags int `yaml:"ResultsNumCautionFlags"`
ResultsNumCautionLaps int `yaml:"ResultsNumCautionLaps"`
ResultsNumLeadChanges int `yaml:"ResultsNumLeadChanges"`
ResultsLapsComplete int `yaml:"ResultsLapsComplete"`
ResultsOfficial int `yaml:"ResultsOfficial"`
} `yaml:"Sessions"`
} `yaml:"SessionInfo"`
CameraInfo struct {
Groups []struct {
GroupNum int `yaml:"GroupNum"`
GroupName string `yaml:"GroupName"`
Cameras []struct {
CameraNum int `yaml:"CameraNum"`
CameraName string `yaml:"CameraName"`
} `yaml:"Cameras"`
IsScenic bool `yaml:"IsScenic,omitempty"`
} `yaml:"Groups"`
} `yaml:"CameraInfo"`
RadioInfo struct {
SelectedRadioNum int `yaml:"SelectedRadioNum"`
Radios []struct {
RadioNum int `yaml:"RadioNum"`
HopCount int `yaml:"HopCount"`
NumFrequencies int `yaml:"NumFrequencies"`
TunedToFrequencyNum int `yaml:"TunedToFrequencyNum"`
ScanningIsOn int `yaml:"ScanningIsOn"`
Frequencies []struct {
FrequencyNum int `yaml:"FrequencyNum"`
FrequencyName string `yaml:"FrequencyName"`
Priority int `yaml:"Priority"`
CarIdx int `yaml:"CarIdx"`
EntryIdx int `yaml:"EntryIdx"`
ClubID int `yaml:"ClubID"`
CanScan int `yaml:"CanScan"`
CanSquawk int `yaml:"CanSquawk"`
Muted int `yaml:"Muted"`
IsMutable int `yaml:"IsMutable"`
IsDeletable int `yaml:"IsDeletable"`
} `yaml:"Frequencies"`
} `yaml:"Radios"`
} `yaml:"RadioInfo"`
DriverInfo struct {
DriverCarIdx int `yaml:"DriverCarIdx"`
DriverUserID int `yaml:"DriverUserID"`
PaceCarIdx int `yaml:"PaceCarIdx"`
DriverHeadPosX float64 `yaml:"DriverHeadPosX"`
DriverHeadPosY float64 `yaml:"DriverHeadPosY"`
DriverHeadPosZ float64 `yaml:"DriverHeadPosZ"`
DriverCarIdleRPM float64 `yaml:"DriverCarIdleRPM"`
DriverCarRedLine float64 `yaml:"DriverCarRedLine"`
DriverCarEngCylinderCount int `yaml:"DriverCarEngCylinderCount"`
DriverCarFuelKgPerLtr float64 `yaml:"DriverCarFuelKgPerLtr"`
DriverCarFuelMaxLtr float64 `yaml:"DriverCarFuelMaxLtr"`
DriverCarMaxFuelPct float64 `yaml:"DriverCarMaxFuelPct"`
DriverCarGearNumForward int `yaml:"DriverCarGearNumForward"`
DriverCarGearNeutral int `yaml:"DriverCarGearNeutral"`
DriverCarGearReverse int `yaml:"DriverCarGearReverse"`
DriverCarSLFirstRPM float64 `yaml:"DriverCarSLFirstRPM"`
DriverCarSLShiftRPM float64 `yaml:"DriverCarSLShiftRPM"`
DriverCarSLLastRPM float64 `yaml:"DriverCarSLLastRPM"`
DriverCarSLBlinkRPM float64 `yaml:"DriverCarSLBlinkRPM"`
DriverCarVersion string `yaml:"DriverCarVersion"`
DriverPitTrkPct float64 `yaml:"DriverPitTrkPct"`
DriverCarEstLapTime float64 `yaml:"DriverCarEstLapTime"`
DriverSetupName string `yaml:"DriverSetupName"`
DriverSetupIsModified int `yaml:"DriverSetupIsModified"`
DriverSetupLoadTypeName string `yaml:"DriverSetupLoadTypeName"`
DriverSetupPassedTech int `yaml:"DriverSetupPassedTech"`
DriverIncidentCount int `yaml:"DriverIncidentCount"`
Drivers []Driver `yaml:"Drivers"`
} `yaml:"DriverInfo"`
SplitTimeInfo struct {
Sectors []struct {
SectorNum int `yaml:"SectorNum"`
SectorStartPct float64 `yaml:"SectorStartPct"`
} `yaml:"Sectors"`
} `yaml:"SplitTimeInfo"`
CarSetup struct {
UpdateCount int `yaml:"UpdateCount"`
TiresAero struct {
LeftFront struct {
StartingPressure string `yaml:"StartingPressure"`
LastHotPressure string `yaml:"LastHotPressure"`
LastTempsOMI string `yaml:"LastTempsOMI"`
TreadRemaining string `yaml:"TreadRemaining"`
} `yaml:"LeftFront"`
LeftRear struct {
StartingPressure string `yaml:"StartingPressure"`
LastHotPressure string `yaml:"LastHotPressure"`
LastTempsOMI string `yaml:"LastTempsOMI"`
TreadRemaining string `yaml:"TreadRemaining"`
} `yaml:"LeftRear"`
RightFront struct {
StartingPressure string `yaml:"StartingPressure"`
LastHotPressure string `yaml:"LastHotPressure"`
LastTempsIMO string `yaml:"LastTempsIMO"`
TreadRemaining string `yaml:"TreadRemaining"`
} `yaml:"RightFront"`
RightRear struct {
StartingPressure string `yaml:"StartingPressure"`
LastHotPressure string `yaml:"LastHotPressure"`
LastTempsIMO string `yaml:"LastTempsIMO"`
TreadRemaining string `yaml:"TreadRemaining"`
} `yaml:"RightRear"`
} `yaml:"TiresAero"`
Chassis struct {
Front struct {
ArbSetting int `yaml:"ArbSetting"`
ToeIn string `yaml:"ToeIn"`
FuelLevel string `yaml:"FuelLevel"`
CrossWeight string `yaml:"CrossWeight"`
} `yaml:"Front"`
LeftFront struct {
CornerWeight string `yaml:"CornerWeight"`
RideHeight string `yaml:"RideHeight"`
SpringPerchOffset string `yaml:"SpringPerchOffset"`
Camber string `yaml:"Camber"`
} `yaml:"LeftFront"`
LeftRear struct {
CornerWeight string `yaml:"CornerWeight"`
RideHeight string `yaml:"RideHeight"`
SpringPerchOffset string `yaml:"SpringPerchOffset"`
Camber string `yaml:"Camber"`
ToeIn string `yaml:"ToeIn"`
} `yaml:"LeftRear"`
InCarDials struct {
DisplayPage string `yaml:"DisplayPage"`
BrakePressureBias string `yaml:"BrakePressureBias"`
} `yaml:"InCarDials"`
RightFront struct {
CornerWeight string `yaml:"CornerWeight"`
RideHeight string `yaml:"RideHeight"`
SpringPerchOffset string `yaml:"SpringPerchOffset"`
Camber string `yaml:"Camber"`
} `yaml:"RightFront"`
RightRear struct {
CornerWeight string `yaml:"CornerWeight"`
RideHeight string `yaml:"RideHeight"`
SpringPerchOffset string `yaml:"SpringPerchOffset"`
Camber string `yaml:"Camber"`
ToeIn string `yaml:"ToeIn"`
} `yaml:"RightRear"`
Rear struct {
ArbSetting int `yaml:"ArbSetting"`
WingSetting int `yaml:"WingSetting"`
} `yaml:"Rear"`
} `yaml:"Chassis"`
} `yaml:"CarSetup"`
}
// Driver ...
type Driver struct {
CarIdx int `yaml:"CarIdx"`
UserName string `yaml:"UserName"`
AbbrevName string `yaml:"AbbrevName"`
Initials string `yaml:"Initials"`
UserID int `yaml:"UserID"`
TeamID int `yaml:"TeamID"`
TeamName string `yaml:"TeamName"`
CarNumber string `yaml:"CarNumber"`
CarNumberRaw int `yaml:"CarNumberRaw"`
CarPath string `yaml:"CarPath"`
CarClassID int `yaml:"CarClassID"`
CarID int `yaml:"CarID"`
CarIsPaceCar int `yaml:"CarIsPaceCar"`
CarIsAI int `yaml:"CarIsAI"`
CarScreenName string `yaml:"CarScreenName"`
CarScreenNameShort string `yaml:"CarScreenNameShort"`
CarClassShortName string `yaml:"CarClassShortName"`
CarClassRelSpeed int `yaml:"CarClassRelSpeed"`
CarClassLicenseLevel int `yaml:"CarClassLicenseLevel"`
CarClassMaxFuelPct string `yaml:"CarClassMaxFuelPct"`
CarClassWeightPenalty string `yaml:"CarClassWeightPenalty"`
CarClassPowerAdjust string `yaml:"CarClassPowerAdjust"`
CarClassDryTireSetLimit string `yaml:"CarClassDryTireSetLimit"`
CarClassColor int `yaml:"CarClassColor"`
CarClassEstLapTime float64 `yaml:"CarClassEstLapTime"`
IRating int `yaml:"IRating"`
LicLevel int `yaml:"LicLevel"`
LicSubLevel int `yaml:"LicSubLevel"`
LicString string `yaml:"LicString"`
LicColor string `yaml:"LicColor"`
IsSpectator int `yaml:"IsSpectator"`
CarDesignStr string `yaml:"CarDesignStr"`
HelmetDesignStr string `yaml:"HelmetDesignStr"`
SuitDesignStr string `yaml:"SuitDesignStr"`
CarNumberDesignStr string `yaml:"CarNumberDesignStr"`
CarSponsor1 int `yaml:"CarSponsor_1"`
CarSponsor2 int `yaml:"CarSponsor_2"`
CurDriverIncidentCount int `yaml:"CurDriverIncidentCount"`
TeamIncidentCount int `yaml:"TeamIncidentCount"`
}
// parseSessionInfo will parse the sessionInfo buffer into the SessionInfoYAML
// struct
func parseSessionInfo(data []byte) (*SessionInfoYAML, error) {
var sessionInfo SessionInfoYAML
err := yaml.Unmarshal(data, &sessionInfo)
if err != nil {
return nil, err
}
return &sessionInfo, nil
}
// ToString will return a readable string of the struct
func (s *SessionInfoYAML) ToString() string {
stringified, _ := json.MarshalIndent(s, "", " ")
return string(stringified)
}
+88
View File
@@ -0,0 +1,88 @@
package sharedMem
import (
"fmt"
"io"
)
// Memory is shared memory struct
type Memory struct {
m *shmi
pos int64
}
// Create is create shared memory
func Create(name string, size uint32) (*Memory, error) {
m, err := create(name, size)
if err != nil {
return nil, err
}
return &Memory{m, 0}, nil
}
// Open is open exist shared memory
func Open(name string, size uint32) (*Memory, error) {
m, err := open(name, size)
if err != nil {
return nil, err
}
return &Memory{m, 0}, nil
}
// Close is close & discard shared memory
func (o *Memory) Close() (err error) {
if o.m != nil {
err = o.m.close()
if err == nil {
o.m = nil
}
}
return err
}
// Read is read shared memory (current position)
func (o *Memory) Read(p []byte) (n int, err error) {
n, err = o.ReadAt(p, o.pos)
if err != nil {
return 0, err
}
o.pos += int64(n)
return n, nil
}
// ReadAt is read shared memory (offset)
func (o *Memory) ReadAt(p []byte, off int64) (n int, err error) {
return o.m.readAt(p, off)
}
// Seek is move read/write position at shared memory
func (o *Memory) Seek(offset int64, whence int) (int64, error) {
switch whence {
case io.SeekStart:
offset += int64(0)
case io.SeekCurrent:
offset += o.pos
case io.SeekEnd:
offset += int64(o.m.size)
}
if offset < 0 || offset >= int64(o.m.size) {
return 0, fmt.Errorf("invalid offset")
}
o.pos = offset
return offset, nil
}
// Write is write shared memory (current position)
func (o *Memory) Write(p []byte) (n int, err error) {
n, err = o.WriteAt(p, o.pos)
if err != nil {
return 0, err
}
o.pos += int64(n)
return n, nil
}
// WriteAt is write shared memory (offset)
func (o *Memory) WriteAt(p []byte, off int64) (n int, err error) {
return o.m.writeAt(p, off)
}
+157
View File
@@ -0,0 +1,157 @@
//go:build darwin && cgo
// +build darwin,cgo
package sharedMem
/*
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/errno.h>
int _create(const char* name, int size, int flag) {
mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
int fd = shm_open(name, flag, mode);
if (fd < 0) {
return -1;
}
struct stat mapstat;
int ret = fstat(fd, &mapstat);
if (ret != -1 && mapstat.st_size == 0) {
if (ftruncate(fd, size) != 0) {
close(fd);
return -2;
}
} else if (ret == -1) {
close(fd);
return -3;
}
return fd;
}
int Create(const char* name, int size) {
int flag = O_RDWR | O_CREAT;
return _create(name, size, flag);
}
int Open(const char* name, int size) {
int flag = O_RDWR;
return _create(name, size, flag);
}
void* Map(int fd, int size) {
void* p = mmap(
NULL, size,
PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
if (p == MAP_FAILED) {
return NULL;
}
return p;
}
void Close(int fd, void* p, int size) {
if (p != NULL) {
munmap(p, size);
}
if (fd != 0) {
close(fd);
}
}
void Delete(const char* name) {
shm_unlink(name);
}
*/
import "C"
import (
"fmt"
"io"
"unsafe"
)
type shmi struct {
name string
fd C.int
v unsafe.Pointer
size int32
parent bool
}
// create shared memory. return shmi object.
// name should not be more than 31 bytes.
func create(name string, size int32) (*shmi, error) {
name = "/" + name
fd := C.Create(C.CString(name), C.int(size))
if fd < 0 {
return nil, fmt.Errorf("create")
}
v := C.Map(fd, C.int(size))
if v == nil {
C.Close(fd, nil, C.int(size))
C.Delete(C.CString(name))
}
return &shmi{name, fd, v, size, true}, nil
}
// open shared memory. return shmi object.
// name should not be more than 31 bytes.
func open(name string, size int32) (*shmi, error) {
name = "/" + name
fd := C.Open(C.CString(name), C.int(size))
if fd < 0 {
return nil, fmt.Errorf("open")
}
v := C.Map(fd, C.int(size))
if v == nil {
C.Close(fd, nil, C.int(size))
C.Delete(C.CString(name))
}
return &shmi{name, fd, v, size, false}, nil
}
func (o *shmi) close() error {
if o.v != nil {
C.Close(o.fd, o.v, C.int(o.size))
o.v = nil
}
if o.parent {
C.Delete(C.CString(o.name))
}
return nil
}
// read shared memory. return read size.
func (o *shmi) readAt(p []byte, off int64) (n int, err error) {
if off >= int64(o.size) {
return 0, io.EOF
}
if max := int64(o.size) - off; int64(len(p)) > max {
p = p[:max]
}
return copyPtr2Slice(uintptr(o.v), p, off, o.size), nil
}
// write shared memory. return write size.
func (o *shmi) writeAt(p []byte, off int64) (n int, err error) {
if off >= int64(o.size) {
return 0, io.EOF
}
if max := int64(o.size) - off; int64(len(p)) > max {
p = p[:max]
}
return copySlice2Ptr(p, uintptr(o.v), off, o.size), nil
}
+148
View File
@@ -0,0 +1,148 @@
//go:build linux && cgo
// +build linux,cgo
package sharedMem
/*
#cgo LDFLAGS: -lrt
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
int _create(const char* name, int size, int flag) {
mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
int fd = shm_open(name, flag, mode);
if (fd < 0) {
return -1;
}
if (ftruncate(fd, size) != 0) {
close(fd);
return -2;
}
return fd;
}
int Create(const char* name, int size) {
int flag = O_RDWR | O_CREAT;
return _create(name, size, flag);
}
int Open(const char* name, int size) {
int flag = O_RDWR;
return _create(name, size, flag);
}
void* Map(int fd, int size) {
void* p = mmap(
NULL, size,
PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
if (p == MAP_FAILED) {
return NULL;
}
return p;
}
void Close(int fd, void* p, int size) {
if (p != NULL) {
munmap(p, size);
}
if (fd != 0) {
close(fd);
}
}
void Delete(const char* name) {
shm_unlink(name);
}
*/
import "C"
import (
"fmt"
"io"
"unsafe"
)
type shmi struct {
name string
fd C.int
v unsafe.Pointer
size int32
parent bool
}
// create shared memory. return shmi object.
func create(name string, size int32) (*shmi, error) {
name = "/" + name
fd := C.Create(C.CString(name), C.int(size))
if fd < 0 {
return nil, fmt.Errorf("create")
}
v := C.Map(fd, C.int(size))
if v == nil {
C.Close(fd, nil, C.int(size))
C.Delete(C.CString(name))
}
return &shmi{name, fd, v, size, true}, nil
}
// open shared memory. return shmi object.
func open(name string, size int32) (*shmi, error) {
name = "/" + name
fd := C.Open(C.CString(name), C.int(size))
if fd < 0 {
return nil, fmt.Errorf("open")
}
v := C.Map(fd, C.int(size))
if v == nil {
C.Close(fd, nil, C.int(size))
C.Delete(C.CString(name))
}
return &shmi{name, fd, v, size, false}, nil
}
func (o *shmi) close() error {
if o.v != nil {
C.Close(o.fd, o.v, C.int(o.size))
o.v = nil
}
if o.parent {
C.Delete(C.CString(o.name))
}
return nil
}
// read shared memory. return read size.
func (o *shmi) readAt(p []byte, off int64) (n int, err error) {
if off >= int64(o.size) {
return 0, io.EOF
}
if max := int64(o.size) - off; int64(len(p)) > max {
p = p[:max]
}
return copyPtr2Slice(uintptr(o.v), p, off, o.size), nil
}
// write shared memory. return write size.
func (o *shmi) writeAt(p []byte, off int64) (n int, err error) {
if off >= int64(o.size) {
return 0, io.EOF
}
if max := int64(o.size) - off; int64(len(p)) > max {
p = p[:max]
}
return copySlice2Ptr(p, uintptr(o.v), off, o.size), nil
}
+82
View File
@@ -0,0 +1,82 @@
package sharedMem
import (
"io"
"log"
"golang.org/x/sys/windows"
)
type shmi struct {
h windows.Handle
v uintptr
size uint32
}
// create shared memory. return shmi object.
func create(name string, size uint32) (*shmi, error) {
fnPtr, _ := windows.UTF16PtrFromString(name)
flProtect := uint32(windows.PAGE_READONLY)
h, errno := windows.CreateFileMapping(
windows.InvalidHandle,
nil,
flProtect,
0,
size,
fnPtr)
if h == 0 {
log.Fatal("could not open memmap file: ", errno)
}
addr, errno := windows.MapViewOfFile(h,
windows.FILE_MAP_READ,
0,
0,
uintptr(size))
if addr == 0 {
log.Printf("error in MapViewOfFile: %v", errno)
}
return &shmi{h, addr, size}, nil
}
// open shared memory. return shmi object.
func open(name string, size uint32) (*shmi, error) {
return create(name, size)
}
func (o *shmi) close() error {
if o.v != uintptr(0) {
windows.UnmapViewOfFile(o.v)
o.v = uintptr(0)
}
if o.h != windows.InvalidHandle {
windows.CloseHandle(o.h)
o.h = windows.InvalidHandle
}
return nil
}
// read shared memory. return read size.
func (o *shmi) readAt(p []byte, off int64) (n int, err error) {
if off >= int64(o.size) {
return 0, io.EOF
}
if max := int64(o.size) - off; int64(len(p)) > max {
p = p[:max]
}
return copyPtr2Slice(o.v, p, off, o.size), nil
}
// write shared memory. return write size.
func (o *shmi) writeAt(p []byte, off int64) (n int, err error) {
if off >= int64(o.size) {
return 0, io.EOF
}
if max := int64(o.size) - off; int64(len(p)) > max {
p = p[:max]
}
return copySlice2Ptr(p, o.v, off, o.size), nil
}
+15
View File
@@ -0,0 +1,15 @@
package sharedMem
import (
"unsafe"
)
func copySlice2Ptr(b []byte, p uintptr, off int64, size uint32) int {
bb := unsafe.Slice((*byte)(*(*unsafe.Pointer)(unsafe.Pointer(&p))), int(size))
return copy(bb[off:], b)
}
func copyPtr2Slice(p uintptr, b []byte, off int64, size uint32) int {
bb := unsafe.Slice((*byte)(*(*unsafe.Pointer)(unsafe.Pointer(&p))), int(size))
return copy(b, bb[off:size])
}
+177 -177
View File
@@ -1,177 +1,177 @@
package ibtReader
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"log"
"math"
"strings"
)
const (
VarHeaderSize = 144
IRSDK_char = 0
IRSDK_bool = 1
IRSDK_int = 2
IRSDK_bitField = 3
IRSDK_float = 4
IRSDK_double = 5
)
// I think I can make an interface if IRSDK types with available types and
// that they need a parser (reads and type coerces I guess)
var (
VarTypes = map[int]VarType{
IRSDK_char: {1, "irsdk_char"},
IRSDK_bool: {1, "irsdk_bool"},
IRSDK_int: {4, "irsdk_int"},
IRSDK_bitField: {4, "irsdk_bitField"},
IRSDK_float: {4, "irsdk_float"},
IRSDK_double: {8, "irsdk_double"},
}
)
type VarType struct {
Size int // Size is the var type size in bytes
Name string // Name is the irsdk var name
}
type IBTVar struct {
Type int32
Offset int32
Count int32
CountAsTime bool
Padding [3]byte
Name [32]byte
Description [64]byte
Unit [32]byte
}
type Var struct {
Type int32
Offset int32
Count int32
CountAsTime bool
Name string
Description string
Unit string
// TODO
// Create an interface for this value
// Represent the IRSDK var types with a struct each that implements the Parse
// method or something like that I guess
Value interface{}
}
func (v *IBTVar) ToString() string {
return fmt.Sprintf(
"Type: %5d (0x%08x)\n"+
"Offset: %5d (0x%08x)\n"+
"Count: %5d (0x%08x)\n"+
"CountAsTime: %5t\n"+
"Name: %s\n"+
"Description: %s\n"+
"Unit: %s",
v.Type, v.Type, v.Offset, v.Offset, v.Count, v.Count,
v.CountAsTime, v.Name, v.Description, v.Unit,
)
}
type varBuffer struct {
tickCount int
bufOffset int
}
type TelemetryVars struct {
LastVersion int
Vars map[string]Var
}
func (i *IBT) readVariablerHeaders() error {
i.Vars = &TelemetryVars{Vars: make(map[string]Var, i.Headers.NumVars)}
var k int32
for k = 0; k < i.Headers.NumVars; k++ {
rbuf := make([]byte, VarHeaderSize)
_, err := i.File.ReadAt(rbuf, int64(i.Headers.VarHeaderOffset+k*VarHeaderSize))
if err != nil {
return err
}
var dst IBTVar
err = binary.Read(bytes.NewBuffer(rbuf[:]), binary.LittleEndian, &dst)
if err != nil {
return err
}
v := Var{
Type: dst.Type,
Offset: dst.Offset,
Count: dst.Count,
CountAsTime: dst.CountAsTime,
Name: strings.TrimRight(string(dst.Name[:]), "\x00"),
Description: strings.TrimRight(string(dst.Description[:]), "\x00"),
Unit: strings.TrimRight(string(dst.Unit[:]), "\x00"),
Value: nil,
}
i.Vars.Vars[v.Name] = v
}
return nil
}
func (i *IBT) readData() error {
// I think that we can add one extra check or verification here
// The file headers tells us how many data frames there are, we can probably
// cap it at that instead of waiting for the read to fail
// Probably wouldn't work on live data tho
start := i.Headers.BufOffset + i.Tick*i.Headers.BufLen
buf := make([]byte, i.Headers.BufLen)
_, err := i.File.ReadAt(buf, int64(start))
if err != nil {
return err
}
for k, v := range i.Vars.Vars {
rbuf := buf[v.Offset : v.Offset+int32(VarTypes[int(v.Type)].Size)]
// Read the value
switch v.Type {
case IRSDK_char:
v.Value = string(rbuf[0])
case IRSDK_bool:
v.Value = int(rbuf[0]) > 0
case IRSDK_int:
v.Value = int(binary.LittleEndian.Uint32(rbuf))
case IRSDK_bitField:
v.Value = fmt.Sprintf("0x%x", int(binary.LittleEndian.Uint32(rbuf)))
case IRSDK_float:
v.Value = math.Float32frombits(uint32(binary.LittleEndian.Uint32(rbuf)))
case IRSDK_double:
v.Value = math.Float64frombits(uint64(binary.LittleEndian.Uint64(rbuf)))
}
// --------------
i.Vars.Vars[k] = v
}
i.Tick++
return nil
}
func (i *IBT) Update() bool {
err := i.readData()
if err != nil && err != io.EOF {
log.Fatalf("What happened?\n%v\n", err)
}
if err == io.EOF {
return false
}
return true
}
package main
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"log"
"math"
"strings"
)
const (
VarHeaderSize = 144
IRSDK_char = 0
IRSDK_bool = 1
IRSDK_int = 2
IRSDK_bitField = 3
IRSDK_float = 4
IRSDK_double = 5
)
// I think I can make an interface if IRSDK types with available types and
// that they need a parser (reads and type coerces I guess)
var (
VarTypes = map[int]VarType{
IRSDK_char: {1, "irsdk_char"},
IRSDK_bool: {1, "irsdk_bool"},
IRSDK_int: {4, "irsdk_int"},
IRSDK_bitField: {4, "irsdk_bitField"},
IRSDK_float: {4, "irsdk_float"},
IRSDK_double: {8, "irsdk_double"},
}
)
type VarType struct {
Size int // Size is the var type size in bytes
Name string // Name is the irsdk var name
}
type IBTVar struct {
Type int32
Offset int32
Count int32
CountAsTime bool
Padding [3]byte
Name [32]byte
Description [64]byte
Unit [32]byte
}
type Var struct {
Type int32
Offset int32
Count int32
CountAsTime bool
Name string
Description string
Unit string
// TODO
// Create an interface for this value
// Represent the IRSDK var types with a struct each that implements the Parse
// method or something like that I guess
Value interface{}
}
func (v *IBTVar) ToString() string {
return fmt.Sprintf(
"Type: %5d (0x%08x)\n"+
"Offset: %5d (0x%08x)\n"+
"Count: %5d (0x%08x)\n"+
"CountAsTime: %5t\n"+
"Name: %s\n"+
"Description: %s\n"+
"Unit: %s",
v.Type, v.Type, v.Offset, v.Offset, v.Count, v.Count,
v.CountAsTime, v.Name, v.Description, v.Unit,
)
}
type varBuffer struct {
tickCount int
bufOffset int
}
type TelemetryVars struct {
LastVersion int
Vars map[string]Var
}
func (i *IBT) readVariablerHeaders() error {
i.Vars = &TelemetryVars{Vars: make(map[string]Var, i.Headers.NumVars)}
var k int32
for k = 0; k < i.Headers.NumVars; k++ {
rbuf := make([]byte, VarHeaderSize)
_, err := i.File.ReadAt(rbuf, int64(i.Headers.VarHeaderOffset+k*VarHeaderSize))
if err != nil {
return err
}
var dst IBTVar
err = binary.Read(bytes.NewBuffer(rbuf[:]), binary.LittleEndian, &dst)
if err != nil {
return err
}
v := Var{
Type: dst.Type,
Offset: dst.Offset,
Count: dst.Count,
CountAsTime: dst.CountAsTime,
Name: strings.TrimRight(string(dst.Name[:]), "\x00"),
Description: strings.TrimRight(string(dst.Description[:]), "\x00"),
Unit: strings.TrimRight(string(dst.Unit[:]), "\x00"),
Value: nil,
}
i.Vars.Vars[v.Name] = v
}
return nil
}
func (i *IBT) readData() error {
// I think that we can add one extra check or verification here
// The file headers tells us how many data frames there are, we can probably
// cap it at that instead of waiting for the read to fail
// Probably wouldn't work on live data tho
start := i.Headers.BufOffset + i.Tick*i.Headers.BufLen
buf := make([]byte, i.Headers.BufLen)
_, err := i.File.ReadAt(buf, int64(start))
if err != nil {
return err
}
for k, v := range i.Vars.Vars {
rbuf := buf[v.Offset : v.Offset+int32(VarTypes[int(v.Type)].Size)]
// Read the value
switch v.Type {
case IRSDK_char:
v.Value = string(rbuf[0])
case IRSDK_bool:
v.Value = int(rbuf[0]) > 0
case IRSDK_int:
v.Value = int(binary.LittleEndian.Uint32(rbuf))
case IRSDK_bitField:
v.Value = fmt.Sprintf("0x%x", int(binary.LittleEndian.Uint32(rbuf)))
case IRSDK_float:
v.Value = math.Float32frombits(uint32(binary.LittleEndian.Uint32(rbuf)))
case IRSDK_double:
v.Value = math.Float64frombits(uint64(binary.LittleEndian.Uint64(rbuf)))
}
// --------------
i.Vars.Vars[k] = v
}
i.Tick++
return nil
}
func (i *IBT) Update() bool {
err := i.readData()
if err != nil && err != io.EOF {
log.Fatalf("What happened?\n%v\n", err)
}
if err == io.EOF {
return false
}
return true
}
+105
View File
@@ -0,0 +1,105 @@
package winutils
import (
"fmt"
"sync"
"time"
"unsafe"
"golang.org/x/sys/windows"
)
const (
WAIT_OBJECT_0 = 0
WAIT_TIMEOUT = 258
)
var (
once sync.Once
user32DLL *windows.LazyDLL = nil
)
// openEvent opens a windows.Handle for a given event
func OpenEvent(eventName string) (windows.Handle, error) {
name, err := windows.UTF16PtrFromString(eventName)
if err != nil {
return 0, err
}
handle, err := windows.OpenEvent(windows.SYNCHRONIZE, false, name)
if err != nil {
return 0, fmt.Errorf("error opening event %s: %w", eventName, err)
}
return handle, nil
}
// closeEvent closes a given windows.Handle
func CloseEvent(h windows.Handle) {
windows.CloseHandle(h)
}
// openEvent waits for a good response for some given time
func CheckValidDataEvent(handle windows.Handle, timeout time.Duration) bool {
t0 := time.Now().UnixNano()
timeoutInt := uint32(timeout / time.Millisecond)
result, err := windows.WaitForSingleObject(handle, timeoutInt)
if err != nil {
remaining := timeoutInt - uint32((time.Now().UnixNano()-t0)/1000000)
if remaining > 0 {
time.Sleep(time.Duration(remaining) * time.Millisecond)
}
return false
}
// Check the result of the wait
if result == WAIT_OBJECT_0 {
return true
} else if result == WAIT_TIMEOUT {
return false
}
return false
}
// loadUser32DLL loads the user32.dll which is used to create some processes
func loadUser32DLL() {
user32DLL = windows.NewLazyDLL("user32.dll")
}
// OpenBroadcastChannel opens up a broadcast channel to send commands to iracing
func OpenBroadcastChannel(name string) (uintptr, error) {
if user32DLL == nil {
once.Do(loadUser32DLL)
}
registerWindowsMessageW := user32DLL.NewProc("RegisterWindowMessageW")
msgPtr, err := windows.UTF16PtrFromString(name)
if err != nil {
return 0, err
}
ret, _, err := registerWindowsMessageW.Call(uintptr(unsafe.Pointer(msgPtr)))
if ret == 0 {
return 0, err
}
return ret, nil
}
// SendBroadcastMessage sends a message trough the broadcast channel
func SendBroadcastMessage(id, p1, p2 uintptr) error {
if user32DLL == nil {
once.Do(loadUser32DLL)
}
sendMsg := user32DLL.NewProc("SendNotifyMessageW")
ret, _, err := sendMsg.Call(0xffff, id, p1, p2)
if ret == 1 {
return nil
} else {
return err
}
}