diff --git a/DotTiled.Benchmark/DotTiled.Benchmark.csproj b/DotTiled.Benchmark/DotTiled.Benchmark.csproj index 3fc12d3..739d619 100644 --- a/DotTiled.Benchmark/DotTiled.Benchmark.csproj +++ b/DotTiled.Benchmark/DotTiled.Benchmark.csproj @@ -9,6 +9,7 @@ + diff --git a/DotTiled.Benchmark/Program.cs b/DotTiled.Benchmark/Program.cs index c44b79b..1abb982 100644 --- a/DotTiled.Benchmark/Program.cs +++ b/DotTiled.Benchmark/Program.cs @@ -1,26 +1,81 @@ using System; +using System.Collections.Immutable; using System.Security.Cryptography; +using System.Text; +using System.Xml; using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Columns; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Order; +using BenchmarkDotNet.Reports; using BenchmarkDotNet.Running; namespace MyBenchmarks { - public class MapLoader + [GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)] + [CategoriesColumn] + [Orderer(SummaryOrderPolicy.FastestToSlowest)] + public class MapLoading { - public MapLoader() + private string _tmxPath = @"C:\Users\Daniel\winrepos\DotTiled\DotTiled.Tests\Serialization\Tmx\TestData\Map\empty-map-csv.tmx"; + private string _tmxContents = ""; + + public MapLoading() { + _tmxContents = System.IO.File.ReadAllText(_tmxPath); } - [Benchmark] - public DotTiled.Map LoadWithDotTiled() + [BenchmarkCategory("MapFromInMemoryTmxString")] + [Benchmark(Baseline = true, Description = "DotTiled")] + public DotTiled.Map LoadWithDotTiledFromInMemoryString() { - throw new NotImplementedException(); + using var stringReader = new StringReader(_tmxContents); + using var xmlReader = XmlReader.Create(stringReader); + using var mapReader = new DotTiled.TmxMapReader(xmlReader, _ => throw new Exception(), _ => throw new Exception()); + return mapReader.ReadMap(); } - [Benchmark] - public TiledLib.Map LoadWithTiledLib() + [BenchmarkCategory("MapFromTmxFile")] + [Benchmark(Baseline = true, Description = "DotTiled")] + public DotTiled.Map LoadWithDotTiledFromFile() { - throw new NotImplementedException(); + using var fileStream = System.IO.File.OpenRead(_tmxPath); + using var xmlReader = XmlReader.Create(fileStream); + using var mapReader = new DotTiled.TmxMapReader(xmlReader, _ => throw new Exception(), _ => throw new Exception()); + return mapReader.ReadMap(); + } + + [BenchmarkCategory("MapFromInMemoryTmxString")] + [Benchmark(Description = "TiledLib")] + public TiledLib.Map LoadWithTiledLibFromInMemoryString() + { + using var memStream = new MemoryStream(Encoding.UTF8.GetBytes(_tmxContents)); + return TiledLib.Map.FromStream(memStream); + } + + [BenchmarkCategory("MapFromTmxFile")] + [Benchmark(Description = "TiledLib")] + public TiledLib.Map LoadWithTiledLibFromFile() + { + using var fileStream = System.IO.File.OpenRead(_tmxPath); + var map = TiledLib.Map.FromStream(fileStream); + return map; + } + + [BenchmarkCategory("MapFromInMemoryTmxString")] + [Benchmark(Description = "TiledCSPlus")] + public TiledCSPlus.TiledMap LoadWithTiledCSPlusFromInMemoryString() + { + using var memStream = new MemoryStream(Encoding.UTF8.GetBytes(_tmxContents)); + return new TiledCSPlus.TiledMap(memStream); + } + + [BenchmarkCategory("MapFromTmxFile")] + [Benchmark(Description = "TiledCSPlus")] + public TiledCSPlus.TiledMap LoadWithTiledCSPlusFromFile() + { + using var fileStream = System.IO.File.OpenRead(_tmxPath); + return new TiledCSPlus.TiledMap(fileStream); } } @@ -28,7 +83,10 @@ namespace MyBenchmarks { public static void Main(string[] args) { - //var summary = BenchmarkRunner.Run(); + var config = BenchmarkDotNet.Configs.DefaultConfig.Instance + .WithOptions(ConfigOptions.DisableOptimizationsValidator) + .AddDiagnoser(BenchmarkDotNet.Diagnosers.MemoryDiagnoser.Default); + var summary = BenchmarkRunner.Run(config); } } } diff --git a/DotTiled.Tests/TmxSerializer/TmxSerializer.DataTests.cs b/DotTiled.Tests/Assert/AssertData.cs similarity index 80% rename from DotTiled.Tests/TmxSerializer/TmxSerializer.DataTests.cs rename to DotTiled.Tests/Assert/AssertData.cs index e08d402..d4b54f0 100644 --- a/DotTiled.Tests/TmxSerializer/TmxSerializer.DataTests.cs +++ b/DotTiled.Tests/Assert/AssertData.cs @@ -1,8 +1,8 @@ namespace DotTiled.Tests; -public partial class TmxSerializerDataTests +public static partial class DotTiledAssert { - public static void AssertData(Data? actual, Data? expected) + internal static void AssertData(Data? expected, Data? actual) { if (expected is null) { @@ -24,11 +24,11 @@ public partial class TmxSerializerDataTests Assert.NotNull(actual.Chunks); Assert.Equal(expected.Chunks.Length, actual.Chunks.Length); for (var i = 0; i < expected.Chunks.Length; i++) - AssertChunk(actual.Chunks[i], expected.Chunks[i]); + AssertChunk(expected.Chunks[i], actual.Chunks[i]); } } - private static void AssertChunk(Chunk actual, Chunk expected) + private static void AssertChunk(Chunk expected, Chunk actual) { // Attributes Assert.Equal(expected.X, actual.X); diff --git a/DotTiled.Tests/TmxSerializer/TmxSerializer.ImageTests.cs b/DotTiled.Tests/Assert/AssertImage.cs similarity index 79% rename from DotTiled.Tests/TmxSerializer/TmxSerializer.ImageTests.cs rename to DotTiled.Tests/Assert/AssertImage.cs index 7d00713..9943c46 100644 --- a/DotTiled.Tests/TmxSerializer/TmxSerializer.ImageTests.cs +++ b/DotTiled.Tests/Assert/AssertImage.cs @@ -1,8 +1,8 @@ namespace DotTiled.Tests; -public partial class TmxSerializerImageTests +public static partial class DotTiledAssert { - public static void AssertImage(Image? actual, Image? expected) + internal static void AssertImage(Image? expected, Image? actual) { if (expected is null) { diff --git a/DotTiled.Tests/TmxSerializer/TmxSerializer.LayerTests.cs b/DotTiled.Tests/Assert/AssertLayer.cs similarity index 66% rename from DotTiled.Tests/TmxSerializer/TmxSerializer.LayerTests.cs rename to DotTiled.Tests/Assert/AssertLayer.cs index c3289a4..57df04d 100644 --- a/DotTiled.Tests/TmxSerializer/TmxSerializer.LayerTests.cs +++ b/DotTiled.Tests/Assert/AssertLayer.cs @@ -1,8 +1,8 @@ namespace DotTiled.Tests; -public partial class TmxSerializerLayerTests +public static partial class DotTiledAssert { - public static void AssertLayer(BaseLayer? actual, BaseLayer? expected) + internal static void AssertLayer(BaseLayer? expected, BaseLayer? actual) { if (expected is null) { @@ -23,11 +23,11 @@ public partial class TmxSerializerLayerTests Assert.Equal(expected.ParallaxX, actual.ParallaxX); Assert.Equal(expected.ParallaxY, actual.ParallaxY); - TmxSerializerPropertiesTests.AssertProperties(actual.Properties, expected.Properties); - AssertLayer((dynamic)actual, (dynamic)expected); + AssertProperties(expected.Properties, actual.Properties); + AssertLayer((dynamic)expected, (dynamic)actual); } - private static void AssertLayer(TileLayer actual, TileLayer expected) + private static void AssertLayer(TileLayer expected, TileLayer actual) { // Attributes Assert.Equal(expected.Width, actual.Width); @@ -36,10 +36,10 @@ public partial class TmxSerializerLayerTests Assert.Equal(expected.Y, actual.Y); Assert.NotNull(actual.Data); - TmxSerializerDataTests.AssertData(actual.Data, expected.Data); + AssertData(expected.Data, actual.Data); } - private static void AssertLayer(ObjectLayer actual, ObjectLayer expected) + private static void AssertLayer(ObjectLayer expected, ObjectLayer actual) { // Attributes Assert.Equal(expected.DrawOrder, actual.DrawOrder); @@ -49,10 +49,10 @@ public partial class TmxSerializerLayerTests Assert.NotNull(actual.Objects); Assert.Equal(expected.Objects.Count, actual.Objects.Count); for (var i = 0; i < expected.Objects.Count; i++) - TmxSerializerObjectTests.AssertObject(actual.Objects[i], expected.Objects[i]); + AssertObject(expected.Objects[i], actual.Objects[i]); } - private static void AssertLayer(ImageLayer actual, ImageLayer expected) + private static void AssertLayer(ImageLayer expected, ImageLayer actual) { // Attributes Assert.Equal(expected.RepeatX, actual.RepeatX); @@ -61,15 +61,15 @@ public partial class TmxSerializerLayerTests Assert.Equal(expected.Y, actual.Y); Assert.NotNull(actual.Image); - TmxSerializerImageTests.AssertImage(actual.Image, expected.Image); + AssertImage(expected.Image, actual.Image); } - private static void AssertLayer(Group actual, Group expected) + private static void AssertLayer(Group expected, Group actual) { // Attributes Assert.NotNull(actual.Layers); Assert.Equal(expected.Layers.Count, actual.Layers.Count); for (var i = 0; i < expected.Layers.Count; i++) - AssertLayer(actual.Layers[i], expected.Layers[i]); + AssertLayer(expected.Layers[i], actual.Layers[i]); } } diff --git a/DotTiled.Tests/Assert/AssertMap.cs b/DotTiled.Tests/Assert/AssertMap.cs new file mode 100644 index 0000000..167d0ad --- /dev/null +++ b/DotTiled.Tests/Assert/AssertMap.cs @@ -0,0 +1,40 @@ +namespace DotTiled.Tests; + +public static partial class DotTiledAssert +{ + internal static void AssertMap(Map expected, Map actual) + { + // Attributes + Assert.Equal(expected.Version, actual.Version); + Assert.Equal(expected.TiledVersion, actual.TiledVersion); + Assert.Equal(expected.Class, actual.Class); + Assert.Equal(expected.Orientation, actual.Orientation); + Assert.Equal(expected.RenderOrder, actual.RenderOrder); + Assert.Equal(expected.CompressionLevel, actual.CompressionLevel); + Assert.Equal(expected.Width, actual.Width); + Assert.Equal(expected.Height, actual.Height); + Assert.Equal(expected.TileWidth, actual.TileWidth); + Assert.Equal(expected.TileHeight, actual.TileHeight); + Assert.Equal(expected.HexSideLength, actual.HexSideLength); + Assert.Equal(expected.StaggerAxis, actual.StaggerAxis); + Assert.Equal(expected.StaggerIndex, actual.StaggerIndex); + Assert.Equal(expected.ParallaxOriginX, actual.ParallaxOriginX); + Assert.Equal(expected.ParallaxOriginY, actual.ParallaxOriginY); + Assert.Equal(expected.BackgroundColor, actual.BackgroundColor); + Assert.Equal(expected.NextLayerID, actual.NextLayerID); + Assert.Equal(expected.NextObjectID, actual.NextObjectID); + Assert.Equal(expected.Infinite, actual.Infinite); + + AssertProperties(actual.Properties, expected.Properties); + + Assert.NotNull(actual.Tilesets); + Assert.Equal(expected.Tilesets.Count, actual.Tilesets.Count); + for (var i = 0; i < expected.Tilesets.Count; i++) + AssertTileset(actual.Tilesets[i], expected.Tilesets[i]); + + Assert.NotNull(actual.Layers); + Assert.Equal(expected.Layers.Count, actual.Layers.Count); + for (var i = 0; i < expected.Layers.Count; i++) + AssertLayer(actual.Layers[i], expected.Layers[i]); + } +} diff --git a/DotTiled.Tests/TmxSerializer/TmxSerializer.ObjectTests.cs b/DotTiled.Tests/Assert/AssertObject.cs similarity index 68% rename from DotTiled.Tests/TmxSerializer/TmxSerializer.ObjectTests.cs rename to DotTiled.Tests/Assert/AssertObject.cs index c593074..68d74eb 100644 --- a/DotTiled.Tests/TmxSerializer/TmxSerializer.ObjectTests.cs +++ b/DotTiled.Tests/Assert/AssertObject.cs @@ -1,8 +1,8 @@ namespace DotTiled.Tests; -public partial class TmxSerializerObjectTests +public static partial class DotTiledAssert { - public static void AssertObject(Object actual, Object expected) + internal static void AssertObject(Object expected, Object actual) { // Attributes Assert.Equal(expected.ID, actual.ID); @@ -17,36 +17,36 @@ public partial class TmxSerializerObjectTests Assert.Equal(expected.Visible, actual.Visible); Assert.Equal(expected.Template, actual.Template); - TmxSerializerPropertiesTests.AssertProperties(actual.Properties, expected.Properties); - AssertObject((dynamic)actual, (dynamic)expected); + AssertProperties(actual.Properties, expected.Properties); + AssertObject((dynamic)expected, (dynamic)actual); } - private static void AssertObject(RectangleObject actual, RectangleObject expected) + private static void AssertObject(RectangleObject expected, RectangleObject actual) { Assert.True(true); // A rectangle object is the same as the abstract Object } - private static void AssertObject(EllipseObject actual, EllipseObject expected) + private static void AssertObject(EllipseObject expected, EllipseObject actual) { Assert.True(true); // An ellipse object is the same as the abstract Object } - private static void AssertObject(PointObject actual, PointObject expected) + private static void AssertObject(PointObject expected, PointObject actual) { Assert.True(true); // A point object is the same as the abstract Object } - private static void AssertObject(PolygonObject actual, PolygonObject expected) + private static void AssertObject(PolygonObject expected, PolygonObject actual) { Assert.Equal(expected.Points, actual.Points); } - private static void AssertObject(PolylineObject actual, PolylineObject expected) + private static void AssertObject(PolylineObject expected, PolylineObject actual) { Assert.Equal(expected.Points, actual.Points); } - private static void AssertObject(TextObject actual, TextObject expected) + private static void AssertObject(TextObject expected, TextObject actual) { // Attributes Assert.Equal(expected.FontFamily, actual.FontFamily); diff --git a/DotTiled.Tests/TmxSerializer/TmxSerializer.PropertiesTests.cs b/DotTiled.Tests/Assert/AssertProperties.cs similarity index 91% rename from DotTiled.Tests/TmxSerializer/TmxSerializer.PropertiesTests.cs rename to DotTiled.Tests/Assert/AssertProperties.cs index e22e5b9..d0a8269 100644 --- a/DotTiled.Tests/TmxSerializer/TmxSerializer.PropertiesTests.cs +++ b/DotTiled.Tests/Assert/AssertProperties.cs @@ -1,8 +1,8 @@ namespace DotTiled.Tests; -public partial class TmxSerializerPropertiesTests +public static partial class DotTiledAssert { - public static void AssertProperties(Dictionary? actual, Dictionary? expected) + internal static void AssertProperties(Dictionary? expected, Dictionary? actual) { if (expected is null) { diff --git a/DotTiled.Tests/TmxSerializer/TmxSerializer.TilesetTests.cs b/DotTiled.Tests/Assert/AssertTileset.cs similarity index 68% rename from DotTiled.Tests/TmxSerializer/TmxSerializer.TilesetTests.cs rename to DotTiled.Tests/Assert/AssertTileset.cs index 04bb54b..8421bd0 100644 --- a/DotTiled.Tests/TmxSerializer/TmxSerializer.TilesetTests.cs +++ b/DotTiled.Tests/Assert/AssertTileset.cs @@ -1,8 +1,8 @@ namespace DotTiled.Tests; -public partial class TmxSerializerTilesetTests +public static partial class DotTiledAssert { - public static void AssertTileset(Tileset actual, Tileset expected) + internal static void AssertTileset(Tileset expected, Tileset actual) { // Attributes Assert.Equal(expected.Version, actual.Version); @@ -22,28 +22,28 @@ public partial class TmxSerializerTilesetTests Assert.Equal(expected.FillMode, actual.FillMode); // At most one of - TmxSerializerImageTests.AssertImage(actual.Image, expected.Image); - AssertTileOffset(actual.TileOffset, expected.TileOffset); - AssertGrid(actual.Grid, expected.Grid); - TmxSerializerPropertiesTests.AssertProperties(actual.Properties, expected.Properties); + AssertImage(expected.Image, actual.Image); + AssertTileOffset(expected.TileOffset, actual.TileOffset); + AssertGrid(expected.Grid, actual.Grid); + AssertProperties(expected.Properties, actual.Properties); // TODO: AssertTerrainTypes(actual.TerrainTypes, expected.TerrainTypes); if (expected.Wangsets is not null) { Assert.NotNull(actual.Wangsets); Assert.Equal(expected.Wangsets.Count, actual.Wangsets.Count); for (var i = 0; i < expected.Wangsets.Count; i++) - AssertWangset(actual.Wangsets[i], expected.Wangsets[i]); + AssertWangset(expected.Wangsets[i], actual.Wangsets[i]); } - AssertTransformations(actual.Transformations, expected.Transformations); + AssertTransformations(expected.Transformations, actual.Transformations); // Any number of Assert.NotNull(actual.Tiles); Assert.Equal(expected.Tiles.Count, actual.Tiles.Count); for (var i = 0; i < expected.Tiles.Count; i++) - AssertTile(actual.Tiles[i], expected.Tiles[i]); + AssertTile(expected.Tiles[i], actual.Tiles[i]); } - private static void AssertTileOffset(TileOffset? actual, TileOffset? expected) + private static void AssertTileOffset(TileOffset? expected, TileOffset? actual) { if (expected is null) { @@ -57,7 +57,7 @@ public partial class TmxSerializerTilesetTests Assert.Equal(expected.Y, actual.Y); } - private static void AssertGrid(Grid? actual, Grid? expected) + private static void AssertGrid(Grid? expected, Grid? actual) { if (expected is null) { @@ -72,7 +72,7 @@ public partial class TmxSerializerTilesetTests Assert.Equal(expected.Height, actual.Height); } - private static void AssertWangset(Wangset actual, Wangset expected) + private static void AssertWangset(Wangset expected, Wangset actual) { // Attributes Assert.Equal(expected.Name, actual.Name); @@ -80,19 +80,19 @@ public partial class TmxSerializerTilesetTests Assert.Equal(expected.Tile, actual.Tile); // At most one of - TmxSerializerPropertiesTests.AssertProperties(actual.Properties, expected.Properties); + AssertProperties(expected.Properties, actual.Properties); if (expected.WangColors is not null) { Assert.NotNull(actual.WangColors); Assert.Equal(expected.WangColors.Count, actual.WangColors.Count); for (var i = 0; i < expected.WangColors.Count; i++) - AssertWangColor(actual.WangColors[i], expected.WangColors[i]); + AssertWangColor(expected.WangColors[i], actual.WangColors[i]); } for (var i = 0; i < expected.WangTiles.Count; i++) - AssertWangTile(actual.WangTiles[i], expected.WangTiles[i]); + AssertWangTile(expected.WangTiles[i], actual.WangTiles[i]); } - private static void AssertWangColor(WangColor actual, WangColor expected) + private static void AssertWangColor(WangColor expected, WangColor actual) { // Attributes Assert.Equal(expected.Name, actual.Name); @@ -101,17 +101,17 @@ public partial class TmxSerializerTilesetTests Assert.Equal(expected.Tile, actual.Tile); Assert.Equal(expected.Probability, actual.Probability); - TmxSerializerPropertiesTests.AssertProperties(actual.Properties, expected.Properties); + AssertProperties(expected.Properties, actual.Properties); } - private static void AssertWangTile(WangTile actual, WangTile expected) + private static void AssertWangTile(WangTile expected, WangTile actual) { // Attributes Assert.Equal(expected.TileID, actual.TileID); Assert.Equal(expected.WangID, actual.WangID); } - private static void AssertTransformations(Transformations? actual, Transformations? expected) + private static void AssertTransformations(Transformations? expected, Transformations? actual) { if (expected is null) { @@ -127,7 +127,7 @@ public partial class TmxSerializerTilesetTests Assert.Equal(expected.PreferUntransformed, actual.PreferUntransformed); } - private static void AssertTile(Tile actual, Tile expected) + private static void AssertTile(Tile expected, Tile actual) { // Attributes Assert.Equal(expected.ID, actual.ID); @@ -139,19 +139,19 @@ public partial class TmxSerializerTilesetTests Assert.Equal(expected.Height, actual.Height); // Elements - TmxSerializerPropertiesTests.AssertProperties(actual.Properties, expected.Properties); - TmxSerializerImageTests.AssertImage(actual.Image, expected.Image); - TmxSerializerLayerTests.AssertLayer(actual.ObjectLayer, expected.ObjectLayer); + AssertProperties(actual.Properties, expected.Properties); + AssertImage(actual.Image, expected.Image); + AssertLayer((BaseLayer?)actual.ObjectLayer, (BaseLayer?)expected.ObjectLayer); if (expected.Animation is not null) { Assert.NotNull(actual.Animation); Assert.Equal(expected.Animation.Count, actual.Animation.Count); for (var i = 0; i < expected.Animation.Count; i++) - AssertFrame(actual.Animation[i], expected.Animation[i]); + AssertFrame(expected.Animation[i], actual.Animation[i]); } } - private static void AssertFrame(Frame actual, Frame expected) + private static void AssertFrame(Frame expected, Frame actual) { // Attributes Assert.Equal(expected.TileID, actual.TileID); diff --git a/DotTiled.Tests/DotTiled.Tests.csproj b/DotTiled.Tests/DotTiled.Tests.csproj index 44236af..5e36559 100644 --- a/DotTiled.Tests/DotTiled.Tests.csproj +++ b/DotTiled.Tests/DotTiled.Tests.csproj @@ -26,7 +26,7 @@ - + diff --git a/DotTiled.Tests/TmxSerializer/TestData/Map/empty-map-base64-gzip.tmx b/DotTiled.Tests/Serialization/Tmx/TestData/Map/empty-map-base64-gzip.tmx similarity index 100% rename from DotTiled.Tests/TmxSerializer/TestData/Map/empty-map-base64-gzip.tmx rename to DotTiled.Tests/Serialization/Tmx/TestData/Map/empty-map-base64-gzip.tmx diff --git a/DotTiled.Tests/TmxSerializer/TestData/Map/empty-map-base64-zlib.tmx b/DotTiled.Tests/Serialization/Tmx/TestData/Map/empty-map-base64-zlib.tmx similarity index 100% rename from DotTiled.Tests/TmxSerializer/TestData/Map/empty-map-base64-zlib.tmx rename to DotTiled.Tests/Serialization/Tmx/TestData/Map/empty-map-base64-zlib.tmx diff --git a/DotTiled.Tests/TmxSerializer/TestData/Map/empty-map-base64.tmx b/DotTiled.Tests/Serialization/Tmx/TestData/Map/empty-map-base64.tmx similarity index 100% rename from DotTiled.Tests/TmxSerializer/TestData/Map/empty-map-base64.tmx rename to DotTiled.Tests/Serialization/Tmx/TestData/Map/empty-map-base64.tmx diff --git a/DotTiled.Tests/TmxSerializer/TestData/Map/empty-map-csv.tmx b/DotTiled.Tests/Serialization/Tmx/TestData/Map/empty-map-csv.tmx similarity index 100% rename from DotTiled.Tests/TmxSerializer/TestData/Map/empty-map-csv.tmx rename to DotTiled.Tests/Serialization/Tmx/TestData/Map/empty-map-csv.tmx diff --git a/DotTiled.Tests/TmxSerializer/TestData/Map/empty-map-properties.cs b/DotTiled.Tests/Serialization/Tmx/TestData/Map/empty-map-properties.cs similarity index 97% rename from DotTiled.Tests/TmxSerializer/TestData/Map/empty-map-properties.cs rename to DotTiled.Tests/Serialization/Tmx/TestData/Map/empty-map-properties.cs index c87c183..795b920 100644 --- a/DotTiled.Tests/TmxSerializer/TestData/Map/empty-map-properties.cs +++ b/DotTiled.Tests/Serialization/Tmx/TestData/Map/empty-map-properties.cs @@ -1,6 +1,6 @@ namespace DotTiled.Tests; -public partial class TmxSerializerMapTests +public partial class TmxMapReaderTests { private static Map EmptyMapWithProperties() => new Map { diff --git a/DotTiled.Tests/TmxSerializer/TestData/Map/empty-map-properties.tmx b/DotTiled.Tests/Serialization/Tmx/TestData/Map/empty-map-properties.tmx similarity index 100% rename from DotTiled.Tests/TmxSerializer/TestData/Map/empty-map-properties.tmx rename to DotTiled.Tests/Serialization/Tmx/TestData/Map/empty-map-properties.tmx diff --git a/DotTiled.Tests/TmxSerializer/TestData/Map/empty-map.cs b/DotTiled.Tests/Serialization/Tmx/TestData/Map/empty-map.cs similarity index 97% rename from DotTiled.Tests/TmxSerializer/TestData/Map/empty-map.cs rename to DotTiled.Tests/Serialization/Tmx/TestData/Map/empty-map.cs index e01516b..12cfc00 100644 --- a/DotTiled.Tests/TmxSerializer/TestData/Map/empty-map.cs +++ b/DotTiled.Tests/Serialization/Tmx/TestData/Map/empty-map.cs @@ -1,6 +1,6 @@ namespace DotTiled.Tests; -public partial class TmxSerializerMapTests +public partial class TmxMapReaderTests { private static Map EmptyMapWithEncodingAndCompression(DataEncoding dataEncoding, DataCompression? compression) => new Map { diff --git a/DotTiled.Tests/TmxSerializer/TestData/Map/map-with-group.cs b/DotTiled.Tests/Serialization/Tmx/TestData/Map/map-with-group.cs similarity index 98% rename from DotTiled.Tests/TmxSerializer/TestData/Map/map-with-group.cs rename to DotTiled.Tests/Serialization/Tmx/TestData/Map/map-with-group.cs index 0bac69e..515312e 100644 --- a/DotTiled.Tests/TmxSerializer/TestData/Map/map-with-group.cs +++ b/DotTiled.Tests/Serialization/Tmx/TestData/Map/map-with-group.cs @@ -1,6 +1,6 @@ namespace DotTiled.Tests; -public partial class TmxSerializerMapTests +public partial class TmxMapReaderTests { private static Map MapWithGroup() => new Map { diff --git a/DotTiled.Tests/TmxSerializer/TestData/Map/map-with-group.tmx b/DotTiled.Tests/Serialization/Tmx/TestData/Map/map-with-group.tmx similarity index 100% rename from DotTiled.Tests/TmxSerializer/TestData/Map/map-with-group.tmx rename to DotTiled.Tests/Serialization/Tmx/TestData/Map/map-with-group.tmx diff --git a/DotTiled.Tests/TmxSerializer/TestData/Map/map-with-object-template.cs b/DotTiled.Tests/Serialization/Tmx/TestData/Map/map-with-object-template.cs similarity index 98% rename from DotTiled.Tests/TmxSerializer/TestData/Map/map-with-object-template.cs rename to DotTiled.Tests/Serialization/Tmx/TestData/Map/map-with-object-template.cs index 31a658e..8f4459e 100644 --- a/DotTiled.Tests/TmxSerializer/TestData/Map/map-with-object-template.cs +++ b/DotTiled.Tests/Serialization/Tmx/TestData/Map/map-with-object-template.cs @@ -1,6 +1,6 @@ namespace DotTiled.Tests; -public partial class TmxSerializerMapTests +public partial class TmxMapReaderTests { private static Map MapWithObjectTemplate() => new Map { diff --git a/DotTiled.Tests/TmxSerializer/TestData/Map/map-with-object-template.tmx b/DotTiled.Tests/Serialization/Tmx/TestData/Map/map-with-object-template.tmx similarity index 100% rename from DotTiled.Tests/TmxSerializer/TestData/Map/map-with-object-template.tmx rename to DotTiled.Tests/Serialization/Tmx/TestData/Map/map-with-object-template.tmx diff --git a/DotTiled.Tests/TmxSerializer/TestData/Map/simple-tileset-embed.cs b/DotTiled.Tests/Serialization/Tmx/TestData/Map/simple-tileset-embed.cs similarity index 97% rename from DotTiled.Tests/TmxSerializer/TestData/Map/simple-tileset-embed.cs rename to DotTiled.Tests/Serialization/Tmx/TestData/Map/simple-tileset-embed.cs index ad27d8e..7dfb740 100644 --- a/DotTiled.Tests/TmxSerializer/TestData/Map/simple-tileset-embed.cs +++ b/DotTiled.Tests/Serialization/Tmx/TestData/Map/simple-tileset-embed.cs @@ -1,6 +1,6 @@ namespace DotTiled.Tests; -public partial class TmxSerializerMapTests +public partial class TmxMapReaderTests { private static Map SimpleMapWithEmbeddedTileset() => new Map { diff --git a/DotTiled.Tests/TmxSerializer/TestData/Map/simple-tileset-embed.tmx b/DotTiled.Tests/Serialization/Tmx/TestData/Map/simple-tileset-embed.tmx similarity index 100% rename from DotTiled.Tests/TmxSerializer/TestData/Map/simple-tileset-embed.tmx rename to DotTiled.Tests/Serialization/Tmx/TestData/Map/simple-tileset-embed.tmx diff --git a/DotTiled.Tests/TmxSerializer/TestData/Template/map-with-object-template.tx b/DotTiled.Tests/Serialization/Tmx/TestData/Template/map-with-object-template.tx similarity index 100% rename from DotTiled.Tests/TmxSerializer/TestData/Template/map-with-object-template.tx rename to DotTiled.Tests/Serialization/Tmx/TestData/Template/map-with-object-template.tx diff --git a/DotTiled.Tests/TmxSerializer/TestData/TestData.cs b/DotTiled.Tests/Serialization/Tmx/TestData/TestData.cs similarity index 70% rename from DotTiled.Tests/TmxSerializer/TestData/TestData.cs rename to DotTiled.Tests/Serialization/Tmx/TestData/TestData.cs index 7dbc2ad..667fec0 100644 --- a/DotTiled.Tests/TmxSerializer/TestData/TestData.cs +++ b/DotTiled.Tests/Serialization/Tmx/TestData/TestData.cs @@ -2,12 +2,12 @@ using System.Xml; namespace DotTiled.Tests; -public static class TmxSerializerTestData +public static class TmxMapReaderTestData { - public static XmlReader GetReaderFor(string testDataFile) + public static XmlReader GetXmlReaderFor(string testDataFile) { var fullyQualifiedTestDataFile = $"DotTiled.Tests.{testDataFile}"; - using var stream = typeof(TmxSerializerTestData).Assembly.GetManifestResourceStream(fullyQualifiedTestDataFile) + using var stream = typeof(TmxMapReaderTestData).Assembly.GetManifestResourceStream(fullyQualifiedTestDataFile) ?? throw new ArgumentException($"Test data file '{fullyQualifiedTestDataFile}' not found"); using var stringReader = new StreamReader(stream); @@ -19,7 +19,7 @@ public static class TmxSerializerTestData public static string GetRawStringFor(string testDataFile) { var fullyQualifiedTestDataFile = $"DotTiled.Tests.{testDataFile}"; - using var stream = typeof(TmxSerializerTestData).Assembly.GetManifestResourceStream(fullyQualifiedTestDataFile) + using var stream = typeof(TmxMapReaderTestData).Assembly.GetManifestResourceStream(fullyQualifiedTestDataFile) ?? throw new ArgumentException($"Test data file '{fullyQualifiedTestDataFile}' not found"); using var stringReader = new StreamReader(stream); diff --git a/DotTiled.Tests/Serialization/Tmx/TmxMapReaderTests.cs b/DotTiled.Tests/Serialization/Tmx/TmxMapReaderTests.cs new file mode 100644 index 0000000..1924ac2 --- /dev/null +++ b/DotTiled.Tests/Serialization/Tmx/TmxMapReaderTests.cs @@ -0,0 +1,150 @@ +using System.Xml; + +namespace DotTiled.Tests; + +public partial class TmxMapReaderTests +{ + [Fact] + public void TmxMapReaderConstructor_XmlReaderIsNull_ThrowsArgumentNullException() + { + // Arrange + XmlReader xmlReader = null!; + Func externalTilesetResolver = (_) => new Tileset(); + Func externalTemplateResolver = (_) => new Template { Object = new RectangleObject { } }; + + // Act + Action act = () => + { + using var _ = new TmxMapReader(xmlReader, externalTilesetResolver, externalTemplateResolver); + }; + + // Assert + Assert.Throws(act); + } + + [Fact] + public void TmxMapReaderConstructor_ExternalTilesetResolverIsNull_ThrowsArgumentNullException() + { + // Arrange + using var stringReader = new StringReader(""); + using var xmlReader = XmlReader.Create(stringReader); + Func externalTilesetResolver = null!; + Func externalTemplateResolver = (_) => new Template { Object = new RectangleObject { } }; + + // Act + Action act = () => + { + using var _ = new TmxMapReader(xmlReader, externalTilesetResolver, externalTemplateResolver); + }; + + // Assert + Assert.Throws(act); + } + + [Fact] + public void TmxMapReaderConstructor_ExternalTemplateResolverIsNull_ThrowsArgumentNullException() + { + // Arrange + using var stringReader = new StringReader(""); + using var xmlReader = XmlReader.Create(stringReader); + Func externalTilesetResolver = (_) => new Tileset(); + Func externalTemplateResolver = null!; + + // Act + Action act = () => + { + using var _ = new TmxMapReader(xmlReader, externalTilesetResolver, externalTemplateResolver); + }; + + // Assert + Assert.Throws(act); + } + + [Fact] + public void TmxMapReaderConstructor_NoneNull_DoesNotThrow() + { + // Arrange + using var stringReader = new StringReader(""); + using var xmlReader = XmlReader.Create(stringReader); + Func externalTilesetResolver = (_) => new Tileset(); + Func externalTemplateResolver = (_) => new Template { Object = new RectangleObject { } }; + + // Act + using var tmxMapReader = new TmxMapReader(xmlReader, externalTilesetResolver, externalTemplateResolver); + + // Assert + Assert.NotNull(tmxMapReader); + } + + public static IEnumerable DeserializeMap_ValidXmlNoExternalTilesets_ReturnsMapWithoutThrowing_Data => + [ + ["Serialization.Tmx.TestData.Map.empty-map-csv.tmx", EmptyMapWithEncodingAndCompression(DataEncoding.Csv, null)], + ["Serialization.Tmx.TestData.Map.empty-map-base64.tmx", EmptyMapWithEncodingAndCompression(DataEncoding.Base64, null)], + ["Serialization.Tmx.TestData.Map.empty-map-base64-gzip.tmx", EmptyMapWithEncodingAndCompression(DataEncoding.Base64, DataCompression.GZip)], + ["Serialization.Tmx.TestData.Map.empty-map-base64-zlib.tmx", EmptyMapWithEncodingAndCompression(DataEncoding.Base64, DataCompression.ZLib)], + ["Serialization.Tmx.TestData.Map.simple-tileset-embed.tmx", SimpleMapWithEmbeddedTileset()], + ["Serialization.Tmx.TestData.Map.empty-map-properties.tmx", EmptyMapWithProperties()], + ]; + + [Theory] + [MemberData(nameof(DeserializeMap_ValidXmlNoExternalTilesets_ReturnsMapWithoutThrowing_Data))] + public void TmxMapReaderReadMap_ValidXmlNoExternalTilesets_ReturnsMapThatEqualsExpected(string testDataFile, Map expectedMap) + { + // Arrange + using var reader = TmxMapReaderTestData.GetXmlReaderFor(testDataFile); + static Template ResolveTemplate(string source) + { + using var xmlTemplateReader = TmxMapReaderTestData.GetXmlReaderFor($"Serialization.Tmx.TestData.Template.{source}"); + using var templateReader = new TxTemplateReader(xmlTemplateReader, ResolveTileset, ResolveTemplate); + return templateReader.ReadTemplate(); + } + static Tileset ResolveTileset(string source) + { + using var xmlTilesetReader = TmxMapReaderTestData.GetXmlReaderFor($"Serialization.Tmx.TestData.Tileset.{source}"); + using var tilesetReader = new TsxTilesetReader(xmlTilesetReader, ResolveTemplate); + return tilesetReader.ReadTileset(); + } + using var mapReader = new TmxMapReader(reader, ResolveTileset, ResolveTemplate); + + // Act + var map = mapReader.ReadMap(); + + // Assert + Assert.NotNull(map); + DotTiledAssert.AssertMap(expectedMap, map); + } + + public static IEnumerable DeserializeMap_ValidXmlExternalTilesetsAndTemplates_ReturnsMapThatEqualsExpected_Data => + [ + ["Serialization.Tmx.TestData.Map.map-with-object-template.tmx", MapWithObjectTemplate()], + ["Serialization.Tmx.TestData.Map.map-with-group.tmx", MapWithGroup()], + ]; + + [Theory] + [MemberData(nameof(DeserializeMap_ValidXmlExternalTilesetsAndTemplates_ReturnsMapThatEqualsExpected_Data))] + public void TmxMapReaderReadMap_ValidXmlExternalTilesetsAndTemplates_ReturnsMapThatEqualsExpected(string testDataFile, Map expectedMap) + { + // Arrange + using var reader = TmxMapReaderTestData.GetXmlReaderFor(testDataFile); + static Template ResolveTemplate(string source) + { + using var xmlTemplateReader = TmxMapReaderTestData.GetXmlReaderFor($"Serialization.Tmx.TestData.Template.{source}"); + using var templateReader = new TxTemplateReader(xmlTemplateReader, ResolveTileset, ResolveTemplate); + return templateReader.ReadTemplate(); + } + static Tileset ResolveTileset(string source) + { + using var xmlTilesetReader = TmxMapReaderTestData.GetXmlReaderFor($"Serialization.Tmx.TestData.Tileset.{source}"); + using var tilesetReader = new TsxTilesetReader(xmlTilesetReader, ResolveTemplate); + return tilesetReader.ReadTileset(); + } + using var mapReader = new TmxMapReader(reader, ResolveTileset, ResolveTemplate); + + // Act + var map = mapReader.ReadMap(); + + // Assert + Assert.NotNull(map); + DotTiledAssert.AssertMap(expectedMap, map); + } +} diff --git a/DotTiled.Tests/TmxSerializer/TmxSerializer.MapTests.cs b/DotTiled.Tests/TmxSerializer/TmxSerializer.MapTests.cs deleted file mode 100644 index 7d513e1..0000000 --- a/DotTiled.Tests/TmxSerializer/TmxSerializer.MapTests.cs +++ /dev/null @@ -1,225 +0,0 @@ -namespace DotTiled.Tests; - -public partial class TmxSerializerMapTests -{ - private static void AssertMap(Map actual, Map expected) - { - // Attributes - Assert.Equal(expected.Version, actual.Version); - Assert.Equal(expected.TiledVersion, actual.TiledVersion); - Assert.Equal(expected.Class, actual.Class); - Assert.Equal(expected.Orientation, actual.Orientation); - Assert.Equal(expected.RenderOrder, actual.RenderOrder); - Assert.Equal(expected.CompressionLevel, actual.CompressionLevel); - Assert.Equal(expected.Width, actual.Width); - Assert.Equal(expected.Height, actual.Height); - Assert.Equal(expected.TileWidth, actual.TileWidth); - Assert.Equal(expected.TileHeight, actual.TileHeight); - Assert.Equal(expected.HexSideLength, actual.HexSideLength); - Assert.Equal(expected.StaggerAxis, actual.StaggerAxis); - Assert.Equal(expected.StaggerIndex, actual.StaggerIndex); - Assert.Equal(expected.ParallaxOriginX, actual.ParallaxOriginX); - Assert.Equal(expected.ParallaxOriginY, actual.ParallaxOriginY); - Assert.Equal(expected.BackgroundColor, actual.BackgroundColor); - Assert.Equal(expected.NextLayerID, actual.NextLayerID); - Assert.Equal(expected.NextObjectID, actual.NextObjectID); - Assert.Equal(expected.Infinite, actual.Infinite); - - TmxSerializerPropertiesTests.AssertProperties(actual.Properties, expected.Properties); - - Assert.NotNull(actual.Tilesets); - Assert.Equal(expected.Tilesets.Count, actual.Tilesets.Count); - for (var i = 0; i < expected.Tilesets.Count; i++) - TmxSerializerTilesetTests.AssertTileset(actual.Tilesets[i], expected.Tilesets[i]); - - Assert.NotNull(actual.Layers); - Assert.Equal(expected.Layers.Count, actual.Layers.Count); - for (var i = 0; i < expected.Layers.Count; i++) - TmxSerializerLayerTests.AssertLayer(actual.Layers[i], expected.Layers[i]); - } - - public static IEnumerable DeserializeMap_ValidXmlNoExternalTilesets_ReturnsMapWithoutThrowing_Data => - [ - ["TmxSerializer.TestData.Map.empty-map-csv.tmx", EmptyMapWithEncodingAndCompression(DataEncoding.Csv, null)], - ["TmxSerializer.TestData.Map.empty-map-base64.tmx", EmptyMapWithEncodingAndCompression(DataEncoding.Base64, null)], - ["TmxSerializer.TestData.Map.empty-map-base64-gzip.tmx", EmptyMapWithEncodingAndCompression(DataEncoding.Base64, DataCompression.GZip)], - ["TmxSerializer.TestData.Map.empty-map-base64-zlib.tmx", EmptyMapWithEncodingAndCompression(DataEncoding.Base64, DataCompression.ZLib)], - ["TmxSerializer.TestData.Map.simple-tileset-embed.tmx", SimpleMapWithEmbeddedTileset()], - ["TmxSerializer.TestData.Map.empty-map-properties.tmx", EmptyMapWithProperties()], - ]; - - [Theory] - [MemberData(nameof(DeserializeMap_ValidXmlNoExternalTilesets_ReturnsMapWithoutThrowing_Data))] - public void DeserializeMapFromXmlReader_ValidXmlNoExternalTilesets_ReturnsMapThatEqualsExpected(string testDataFile, Map expectedMap) - { - // Arrange - using var reader = TmxSerializerTestData.GetReaderFor(testDataFile); - var testDataFileText = TmxSerializerTestData.GetRawStringFor(testDataFile); - Func externalTilesetResolver = (TmxSerializer serializer, string s) => - throw new NotSupportedException("External tilesets are not supported in this test"); - Func externalTemplateResolver = (TmxSerializer serializer, string s) => - throw new NotSupportedException("External templates are not supported in this test"); - var tmxSerializer = new TmxSerializer( - externalTilesetResolver, - externalTemplateResolver); - - // Act - var map = tmxSerializer.DeserializeMap(reader); - var raw = tmxSerializer.DeserializeMap(testDataFileText); - - // Assert - Assert.NotNull(map); - AssertMap(map, expectedMap); - - Assert.NotNull(raw); - AssertMap(raw, expectedMap); - - AssertMap(map, raw); - } - - [Theory] - [MemberData(nameof(DeserializeMap_ValidXmlNoExternalTilesets_ReturnsMapWithoutThrowing_Data))] - public void DeserializeMapFromString_ValidXmlNoExternalTilesets_ReturnsMapThatEqualsExpected(string testDataFile, Map expectedMap) - { - // Arrange - var testDataFileText = TmxSerializerTestData.GetRawStringFor(testDataFile); - Func externalTilesetResolver = (TmxSerializer serializer, string s) => - throw new NotSupportedException("External tilesets are not supported in this test"); - Func externalTemplateResolver = (TmxSerializer serializer, string s) => - throw new NotSupportedException("External templates are not supported in this test"); - var tmxSerializer = new TmxSerializer( - externalTilesetResolver, - externalTemplateResolver); - - // Act - var raw = tmxSerializer.DeserializeMap(testDataFileText); - - // Assert - Assert.NotNull(raw); - AssertMap(raw, expectedMap); - } - - [Theory] - [MemberData(nameof(DeserializeMap_ValidXmlNoExternalTilesets_ReturnsMapWithoutThrowing_Data))] - public void DeserializeMapFromStringFromXmlReader_ValidXmlNoExternalTilesets_Equal(string testDataFile, Map expectedMap) - { - // Arrange - using var reader = TmxSerializerTestData.GetReaderFor(testDataFile); - var testDataFileText = TmxSerializerTestData.GetRawStringFor(testDataFile); - Func externalTilesetResolver = (TmxSerializer serializer, string s) => - throw new NotSupportedException("External tilesets are not supported in this test"); - Func externalTemplateResolver = (TmxSerializer serializer, string s) => - throw new NotSupportedException("External templates are not supported in this test"); - var tmxSerializer = new TmxSerializer( - externalTilesetResolver, - externalTemplateResolver); - - // Act - var map = tmxSerializer.DeserializeMap(reader); - var raw = tmxSerializer.DeserializeMap(testDataFileText); - - // Assert - Assert.NotNull(map); - Assert.NotNull(raw); - - AssertMap(map, raw); - AssertMap(map, expectedMap); - AssertMap(raw, expectedMap); - } - - public static IEnumerable DeserializeMap_ValidXmlExternalTilesetsAndTemplates_ReturnsMapThatEqualsExpected_Data => - [ - ["TmxSerializer.TestData.Map.map-with-object-template.tmx", MapWithObjectTemplate()], - ["TmxSerializer.TestData.Map.map-with-group.tmx", MapWithGroup()], - ]; - - [Theory] - [MemberData(nameof(DeserializeMap_ValidXmlExternalTilesetsAndTemplates_ReturnsMapThatEqualsExpected_Data))] - public void DeserializeMapFromXmlReader_ValidXmlExternalTilesetsAndTemplates_ReturnsMapThatEqualsExpected(string testDataFile, Map expectedMap) - { - // Arrange - using var reader = TmxSerializerTestData.GetReaderFor(testDataFile); - Func externalTilesetResolver = (TmxSerializer serializer, string s) => - { - using var tilesetReader = TmxSerializerTestData.GetReaderFor($"TmxSerializer.TestData.Tileset.{s}"); - return serializer.DeserializeTileset(tilesetReader); - }; - Func externalTemplateResolver = (TmxSerializer serializer, string s) => - { - using var templateReader = TmxSerializerTestData.GetReaderFor($"TmxSerializer.TestData.Template.{s}"); - return serializer.DeserializeTemplate(templateReader); - }; - var tmxSerializer = new TmxSerializer( - externalTilesetResolver, - externalTemplateResolver); - - // Act - var map = tmxSerializer.DeserializeMap(reader); - - // Assert - Assert.NotNull(map); - AssertMap(map, expectedMap); - } - - [Theory] - [MemberData(nameof(DeserializeMap_ValidXmlExternalTilesetsAndTemplates_ReturnsMapThatEqualsExpected_Data))] - public void DeserializeMapFromString_ValidXmlExternalTilesetsAndTemplates_ReturnsMapThatEqualsExpected(string testDataFile, Map expectedMap) - { - // Arrange - var testDataFileText = TmxSerializerTestData.GetRawStringFor(testDataFile); - Func externalTilesetResolver = (TmxSerializer serializer, string s) => - { - using var tilesetReader = TmxSerializerTestData.GetReaderFor($"TmxSerializer.TestData.Tileset.{s}"); - return serializer.DeserializeTileset(tilesetReader); - }; - Func externalTemplateResolver = (TmxSerializer serializer, string s) => - { - using var templateReader = TmxSerializerTestData.GetReaderFor($"TmxSerializer.TestData.Template.{s}"); - return serializer.DeserializeTemplate(templateReader); - }; - var tmxSerializer = new TmxSerializer( - externalTilesetResolver, - externalTemplateResolver); - - // Act - var map = tmxSerializer.DeserializeMap(testDataFileText); - - // Assert - Assert.NotNull(map); - AssertMap(map, expectedMap); - } - - [Theory] - [MemberData(nameof(DeserializeMap_ValidXmlExternalTilesetsAndTemplates_ReturnsMapThatEqualsExpected_Data))] - public void DeserializeMapFromStringFromXmlReader_ValidXmlExternalTilesetsAndTemplates_Equal(string testDataFile, Map expectedMap) - { - // Arrange - using var reader = TmxSerializerTestData.GetReaderFor(testDataFile); - var testDataFileText = TmxSerializerTestData.GetRawStringFor(testDataFile); - Func externalTilesetResolver = (TmxSerializer serializer, string s) => - { - using var tilesetReader = TmxSerializerTestData.GetReaderFor($"TmxSerializer.TestData.Tileset.{s}"); - return serializer.DeserializeTileset(tilesetReader); - }; - Func externalTemplateResolver = (TmxSerializer serializer, string s) => - { - using var templateReader = TmxSerializerTestData.GetReaderFor($"TmxSerializer.TestData.Template.{s}"); - return serializer.DeserializeTemplate(templateReader); - }; - var tmxSerializer = new TmxSerializer( - externalTilesetResolver, - externalTemplateResolver); - - // Act - var map = tmxSerializer.DeserializeMap(reader); - var raw = tmxSerializer.DeserializeMap(testDataFileText); - - // Assert - Assert.NotNull(map); - Assert.NotNull(raw); - - AssertMap(map, raw); - AssertMap(map, expectedMap); - AssertMap(raw, expectedMap); - } -} diff --git a/DotTiled.Tests/TmxSerializer/TmxSerializerTests.cs b/DotTiled.Tests/TmxSerializer/TmxSerializerTests.cs deleted file mode 100644 index 0c27cf1..0000000 --- a/DotTiled.Tests/TmxSerializer/TmxSerializerTests.cs +++ /dev/null @@ -1,32 +0,0 @@ -namespace DotTiled.Tests; - -public class TmxSerializerTests -{ - [Fact] - public void TmxSerializerConstructor_ExternalTilesetResolverIsNull_ThrowsArgumentNullException() - { - // Arrange - Func externalTilesetResolver = null!; - Func externalTemplateResolver = null!; - - // Act - Action act = () => _ = new TmxSerializer(externalTilesetResolver, externalTemplateResolver); - - // Assert - Assert.Throws(act); - } - - [Fact] - public void TmxSerializerConstructor_ExternalTilesetResolverIsNotNull_DoesNotThrow() - { - // Arrange - Func externalTilesetResolver = (_, _) => new Tileset(); - Func externalTemplateResolver = (_, _) => new Template { Object = new RectangleObject { } }; - - // Act - var tmxSerializer = new TmxSerializer(externalTilesetResolver, externalTemplateResolver); - - // Assert - Assert.NotNull(tmxSerializer); - } -} diff --git a/DotTiled/Model/Map.cs b/DotTiled/Model/Map.cs index 99868b1..246f21c 100644 --- a/DotTiled/Model/Map.cs +++ b/DotTiled/Model/Map.cs @@ -60,5 +60,5 @@ public class Map // Any number of public List Tilesets { get; set; } = []; public List Layers { get; set; } = []; - // public List Groups { get; set; } = []; + public List Groups { get; set; } = []; } diff --git a/DotTiled/Serialization/IMapReader.cs b/DotTiled/Serialization/IMapReader.cs new file mode 100644 index 0000000..97c1fd7 --- /dev/null +++ b/DotTiled/Serialization/IMapReader.cs @@ -0,0 +1,8 @@ +using System; + +namespace DotTiled; + +public interface IMapReader : IDisposable +{ + Map ReadMap(); +} diff --git a/DotTiled/Serialization/ITemplateReader.cs b/DotTiled/Serialization/ITemplateReader.cs new file mode 100644 index 0000000..8de77bc --- /dev/null +++ b/DotTiled/Serialization/ITemplateReader.cs @@ -0,0 +1,8 @@ +using System; + +namespace DotTiled; + +public interface ITemplateReader : IDisposable +{ + Template ReadTemplate(); +} diff --git a/DotTiled/Serialization/ITilesetReader.cs b/DotTiled/Serialization/ITilesetReader.cs new file mode 100644 index 0000000..37f5257 --- /dev/null +++ b/DotTiled/Serialization/ITilesetReader.cs @@ -0,0 +1,8 @@ +using System; + +namespace DotTiled; + +public interface ITilesetReader : IDisposable +{ + Tileset ReadTileset(); +} diff --git a/DotTiled/TmxSerializer/ExtensionsXmlReader.cs b/DotTiled/Serialization/Tmx/ExtensionsXmlReader.cs similarity index 100% rename from DotTiled/TmxSerializer/ExtensionsXmlReader.cs rename to DotTiled/Serialization/Tmx/ExtensionsXmlReader.cs diff --git a/DotTiled/TmxSerializer/TmxSerializer.Chunk.cs b/DotTiled/Serialization/Tmx/Tmx.Chunk.cs similarity index 89% rename from DotTiled/TmxSerializer/TmxSerializer.Chunk.cs rename to DotTiled/Serialization/Tmx/Tmx.Chunk.cs index 47a2cb2..9d06082 100644 --- a/DotTiled/TmxSerializer/TmxSerializer.Chunk.cs +++ b/DotTiled/Serialization/Tmx/Tmx.Chunk.cs @@ -2,9 +2,9 @@ using System.Xml; namespace DotTiled; -public partial class TmxSerializer +internal partial class Tmx { - private Chunk ReadChunk(XmlReader reader, DataEncoding? encoding, DataCompression? compression) + internal static Chunk ReadChunk(XmlReader reader, DataEncoding? encoding, DataCompression? compression) { var x = reader.GetRequiredAttributeParseable("x"); var y = reader.GetRequiredAttributeParseable("y"); diff --git a/DotTiled/TmxSerializer/TmxSerializer.Data.cs b/DotTiled/Serialization/Tmx/Tmx.Data.cs similarity index 85% rename from DotTiled/TmxSerializer/TmxSerializer.Data.cs rename to DotTiled/Serialization/Tmx/Tmx.Data.cs index 4725614..85598e0 100644 --- a/DotTiled/TmxSerializer/TmxSerializer.Data.cs +++ b/DotTiled/Serialization/Tmx/Tmx.Data.cs @@ -7,9 +7,9 @@ using System.Xml; namespace DotTiled; -public partial class TmxSerializer +internal partial class Tmx { - private Data ReadData(XmlReader reader, bool usesChunks) + internal static Data ReadData(XmlReader reader, bool usesChunks) { var encoding = reader.GetOptionalAttributeEnum("encoding", e => e switch { @@ -46,7 +46,7 @@ public partial class TmxSerializer return new Data { Encoding = encoding, Compression = compression, GlobalTileIDs = rawDataGlobalTileIDs, FlippingFlags = rawDataFlippingFlags, Chunks = null }; } - private (uint[] GlobalTileIDs, FlippingFlags[] FlippingFlags) ReadAndClearFlippingFlagsFromGIDs(uint[] globalTileIDs) + internal static (uint[] GlobalTileIDs, FlippingFlags[] FlippingFlags) ReadAndClearFlippingFlagsFromGIDs(uint[] globalTileIDs) { var clearedGlobalTileIDs = new uint[globalTileIDs.Length]; var flippingFlags = new FlippingFlags[globalTileIDs.Length]; @@ -61,12 +61,12 @@ public partial class TmxSerializer return (clearedGlobalTileIDs, flippingFlags); } - private uint[] ReadTileChildrenInWrapper(string wrapper, XmlReader reader) + internal static uint[] ReadTileChildrenInWrapper(string wrapper, XmlReader reader) { return reader.ReadList(wrapper, "tile", (r) => r.GetOptionalAttributeParseable("gid") ?? 0).ToArray(); } - private uint[] ReadRawData(XmlReader reader, DataEncoding encoding, DataCompression? compression) + internal static uint[] ReadRawData(XmlReader reader, DataEncoding encoding, DataCompression? compression) { var data = reader.ReadElementContentAsString(); if (encoding == DataEncoding.Csv) @@ -87,7 +87,7 @@ public partial class TmxSerializer return decompressed; } - private uint[] ParseCsvData(string data) + internal static uint[] ParseCsvData(string data) { var values = data .Split((char[])['\n', '\r', ','], StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) @@ -96,7 +96,7 @@ public partial class TmxSerializer return values; } - private uint[] ReadMemoryStreamAsInt32Array(Stream stream) + internal static uint[] ReadMemoryStreamAsInt32Array(Stream stream) { var finalValues = new List(); var int32Bytes = new byte[4]; @@ -108,13 +108,13 @@ public partial class TmxSerializer return finalValues.ToArray(); } - private uint[] DecompressGZip(MemoryStream stream) + internal static uint[] DecompressGZip(MemoryStream stream) { using var decompressedStream = new GZipStream(stream, CompressionMode.Decompress); return ReadMemoryStreamAsInt32Array(decompressedStream); } - private uint[] DecompressZLib(MemoryStream stream) + internal static uint[] DecompressZLib(MemoryStream stream) { using var decompressedStream = new ZLibStream(stream, CompressionMode.Decompress); return ReadMemoryStreamAsInt32Array(decompressedStream); diff --git a/DotTiled/TmxSerializer/TmxSerializer.Helpers.cs b/DotTiled/Serialization/Tmx/Tmx.Helpers.cs similarity index 94% rename from DotTiled/TmxSerializer/TmxSerializer.Helpers.cs rename to DotTiled/Serialization/Tmx/Tmx.Helpers.cs index 14fcfaa..bfc04f4 100644 --- a/DotTiled/TmxSerializer/TmxSerializer.Helpers.cs +++ b/DotTiled/Serialization/Tmx/Tmx.Helpers.cs @@ -2,7 +2,7 @@ using System; namespace DotTiled; -public partial class TmxSerializer +internal partial class Tmx { private static class Helpers { diff --git a/DotTiled/TmxSerializer/TmxSerializer.Map.cs b/DotTiled/Serialization/Tmx/Tmx.Map.cs similarity index 89% rename from DotTiled/TmxSerializer/TmxSerializer.Map.cs rename to DotTiled/Serialization/Tmx/Tmx.Map.cs index 799b217..d43d0b1 100644 --- a/DotTiled/TmxSerializer/TmxSerializer.Map.cs +++ b/DotTiled/Serialization/Tmx/Tmx.Map.cs @@ -6,9 +6,9 @@ using System.Xml; namespace DotTiled; -public partial class TmxSerializer +internal partial class Tmx { - private Map ReadMap(XmlReader reader) + internal static Map ReadMap(XmlReader reader, Func externalTilesetResolver, Func externalTemplateResolver) { // Attributes var version = reader.GetRequiredAttribute("version"); @@ -65,11 +65,11 @@ public partial class TmxSerializer reader.ProcessChildren("map", (r, elementName) => elementName switch { "properties" => () => Helpers.SetAtMostOnce(ref properties, ReadProperties(r), "Properties"), - "tileset" => () => tilesets.Add(ReadTileset(r)), + "tileset" => () => tilesets.Add(ReadTileset(r, externalTilesetResolver, externalTemplateResolver)), "layer" => () => layers.Add(ReadTileLayer(r, dataUsesChunks: infinite)), - "objectgroup" => () => layers.Add(ReadObjectLayer(r)), + "objectgroup" => () => layers.Add(ReadObjectLayer(r, externalTemplateResolver)), "imagelayer" => () => layers.Add(ReadImageLayer(r)), - "group" => () => layers.Add(ReadGroup(r)), + "group" => () => layers.Add(ReadGroup(r, externalTemplateResolver)), _ => r.Skip }); diff --git a/DotTiled/TmxSerializer/TmxSerializer.ObjectLayer.cs b/DotTiled/Serialization/Tmx/Tmx.ObjectLayer.cs similarity index 89% rename from DotTiled/TmxSerializer/TmxSerializer.ObjectLayer.cs rename to DotTiled/Serialization/Tmx/Tmx.ObjectLayer.cs index df97300..2c52429 100644 --- a/DotTiled/TmxSerializer/TmxSerializer.ObjectLayer.cs +++ b/DotTiled/Serialization/Tmx/Tmx.ObjectLayer.cs @@ -7,9 +7,9 @@ using System.Xml; namespace DotTiled; -public partial class TmxSerializer +internal partial class Tmx { - private ObjectLayer ReadObjectLayer(XmlReader reader) + internal static ObjectLayer ReadObjectLayer(XmlReader reader, Func externalTemplateResolver) { // Attributes var id = reader.GetRequiredAttributeParseable("id"); @@ -41,7 +41,7 @@ public partial class TmxSerializer reader.ProcessChildren("objectgroup", (r, elementName) => elementName switch { "properties" => () => Helpers.SetAtMostOnce(ref properties, ReadProperties(r), "Properties"), - "object" => () => objects.Add(ReadObject(r)), + "object" => () => objects.Add(ReadObject(r, externalTemplateResolver)), _ => r.Skip }); @@ -68,7 +68,7 @@ public partial class TmxSerializer }; } - private Object ReadObject(XmlReader reader) + internal static Object ReadObject(XmlReader reader, Func externalTemplateResolver) { // Attributes var template = reader.GetOptionalAttribute("template"); @@ -88,7 +88,7 @@ public partial class TmxSerializer // Perform template copy first if (template is not null) { - var resolvedTemplate = _externalTemplateResolver(this, template); + var resolvedTemplate = externalTemplateResolver(template); var templObj = resolvedTemplate.Object; idDefault = templObj.ID; @@ -152,7 +152,7 @@ public partial class TmxSerializer return obj; } - private Dictionary MergeProperties(Dictionary? baseProperties, Dictionary overrideProperties) + internal static Dictionary MergeProperties(Dictionary? baseProperties, Dictionary overrideProperties) { if (baseProperties is null) return overrideProperties ?? new Dictionary(); @@ -184,19 +184,19 @@ public partial class TmxSerializer return result; } - private EllipseObject ReadEllipseObject(XmlReader reader) + internal static EllipseObject ReadEllipseObject(XmlReader reader) { reader.Skip(); return new EllipseObject { }; } - private PointObject ReadPointObject(XmlReader reader) + internal static PointObject ReadPointObject(XmlReader reader) { reader.Skip(); return new PointObject { }; } - private PolygonObject ReadPolygonObject(XmlReader reader) + internal static PolygonObject ReadPolygonObject(XmlReader reader) { // Attributes var points = reader.GetRequiredAttributeParseable>("points", s => @@ -214,7 +214,7 @@ public partial class TmxSerializer return new PolygonObject { Points = points }; } - private PolylineObject ReadPolylineObject(XmlReader reader) + internal static PolylineObject ReadPolylineObject(XmlReader reader) { // Attributes var points = reader.GetRequiredAttributeParseable>("points", s => @@ -232,7 +232,7 @@ public partial class TmxSerializer return new PolylineObject { Points = points }; } - private TextObject ReadTextObject(XmlReader reader) + internal static TextObject ReadTextObject(XmlReader reader) { // Attributes var fontFamily = reader.GetOptionalAttribute("fontfamily") ?? "sans-serif"; @@ -280,7 +280,7 @@ public partial class TmxSerializer }; } - private Template ReadTemplate(XmlReader reader) + internal static Template ReadTemplate(XmlReader reader, Func externalTilesetResolver, Func externalTemplateResolver) { // No attributes @@ -292,8 +292,8 @@ public partial class TmxSerializer reader.ProcessChildren("template", (r, elementName) => elementName switch { - "tileset" => () => Helpers.SetAtMostOnce(ref tileset, ReadTileset(r), "Tileset"), - "object" => () => Helpers.SetAtMostOnce(ref obj, ReadObject(r), "Object"), + "tileset" => () => Helpers.SetAtMostOnce(ref tileset, ReadTileset(r, externalTilesetResolver, externalTemplateResolver), "Tileset"), + "object" => () => Helpers.SetAtMostOnce(ref obj, ReadObject(r, externalTemplateResolver), "Object"), _ => r.Skip }); diff --git a/DotTiled/TmxSerializer/TmxSerializer.Properties.cs b/DotTiled/Serialization/Tmx/Tmx.Properties.cs similarity index 92% rename from DotTiled/TmxSerializer/TmxSerializer.Properties.cs rename to DotTiled/Serialization/Tmx/Tmx.Properties.cs index 1e7cd7e..5609a85 100644 --- a/DotTiled/TmxSerializer/TmxSerializer.Properties.cs +++ b/DotTiled/Serialization/Tmx/Tmx.Properties.cs @@ -4,9 +4,9 @@ using System.Xml; namespace DotTiled; -public partial class TmxSerializer +internal partial class Tmx { - private Dictionary ReadProperties(XmlReader reader) + internal static Dictionary ReadProperties(XmlReader reader) { return reader.ReadList("properties", "property", (r) => { @@ -40,7 +40,7 @@ public partial class TmxSerializer }).ToDictionary(x => x.name, x => x.property); } - private ClassProperty ReadClassProperty(XmlReader reader) + internal static ClassProperty ReadClassProperty(XmlReader reader) { var name = reader.GetRequiredAttribute("name"); var propertyType = reader.GetRequiredAttribute("propertytype"); diff --git a/DotTiled/TmxSerializer/TmxSerializer.TileLayer.cs b/DotTiled/Serialization/Tmx/Tmx.TileLayer.cs similarity index 92% rename from DotTiled/TmxSerializer/TmxSerializer.TileLayer.cs rename to DotTiled/Serialization/Tmx/Tmx.TileLayer.cs index bd23d1f..e162542 100644 --- a/DotTiled/TmxSerializer/TmxSerializer.TileLayer.cs +++ b/DotTiled/Serialization/Tmx/Tmx.TileLayer.cs @@ -5,9 +5,9 @@ using System.Xml; namespace DotTiled; -public partial class TmxSerializer +internal partial class Tmx { - private TileLayer ReadTileLayer(XmlReader reader, bool dataUsesChunks) + internal static TileLayer ReadTileLayer(XmlReader reader, bool dataUsesChunks) { var id = reader.GetRequiredAttributeParseable("id"); var name = reader.GetOptionalAttribute("name") ?? ""; @@ -55,7 +55,7 @@ public partial class TmxSerializer }; } - private ImageLayer ReadImageLayer(XmlReader reader) + internal static ImageLayer ReadImageLayer(XmlReader reader) { var id = reader.GetRequiredAttributeParseable("id"); var name = reader.GetOptionalAttribute("name") ?? ""; @@ -103,7 +103,7 @@ public partial class TmxSerializer }; } - private Group ReadGroup(XmlReader reader) + internal static Group ReadGroup(XmlReader reader, Func externalTemplateResolver) { var id = reader.GetRequiredAttributeParseable("id"); var name = reader.GetOptionalAttribute("name") ?? ""; @@ -123,9 +123,9 @@ public partial class TmxSerializer { "properties" => () => Helpers.SetAtMostOnce(ref properties, ReadProperties(r), "Properties"), "layer" => () => layers.Add(ReadTileLayer(r, dataUsesChunks: false)), - "objectgroup" => () => layers.Add(ReadObjectLayer(r)), + "objectgroup" => () => layers.Add(ReadObjectLayer(r, externalTemplateResolver)), "imagelayer" => () => layers.Add(ReadImageLayer(r)), - "group" => () => layers.Add(ReadGroup(r)), + "group" => () => layers.Add(ReadGroup(r, externalTemplateResolver)), _ => r.Skip }); diff --git a/DotTiled/TmxSerializer/TmxSerializer.Tileset.cs b/DotTiled/Serialization/Tmx/Tmx.Tileset.cs similarity index 90% rename from DotTiled/TmxSerializer/TmxSerializer.Tileset.cs rename to DotTiled/Serialization/Tmx/Tmx.Tileset.cs index c16e037..1996bc2 100644 --- a/DotTiled/TmxSerializer/TmxSerializer.Tileset.cs +++ b/DotTiled/Serialization/Tmx/Tmx.Tileset.cs @@ -5,9 +5,9 @@ using System.Xml; namespace DotTiled; -public partial class TmxSerializer +internal partial class Tmx { - private Tileset ReadTileset(XmlReader reader) + internal static Tileset ReadTileset(XmlReader reader, Func? externalTilesetResolver, Func externalTemplateResolver) { // Attributes var version = reader.GetOptionalAttribute("version"); @@ -66,14 +66,17 @@ public partial class TmxSerializer "properties" => () => Helpers.SetAtMostOnce(ref properties, ReadProperties(r), "Properties"), "wangsets" => () => Helpers.SetAtMostOnce(ref wangsets, ReadWangsets(r), "Wangsets"), "transformations" => () => Helpers.SetAtMostOnce(ref transformations, ReadTransformations(r), "Transformations"), - "tile" => () => tiles.Add(ReadTile(r)), + "tile" => () => tiles.Add(ReadTile(r, externalTemplateResolver)), _ => r.Skip }); // Check if tileset is referring to external file if (source is not null) { - var resolvedTileset = _externalTilesetResolver(this, source); + if (externalTilesetResolver is null) + throw new InvalidOperationException("External tileset resolver is required to resolve external tilesets."); + + var resolvedTileset = externalTilesetResolver(source); resolvedTileset.FirstGID = firstGID; resolvedTileset.Source = null; return resolvedTileset; @@ -106,7 +109,7 @@ public partial class TmxSerializer }; } - private Image ReadImage(XmlReader reader) + internal static Image ReadImage(XmlReader reader) { // Attributes var format = reader.GetOptionalAttributeEnum("format", s => s switch @@ -138,7 +141,7 @@ public partial class TmxSerializer }; } - private TileOffset ReadTileOffset(XmlReader reader) + internal static TileOffset ReadTileOffset(XmlReader reader) { // Attributes var x = reader.GetOptionalAttributeParseable("x") ?? 0f; @@ -148,7 +151,7 @@ public partial class TmxSerializer return new TileOffset { X = x, Y = y }; } - private Grid ReadGrid(XmlReader reader) + internal static Grid ReadGrid(XmlReader reader) { // Attributes var orientation = reader.GetOptionalAttributeEnum("orientation", s => s switch @@ -164,7 +167,7 @@ public partial class TmxSerializer return new Grid { Orientation = orientation, Width = width, Height = height }; } - private Transformations ReadTransformations(XmlReader reader) + internal static Transformations ReadTransformations(XmlReader reader) { // Attributes var hFlip = reader.GetOptionalAttributeParseable("hflip") ?? false; @@ -176,7 +179,7 @@ public partial class TmxSerializer return new Transformations { HFlip = hFlip, VFlip = vFlip, Rotate = rotate, PreferUntransformed = preferUntransformed }; } - private Tile ReadTile(XmlReader reader) + internal static Tile ReadTile(XmlReader reader, Func externalTemplateResolver) { // Attributes var id = reader.GetRequiredAttributeParseable("id"); @@ -197,7 +200,7 @@ public partial class TmxSerializer { "properties" => () => Helpers.SetAtMostOnce(ref properties, ReadProperties(r), "Properties"), "image" => () => Helpers.SetAtMostOnce(ref image, ReadImage(r), "Image"), - "objectgroup" => () => Helpers.SetAtMostOnce(ref objectLayer, ReadObjectLayer(r), "ObjectLayer"), + "objectgroup" => () => Helpers.SetAtMostOnce(ref objectLayer, ReadObjectLayer(r, externalTemplateResolver), "ObjectLayer"), "animation" => () => Helpers.SetAtMostOnce(ref animation, r.ReadList("animation", "frame", (ar) => { var tileID = ar.GetRequiredAttributeParseable("tileid"); @@ -223,12 +226,12 @@ public partial class TmxSerializer }; } - private List ReadWangsets(XmlReader reader) + internal static List ReadWangsets(XmlReader reader) { return reader.ReadList("wangsets", "wangset", ReadWangset); } - private Wangset ReadWangset(XmlReader reader) + internal static Wangset ReadWangset(XmlReader reader) { // Attributes var name = reader.GetRequiredAttribute("name"); @@ -262,7 +265,7 @@ public partial class TmxSerializer }; } - private WangColor ReadWangColor(XmlReader reader) + internal static WangColor ReadWangColor(XmlReader reader) { // Attributes var name = reader.GetRequiredAttribute("name"); @@ -291,7 +294,7 @@ public partial class TmxSerializer }; } - private WangTile ReadWangTile(XmlReader reader) + internal static WangTile ReadWangTile(XmlReader reader) { // Attributes var tileID = reader.GetRequiredAttributeParseable("tileid"); diff --git a/DotTiled/Serialization/Tmx/TmxMapReader.cs b/DotTiled/Serialization/Tmx/TmxMapReader.cs new file mode 100644 index 0000000..d7e631a --- /dev/null +++ b/DotTiled/Serialization/Tmx/TmxMapReader.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Xml; + +namespace DotTiled; + +public class TmxMapReader : IMapReader +{ + // External resolvers + private readonly Func _externalTilesetResolver; + private readonly Func _externalTemplateResolver; + + private readonly XmlReader _reader; + private bool disposedValue; + + public TmxMapReader(XmlReader reader, Func externalTilesetResolver, Func externalTemplateResolver) + { + _reader = reader ?? throw new ArgumentNullException(nameof(reader)); + _externalTilesetResolver = externalTilesetResolver ?? throw new ArgumentNullException(nameof(externalTilesetResolver)); + _externalTemplateResolver = externalTemplateResolver ?? throw new ArgumentNullException(nameof(externalTemplateResolver)); + + // Prepare reader + _reader.MoveToContent(); + } + + public Map ReadMap() + { + return Tmx.ReadMap(_reader, _externalTilesetResolver, _externalTemplateResolver); + } + + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + // TODO: dispose managed state (managed objects) + _reader.Dispose(); + } + + // TODO: free unmanaged resources (unmanaged objects) and override finalizer + // TODO: set large fields to null + disposedValue = true; + } + } + + // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources + // ~TmxTiledMapReader() + // { + // // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + // Dispose(disposing: false); + // } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + System.GC.SuppressFinalize(this); + } +} diff --git a/DotTiled/Serialization/Tmx/TsxTilesetReader.cs b/DotTiled/Serialization/Tmx/TsxTilesetReader.cs new file mode 100644 index 0000000..c089129 --- /dev/null +++ b/DotTiled/Serialization/Tmx/TsxTilesetReader.cs @@ -0,0 +1,50 @@ +using System; +using System.Xml; + +namespace DotTiled; + +public class TsxTilesetReader : ITilesetReader +{ + // External resolvers + private readonly Func _externalTemplateResolver; + + private readonly XmlReader _reader; + private bool disposedValue; + + public TsxTilesetReader(XmlReader reader, Func externalTemplateResolver) + { + _reader = reader ?? throw new ArgumentNullException(nameof(reader)); + _externalTemplateResolver = externalTemplateResolver ?? throw new ArgumentNullException(nameof(externalTemplateResolver)); + } + + public Tileset ReadTileset() => Tmx.ReadTileset(_reader, null, _externalTemplateResolver); + + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + // TODO: dispose managed state (managed objects) + } + + // TODO: free unmanaged resources (unmanaged objects) and override finalizer + // TODO: set large fields to null + disposedValue = true; + } + } + + // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources + // ~TsxTilesetReader() + // { + // // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + // Dispose(disposing: false); + // } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + System.GC.SuppressFinalize(this); + } +} diff --git a/DotTiled/Serialization/Tmx/TxTemplateReader.cs b/DotTiled/Serialization/Tmx/TxTemplateReader.cs new file mode 100644 index 0000000..24b95f4 --- /dev/null +++ b/DotTiled/Serialization/Tmx/TxTemplateReader.cs @@ -0,0 +1,55 @@ +using System; +using System.Xml; + +namespace DotTiled; + +public class TxTemplateReader : ITemplateReader +{ + // Resolvers + private readonly Func _externalTilesetResolver; + private readonly Func _externalTemplateResolver; + + private readonly XmlReader _reader; + private bool disposedValue; + + public TxTemplateReader(XmlReader reader, Func externalTilesetResolver, Func externalTemplateResolver) + { + _reader = reader ?? throw new ArgumentNullException(nameof(reader)); + _externalTilesetResolver = externalTilesetResolver ?? throw new ArgumentNullException(nameof(externalTilesetResolver)); + _externalTemplateResolver = externalTemplateResolver ?? throw new ArgumentNullException(nameof(externalTemplateResolver)); + + // Prepare reader + _reader.MoveToContent(); + } + + public Template ReadTemplate() => Tmx.ReadTemplate(_reader, _externalTilesetResolver, _externalTemplateResolver); + + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + // TODO: dispose managed state (managed objects) + } + + // TODO: free unmanaged resources (unmanaged objects) and override finalizer + // TODO: set large fields to null + disposedValue = true; + } + } + + // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources + // ~TxTemplateReader() + // { + // // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + // Dispose(disposing: false); + // } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + System.GC.SuppressFinalize(this); + } +} diff --git a/DotTiled/TmxSerializer/TmxSerializer.cs b/DotTiled/TmxSerializer/TmxSerializer.cs deleted file mode 100644 index 5f5c604..0000000 --- a/DotTiled/TmxSerializer/TmxSerializer.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using System.IO; -using System.Xml; - -namespace DotTiled; - -public partial class TmxSerializer -{ - private readonly Func _externalTilesetResolver; - private readonly Func _externalTemplateResolver; - - public TmxSerializer( - Func externalTilesetResolver, - Func externalTemplateResolver - ) - { - _externalTilesetResolver = externalTilesetResolver ?? throw new ArgumentNullException(nameof(externalTilesetResolver)); - _externalTemplateResolver = externalTemplateResolver ?? throw new ArgumentNullException(nameof(externalTemplateResolver)); - } - - public Map DeserializeMap(XmlReader reader) - { - reader.ReadToFollowing("map"); - return ReadMap(reader); - } - - public Map DeserializeMap(string xml) - { - using var stringReader = new StringReader(xml); - using var reader = XmlReader.Create(stringReader); - return DeserializeMap(reader); - } - - public Tileset DeserializeTileset(XmlReader reader) - { - reader.ReadToFollowing("tileset"); - return ReadTileset(reader); - } - - public Template DeserializeTemplate(XmlReader reader) - { - reader.ReadToFollowing("template"); - return ReadTemplate(reader); - } -} diff --git a/README.md b/README.md index e0a1074..2fec131 100644 --- a/README.md +++ b/README.md @@ -4,45 +4,71 @@ DotTiled is a simple and easy-to-use library for loading, saving, and managing [Tiled maps and tilesets](https://mapeditor.org) in your .NET projects. After [TiledCS](https://github.com/TheBoneJarmer/TiledCS) unfortunately became unmaintained (since 2022), I aimed to create a new library that could fill its shoes. DotTiled is the result of that effort. -- [Feature coverage](#feature-coverage) +DotTiled is designed to be a lightweight and efficient library that provides a simple API for loading and managing Tiled maps and tilesets. It is built with performance in mind and aims to be as fast and memory-efficient as possible. Targeting `netstandard2.0` and `net8.0` allows DotTiled to be used in popular game engines like Unity and Godot, as well as in popular game development frameworks like MonoGame. + - [Alternative libraries and comparison + benchmarks](#alternative-libraries-and-comparison) - [Quickstart](#quickstart) - [Installing DotTiled](#installing-dottiled) - - [Constructing a `TmxSerializer`](#constructing-a-tmxserializer) - - [Loading a `.tmx` map](#loading-a-tmx-map) - - [Loading a `.tsx` tileset](#loading-a-tsx-tileset) - - [Constructing a `TmjSerializer`](#constructing-a-tmjserializer) - - [Loading a `.tmj` map](#loading-a-tmj-map) - - [Loading a `.tsj` tileset](#loading-a-tsj-tileset) - -# Feature coverage - -**TBD**: Add a table displaying the features that DotTiled supports, and which features are not yet supported, and perhaps why they are not supported (yet or ever). # Alternative libraries and comparison Other similar libraries exist, and you may want to consider them for your project as well: -| Library | Actively maintained | Supported formats | Feature coverage | Tiled version compatibility | Docs | License | Benchmark rank* | -| --- | --- | --- | --- | --- | --- | --- | --- | -| **DotTiled** | ✅ | `.tmx` `.tsx` `.tmj` `.tsj` `.tx` | | | Usage, API, XML docs | | | -| [TiledLib](https://github.com/Ragath/TiledLib.Net) |✅| | | | | | | -| [TiledCSPlus](https://github.com/nolemretaWxd/TiledCSPlus) |✅| | | | | | | -| [TiledSharp](https://github.com/marshallward/TiledSharp) |❌| | | | | | | -| [TiledCS](https://github.com/TheBoneJarmer/TiledCS) |❌| | | | | | | -| [TiledNet](https://github.com/napen123/Tiled.Net) |❌| | | | | | | +|**Comparison**|**DotTiled**|[TiledLib](https://github.com/Ragath/TiledLib.Net)|[TiledCSPlus](https://github.com/nolemretaWxd/TiledCSPlus)|[TiledSharp](https://github.com/marshallward/TiledSharp)|[TiledCS](https://github.com/TheBoneJarmer/TiledCS)|[TiledNet](https://github.com/napen123/Tiled.Net)| +|---------------------------------|:-----------------------:|:--------:|:-----------:|:----------:|:-------:|:------:| +| Actively maintained | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | +| Benchmark (time)* | 1.00 | 1.81 | 2.12 | - | - | - | +| Benchmark (memory)* | 1.00 | 1.42 | 2.03 | - | - | - | +| .NET Targets | `net8.0`
`netstandard2.0` |`net6.0`
`net7.0`|`netstandard2.1`|`netstandard2.0`|`netstandard2.0`|`net45`| +| Docs |Usage,
XML Docs|Usage|Usage, API,
XML Docs|Usage, API|Usage, XML Docs|Usage, XML Docs| +| License | MIT | MIT | MIT | Apache-2.0 | MIT | BSD 3-Clause | +| *Feature coverage
comparison below*|✅/❌|✅/❌|✅/❌|✅/❌|✅/❌|✅/❌| > [!NOTE] -> *Benchmark rank is based on the libraries' speed and memory usage when loading different types of maps and tilesets. Further explanations and details can be found in the below collapsible section. +> *Both benchmark time and memory ratios are relative to DotTiled. Lower is better. Benchmark (time) refers to the execution time of loading the same map from an in-memory string that contains XML data in the `.tmx` format. Benchmark (memory) refers to the memory allocated during that loading process. For further details on the benchmark results, see the collapsible section below.
-Comparison and benchmark details +Feature coverage comparison -**TODO: Add table displaying feature availability** +| **Comparison**|**DotTiled**|[TiledLib](https://github.com/Ragath/TiledLib.Net)|[TiledCSPlus](https://github.com/nolemretaWxd/TiledCSPlus)|[TiledSharp](https://github.com/marshallward/TiledSharp)|[TiledCS](https://github.com/TheBoneJarmer/TiledCS)|[TiledNet](https://github.com/napen123/Tiled.Net)| +|---------------------------------|:-:|:-:|:-:|:-:|:-:|:-:| +| Full XML support `.tmx` |✅/❌|✅/❌|✅/❌|✅/❌|✅/❌|✅/❌| +| Full JSON support `.tmj` |✅/❌|✅/❌|✅/❌|✅/❌|✅/❌|✅/❌| +| Load from string (implies file) |✅/❌|✅/❌|✅/❌|✅/❌|✅/❌|✅/❌| +| Load from file |✅/❌|✅/❌|✅/❌|✅/❌|✅/❌|✅/❌| +| External tilesets |✅/❌|✅/❌|✅/❌|✅/❌|✅/❌|✅/❌| +| Template files |✅/❌|✅/❌|✅/❌|✅/❌|✅/❌|✅/❌| +| Property custom types |✅/❌|✅/❌|✅/❌|✅/❌|✅/❌|✅/❌| +| Hierarchical layers (groups) |✅/❌|✅/❌|✅/❌|✅/❌|✅/❌|✅/❌| +| Infinite maps |✅/❌|✅/❌|✅/❌|✅/❌|✅/❌|✅/❌| -**TODO: Add table displaying benchmark results** +
+ +
+ +Benchmark details + + +The following benchmark results were gathered using the `DotTiled.Benchmark` project which uses [BenchmarkDotNet](https://benchmarkdotnet.org/) to compare the performance of DotTiled with other similar libraries. The benchmark results are grouped by category and show the mean execution time, memory consumption metrics, and ratio to DotTiled. + +``` +BenchmarkDotNet v0.13.12, Windows 10 (10.0.19045.4651/22H2/2022Update) +12th Gen Intel Core i7-12700K, 1 CPU, 20 logical and 12 physical cores +.NET SDK 8.0.202 + [Host] : .NET 8.0.3 (8.0.324.11423), X64 RyuJIT AVX2 + DefaultJob : .NET 8.0.3 (8.0.324.11423), X64 RyuJIT AVX2 +``` +| Method | Categories | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Gen1 | Allocated | Alloc Ratio | +|------------ |------------------------- |----------:|----------:|----------:|------:|--------:|-------:|-------:|----------:|------------:| +| DotTiled | MapFromInMemoryTmxString | 2.991 μs | 0.0266 μs | 0.0236 μs | 1.00 | 0.00 | 1.2817 | 0.0610 | 16.37 KB | 1.00 | +| TiledLib | MapFromInMemoryTmxString | 5.405 μs | 0.0466 μs | 0.0413 μs | 1.81 | 0.02 | 1.8158 | 0.1068 | 23.32 KB | 1.42 | +| TiledCSPlus | MapFromInMemoryTmxString | 6.354 μs | 0.0703 μs | 0.0587 μs | 2.12 | 0.03 | 2.5940 | 0.1831 | 33.23 KB | 2.03 | +| | | | | | | | | | | | +| DotTiled | MapFromTmxFile | 28.570 μs | 0.1216 μs | 0.1137 μs | 1.00 | 0.00 | 1.0376 | - | 13.88 KB | 1.00 | +| TiledCSPlus | MapFromTmxFile | 33.377 μs | 0.1086 μs | 0.1016 μs | 1.17 | 0.01 | 2.8076 | 0.1221 | 36.93 KB | 2.66 | +| TiledLib | MapFromTmxFile | 36.077 μs | 0.1900 μs | 0.1777 μs | 1.26 | 0.01 | 2.0752 | 0.1221 | 27.1 KB | 1.95 |
@@ -57,55 +83,3 @@ DotTiled is available as a NuGet package. You can install it by using the NuGet ```pwsh dotnet add package DotTiled ``` - - -### Constructing a `TmxSerializer` - -There are few details to be aware of for your `TmxSerializer`: - -```csharp -// A map may or may not contain tilesets that are stored in external -// files. To deserialize a map, you must provide a way to resolve such -// tilesets. -Func tilesetResolver = - (TmxSerializer serializer, string path) => - { - string tilesetXml = fileSystem.ReadAllText(path); - return serializer.DeserializeTileset(tilesetXml); - }; - -// A map may or may not contain objects that reference template files. -// To deserialize a map, you must provide a way to resolve such templates. -Func templateResolver = - (TmxSerializer serializer, string path) => - { - string templateXml = fileSystem.ReadAllText(path); - return serializer.DeserializeTemplate(templateXml); - }; - -var tmxSerializer = new TmxSerializer(tilesetResolver, templateResolver); -``` - -### Loading a `.tmx` map - -The `TmxSerializer` has several overloads for `DeserializeMap` that allow you to load a map from a number of different sources. - -```csharp -string mapXml = fileSystem.ReadAllText("path/to/map.tmx"); -Map mapFromRaw = tmxSerializer.DeserializeMap(mapXml); // From raw XML string in memory - -using var reader = fileSystem.OpenXmlReader("path/to/map.tmx"); -Map mapFromReader = tmxSerializer.DeserializeMap(reader); // From XML reader -``` - -### Loading a `.tsx` tileset - -Similar to maps, the `TmxSerializer` has several overloads for `DeserializeTileset` that allow you to load a tileset from a number of different sources. - -```csharp -string tilesetXml = fileSystem.ReadAllText("path/to/tileset.tsx"); -Tileset tileset = tmxSerializer.DeserializeTileset(tilesetXml); // From raw XML string in memory - -using var reader = fileSystem.OpenXmlReader("path/to/tileset.tsx"); -Tileset tileset = tmxSerializer.DeserializeTileset(reader); // From XML reader -``` \ No newline at end of file