Parsed byte array turns into garbage after being parsed into a struct

Björn Kaiser

I am receiving data packets over UDP that I parse into structs and in a specific situation my data gets all messed up and I can't figure out what the problem might be. Let me give you an overview of what I am doing.

  1. UDP Receiver receives the data packet and passes it to a callback - size of the byte array at this stage is 1307 bytes which is the expected size for the packet
  2. The byte array is marshalled (is that the right term?) into a struct - size of the struct is now 1484 bytes (not sure if that is relevant)

In this example the data is send from the Formula 1 2020 game and the curious thing is that the first entry in the m_carTelemetryData array (see below) is always fine, the data is all sound, however every entry of the 22 after the first one is totally messed up with either 0 values, null values or completely outlandish values for all the different fields in the struct.

I tried several things already to pinpoint the issue but have now reached the end of my knowledge about the things I am dealing with here. My best guess is that something is going wrong when converting the data into a struct or something else is going on that causes a misalignment (?) of the data.

What I tried so far

  • Changed my code from "magic marshalling" to manual reading the data byte-by-byte using BinaryReader - no luck
  • Manually checked the data using BitConverter.ToFoo(bytes, offset) - no luck
  • Yolo changed the Pack attribute assuming that's where I got wrong - no luck
  • Double-checked the documentation to make sure I got the data types right - I am fairly confident that I "translated" them correctly
  • Banged my head against a wall - still no luck

My questions:

  • Is there something obvious wrong with my code that I am simply not seeing?
  • Side question: am I wrong in my assumption that the size of the struct should match the suze of the byte array it has been created from?

Here is the code for reference (if anything helpful is missing, please let me know):

Packet Header

[StructLayout(LayoutKind.Sequential), Pack = 1]
struct PacketHeader2020 {
    public ushort m_packetFormat;
    public byte m_gameMajorVersion;
    public byte m_gameMinorVersion;
    public byte m_packetVersion;
    public byte m_packetId;
    public ulong m_sessionUUID;
    public float m_sessionTime;
    public uint m_frameIdentifier;
    public byte m_playerCarIndex;
    public byte m_secondaryPlayerCarIndex;
}

Packet

public struct CarTelemetryPacket2020
{
    public PacketHeader2020 m_header;
    
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 22)]
    public CarTelemetryData2020[] m_carTelemetryData;

    public ButtonFlag m_buttonStatus;
    public byte m_mfdPanelIndex;
    public byte  m_mfdPanelIndexSecondaryPlayer;
    public byte m_suggestedGear;
};

CarTelemetryData2020

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct CarTelemetryData2020
{
    public ushort m_speed;
    public float m_throttle;
    public float m_steer;
    public float m_brake;
    public byte m_clutch;
    public sbyte m_gear;
    public ushort m_engineRPM;
    public byte m_drs;
    public byte m_revLightsPercent;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    public ushort[] m_brakesTemperature;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    public ushort[] m_tyresSurfaceTemperature;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    public ushort[] m_tyresInnerTemperature;
    public ushort m_engineTemperature;
    
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    public float[] m_tyresPressure;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    public SurfaceType[] m_surfaceType;
}

byte[] -> struct

public static T ByteArrayToStructure<T>(byte[] bytes) where T : struct
{
  var handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
  try
  {
    return (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
  }
  finally
  {
    handle.Free();
  }
}
PeterJ

I just pasted your code into LinqPad and found a few problems, the CarTelemetryPacket2020 structure needed a pack clause. Also m_tyresSurfaceTemperature and m_tyresInnerTemperature should have been a byte. The following returns a size of 1307 as per the protocol specification, you're right that the sizes should have matched up and I don't see any other obvious problems with your code.

void Main()
{
    System.Runtime.InteropServices.Marshal.SizeOf(typeof(CarTelemetryPacket2020)).Dump();
}

[StructLayout(LayoutKind.Sequential, Pack = 1) ]
public struct PacketHeader2020
{
    public ushort m_packetFormat;
    public byte m_gameMajorVersion;
    public byte m_gameMinorVersion;
    public byte m_packetVersion;
    public byte m_packetId;
    public ulong m_sessionUUID;
    public float m_sessionTime;
    public uint m_frameIdentifier;
    public byte m_playerCarIndex;
    public byte m_secondaryPlayerCarIndex;
}

// Added pack = 1
[StructLayout(LayoutKind.Sequential, Pack = 1)] 
public struct CarTelemetryPacket2020
{
    public PacketHeader2020 m_header;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 22)]
    public CarTelemetryData2020[] m_carTelemetryData;

    public UInt32 m_buttonStatus;
    public byte m_mfdPanelIndex;
    public byte m_mfdPanelIndexSecondaryPlayer;
    public byte m_suggestedGear;
};

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct CarTelemetryData2020
{
    public ushort m_speed;
    public float m_throttle;
    public float m_steer;
    public float m_brake;
    public byte m_clutch;
    public sbyte m_gear;
    public ushort m_engineRPM;
    public byte m_drs;
    public byte m_revLightsPercent;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    public ushort[] m_brakesTemperature;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    // Changed following to byte
    public byte[] m_tyresSurfaceTemperature;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    // Changed following to byte
    public byte[] m_tyresInnerTemperature;
    public ushort m_engineTemperature;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    public float[] m_tyresPressure;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    public byte[] m_surfaceType;
}

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

From Dev

Mathjax being parsed with Jekyll

From Dev

Wierd XML not being parsed

From Dev

How to pass content to controller from service after it is done being parsed?

From Dev

Array entries to be parsed to JSON

From Dev

looping through a parsed array

From Dev

PHP file is not parsed after ">" operator

From Dev

How to store the parsed data in the array?

From Dev

Array parsed as a String on function call

From Dev

The value '-1' cannot be parsed as the type 'Byte'

From Dev

The value '-1' cannot be parsed as the type 'Byte'

From Dev

Parse xml - child items not being parsed

From Dev

Post permalinks not being parsed in Jekyll on GitHub Pages

From Dev

JSON parsed data not being called? Swift

From Dev

X-Editable data binding not being parsed

From Dev

HTML not being parsed to view - angular.js

From Dev

Post permalinks not being parsed in Jekyll on GitHub Pages

From Dev

JSON parsed data not being called? Swift

From Dev

Google BigQuery - Python Query not being parsed correctly

From Dev

swift: Alphabetiz data being parsed from a JSON

From Dev

PHP RegEx is being parsed 2 times

From Dev

How to remove space being parsed from string?

From Dev

Calling linker function only after the template was parsed

From Dev

Store each parsed value of JSON into an array

From Dev

How to sort an array of objects by a parsed date in Powershell

From Dev

Put results of Android Wifi Scan into parsed array

From Dev

Get first and last date from a parsed array

From Dev

Store each parsed value of JSON into an array

From Dev

Why is this json example an object is parsed as an array

From Dev

Replace text with parsed HTML from array

Related Related

HotTag

Archive