358 lines
9.9 KiB
C#
358 lines
9.9 KiB
C#
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
using System;
|
|
using System.IO;
|
|
using System.Text;
|
|
|
|
namespace EpicGames.Tracing.UnrealInsights.Events
|
|
{
|
|
public class Field
|
|
{
|
|
public bool? Bool { get; }
|
|
public int? Int { get; }
|
|
public long? Long { get; }
|
|
public double? Float { get; }
|
|
public string? String { get; }
|
|
readonly byte[]? _array;
|
|
public byte[]? GetArray()
|
|
{
|
|
return _array;
|
|
}
|
|
|
|
private Field(bool value) { Bool = value; }
|
|
private Field(int value) { Int = value; }
|
|
private Field(long value) { Long = value; }
|
|
private Field(float value) { Float = value; }
|
|
private Field(double value) { Float = value; }
|
|
private Field(string value) { String = value; }
|
|
private Field(byte[] value) { _array = value; }
|
|
public static Field FromBool(bool value) => new Field(value);
|
|
public static Field FromInt(int value) => new Field(value);
|
|
public static Field FromLong(long value) => new Field(value);
|
|
public static Field FromFloat(float value) => new Field(value);
|
|
public static Field FromDouble(double value) => new Field(value);
|
|
public static Field FromString(string value) => new Field(value);
|
|
public static Field FromArray(byte[] value) => new Field(value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Represents a generic event in the stream with fields stored dynamically
|
|
/// </summary>
|
|
public class GenericEvent : ITraceEvent
|
|
{
|
|
public uint Serial { get; }
|
|
readonly Field[] _fields;
|
|
public Field[] GetFields() => _fields;
|
|
public EventType Type => _eventType;
|
|
private readonly EventType _eventType;
|
|
|
|
public GenericEvent(uint serial, Field[] fields, EventType eventType)
|
|
{
|
|
Serial = serial;
|
|
_fields = fields;
|
|
_eventType = eventType;
|
|
}
|
|
|
|
public ushort Size
|
|
{
|
|
get
|
|
{
|
|
ushort totalSize = 0;
|
|
if (_eventType.HasSerial)
|
|
{
|
|
totalSize += 3; // 24-bit serial
|
|
}
|
|
|
|
totalSize += _eventType.GetEventSize();
|
|
if (_eventType.MaybeHasAux())
|
|
{
|
|
for (int i = 0; i < _eventType.Fields.Count; i++)
|
|
{
|
|
EventTypeField fieldType = _eventType.Fields[i];
|
|
if (!fieldType.IsAuxData())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
ushort auxDataSize;
|
|
if (fieldType.TypeInfo == EventTypeField.TypeAnsiString)
|
|
{
|
|
auxDataSize = (ushort) _fields[i].String!.Length;
|
|
}
|
|
else if (fieldType.TypeInfo == EventTypeField.TypeWideString)
|
|
{
|
|
auxDataSize = (ushort) (_fields[i].String!.Length * 2);
|
|
}
|
|
else
|
|
{
|
|
auxDataSize = (ushort) _fields[i].GetArray()!.Length;
|
|
}
|
|
|
|
totalSize += sizeof(uint); // AuxHeader
|
|
totalSize += auxDataSize;
|
|
}
|
|
totalSize += sizeof(byte); // AuxTerminal UID
|
|
}
|
|
|
|
return totalSize;
|
|
}
|
|
}
|
|
|
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter")]
|
|
public void Serialize(ushort uid, BinaryWriter writer)
|
|
{
|
|
if (_eventType.HasSerial)
|
|
{
|
|
byte serialLow = (byte)(Serial & 0xFF);
|
|
writer.Write(serialLow);
|
|
writer.Write(BinaryReaderExtensions.GetHighWord(Serial));
|
|
}
|
|
|
|
for (int i = 0; i < _eventType.Fields.Count; i++)
|
|
{
|
|
EventTypeField fieldType = _eventType.Fields[i];
|
|
|
|
if (fieldType.TypeInfo == EventTypeField.TypeInt8)
|
|
{
|
|
writer.Write((byte)_fields[i].Int!);
|
|
}
|
|
else if (fieldType.TypeInfo == EventTypeField.TypeInt16)
|
|
{
|
|
writer.Write((ushort)_fields[i].Int!);
|
|
}
|
|
else if (fieldType.TypeInfo == EventTypeField.TypeInt32)
|
|
{
|
|
writer.Write((uint)_fields[i].Int!);
|
|
}
|
|
else if (fieldType.TypeInfo == EventTypeField.TypeInt64)
|
|
{
|
|
writer.Write((ulong)_fields[i].Long!);
|
|
}
|
|
else if (fieldType.TypeInfo == EventTypeField.TypePointer)
|
|
{
|
|
writer.Write((ulong)_fields[i].Long!);
|
|
}
|
|
else if (fieldType.TypeInfo == EventTypeField.TypeFloat32)
|
|
{
|
|
writer.Write((float)_fields[i].Float!);
|
|
}
|
|
else if (fieldType.TypeInfo == EventTypeField.TypeFloat64)
|
|
{
|
|
writer.Write((double)_fields[i].Float!);
|
|
}
|
|
else if (fieldType.TypeInfo == EventTypeField.TypeAnsiString)
|
|
{
|
|
} // Write later as aux-data
|
|
else if (fieldType.TypeInfo == EventTypeField.TypeWideString)
|
|
{
|
|
} // Write later as aux-data
|
|
else if (fieldType.TypeInfo == EventTypeField.TypeArray)
|
|
{
|
|
} // Write later as aux-data
|
|
else if (fieldType.TypeInfo == EventTypeField.TypeBool)
|
|
{
|
|
writer.Write(_fields[i].Bool!.Value ? (byte)1 : (byte)0);
|
|
}
|
|
else
|
|
{
|
|
throw new Exception($"Found unknown TypeInfo {fieldType.TypeInfo}");
|
|
}
|
|
}
|
|
|
|
if (_eventType.MaybeHasAux())
|
|
{
|
|
for (int i = 0; i < _eventType.Fields.Count; i++)
|
|
{
|
|
EventTypeField fieldType = _eventType.Fields[i];
|
|
|
|
if (fieldType.Size > 0)
|
|
{
|
|
continue; // Skip any non-aux fields
|
|
}
|
|
|
|
ushort auxUid = PredefinedEventUid.AuxData;
|
|
if (!_eventType.IsImportant())
|
|
{
|
|
// Well-known UIDs are only shifted in non-important events
|
|
auxUid = (ushort) (auxUid << 1);
|
|
}
|
|
|
|
byte[] data;
|
|
if (fieldType.TypeInfo == EventTypeField.TypeAnsiString)
|
|
{
|
|
data = Encoding.ASCII.GetBytes(_fields[i].String!);
|
|
}
|
|
else if (fieldType.TypeInfo == EventTypeField.TypeWideString)
|
|
{
|
|
data = Encoding.Unicode.GetBytes(_fields[i].String!);
|
|
}
|
|
else
|
|
{
|
|
data = _fields[i].GetArray()!;
|
|
}
|
|
|
|
uint auxHeader = 0;
|
|
auxHeader = SetBits(auxHeader, auxUid, 0, 8);
|
|
auxHeader = SetBits(auxHeader, (uint) i, 8, 5);
|
|
auxHeader = SetBits(auxHeader, (uint) data.Length, 13, 19);
|
|
|
|
writer.Write(auxHeader);
|
|
writer.Write(data);
|
|
}
|
|
|
|
ushort auxDataTerminalUid = PredefinedEventUid.AuxDataTerminal;
|
|
|
|
if (!_eventType.IsImportant())
|
|
{
|
|
// Well-known UIDs are only shifted in non-important events
|
|
auxDataTerminalUid = (ushort) (auxDataTerminalUid << 1);
|
|
}
|
|
writer.Write((byte)auxDataTerminalUid);
|
|
}
|
|
}
|
|
|
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter")]
|
|
public static GenericEvent Deserialize(ushort uid, BinaryReader reader, EventType eventType)
|
|
{
|
|
Field[] fields = new Field[eventType.Fields.Count];
|
|
uint serial = 0;
|
|
|
|
if (!eventType.IsNoSync() && !eventType.IsImportant())
|
|
{
|
|
// Read uint24 as serial
|
|
byte serialLow = reader.ReadByte();
|
|
ushort serialHigh = reader.ReadUInt16();
|
|
serial = serialLow | ((uint)serialHigh << 16);
|
|
}
|
|
|
|
for (int i = 0; i < eventType.Fields.Count; i++)
|
|
{
|
|
EventTypeField fieldType = eventType.Fields[i];
|
|
|
|
if (fieldType.TypeInfo == EventTypeField.TypeInt8)
|
|
{
|
|
fields[i] = Field.FromInt(reader.ReadByte());
|
|
}
|
|
else if (fieldType.TypeInfo == EventTypeField.TypeInt16)
|
|
{
|
|
fields[i] = Field.FromInt(reader.ReadUInt16());
|
|
}
|
|
else if (fieldType.TypeInfo == EventTypeField.TypeInt32)
|
|
{
|
|
fields[i] = Field.FromInt((int)reader.ReadUInt32());
|
|
}
|
|
else if (fieldType.TypeInfo == EventTypeField.TypeInt64)
|
|
{
|
|
fields[i] = Field.FromLong((long) reader.ReadUInt64());
|
|
}
|
|
else if (fieldType.TypeInfo == EventTypeField.TypePointer)
|
|
{
|
|
fields[i] = Field.FromLong((long) reader.ReadUInt64());
|
|
}
|
|
else if (fieldType.TypeInfo == EventTypeField.TypeFloat32)
|
|
{
|
|
fields[i] = Field.FromFloat(reader.ReadSingle());
|
|
}
|
|
else if (fieldType.TypeInfo == EventTypeField.TypeFloat64)
|
|
{
|
|
fields[i] = Field.FromDouble(reader.ReadDouble());
|
|
}
|
|
else if (fieldType.TypeInfo == EventTypeField.TypeAnsiString)
|
|
{
|
|
} // Read later as aux-data
|
|
else if (fieldType.TypeInfo == EventTypeField.TypeWideString)
|
|
{
|
|
} // Read later as aux-data
|
|
else if (fieldType.TypeInfo == EventTypeField.TypeArray)
|
|
{
|
|
} // Read later as aux-data
|
|
else if (fieldType.TypeInfo == EventTypeField.TypeBool)
|
|
{
|
|
fields[i] = Field.FromBool(reader.ReadByte() == 1);
|
|
}
|
|
else
|
|
{
|
|
throw new Exception($"Found unknown TypeInfo {fieldType.TypeInfo}");
|
|
}
|
|
}
|
|
|
|
if (eventType.MaybeHasAux())
|
|
{
|
|
for (;;)
|
|
{
|
|
ushort auxUid = reader.ReadByte();
|
|
if (!eventType.IsImportant())
|
|
{
|
|
// Well-known UIDs are only shifted in non-important events
|
|
auxUid = (ushort) (auxUid >> 1);
|
|
}
|
|
|
|
if (auxUid == PredefinedEventUid.AuxData)
|
|
{
|
|
reader.BaseStream.Position -= 1; // Include the UID for parsing rest of header as a single uint32
|
|
uint auxHeader = reader.ReadUInt32();
|
|
|
|
int fieldIndex = (int)ReadBits(auxHeader, 8, 5);
|
|
uint size = ReadBits(auxHeader, 13, 19);
|
|
|
|
byte[] auxData = reader.ReadBytesStrict((int)size);
|
|
|
|
if (eventType.Fields[fieldIndex].TypeInfo == EventTypeField.TypeAnsiString)
|
|
{
|
|
fields[fieldIndex] = Field.FromString(Encoding.ASCII.GetString(auxData));
|
|
}
|
|
else if (eventType.Fields[fieldIndex].TypeInfo == EventTypeField.TypeWideString)
|
|
{
|
|
fields[fieldIndex] = Field.FromString(Encoding.Unicode.GetString(auxData));
|
|
}
|
|
else
|
|
{
|
|
fields[fieldIndex] = Field.FromArray(auxData);
|
|
}
|
|
}
|
|
else if (auxUid == PredefinedEventUid.AuxDataTerminal)
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
throw new Exception($"Invalid AuxUid found: {auxUid} / 0x{auxUid:X4}");
|
|
}
|
|
}
|
|
}
|
|
|
|
return new GenericEvent(serial, fields, eventType);
|
|
}
|
|
|
|
private static uint SetBits(uint word, uint value, int pos, int size)
|
|
{
|
|
uint mask = (((uint)1 << size) - 1) << pos;
|
|
word &= ~mask;
|
|
word |= (value << pos) & mask;
|
|
return word;
|
|
}
|
|
|
|
private static uint ReadBits(uint word, int pos, int size)
|
|
{
|
|
uint mask = (((uint)1 << size) - 1) << pos;
|
|
return (word & mask) >> pos;
|
|
}
|
|
|
|
internal static (ushort Uid, int FieldIndex, int Size) DeserializeAuxHeader(uint header)
|
|
{
|
|
ushort uid = (ushort)ReadBits(header, 0, 8);
|
|
int fieldIndex = (int)ReadBits(header, 8, 5);
|
|
int size = (int)ReadBits(header, 13, 19);
|
|
return (uid, fieldIndex, size);
|
|
}
|
|
|
|
internal static uint SerializeAuxHeader(ushort uid, int fieldIndex, int size)
|
|
{
|
|
uint header = 0;
|
|
header = SetBits(header, uid, 0, 8);
|
|
header = SetBits(header, (uint) fieldIndex, 8, 5);
|
|
header = SetBits(header, (uint) size, 13, 19);
|
|
return header;
|
|
}
|
|
}
|
|
} |