I am trying to write a parser using the C# Fit SDK to parse fit files into JSON and back to FIT preserving the message types that are unknown (i.e. not in the official SDK but present in the Fit file). This is done by using the original fit file as basis for the unknown fields. This means that I use the messages read from the original FIT file and rewrite them to the new one (without change). However, I came across and edge case where writing the Fit message to the file resulted in an infinit loop in the Mesg.cs WriteField method (specifically at the first while loop):
private static void WriteField(FieldBase field, byte size, BinaryWriter bw) { byte baseType = (byte)(field.Type & Fit.BaseTypeNumMask); // The field could be blank, correctly formed or partially filled while (field.GetSize() < size) { if (baseType == Fit.String) { // Figure out how much we have to pad byte padAmount = (byte)(size - field.GetSize()); //Has to be a string. try { // Get the Last Value of the field byte[] value = (byte[])field.GetValue(field.GetNumValues() - 1); List<byte> temp = new List<byte>(); if (value != null) { temp.AddRange(value); } for (byte i = 0; i < padAmount; i++) { temp.Add( Convert.ToByte( Fit.BaseType[baseType].invalidValue)); } field.SetValue(temp.ToArray()); } catch (Exception) { throw new FitException( "Exception occurred while resizing field to match definition."); } } else { field.AddValue(Fit.BaseType[baseType].invalidValue); } } // ...
The problem is that for message Num 147 (its not documented, but apparently its the list of the saved sensors) the sensor name (field Num 2, type Fit.String) sometimes has two or more byte[] as value (the first is always the null terminated byte[] for the string and the rest is padding with just one 0 element).
So basically this is the message before entering the while loop (the first element of the value - byte[15] is the string name of the sensor converted to byte[], the second is I suppose padding?):
The size of the message definition is 17 but the size of the current filed value is only 16 so the code tries to add more padding in the while loop. Then the code gets the value of the last field:
// Get the Last Value of the field byte[] value = (byte[])field.GetValue(field.GetNumValues() - 1);
But then it actually ads this to the beginning of the field value (`field.SetValue(temp.ToArray()`) instead of appending to the end which essentially just overrides the first element resulting in the following:
Now this results in an infinite loop as the `while (field.GetSize() < size)` will never be false as the "padding" (via the temp variable) actually overrides the first element of the value list and the size never reaches 17 (it will swap between 3 and 16 on each iteration).
I think the code should use the field.SetValue method's overload where an index is provided like this:
field.SetValue(field.GetNumValues()-1, temp.ToArray());
This would make sure that the padding is actually appended to the end of the last element instead of the start, increasing the size of the field by every iteration of the while loop (and do not override the actual value on the 0 position).
Let me know if further details is needed.