Skip to content

Commit

Permalink
Read particle file issue (#938)
Browse files Browse the repository at this point in the history
* Correct exception message

* Pass serializer options as param for MultiDimensional reads

* Refactor to use TryGetRegion

* Update for System.Text.Json parsing
  • Loading branch information
AristurtleDev authored Aug 28, 2024
1 parent da81c35 commit b1ad92a
Show file tree
Hide file tree
Showing 9 changed files with 315 additions and 28 deletions.
303 changes: 292 additions & 11 deletions source/MonoGame.Extended/Particles/Serialization/ProfileJsonConverter.cs
Original file line number Diff line number Diff line change
@@ -1,25 +1,306 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Unicode;
using Microsoft.Xna.Framework;
using MonoGame.Extended.Particles.Profiles;
using MonoGame.Extended.Serialization.Json;

namespace MonoGame.Extended.Particles.Serialization
{
public class ProfileJsonConverter : BaseTypeJsonConverter<Profile>
public class ProfileJsonConverter : JsonConverter<Profile>
{
public ProfileJsonConverter()
: base(GetSupportedTypes(), nameof(Profile))
/// <inheritdoc />
public override bool CanConvert(Type typeToConvert) => typeToConvert == typeof(Profile);

/// <inheritdoc />
public override Profile Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartObject)
{
throw new JsonException($"Expected {nameof(JsonTokenType.StartObject)} token");
}

Profile profile = null;

while (reader.Read())
{
if (reader.TokenType == JsonTokenType.EndObject)
{
break;
}

if (reader.TokenType == JsonTokenType.PropertyName)
{
var propertyName = reader.GetString();
reader.Read();

if (propertyName.Equals("type", StringComparison.InvariantCultureIgnoreCase))
{
var type = reader.GetString();
profile = type switch
{
nameof(Profile.Point) => Profile.Point(),
nameof(Profile.Line) => ReadLineProfile(ref reader),
nameof(Profile.Ring) => ReadRingProfile(ref reader),
nameof(Profile.Box) => ReadBoxProfile(ref reader),
nameof(Profile.BoxFill) => ReadBoxFillProfile(ref reader),
nameof(Profile.BoxUniform) => ReadBoxUniformProfile(ref reader),
nameof(Profile.Circle) => ReadCircleProfile(ref reader),
nameof(Profile.Spray) => ReadSprayProfile(ref reader),

_ => throw new NotSupportedException($"The profile type {type} is not supported at this time")
};
}
}
}

return profile;
}

private static Profile ReadLineProfile(ref Utf8JsonReader reader)
{
reader.Read();
Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
Debug.Assert(reader.GetString().Equals("axis", StringComparison.InvariantCultureIgnoreCase));
reader.Read();
string[] split = reader.GetString().Split(" ", StringSplitOptions.RemoveEmptyEntries);
Debug.Assert(split.Length == 2);
Vector2 axis = new Vector2(float.Parse(split[0]), float.Parse(split[1]));

reader.Read();
Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
Debug.Assert(reader.GetString().Equals("length", StringComparison.InvariantCultureIgnoreCase));
reader.Read();
float length = reader.GetSingle();

return Profile.Line(axis, length);
}

private static Profile ReadRingProfile(ref Utf8JsonReader reader)
{
reader.Read();
Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
Debug.Assert(reader.GetString().Equals("radius", StringComparison.InvariantCultureIgnoreCase));
reader.Read();
float radius = reader.GetSingle();

reader.Read();
Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
Debug.Assert(reader.GetString().Equals("radiate", StringComparison.InvariantCultureIgnoreCase));
reader.Read();
int radiate = reader.GetInt32();

return Profile.Ring(radius, (Profile.CircleRadiation)radiate);
}

private static Profile ReadBoxProfile(ref Utf8JsonReader reader)
{
reader.Read();
Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
Debug.Assert(reader.GetString().Equals("width", StringComparison.InvariantCultureIgnoreCase));
reader.Read();
float width = reader.GetSingle();

reader.Read();
Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
Debug.Assert(reader.GetString().Equals("height", StringComparison.InvariantCultureIgnoreCase));
reader.Read();
float height = reader.GetSingle();

return Profile.Box(width, height);
}

private static Profile ReadBoxFillProfile(ref Utf8JsonReader reader)
{
reader.Read();
Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
Debug.Assert(reader.GetString().Equals("width", StringComparison.InvariantCultureIgnoreCase));
reader.Read();
float width = reader.GetSingle();

reader.Read();
Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
Debug.Assert(reader.GetString().Equals("height", StringComparison.InvariantCultureIgnoreCase));
reader.Read();
float height = reader.GetSingle();

return Profile.BoxFill(width, height);
}

private static Profile ReadBoxUniformProfile(ref Utf8JsonReader reader)
{
reader.Read();
Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
Debug.Assert(reader.GetString().Equals("width", StringComparison.InvariantCultureIgnoreCase));
reader.Read();
float width = reader.GetSingle();

reader.Read();
Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
Debug.Assert(reader.GetString().Equals("height", StringComparison.InvariantCultureIgnoreCase));
reader.Read();
float height = reader.GetSingle();

return Profile.BoxUniform(width, height);
}

private static Profile ReadCircleProfile(ref Utf8JsonReader reader)
{
reader.Read();
Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
Debug.Assert(reader.GetString().Equals("radius", StringComparison.InvariantCultureIgnoreCase));
reader.Read();
float radius = reader.GetSingle();

reader.Read();
Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
Debug.Assert(reader.GetString().Equals("radiate", StringComparison.InvariantCultureIgnoreCase));
reader.Read();
int radiate = reader.GetInt32();

return Profile.Circle(radius, (Profile.CircleRadiation)radiate);

}

private static Profile ReadSprayProfile(ref Utf8JsonReader reader)
{
reader.Read();
Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
Debug.Assert(reader.GetString().Equals("direction", StringComparison.InvariantCultureIgnoreCase));
reader.Read();
string[] split = reader.GetString().Split(" ", StringSplitOptions.RemoveEmptyEntries);
Debug.Assert(split.Length == 2);
Vector2 direction = new Vector2(float.Parse(split[0]), float.Parse(split[1]));

reader.Read();
Debug.Assert(reader.TokenType == JsonTokenType.PropertyName);
Debug.Assert(reader.GetString().Equals("spread", StringComparison.InvariantCultureIgnoreCase));
reader.Read();
float spread = reader.GetSingle();

return Profile.Spray(direction, spread);
}

/// <inheritdoc />
public override void Write(Utf8JsonWriter writer, Profile value, JsonSerializerOptions options)
{
ArgumentNullException.ThrowIfNull(writer);
var type = value.GetType().ToString();
switch (type)
{
case nameof(PointProfile):
WritePointProfile(ref writer, (PointProfile)value);
break;

case nameof(LineProfile):
WriteLineProfile(ref writer, (LineProfile)value);
break;

case nameof(RingProfile):
WriteRingProfile(ref writer, (RingProfile)value);
break;

case nameof(BoxProfile):
WriteBoxProfile(ref writer, (BoxProfile)value);
break;

case nameof(BoxFillProfile):
WriteBoxFillProfile(ref writer, (BoxFillProfile)value);
break;

case nameof(BoxUniformProfile):
WriteBoxUniformProfile(ref writer, (BoxUniformProfile)value);
break;

case nameof(CircleProfile):
WriteCircleProfile(ref writer, (CircleProfile)value);
break;

case nameof(SprayProfile):
WriteSprayProfile(ref writer, (SprayProfile)value);
break;

default:
throw new InvalidOperationException("Unknown profile type");
}
}

private static void WritePointProfile(ref Utf8JsonWriter writer, PointProfile value)
{
writer.WriteStartObject();
writer.WritePropertyName("type");
writer.WriteStringValue(value.GetType().ToString());
writer.WriteEndObject();
}

private static void WriteLineProfile(ref Utf8JsonWriter writer, LineProfile value)
{
writer.WriteStartObject();
writer.WriteString("type", value.GetType().ToString());
writer.WriteString("axis", $"{value.Axis.X} {value.Axis.Y}");
writer.WriteNumber("length", value.Length);
writer.WriteEndObject();
}

private static void WriteRingProfile(ref Utf8JsonWriter writer, RingProfile value)
{
writer.WriteStartObject();
writer.WriteString("type", value.GetType().ToString());
writer.WriteNumber("radius", value.Radius);
writer.WriteNumber("radiate", (int)value.Radiate);
writer.WriteEndObject();
}

private static void WriteBoxProfile(ref Utf8JsonWriter writer, BoxProfile value)
{
writer.WriteStartObject();
writer.WriteString("type", value.GetType().ToString());
writer.WriteNumber("width", value.Width);
writer.WriteNumber("height", value.Height);
writer.WriteEndObject();
}

private static void WriteBoxFillProfile(ref Utf8JsonWriter writer, BoxFillProfile value)
{
writer.WriteStartObject();
writer.WriteString("type", value.GetType().ToString());
writer.WriteNumber("width", value.Width);
writer.WriteNumber("height", value.Height);
writer.WriteEndObject();
}

private static void WriteBoxUniformProfile(ref Utf8JsonWriter writer, BoxUniformProfile value)
{
writer.WriteStartObject();
writer.WriteString("type", value.GetType().ToString());
writer.WriteNumber("width", value.Width);
writer.WriteNumber("height", value.Height);
writer.WriteEndObject();
}

private static void WriteCircleProfile(ref Utf8JsonWriter writer, CircleProfile value)
{
writer.WriteStartObject();
writer.WriteString("type", value.GetType().ToString());
writer.WriteNumber("radius", value.Radius);
writer.WriteNumber("radiate", (int)value.Radiate);
writer.WriteEndObject();

}

private static IEnumerable<TypeInfo> GetSupportedTypes()
private static void WriteSprayProfile(ref Utf8JsonWriter writer, SprayProfile value)
{
return typeof(Profile)
.GetTypeInfo()
.Assembly
.DefinedTypes
.Where(type => type.IsSubclassOf(typeof(Profile)) && !type.IsAbstract);
writer.WriteStartObject();
writer.WriteString("type", value.GetType().ToString());
writer.WriteString("direction", $"{value.Direction.X} {value.Direction.Y}");
writer.WriteNumber("spread", value.Spread);
writer.WriteEndObject();
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class RangeJsonConverter<T> : JsonConverter<Range<T>> where T : IComparab
/// <inheritdoc />
public override Range<T> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
Span<T> values = reader.ReadAsMultiDimensional<T>();
Span<T> values = reader.ReadAsMultiDimensional<T>(options);

if (values.Length == 2)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class RectangleFJsonConverter : JsonConverter<RectangleF>
/// <inheritdoc />
public override RectangleF Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var values = reader.ReadAsMultiDimensional<float>();
var values = reader.ReadAsMultiDimensional<float>(options);
return new RectangleF(values[0], values[1], values[2], values[3]);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class Size2JsonConverter : JsonConverter<SizeF>
/// </exception>
public override SizeF Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var values = reader.ReadAsMultiDimensional<float>();
var values = reader.ReadAsMultiDimensional<float>(options);

if (values.Length == 2)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class SizeJsonConverter : JsonConverter<Size>
/// </exception>
public override Size Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var values = reader.ReadAsMultiDimensional<int>();
var values = reader.ReadAsMultiDimensional<int>(options);

if (values.Length == 2)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,14 @@ public TextureRegionService()

public Texture2DRegion GetTextureRegion(string name)
{
return TextureAtlases
.Select(textureAtlas => textureAtlas.GetRegion(name))
.FirstOrDefault(region => region != null);
foreach (Texture2DAtlas atlas in TextureAtlases)
{
if (atlas.TryGetRegion(name, out Texture2DRegion region))
{
return region;
}
}
return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class ThicknessJsonConverter : JsonConverter<Thickness>
/// <inheritdoc />
public override Thickness Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var values = reader.ReadAsMultiDimensional<int>();
var values = reader.ReadAsMultiDimensional<int>(options);
return Thickness.FromValues(values);
}

Expand Down
Loading

0 comments on commit b1ad92a

Please sign in to comment.