1 // Copyright 2001-2019 Crytek GmbH / Crytek Group. All rights reserved.
4 using System
.Collections
.Generic
;
7 using System
.Runtime
.InteropServices
;
8 using System
.Reflection
;
21 class EventPropertyDesc
23 public EFrameElementType type
;
24 public FieldInfo field
;
26 public EventPropertyDesc(EFrameElementType type
, FieldInfo field
)
35 public readonly Type propertiesType
;
36 public readonly EventPropertyDesc
[] properties
;
38 public EventClassDesc(Type propertiesType
, EventPropertyDesc
[] properties
)
40 this.propertiesType
= propertiesType
;
41 this.properties
= properties
;
47 Dictionary
<UInt64
, PendingInterval
> m_pendingIntervals
= new Dictionary
<UInt64
, PendingInterval
>();
48 byte m_nextEventSequence
= 0;
49 Int64 m_lastTimestamp
= 0;
51 Dictionary
<UInt32
, EventClassDesc
> m_classDescs
= new Dictionary
<UInt32
, EventClassDesc
>();
52 Dictionary
<UInt32
, IIntervalSink
> m_sinks
= new Dictionary
<uint, IIntervalSink
>();
54 public EventReader(IIntervalSink
[] intervalSinks
)
56 foreach (IIntervalSink sink
in intervalSinks
)
57 m_sinks
[sink
.ClassId
] = sink
;
60 public void ReadEvents(IBinaryLogDataStream stream
)
62 UInt32 eventStreamLen
= stream
.ReadUInt32();
63 int eventStreamPos
= 0;
64 for (eventStreamPos
= 0; eventStreamPos
< (int)eventStreamLen
; )
66 byte eventId
= stream
.ReadByte();
67 byte sequence
= stream
.ReadByte();
68 UInt16 eventLengthInWords
= stream
.ReadUInt16();
69 Int64 timeStamp
= stream
.ReadInt64();
71 if (sequence
!= m_nextEventSequence
)
72 throw new ApplicationException("Event sequence mismatch");
73 if (timeStamp
< m_lastTimestamp
)
74 throw new ApplicationException("Timestamps go backwards");
76 ++m_nextEventSequence
;
77 m_lastTimestamp
= timeStamp
;
79 int eventLength
= eventLengthInWords
* 4 - 12;
81 switch ((EventId
)eventId
)
83 case EventId
.EI_DefineClass
: ReadDefineClass(timeStamp
, eventLength
, stream
); break;
84 case EventId
.EI_BeginInterval
: ReadBeginInterval(timeStamp
, eventLength
, stream
); break;
85 case EventId
.EI_EndInterval
: ReadEndInterval(timeStamp
, eventLength
, stream
); break;
86 case EventId
.EI_ModifyInterval
: ReadModifyInterval(timeStamp
, eventLength
, stream
); break;
87 case EventId
.EI_ModifyIntervalBit
: ReadModifyIntervalBit(timeStamp
, eventLength
, stream
); break;
88 default: throw new ApplicationException("Unknown event type");
91 eventStreamPos
+= eventLengthInWords
* 4;
94 if (eventStreamPos
!= eventStreamLen
)
95 throw new ApplicationException("Event stream is corrupt");
98 private void ReadDefineClass(Int64 timeStamp
, int eventLength
, IBinaryLogDataStream stream
)
100 UInt32 classId
= stream
.ReadUInt32();
101 UInt32 numElements
= stream
.ReadUInt32();
103 byte[] descBytes
= stream
.ReadBytes(eventLength
- 8);
105 IIntervalSink sink
= m_sinks
[classId
];
106 Type propType
= sink
.PropertiesType
;
108 EventPropertyDesc
[] properties
= new EventPropertyDesc
[numElements
];
111 for (UInt32 elementIdx
= 0; elementIdx
< numElements
; ++elementIdx
)
113 byte typeId
= descBytes
[p
++];
115 int nullTermIdx
= Array
.IndexOf
<byte>(descBytes
, 0, p
);
116 string name
= System
.Text
.Encoding
.ASCII
.GetString(descBytes
, p
, (nullTermIdx
>= 0 ? nullTermIdx
: descBytes
.Length
) - p
);
119 // Find a field with the same name in the C# type
120 FieldInfo field
= propType
.GetField(name
, BindingFlags
.Instance
| BindingFlags
.NonPublic
| BindingFlags
.Public
);
121 properties
[elementIdx
] = new EventPropertyDesc((EFrameElementType
)typeId
, field
);
124 m_classDescs
[classId
] = new EventClassDesc(propType
, properties
);
127 private void ReadBeginInterval(Int64 timeStamp
, int eventLength
, IBinaryLogDataStream stream
)
129 UInt64 ivId
= stream
.ReadUInt64();
130 UInt32 classId
= stream
.ReadUInt32();
132 EventClassDesc classDesc
= m_classDescs
[classId
];
134 var pi
= new PendingInterval();
136 Interval iv
= (Interval
)Activator
.CreateInstance(classDesc
.propertiesType
);
137 iv
.StartUs
= timeStamp
;
138 iv
.EndUs
= Int64
.MaxValue
;
140 pi
.classId
= classId
;
141 pi
.sink
= m_sinks
[classId
];
143 byte[] values
= stream
.ReadBytes(eventLength
- 12);
146 for (int i
= 0, c
= classDesc
.properties
.Length
; i
< c
; ++i
)
150 switch (classDesc
.properties
[i
].type
)
152 case EFrameElementType
.Float
:
153 val
= EndianBitConverter
.ToSingle(values
, p
, stream
.Endianness
);
157 case EFrameElementType
.Int
:
158 val
= EndianBitConverter
.ToInt32(values
, p
, stream
.Endianness
);
162 case EFrameElementType
.String
:
164 int nullTerm
= Array
.IndexOf
<byte>(values
, 0, p
);
165 string s
= System
.Text
.Encoding
.ASCII
.GetString(values
, p
, nullTerm
- p
);
166 p
= (nullTerm
+ 4) & ~
3;
171 case EFrameElementType
.Int64
:
172 val
= EndianBitConverter
.ToInt64(values
, p
, stream
.Endianness
);
177 throw new ApplicationException("Unhandled property type");
180 if (classDesc
.properties
[i
].field
!= null)
181 classDesc
.properties
[i
].field
.SetValue(iv
, val
);
185 m_pendingIntervals
[ivId
] = pi
;
187 pi
.sink
.OnBegunInterval(ivId
, iv
);
190 private void ReadModifyInterval(Int64 timeStamp
, int eventLength
, IBinaryLogDataStream stream
)
192 UInt64 ivId
= stream
.ReadUInt64();
193 UInt32 classId
= stream
.ReadUInt32();
194 UInt32 field
= stream
.ReadUInt32();
195 UInt32 fieldId
= field
& 0x7fffffff;
196 UInt32 splitInterval
= field
& 0x80000000;
198 byte[] values
= stream
.ReadBytes(eventLength
- 16);
200 if (m_pendingIntervals
.ContainsKey(ivId
))
202 EventClassDesc classDesc
= m_classDescs
[classId
];
203 PendingInterval pi
= m_pendingIntervals
[ivId
];
206 if (splitInterval
!= 0)
208 iv
.EndUs
= timeStamp
;
210 Interval ivClone
= (Interval
)iv
.Clone();
212 pi
.sink
.OnFinalisedInterval(ivId
, iv
, true);
216 iv
.StartUs
= timeStamp
;
217 iv
.EndUs
= Int64
.MaxValue
;
220 EEndian srcEndian
= stream
.Endianness
;
223 switch (classDesc
.properties
[fieldId
].type
)
225 case EFrameElementType
.Float
:
226 val
= EndianBitConverter
.ToSingle(values
, 0, srcEndian
);
229 case EFrameElementType
.Int
:
230 val
= EndianBitConverter
.ToInt32(values
, 0, srcEndian
);
233 case EFrameElementType
.String
:
235 int nullTerm
= Array
.IndexOf
<byte>(values
, 0, 0);
236 string s
= System
.Text
.Encoding
.ASCII
.GetString(values
, 0, nullTerm
);
241 case EFrameElementType
.Int64
:
242 val
= EndianBitConverter
.ToInt64(values
, 0, srcEndian
);
246 throw new ApplicationException("Unhandled property type");
249 if (classDesc
.properties
[fieldId
].field
!= null)
250 classDesc
.properties
[fieldId
].field
.SetValue(iv
, val
);
252 if (splitInterval
!= 0)
254 pi
.sink
.OnBegunInterval(ivId
, iv
);
259 //throw new ApplicationException("Unknown interval");
263 private void ReadModifyIntervalBit(Int64 timeStamp
, int eventLength
, IBinaryLogDataStream stream
)
265 UInt64 ivId
= stream
.ReadUInt64();
266 UInt32 classId
= stream
.ReadUInt32();
267 UInt32 field
= stream
.ReadUInt32();
268 UInt32 fieldId
= field
& 0x7fffffff;
269 UInt32 splitInterval
= field
& 0x80000000;
271 byte[] values
= stream
.ReadBytes(eventLength
- 16);
273 if (m_pendingIntervals
.ContainsKey(ivId
))
275 EventClassDesc classDesc
= m_classDescs
[classId
];
276 PendingInterval pi
= m_pendingIntervals
[ivId
];
279 if (splitInterval
!= 0)
281 iv
.EndUs
= timeStamp
;
283 Interval ivClone
= (Interval
)iv
.Clone();
285 pi
.sink
.OnFinalisedInterval(ivId
, iv
, true);
289 iv
.StartUs
= timeStamp
;
290 iv
.EndUs
= Int64
.MaxValue
;
293 EEndian srcEndian
= stream
.Endianness
;
295 object valMask
= null, valOr
= null;
296 switch (classDesc
.properties
[fieldId
].type
)
298 case EFrameElementType
.Int
:
299 valMask
= EndianBitConverter
.ToInt32(values
, 0, srcEndian
);
300 valOr
= EndianBitConverter
.ToInt32(values
, 4, srcEndian
);
303 case EFrameElementType
.Int64
:
304 valMask
= EndianBitConverter
.ToInt64(values
, 0, srcEndian
);
305 valOr
= EndianBitConverter
.ToInt64(values
, 8, srcEndian
);
309 throw new ApplicationException("Unhandled property type");
312 if (classDesc
.properties
[fieldId
].field
!= null)
314 object oldVal
= classDesc
.properties
[fieldId
].field
.GetValue(iv
);
316 switch (classDesc
.properties
[fieldId
].type
)
318 case EFrameElementType
.Int
:
319 classDesc
.properties
[fieldId
].field
.SetValue(iv
, ((int)oldVal
& (int)valMask
) | ((int)valOr
));
322 case EFrameElementType
.Int64
:
323 classDesc
.properties
[fieldId
].field
.SetValue(iv
, ((long)oldVal
& (long)valMask
) | ((long)valOr
));
328 if (splitInterval
!= 0)
330 pi
.sink
.OnBegunInterval(ivId
, iv
);
335 //throw new ApplicationException("Unknown interval");
339 private void ReadEndInterval(Int64 timeStamp
, int eventLength
, IBinaryLogDataStream stream
)
341 UInt64 ivId
= stream
.ReadUInt64();
343 if (m_pendingIntervals
.ContainsKey(ivId
))
345 PendingInterval pi
= m_pendingIntervals
[ivId
];
348 iv
.EndUs
= timeStamp
;
349 pi
.sink
.OnFinalisedInterval(ivId
, iv
, false);
351 m_pendingIntervals
.Remove(ivId
);
355 class PendingInterval
357 public UInt32 classId
;
360 public IIntervalSink sink
;