private ResolvedTagPropertyInfo ResolvePropertyInfo(TagIndexEntry tagInfo, string propertyPath) { var topTagType = TagFactory.GetTypeForTag(tagInfo.Tag); var steps = PropertyAccessorParser.ExtractProperties(propertyPath); var offset = 0; var stepType = topTagType; foreach (var step in steps) { var prop = stepType.GetProperty(step.PropertyName); if (prop == null) { throw new Exception($"Couldn't find property '{step.PropertyName}' on type '{stepType}'"); } if (step.AccessType == PropertyAccessorParser.PropertyAccessType.Normal) { offset += BlamSerializer.StartsAt(stepType, step.PropertyName); stepType = prop.PropertyType; } else if (step.AccessType == PropertyAccessorParser.PropertyAccessType.ElementAccess) { if (prop.PropertyType.IsArray == false || step.ElementArgument is not int) { throw new NotSupportedException("Only arrays are currently supported for element access"); } var elementSize = BlamSerializer.SizeOf(prop.PropertyType.GetElementType()); var elementOffset = (elementSize * ((int)step.ElementArgument)); if (prop.GetCustomAttribute <ReferenceArrayAttribute>() != null) { var startsAt = BlamSerializer.StartsAt(stepType, step.PropertyName); // Read element array base offset var baseOffset = new SecondaryOffset(this.originalMap, this.mapToPatch.ReadInt32At(tagInfo.Offset.Value + offset + startsAt + 4)); // baseOffset is the absolute offset, need to subtract tag offset and prior property offsets to get relative offset += baseOffset.Value - tagInfo.Offset.Value - offset + elementOffset; } else if (prop.GetCustomAttribute <PrimitiveArrayAttribute>() != null) { offset += elementOffset; } else { throw new Exception("Only primitive and reference arrays are supported"); } stepType = prop.PropertyType.GetElementType(); } } return(new ResolvedTagPropertyInfo() { RelativeOffset = offset, PropertyType = stepType }); }