/// <summary> /// Gets all properties decorated with <see cref="RecordFieldAttribute"/> from the given type. /// </summary> /// <param name="recordType">The type to get the properties from.</param> /// <returns>An unordered set of properties.</returns> /// <exception cref="IncompatibleRecordArrayTypeException"> /// Thrown if a property which does not support a <see cref="RecordFieldArrayAttribute"/> is decorated with one. /// </exception> /// <exception cref="InvalidFieldAttributeException"> /// Thrown if a property is decorated with an invalid <see cref="RecordFieldAttribute"/>. /// </exception> public static IEnumerable <PropertyInfo> GetRecordProperties(Type recordType) { var recordProperties = recordType.GetProperties ( BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy ) .Where(p => p.IsDefined(typeof(RecordFieldAttribute))) .ToList(); // Do a bit of error checking foreach (var property in recordProperties) { if (property.GetCustomAttributes().Any(a => a is RecordFieldArrayAttribute)) { if (!DBCDeserializer.IsPropertyTypeCompatibleWithArrayAttribute(property.PropertyType)) { throw new IncompatibleRecordArrayTypeException ( "Incompatible property definition decorated with RecordFieldArray. Use an array or something that implements IList<T>.", property.PropertyType ); } } var versionAttribute = GetPropertyFieldAttribute(property); if ((versionAttribute.RemovedIn < versionAttribute.IntroducedIn) && versionAttribute.RemovedIn != WarcraftVersion.Unknown) { throw new InvalidFieldAttributeException("The field was marked as having been removed before it was introduced."); } } return(recordProperties); }
/// <summary> /// Gets the absolute size in bytes of the given record type. /// </summary> /// <param name="version">The version of the record.</param> /// <param name="recordType">The type to get the size of.</param> /// <returns>The absolute size in bytes of the record.</returns> public static int GetRecordSize(WarcraftVersion version, Type recordType) { var size = 0; foreach (var recordProperty in GetVersionRelevantProperties(version, recordType)) { switch (recordProperty.PropertyType) { // Single-field types case Type foreignKeyType when foreignKeyType.IsGenericType && foreignKeyType.GetGenericTypeDefinition() == typeof(ForeignKey <>): case Type stringRefType when stringRefType == typeof(StringReference): case Type enumType when enumType.IsEnum: { var underlyingType = DBCDeserializer.GetUnderlyingStoredPrimitiveType(recordProperty.PropertyType); size += Marshal.SizeOf(underlyingType); break; } // Multi-field types case Type genericListType when genericListType.IsGenericType && genericListType.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IList <>)): case Type arrayType when arrayType.IsArray: { var elementSize = Marshal.SizeOf(DBCDeserializer.GetUnderlyingStoredPrimitiveType(recordProperty.PropertyType)); var arrayInfoAttribute = GetVersionRelevantPropertyFieldArrayAttribute(version, recordProperty); size += (int)(elementSize * arrayInfoAttribute.Count); break; } // Special version-variant length handling case Type locStringRefType when locStringRefType == typeof(LocalizedStringReference): { size += LocalizedStringReference.GetFieldCount(version) * sizeof(uint); break; } case Type registeredType when CustomFieldTypeStorageSizes.ContainsKey(registeredType): { size += CustomFieldTypeStorageSizes[registeredType]; break; } default: { size += Marshal.SizeOf(recordProperty.PropertyType); break; } } } return(size); }