mirror of
https://github.com/dcronqvist/DotTiled.git
synced 2025-02-05 08:52:50 +02:00
No more TmxSerializer, now different readers for each file format
This commit is contained in:
parent
453200bbb2
commit
bafbd3d6c7
35 changed files with 255 additions and 1486 deletions
|
@ -1,8 +1,8 @@
|
||||||
namespace DotTiled.Tests;
|
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)
|
if (expected is null)
|
||||||
{
|
{
|
||||||
|
@ -24,11 +24,11 @@ public partial class TmxSerializerDataTests
|
||||||
Assert.NotNull(actual.Chunks);
|
Assert.NotNull(actual.Chunks);
|
||||||
Assert.Equal(expected.Chunks.Length, actual.Chunks.Length);
|
Assert.Equal(expected.Chunks.Length, actual.Chunks.Length);
|
||||||
for (var i = 0; i < expected.Chunks.Length; i++)
|
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
|
// Attributes
|
||||||
Assert.Equal(expected.X, actual.X);
|
Assert.Equal(expected.X, actual.X);
|
|
@ -1,8 +1,8 @@
|
||||||
namespace DotTiled.Tests;
|
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)
|
if (expected is null)
|
||||||
{
|
{
|
|
@ -1,8 +1,8 @@
|
||||||
namespace DotTiled.Tests;
|
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)
|
if (expected is null)
|
||||||
{
|
{
|
||||||
|
@ -23,11 +23,11 @@ public partial class TmxSerializerLayerTests
|
||||||
Assert.Equal(expected.ParallaxX, actual.ParallaxX);
|
Assert.Equal(expected.ParallaxX, actual.ParallaxX);
|
||||||
Assert.Equal(expected.ParallaxY, actual.ParallaxY);
|
Assert.Equal(expected.ParallaxY, actual.ParallaxY);
|
||||||
|
|
||||||
TmxSerializerPropertiesTests.AssertProperties(actual.Properties, expected.Properties);
|
AssertProperties(expected.Properties, actual.Properties);
|
||||||
AssertLayer((dynamic)actual, (dynamic)expected);
|
AssertLayer((dynamic)expected, (dynamic)actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AssertLayer(TileLayer actual, TileLayer expected)
|
private static void AssertLayer(TileLayer expected, TileLayer actual)
|
||||||
{
|
{
|
||||||
// Attributes
|
// Attributes
|
||||||
Assert.Equal(expected.Width, actual.Width);
|
Assert.Equal(expected.Width, actual.Width);
|
||||||
|
@ -36,10 +36,10 @@ public partial class TmxSerializerLayerTests
|
||||||
Assert.Equal(expected.Y, actual.Y);
|
Assert.Equal(expected.Y, actual.Y);
|
||||||
|
|
||||||
Assert.NotNull(actual.Data);
|
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
|
// Attributes
|
||||||
Assert.Equal(expected.DrawOrder, actual.DrawOrder);
|
Assert.Equal(expected.DrawOrder, actual.DrawOrder);
|
||||||
|
@ -49,10 +49,10 @@ public partial class TmxSerializerLayerTests
|
||||||
Assert.NotNull(actual.Objects);
|
Assert.NotNull(actual.Objects);
|
||||||
Assert.Equal(expected.Objects.Count, actual.Objects.Count);
|
Assert.Equal(expected.Objects.Count, actual.Objects.Count);
|
||||||
for (var i = 0; i < expected.Objects.Count; i++)
|
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
|
// Attributes
|
||||||
Assert.Equal(expected.RepeatX, actual.RepeatX);
|
Assert.Equal(expected.RepeatX, actual.RepeatX);
|
||||||
|
@ -61,15 +61,15 @@ public partial class TmxSerializerLayerTests
|
||||||
Assert.Equal(expected.Y, actual.Y);
|
Assert.Equal(expected.Y, actual.Y);
|
||||||
|
|
||||||
Assert.NotNull(actual.Image);
|
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
|
// Attributes
|
||||||
Assert.NotNull(actual.Layers);
|
Assert.NotNull(actual.Layers);
|
||||||
Assert.Equal(expected.Layers.Count, actual.Layers.Count);
|
Assert.Equal(expected.Layers.Count, actual.Layers.Count);
|
||||||
for (var i = 0; i < expected.Layers.Count; i++)
|
for (var i = 0; i < expected.Layers.Count; i++)
|
||||||
AssertLayer(actual.Layers[i], expected.Layers[i]);
|
AssertLayer(expected.Layers[i], actual.Layers[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
40
DotTiled.Tests/Assert/AssertMap.cs
Normal file
40
DotTiled.Tests/Assert/AssertMap.cs
Normal file
|
@ -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]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
namespace DotTiled.Tests;
|
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
|
// Attributes
|
||||||
Assert.Equal(expected.ID, actual.ID);
|
Assert.Equal(expected.ID, actual.ID);
|
||||||
|
@ -17,36 +17,36 @@ public partial class TmxSerializerObjectTests
|
||||||
Assert.Equal(expected.Visible, actual.Visible);
|
Assert.Equal(expected.Visible, actual.Visible);
|
||||||
Assert.Equal(expected.Template, actual.Template);
|
Assert.Equal(expected.Template, actual.Template);
|
||||||
|
|
||||||
TmxSerializerPropertiesTests.AssertProperties(actual.Properties, expected.Properties);
|
AssertProperties(actual.Properties, expected.Properties);
|
||||||
AssertObject((dynamic)actual, (dynamic)expected);
|
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
|
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
|
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
|
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);
|
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);
|
Assert.Equal(expected.Points, actual.Points);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AssertObject(TextObject actual, TextObject expected)
|
private static void AssertObject(TextObject expected, TextObject actual)
|
||||||
{
|
{
|
||||||
// Attributes
|
// Attributes
|
||||||
Assert.Equal(expected.FontFamily, actual.FontFamily);
|
Assert.Equal(expected.FontFamily, actual.FontFamily);
|
|
@ -1,8 +1,8 @@
|
||||||
namespace DotTiled.Tests;
|
namespace DotTiled.Tests;
|
||||||
|
|
||||||
public partial class TmxSerializerPropertiesTests
|
public static partial class DotTiledAssert
|
||||||
{
|
{
|
||||||
public static void AssertProperties(Dictionary<string, IProperty>? actual, Dictionary<string, IProperty>? expected)
|
internal static void AssertProperties(Dictionary<string, IProperty>? expected, Dictionary<string, IProperty>? actual)
|
||||||
{
|
{
|
||||||
if (expected is null)
|
if (expected is null)
|
||||||
{
|
{
|
|
@ -1,8 +1,8 @@
|
||||||
namespace DotTiled.Tests;
|
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
|
// Attributes
|
||||||
Assert.Equal(expected.Version, actual.Version);
|
Assert.Equal(expected.Version, actual.Version);
|
||||||
|
@ -22,28 +22,28 @@ public partial class TmxSerializerTilesetTests
|
||||||
Assert.Equal(expected.FillMode, actual.FillMode);
|
Assert.Equal(expected.FillMode, actual.FillMode);
|
||||||
|
|
||||||
// At most one of
|
// At most one of
|
||||||
TmxSerializerImageTests.AssertImage(actual.Image, expected.Image);
|
AssertImage(expected.Image, actual.Image);
|
||||||
AssertTileOffset(actual.TileOffset, expected.TileOffset);
|
AssertTileOffset(expected.TileOffset, actual.TileOffset);
|
||||||
AssertGrid(actual.Grid, expected.Grid);
|
AssertGrid(expected.Grid, actual.Grid);
|
||||||
TmxSerializerPropertiesTests.AssertProperties(actual.Properties, expected.Properties);
|
AssertProperties(expected.Properties, actual.Properties);
|
||||||
// TODO: AssertTerrainTypes(actual.TerrainTypes, expected.TerrainTypes);
|
// TODO: AssertTerrainTypes(actual.TerrainTypes, expected.TerrainTypes);
|
||||||
if (expected.Wangsets is not null)
|
if (expected.Wangsets is not null)
|
||||||
{
|
{
|
||||||
Assert.NotNull(actual.Wangsets);
|
Assert.NotNull(actual.Wangsets);
|
||||||
Assert.Equal(expected.Wangsets.Count, actual.Wangsets.Count);
|
Assert.Equal(expected.Wangsets.Count, actual.Wangsets.Count);
|
||||||
for (var i = 0; i < expected.Wangsets.Count; i++)
|
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
|
// Any number of
|
||||||
Assert.NotNull(actual.Tiles);
|
Assert.NotNull(actual.Tiles);
|
||||||
Assert.Equal(expected.Tiles.Count, actual.Tiles.Count);
|
Assert.Equal(expected.Tiles.Count, actual.Tiles.Count);
|
||||||
for (var i = 0; i < expected.Tiles.Count; i++)
|
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)
|
if (expected is null)
|
||||||
{
|
{
|
||||||
|
@ -57,7 +57,7 @@ public partial class TmxSerializerTilesetTests
|
||||||
Assert.Equal(expected.Y, actual.Y);
|
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)
|
if (expected is null)
|
||||||
{
|
{
|
||||||
|
@ -72,7 +72,7 @@ public partial class TmxSerializerTilesetTests
|
||||||
Assert.Equal(expected.Height, actual.Height);
|
Assert.Equal(expected.Height, actual.Height);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AssertWangset(Wangset actual, Wangset expected)
|
private static void AssertWangset(Wangset expected, Wangset actual)
|
||||||
{
|
{
|
||||||
// Attributes
|
// Attributes
|
||||||
Assert.Equal(expected.Name, actual.Name);
|
Assert.Equal(expected.Name, actual.Name);
|
||||||
|
@ -80,19 +80,19 @@ public partial class TmxSerializerTilesetTests
|
||||||
Assert.Equal(expected.Tile, actual.Tile);
|
Assert.Equal(expected.Tile, actual.Tile);
|
||||||
|
|
||||||
// At most one of
|
// At most one of
|
||||||
TmxSerializerPropertiesTests.AssertProperties(actual.Properties, expected.Properties);
|
AssertProperties(expected.Properties, actual.Properties);
|
||||||
if (expected.WangColors is not null)
|
if (expected.WangColors is not null)
|
||||||
{
|
{
|
||||||
Assert.NotNull(actual.WangColors);
|
Assert.NotNull(actual.WangColors);
|
||||||
Assert.Equal(expected.WangColors.Count, actual.WangColors.Count);
|
Assert.Equal(expected.WangColors.Count, actual.WangColors.Count);
|
||||||
for (var i = 0; i < expected.WangColors.Count; i++)
|
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++)
|
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
|
// Attributes
|
||||||
Assert.Equal(expected.Name, actual.Name);
|
Assert.Equal(expected.Name, actual.Name);
|
||||||
|
@ -101,17 +101,17 @@ public partial class TmxSerializerTilesetTests
|
||||||
Assert.Equal(expected.Tile, actual.Tile);
|
Assert.Equal(expected.Tile, actual.Tile);
|
||||||
Assert.Equal(expected.Probability, actual.Probability);
|
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
|
// Attributes
|
||||||
Assert.Equal(expected.TileID, actual.TileID);
|
Assert.Equal(expected.TileID, actual.TileID);
|
||||||
Assert.Equal(expected.WangID, actual.WangID);
|
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)
|
if (expected is null)
|
||||||
{
|
{
|
||||||
|
@ -127,7 +127,7 @@ public partial class TmxSerializerTilesetTests
|
||||||
Assert.Equal(expected.PreferUntransformed, actual.PreferUntransformed);
|
Assert.Equal(expected.PreferUntransformed, actual.PreferUntransformed);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AssertTile(Tile actual, Tile expected)
|
private static void AssertTile(Tile expected, Tile actual)
|
||||||
{
|
{
|
||||||
// Attributes
|
// Attributes
|
||||||
Assert.Equal(expected.ID, actual.ID);
|
Assert.Equal(expected.ID, actual.ID);
|
||||||
|
@ -139,19 +139,19 @@ public partial class TmxSerializerTilesetTests
|
||||||
Assert.Equal(expected.Height, actual.Height);
|
Assert.Equal(expected.Height, actual.Height);
|
||||||
|
|
||||||
// Elements
|
// Elements
|
||||||
TmxSerializerPropertiesTests.AssertProperties(actual.Properties, expected.Properties);
|
AssertProperties(actual.Properties, expected.Properties);
|
||||||
TmxSerializerImageTests.AssertImage(actual.Image, expected.Image);
|
AssertImage(actual.Image, expected.Image);
|
||||||
TmxSerializerLayerTests.AssertLayer(actual.ObjectLayer, expected.ObjectLayer);
|
AssertLayer((BaseLayer?)actual.ObjectLayer, (BaseLayer?)expected.ObjectLayer);
|
||||||
if (expected.Animation is not null)
|
if (expected.Animation is not null)
|
||||||
{
|
{
|
||||||
Assert.NotNull(actual.Animation);
|
Assert.NotNull(actual.Animation);
|
||||||
Assert.Equal(expected.Animation.Count, actual.Animation.Count);
|
Assert.Equal(expected.Animation.Count, actual.Animation.Count);
|
||||||
for (var i = 0; i < expected.Animation.Count; i++)
|
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
|
// Attributes
|
||||||
Assert.Equal(expected.TileID, actual.TileID);
|
Assert.Equal(expected.TileID, actual.TileID);
|
|
@ -26,7 +26,7 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<!-- TmxSerializer test data -->
|
<!-- TmxSerializer test data -->
|
||||||
<EmbeddedResource Include="TmxSerializer/TestData/**/*" />
|
<EmbeddedResource Include="Serialization/Tmx/TestData/**/*" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
namespace DotTiled.Tests;
|
namespace DotTiled.Tests;
|
||||||
|
|
||||||
public partial class TmxSerializerMapTests
|
public partial class TmxMapReaderTests
|
||||||
{
|
{
|
||||||
private static Map EmptyMapWithProperties() => new Map
|
private static Map EmptyMapWithProperties() => new Map
|
||||||
{
|
{
|
|
@ -1,6 +1,6 @@
|
||||||
namespace DotTiled.Tests;
|
namespace DotTiled.Tests;
|
||||||
|
|
||||||
public partial class TmxSerializerMapTests
|
public partial class TmxMapReaderTests
|
||||||
{
|
{
|
||||||
private static Map EmptyMapWithEncodingAndCompression(DataEncoding dataEncoding, DataCompression? compression) => new Map
|
private static Map EmptyMapWithEncodingAndCompression(DataEncoding dataEncoding, DataCompression? compression) => new Map
|
||||||
{
|
{
|
|
@ -1,6 +1,6 @@
|
||||||
namespace DotTiled.Tests;
|
namespace DotTiled.Tests;
|
||||||
|
|
||||||
public partial class TmxSerializerMapTests
|
public partial class TmxMapReaderTests
|
||||||
{
|
{
|
||||||
private static Map MapWithGroup() => new Map
|
private static Map MapWithGroup() => new Map
|
||||||
{
|
{
|
|
@ -1,6 +1,6 @@
|
||||||
namespace DotTiled.Tests;
|
namespace DotTiled.Tests;
|
||||||
|
|
||||||
public partial class TmxSerializerMapTests
|
public partial class TmxMapReaderTests
|
||||||
{
|
{
|
||||||
private static Map MapWithObjectTemplate() => new Map
|
private static Map MapWithObjectTemplate() => new Map
|
||||||
{
|
{
|
|
@ -1,6 +1,6 @@
|
||||||
namespace DotTiled.Tests;
|
namespace DotTiled.Tests;
|
||||||
|
|
||||||
public partial class TmxSerializerMapTests
|
public partial class TmxMapReaderTests
|
||||||
{
|
{
|
||||||
private static Map SimpleMapWithEmbeddedTileset() => new Map
|
private static Map SimpleMapWithEmbeddedTileset() => new Map
|
||||||
{
|
{
|
|
@ -2,12 +2,12 @@ using System.Xml;
|
||||||
|
|
||||||
namespace DotTiled.Tests;
|
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}";
|
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");
|
?? throw new ArgumentException($"Test data file '{fullyQualifiedTestDataFile}' not found");
|
||||||
|
|
||||||
using var stringReader = new StreamReader(stream);
|
using var stringReader = new StreamReader(stream);
|
||||||
|
@ -19,7 +19,7 @@ public static class TmxSerializerTestData
|
||||||
public static string GetRawStringFor(string testDataFile)
|
public static string GetRawStringFor(string testDataFile)
|
||||||
{
|
{
|
||||||
var fullyQualifiedTestDataFile = $"DotTiled.Tests.{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");
|
?? throw new ArgumentException($"Test data file '{fullyQualifiedTestDataFile}' not found");
|
||||||
|
|
||||||
using var stringReader = new StreamReader(stream);
|
using var stringReader = new StreamReader(stream);
|
150
DotTiled.Tests/Serialization/Tmx/TmxMapReaderTests.cs
Normal file
150
DotTiled.Tests/Serialization/Tmx/TmxMapReaderTests.cs
Normal file
|
@ -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<string, Tileset> externalTilesetResolver = (_) => new Tileset();
|
||||||
|
Func<string, Template> externalTemplateResolver = (_) => new Template { Object = new RectangleObject { } };
|
||||||
|
|
||||||
|
// Act
|
||||||
|
Action act = () =>
|
||||||
|
{
|
||||||
|
using var _ = new TmxMapReader(xmlReader, externalTilesetResolver, externalTemplateResolver);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Throws<ArgumentNullException>(act);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TmxMapReaderConstructor_ExternalTilesetResolverIsNull_ThrowsArgumentNullException()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
using var stringReader = new StringReader("<map></map>");
|
||||||
|
using var xmlReader = XmlReader.Create(stringReader);
|
||||||
|
Func<string, Tileset> externalTilesetResolver = null!;
|
||||||
|
Func<string, Template> externalTemplateResolver = (_) => new Template { Object = new RectangleObject { } };
|
||||||
|
|
||||||
|
// Act
|
||||||
|
Action act = () =>
|
||||||
|
{
|
||||||
|
using var _ = new TmxMapReader(xmlReader, externalTilesetResolver, externalTemplateResolver);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Throws<ArgumentNullException>(act);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TmxMapReaderConstructor_ExternalTemplateResolverIsNull_ThrowsArgumentNullException()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
using var stringReader = new StringReader("<map></map>");
|
||||||
|
using var xmlReader = XmlReader.Create(stringReader);
|
||||||
|
Func<string, Tileset> externalTilesetResolver = (_) => new Tileset();
|
||||||
|
Func<string, Template> externalTemplateResolver = null!;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
Action act = () =>
|
||||||
|
{
|
||||||
|
using var _ = new TmxMapReader(xmlReader, externalTilesetResolver, externalTemplateResolver);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Throws<ArgumentNullException>(act);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TmxMapReaderConstructor_NoneNull_DoesNotThrow()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
using var stringReader = new StringReader("<map></map>");
|
||||||
|
using var xmlReader = XmlReader.Create(stringReader);
|
||||||
|
Func<string, Tileset> externalTilesetResolver = (_) => new Tileset();
|
||||||
|
Func<string, Template> externalTemplateResolver = (_) => new Template { Object = new RectangleObject { } };
|
||||||
|
|
||||||
|
// Act
|
||||||
|
using var tmxMapReader = new TmxMapReader(xmlReader, externalTilesetResolver, externalTemplateResolver);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(tmxMapReader);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> 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<object[]> 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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,242 +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<object[]> 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<TmxSerializer, string, Tileset> externalTilesetResolver = (TmxSerializer serializer, string s) =>
|
|
||||||
throw new NotSupportedException("External tilesets are not supported in this test");
|
|
||||||
Func<TmxSerializer, string, Template> externalTemplateResolver = (TmxSerializer serializer, string s) =>
|
|
||||||
throw new NotSupportedException("External templates are not supported in this test");
|
|
||||||
var tmxSerializer = new TmxSerializer(
|
|
||||||
externalTilesetResolver,
|
|
||||||
externalTemplateResolver);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
|
|
||||||
static Template ResolveTemplate(string source)
|
|
||||||
{
|
|
||||||
using var xmlTemplateReader = TmxSerializerTestData.GetReaderFor($"TmxSerializer.TestData.Template.{source}");
|
|
||||||
using var templateReader = new TxTemplateReader(xmlTemplateReader, ResolveTileset, ResolveTemplate);
|
|
||||||
return templateReader.ReadTemplate();
|
|
||||||
}
|
|
||||||
|
|
||||||
static Tileset ResolveTileset(string source)
|
|
||||||
{
|
|
||||||
using var xmlTilesetReader = TmxSerializerTestData.GetReaderFor($"TmxSerializer.TestData.Tileset.{source}");
|
|
||||||
using var tilesetReader = new TsxTilesetReader(xmlTilesetReader, ResolveTemplate);
|
|
||||||
return tilesetReader.ReadTileset();
|
|
||||||
}
|
|
||||||
|
|
||||||
var mapReader = new TmxMapReader(reader, ResolveTileset, ResolveTemplate);
|
|
||||||
|
|
||||||
var map = mapReader.ReadMap();
|
|
||||||
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<TmxSerializer, string, Tileset> externalTilesetResolver = (TmxSerializer serializer, string s) =>
|
|
||||||
throw new NotSupportedException("External tilesets are not supported in this test");
|
|
||||||
Func<TmxSerializer, string, Template> 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<TmxSerializer, string, Tileset> externalTilesetResolver = (TmxSerializer serializer, string s) =>
|
|
||||||
throw new NotSupportedException("External tilesets are not supported in this test");
|
|
||||||
Func<TmxSerializer, string, Template> 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<object[]> 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<TmxSerializer, string, Tileset> externalTilesetResolver = (TmxSerializer serializer, string s) =>
|
|
||||||
{
|
|
||||||
using var tilesetReader = TmxSerializerTestData.GetReaderFor($"TmxSerializer.TestData.Tileset.{s}");
|
|
||||||
return serializer.DeserializeTileset(tilesetReader);
|
|
||||||
};
|
|
||||||
Func<TmxSerializer, string, Template> 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<TmxSerializer, string, Tileset> externalTilesetResolver = (TmxSerializer serializer, string s) =>
|
|
||||||
{
|
|
||||||
using var tilesetReader = TmxSerializerTestData.GetReaderFor($"TmxSerializer.TestData.Tileset.{s}");
|
|
||||||
return serializer.DeserializeTileset(tilesetReader);
|
|
||||||
};
|
|
||||||
Func<TmxSerializer, string, Template> 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<TmxSerializer, string, Tileset> externalTilesetResolver = (TmxSerializer serializer, string s) =>
|
|
||||||
{
|
|
||||||
using var tilesetReader = TmxSerializerTestData.GetReaderFor($"TmxSerializer.TestData.Tileset.{s}");
|
|
||||||
return serializer.DeserializeTileset(tilesetReader);
|
|
||||||
};
|
|
||||||
Func<TmxSerializer, string, Template> 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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
namespace DotTiled.Tests;
|
|
||||||
|
|
||||||
public class TmxSerializerTests
|
|
||||||
{
|
|
||||||
[Fact]
|
|
||||||
public void TmxSerializerConstructor_ExternalTilesetResolverIsNull_ThrowsArgumentNullException()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
Func<TmxSerializer, string, Tileset> externalTilesetResolver = null!;
|
|
||||||
Func<TmxSerializer, string, Template> externalTemplateResolver = null!;
|
|
||||||
|
|
||||||
// Act
|
|
||||||
Action act = () => _ = new TmxSerializer(externalTilesetResolver, externalTemplateResolver);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Throws<ArgumentNullException>(act);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void TmxSerializerConstructor_ExternalTilesetResolverIsNotNull_DoesNotThrow()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
Func<TmxSerializer, string, Tileset> externalTilesetResolver = (_, _) => new Tileset();
|
|
||||||
Func<TmxSerializer, string, Template> externalTemplateResolver = (_, _) => new Template { Object = new RectangleObject { } };
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var tmxSerializer = new TmxSerializer(externalTilesetResolver, externalTemplateResolver);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.NotNull(tmxSerializer);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
using System.Xml;
|
|
||||||
|
|
||||||
namespace DotTiled;
|
|
||||||
|
|
||||||
public partial class TmxSerializer
|
|
||||||
{
|
|
||||||
private Chunk ReadChunk(XmlReader reader, DataEncoding? encoding, DataCompression? compression)
|
|
||||||
{
|
|
||||||
var x = reader.GetRequiredAttributeParseable<int>("x");
|
|
||||||
var y = reader.GetRequiredAttributeParseable<int>("y");
|
|
||||||
var width = reader.GetRequiredAttributeParseable<uint>("width");
|
|
||||||
var height = reader.GetRequiredAttributeParseable<uint>("height");
|
|
||||||
|
|
||||||
var usesTileChildrenInsteadOfRawData = encoding is null;
|
|
||||||
if (usesTileChildrenInsteadOfRawData)
|
|
||||||
{
|
|
||||||
var globalTileIDsWithFlippingFlags = ReadTileChildrenInWrapper("chunk", reader);
|
|
||||||
var (globalTileIDs, flippingFlags) = ReadAndClearFlippingFlagsFromGIDs(globalTileIDsWithFlippingFlags);
|
|
||||||
return new Chunk { X = x, Y = y, Width = width, Height = height, GlobalTileIDs = globalTileIDs, FlippingFlags = flippingFlags };
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var globalTileIDsWithFlippingFlags = ReadRawData(reader, encoding!.Value, compression);
|
|
||||||
var (globalTileIDs, flippingFlags) = ReadAndClearFlippingFlagsFromGIDs(globalTileIDsWithFlippingFlags);
|
|
||||||
return new Chunk { X = x, Y = y, Width = width, Height = height, GlobalTileIDs = globalTileIDs, FlippingFlags = flippingFlags };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,122 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.IO.Compression;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Xml;
|
|
||||||
|
|
||||||
namespace DotTiled;
|
|
||||||
|
|
||||||
public partial class TmxSerializer
|
|
||||||
{
|
|
||||||
private Data ReadData(XmlReader reader, bool usesChunks)
|
|
||||||
{
|
|
||||||
var encoding = reader.GetOptionalAttributeEnum<DataEncoding>("encoding", e => e switch
|
|
||||||
{
|
|
||||||
"csv" => DataEncoding.Csv,
|
|
||||||
"base64" => DataEncoding.Base64,
|
|
||||||
_ => throw new XmlException("Invalid encoding")
|
|
||||||
});
|
|
||||||
var compression = reader.GetOptionalAttributeEnum<DataCompression>("compression", c => c switch
|
|
||||||
{
|
|
||||||
"gzip" => DataCompression.GZip,
|
|
||||||
"zlib" => DataCompression.ZLib,
|
|
||||||
"zstd" => DataCompression.ZStd,
|
|
||||||
_ => throw new XmlException("Invalid compression")
|
|
||||||
});
|
|
||||||
|
|
||||||
if (usesChunks)
|
|
||||||
{
|
|
||||||
var chunks = reader
|
|
||||||
.ReadList("data", "chunk", (r) => ReadChunk(r, encoding, compression))
|
|
||||||
.ToArray();
|
|
||||||
return new Data { Encoding = encoding, Compression = compression, GlobalTileIDs = null, Chunks = chunks };
|
|
||||||
}
|
|
||||||
|
|
||||||
var usesTileChildrenInsteadOfRawData = encoding is null && compression is null;
|
|
||||||
if (usesTileChildrenInsteadOfRawData)
|
|
||||||
{
|
|
||||||
var tileChildrenGlobalTileIDsWithFlippingFlags = ReadTileChildrenInWrapper("data", reader);
|
|
||||||
var (tileChildrenGlobalTileIDs, tileChildrenFlippingFlags) = ReadAndClearFlippingFlagsFromGIDs(tileChildrenGlobalTileIDsWithFlippingFlags);
|
|
||||||
return new Data { Encoding = encoding, Compression = compression, GlobalTileIDs = tileChildrenGlobalTileIDs, FlippingFlags = tileChildrenFlippingFlags, Chunks = null };
|
|
||||||
}
|
|
||||||
|
|
||||||
var rawDataGlobalTileIDsWithFlippingFlags = ReadRawData(reader, encoding!.Value, compression);
|
|
||||||
var (rawDataGlobalTileIDs, rawDataFlippingFlags) = ReadAndClearFlippingFlagsFromGIDs(rawDataGlobalTileIDsWithFlippingFlags);
|
|
||||||
return new Data { Encoding = encoding, Compression = compression, GlobalTileIDs = rawDataGlobalTileIDs, FlippingFlags = rawDataFlippingFlags, Chunks = null };
|
|
||||||
}
|
|
||||||
|
|
||||||
private (uint[] GlobalTileIDs, FlippingFlags[] FlippingFlags) ReadAndClearFlippingFlagsFromGIDs(uint[] globalTileIDs)
|
|
||||||
{
|
|
||||||
var clearedGlobalTileIDs = new uint[globalTileIDs.Length];
|
|
||||||
var flippingFlags = new FlippingFlags[globalTileIDs.Length];
|
|
||||||
for (var i = 0; i < globalTileIDs.Length; i++)
|
|
||||||
{
|
|
||||||
var gid = globalTileIDs[i];
|
|
||||||
var flags = gid & 0xF0000000u;
|
|
||||||
flippingFlags[i] = (FlippingFlags)flags;
|
|
||||||
clearedGlobalTileIDs[i] = gid & 0x0FFFFFFFu;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (clearedGlobalTileIDs, flippingFlags);
|
|
||||||
}
|
|
||||||
|
|
||||||
private uint[] ReadTileChildrenInWrapper(string wrapper, XmlReader reader)
|
|
||||||
{
|
|
||||||
return reader.ReadList(wrapper, "tile", (r) => r.GetOptionalAttributeParseable<uint>("gid") ?? 0).ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
private uint[] ReadRawData(XmlReader reader, DataEncoding encoding, DataCompression? compression)
|
|
||||||
{
|
|
||||||
var data = reader.ReadElementContentAsString();
|
|
||||||
if (encoding == DataEncoding.Csv)
|
|
||||||
return ParseCsvData(data);
|
|
||||||
|
|
||||||
using var bytes = new MemoryStream(Convert.FromBase64String(data));
|
|
||||||
if (compression is null)
|
|
||||||
return ReadMemoryStreamAsInt32Array(bytes);
|
|
||||||
|
|
||||||
var decompressed = compression switch
|
|
||||||
{
|
|
||||||
DataCompression.GZip => DecompressGZip(bytes),
|
|
||||||
DataCompression.ZLib => DecompressZLib(bytes),
|
|
||||||
DataCompression.ZStd => throw new NotSupportedException("ZStd compression is not supported."),
|
|
||||||
_ => throw new XmlException("Invalid compression")
|
|
||||||
};
|
|
||||||
|
|
||||||
return decompressed;
|
|
||||||
}
|
|
||||||
|
|
||||||
private uint[] ParseCsvData(string data)
|
|
||||||
{
|
|
||||||
var values = data
|
|
||||||
.Split((char[])['\n', '\r', ','], StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
|
|
||||||
.Select(uint.Parse)
|
|
||||||
.ToArray();
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
private uint[] ReadMemoryStreamAsInt32Array(Stream stream)
|
|
||||||
{
|
|
||||||
var finalValues = new List<uint>();
|
|
||||||
var int32Bytes = new byte[4];
|
|
||||||
while (stream.Read(int32Bytes, 0, 4) == 4)
|
|
||||||
{
|
|
||||||
var value = BitConverter.ToUInt32(int32Bytes, 0);
|
|
||||||
finalValues.Add(value);
|
|
||||||
}
|
|
||||||
return finalValues.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
private uint[] DecompressGZip(MemoryStream stream)
|
|
||||||
{
|
|
||||||
using var decompressedStream = new GZipStream(stream, CompressionMode.Decompress);
|
|
||||||
return ReadMemoryStreamAsInt32Array(decompressedStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
private uint[] DecompressZLib(MemoryStream stream)
|
|
||||||
{
|
|
||||||
using var decompressedStream = new ZLibStream(stream, CompressionMode.Decompress);
|
|
||||||
return ReadMemoryStreamAsInt32Array(decompressedStream);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace DotTiled;
|
|
||||||
|
|
||||||
public partial class TmxSerializer
|
|
||||||
{
|
|
||||||
private static class Helpers
|
|
||||||
{
|
|
||||||
public static void SetAtMostOnce<T>(ref T? field, T value, string fieldName)
|
|
||||||
{
|
|
||||||
if (field is not null)
|
|
||||||
throw new InvalidOperationException($"{fieldName} already set");
|
|
||||||
|
|
||||||
field = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SetAtMostOnceUsingCounter<T>(ref T? field, T value, string fieldName, ref int counter)
|
|
||||||
{
|
|
||||||
if (counter > 0)
|
|
||||||
throw new InvalidOperationException($"{fieldName} already set");
|
|
||||||
|
|
||||||
field = value;
|
|
||||||
counter++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,102 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Globalization;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Xml;
|
|
||||||
|
|
||||||
namespace DotTiled;
|
|
||||||
|
|
||||||
public partial class TmxSerializer
|
|
||||||
{
|
|
||||||
private Map ReadMap(XmlReader reader)
|
|
||||||
{
|
|
||||||
// Attributes
|
|
||||||
var version = reader.GetRequiredAttribute("version");
|
|
||||||
var tiledVersion = reader.GetRequiredAttribute("tiledversion");
|
|
||||||
var @class = reader.GetOptionalAttribute("class") ?? "";
|
|
||||||
var orientation = reader.GetRequiredAttributeEnum<MapOrientation>("orientation", s => s switch
|
|
||||||
{
|
|
||||||
"orthogonal" => MapOrientation.Orthogonal,
|
|
||||||
"isometric" => MapOrientation.Isometric,
|
|
||||||
"staggered" => MapOrientation.Staggered,
|
|
||||||
"hexagonal" => MapOrientation.Hexagonal,
|
|
||||||
_ => throw new Exception($"Unknown orientation '{s}'")
|
|
||||||
});
|
|
||||||
var renderOrder = reader.GetOptionalAttributeEnum<RenderOrder>("renderorder", s => s switch
|
|
||||||
{
|
|
||||||
"right-down" => RenderOrder.RightDown,
|
|
||||||
"right-up" => RenderOrder.RightUp,
|
|
||||||
"left-down" => RenderOrder.LeftDown,
|
|
||||||
"left-up" => RenderOrder.LeftUp,
|
|
||||||
_ => throw new Exception($"Unknown render order '{s}'")
|
|
||||||
}) ?? RenderOrder.RightDown;
|
|
||||||
var compressionLevel = reader.GetOptionalAttributeParseable<int>("compressionlevel") ?? -1;
|
|
||||||
var width = reader.GetRequiredAttributeParseable<uint>("width");
|
|
||||||
var height = reader.GetRequiredAttributeParseable<uint>("height");
|
|
||||||
var tileWidth = reader.GetRequiredAttributeParseable<uint>("tilewidth");
|
|
||||||
var tileHeight = reader.GetRequiredAttributeParseable<uint>("tileheight");
|
|
||||||
var hexSideLength = reader.GetOptionalAttributeParseable<uint>("hexsidelength");
|
|
||||||
var staggerAxis = reader.GetOptionalAttributeEnum<StaggerAxis>("staggeraxis", s => s switch
|
|
||||||
{
|
|
||||||
"x" => StaggerAxis.X,
|
|
||||||
"y" => StaggerAxis.Y,
|
|
||||||
_ => throw new Exception($"Unknown stagger axis '{s}'")
|
|
||||||
});
|
|
||||||
var staggerIndex = reader.GetOptionalAttributeEnum<StaggerIndex>("staggerindex", s => s switch
|
|
||||||
{
|
|
||||||
"odd" => StaggerIndex.Odd,
|
|
||||||
"even" => StaggerIndex.Even,
|
|
||||||
_ => throw new Exception($"Unknown stagger index '{s}'")
|
|
||||||
});
|
|
||||||
var parallaxOriginX = reader.GetOptionalAttributeParseable<float>("parallaxoriginx") ?? 0.0f;
|
|
||||||
var parallaxOriginY = reader.GetOptionalAttributeParseable<float>("parallaxoriginy") ?? 0.0f;
|
|
||||||
var backgroundColor = reader.GetOptionalAttributeClass<Color>("backgroundcolor") ?? Color.Parse("#00000000", CultureInfo.InvariantCulture);
|
|
||||||
var nextLayerID = reader.GetRequiredAttributeParseable<uint>("nextlayerid");
|
|
||||||
var nextObjectID = reader.GetRequiredAttributeParseable<uint>("nextobjectid");
|
|
||||||
var infinite = (reader.GetOptionalAttributeParseable<uint>("infinite") ?? 0) == 1;
|
|
||||||
|
|
||||||
// At most one of
|
|
||||||
Dictionary<string, IProperty>? properties = null;
|
|
||||||
|
|
||||||
// Any number of
|
|
||||||
List<BaseLayer> layers = [];
|
|
||||||
List<Tileset> tilesets = [];
|
|
||||||
|
|
||||||
reader.ProcessChildren("map", (r, elementName) => elementName switch
|
|
||||||
{
|
|
||||||
"properties" => () => Helpers.SetAtMostOnce(ref properties, ReadProperties(r), "Properties"),
|
|
||||||
"tileset" => () => tilesets.Add(ReadTileset(r)),
|
|
||||||
"layer" => () => layers.Add(ReadTileLayer(r, dataUsesChunks: infinite)),
|
|
||||||
"objectgroup" => () => layers.Add(ReadObjectLayer(r)),
|
|
||||||
"imagelayer" => () => layers.Add(ReadImageLayer(r)),
|
|
||||||
"group" => () => layers.Add(ReadGroup(r)),
|
|
||||||
_ => r.Skip
|
|
||||||
});
|
|
||||||
|
|
||||||
return new Map
|
|
||||||
{
|
|
||||||
Version = version,
|
|
||||||
TiledVersion = tiledVersion,
|
|
||||||
Class = @class,
|
|
||||||
Orientation = orientation,
|
|
||||||
RenderOrder = renderOrder,
|
|
||||||
CompressionLevel = compressionLevel,
|
|
||||||
Width = width,
|
|
||||||
Height = height,
|
|
||||||
TileWidth = tileWidth,
|
|
||||||
TileHeight = tileHeight,
|
|
||||||
HexSideLength = hexSideLength,
|
|
||||||
StaggerAxis = staggerAxis,
|
|
||||||
StaggerIndex = staggerIndex,
|
|
||||||
ParallaxOriginX = parallaxOriginX,
|
|
||||||
ParallaxOriginY = parallaxOriginY,
|
|
||||||
BackgroundColor = backgroundColor,
|
|
||||||
NextLayerID = nextLayerID,
|
|
||||||
NextObjectID = nextObjectID,
|
|
||||||
Infinite = infinite,
|
|
||||||
Properties = properties,
|
|
||||||
Tilesets = tilesets,
|
|
||||||
Layers = layers
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,309 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Globalization;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Numerics;
|
|
||||||
using System.Xml;
|
|
||||||
|
|
||||||
namespace DotTiled;
|
|
||||||
|
|
||||||
public partial class TmxSerializer
|
|
||||||
{
|
|
||||||
private ObjectLayer ReadObjectLayer(XmlReader reader)
|
|
||||||
{
|
|
||||||
// Attributes
|
|
||||||
var id = reader.GetRequiredAttributeParseable<uint>("id");
|
|
||||||
var name = reader.GetOptionalAttribute("name") ?? "";
|
|
||||||
var @class = reader.GetOptionalAttribute("class") ?? "";
|
|
||||||
var x = reader.GetOptionalAttributeParseable<uint>("x") ?? 0;
|
|
||||||
var y = reader.GetOptionalAttributeParseable<uint>("y") ?? 0;
|
|
||||||
var width = reader.GetOptionalAttributeParseable<uint>("width");
|
|
||||||
var height = reader.GetOptionalAttributeParseable<uint>("height");
|
|
||||||
var opacity = reader.GetOptionalAttributeParseable<float>("opacity") ?? 1.0f;
|
|
||||||
var visible = reader.GetOptionalAttributeParseable<bool>("visible") ?? true;
|
|
||||||
var tintColor = reader.GetOptionalAttributeClass<Color>("tintcolor");
|
|
||||||
var offsetX = reader.GetOptionalAttributeParseable<float>("offsetx") ?? 0.0f;
|
|
||||||
var offsetY = reader.GetOptionalAttributeParseable<float>("offsety") ?? 0.0f;
|
|
||||||
var parallaxX = reader.GetOptionalAttributeParseable<float>("parallaxx") ?? 1.0f;
|
|
||||||
var parallaxY = reader.GetOptionalAttributeParseable<float>("parallaxy") ?? 1.0f;
|
|
||||||
var color = reader.GetOptionalAttributeClass<Color>("color");
|
|
||||||
var drawOrder = reader.GetOptionalAttributeEnum<DrawOrder>("draworder", s => s switch
|
|
||||||
{
|
|
||||||
"topdown" => DrawOrder.TopDown,
|
|
||||||
"index" => DrawOrder.Index,
|
|
||||||
_ => throw new Exception($"Unknown draw order '{s}'")
|
|
||||||
}) ?? DrawOrder.TopDown;
|
|
||||||
|
|
||||||
// Elements
|
|
||||||
Dictionary<string, IProperty>? properties = null;
|
|
||||||
List<Object> objects = [];
|
|
||||||
|
|
||||||
reader.ProcessChildren("objectgroup", (r, elementName) => elementName switch
|
|
||||||
{
|
|
||||||
"properties" => () => Helpers.SetAtMostOnce(ref properties, ReadProperties(r), "Properties"),
|
|
||||||
"object" => () => objects.Add(ReadObject(r)),
|
|
||||||
_ => r.Skip
|
|
||||||
});
|
|
||||||
|
|
||||||
return new ObjectLayer
|
|
||||||
{
|
|
||||||
ID = id,
|
|
||||||
Name = name,
|
|
||||||
Class = @class,
|
|
||||||
X = x,
|
|
||||||
Y = y,
|
|
||||||
Width = width,
|
|
||||||
Height = height,
|
|
||||||
Opacity = opacity,
|
|
||||||
Visible = visible,
|
|
||||||
TintColor = tintColor,
|
|
||||||
OffsetX = offsetX,
|
|
||||||
OffsetY = offsetY,
|
|
||||||
ParallaxX = parallaxX,
|
|
||||||
ParallaxY = parallaxY,
|
|
||||||
Color = color,
|
|
||||||
Properties = properties,
|
|
||||||
DrawOrder = drawOrder,
|
|
||||||
Objects = objects
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private Object ReadObject(XmlReader reader)
|
|
||||||
{
|
|
||||||
// Attributes
|
|
||||||
var template = reader.GetOptionalAttribute("template");
|
|
||||||
|
|
||||||
uint? idDefault = null;
|
|
||||||
string nameDefault = "";
|
|
||||||
string typeDefault = "";
|
|
||||||
float xDefault = 0f;
|
|
||||||
float yDefault = 0f;
|
|
||||||
float widthDefault = 0f;
|
|
||||||
float heightDefault = 0f;
|
|
||||||
float rotationDefault = 0f;
|
|
||||||
uint? gidDefault = null;
|
|
||||||
bool visibleDefault = true;
|
|
||||||
Dictionary<string, IProperty>? propertiesDefault = null;
|
|
||||||
|
|
||||||
// Perform template copy first
|
|
||||||
if (template is not null)
|
|
||||||
{
|
|
||||||
var resolvedTemplate = _externalTemplateResolver(this, template);
|
|
||||||
var templObj = resolvedTemplate.Object;
|
|
||||||
|
|
||||||
idDefault = templObj.ID;
|
|
||||||
nameDefault = templObj.Name;
|
|
||||||
typeDefault = templObj.Type;
|
|
||||||
xDefault = templObj.X;
|
|
||||||
yDefault = templObj.Y;
|
|
||||||
widthDefault = templObj.Width;
|
|
||||||
heightDefault = templObj.Height;
|
|
||||||
rotationDefault = templObj.Rotation;
|
|
||||||
gidDefault = templObj.GID;
|
|
||||||
visibleDefault = templObj.Visible;
|
|
||||||
propertiesDefault = templObj.Properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
var id = reader.GetOptionalAttributeParseable<uint>("id") ?? idDefault;
|
|
||||||
var name = reader.GetOptionalAttribute("name") ?? nameDefault;
|
|
||||||
var type = reader.GetOptionalAttribute("type") ?? typeDefault;
|
|
||||||
var x = reader.GetOptionalAttributeParseable<float>("x") ?? xDefault;
|
|
||||||
var y = reader.GetOptionalAttributeParseable<float>("y") ?? yDefault;
|
|
||||||
var width = reader.GetOptionalAttributeParseable<float>("width") ?? widthDefault;
|
|
||||||
var height = reader.GetOptionalAttributeParseable<float>("height") ?? heightDefault;
|
|
||||||
var rotation = reader.GetOptionalAttributeParseable<float>("rotation") ?? rotationDefault;
|
|
||||||
var gid = reader.GetOptionalAttributeParseable<uint>("gid") ?? gidDefault;
|
|
||||||
var visible = reader.GetOptionalAttributeParseable<bool>("visible") ?? visibleDefault;
|
|
||||||
|
|
||||||
// Elements
|
|
||||||
Object? obj = null;
|
|
||||||
int propertiesCounter = 0;
|
|
||||||
Dictionary<string, IProperty>? properties = propertiesDefault;
|
|
||||||
|
|
||||||
reader.ProcessChildren("object", (r, elementName) => elementName switch
|
|
||||||
{
|
|
||||||
"properties" => () => Helpers.SetAtMostOnceUsingCounter(ref properties, MergeProperties(properties, ReadProperties(r)), "Properties", ref propertiesCounter),
|
|
||||||
"ellipse" => () => Helpers.SetAtMostOnce(ref obj, ReadEllipseObject(r), "Object marker"),
|
|
||||||
"point" => () => Helpers.SetAtMostOnce(ref obj, ReadPointObject(r), "Object marker"),
|
|
||||||
"polygon" => () => Helpers.SetAtMostOnce(ref obj, ReadPolygonObject(r), "Object marker"),
|
|
||||||
"polyline" => () => Helpers.SetAtMostOnce(ref obj, ReadPolylineObject(r), "Object marker"),
|
|
||||||
"text" => () => Helpers.SetAtMostOnce(ref obj, ReadTextObject(r), "Object marker"),
|
|
||||||
_ => throw new Exception($"Unknown object marker '{elementName}'")
|
|
||||||
});
|
|
||||||
|
|
||||||
if (obj is null)
|
|
||||||
{
|
|
||||||
obj = new RectangleObject { ID = id };
|
|
||||||
reader.Skip();
|
|
||||||
}
|
|
||||||
|
|
||||||
obj.Name = name;
|
|
||||||
obj.Type = type;
|
|
||||||
obj.X = x;
|
|
||||||
obj.Y = y;
|
|
||||||
obj.Width = width;
|
|
||||||
obj.Height = height;
|
|
||||||
obj.Rotation = rotation;
|
|
||||||
obj.GID = gid;
|
|
||||||
obj.Visible = visible;
|
|
||||||
obj.Template = template;
|
|
||||||
obj.Properties = properties;
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Dictionary<string, IProperty> MergeProperties(Dictionary<string, IProperty>? baseProperties, Dictionary<string, IProperty> overrideProperties)
|
|
||||||
{
|
|
||||||
if (baseProperties is null)
|
|
||||||
return overrideProperties ?? new Dictionary<string, IProperty>();
|
|
||||||
|
|
||||||
if (overrideProperties is null)
|
|
||||||
return baseProperties;
|
|
||||||
|
|
||||||
var result = new Dictionary<string, IProperty>(baseProperties);
|
|
||||||
foreach (var (key, value) in overrideProperties)
|
|
||||||
{
|
|
||||||
if (!result.TryGetValue(key, out var baseProp))
|
|
||||||
{
|
|
||||||
result[key] = value;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (value is ClassProperty classProp)
|
|
||||||
{
|
|
||||||
((ClassProperty)baseProp).Properties = MergeProperties(((ClassProperty)baseProp).Properties, classProp.Properties);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result[key] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private EllipseObject ReadEllipseObject(XmlReader reader)
|
|
||||||
{
|
|
||||||
reader.Skip();
|
|
||||||
return new EllipseObject { };
|
|
||||||
}
|
|
||||||
|
|
||||||
private PointObject ReadPointObject(XmlReader reader)
|
|
||||||
{
|
|
||||||
reader.Skip();
|
|
||||||
return new PointObject { };
|
|
||||||
}
|
|
||||||
|
|
||||||
private PolygonObject ReadPolygonObject(XmlReader reader)
|
|
||||||
{
|
|
||||||
// Attributes
|
|
||||||
var points = reader.GetRequiredAttributeParseable<List<Vector2>>("points", s =>
|
|
||||||
{
|
|
||||||
// Takes on format "x1,y1 x2,y2 x3,y3 ..."
|
|
||||||
var coords = s.Split(' ');
|
|
||||||
return coords.Select(c =>
|
|
||||||
{
|
|
||||||
var xy = c.Split(',');
|
|
||||||
return new Vector2(float.Parse(xy[0], CultureInfo.InvariantCulture), float.Parse(xy[1], CultureInfo.InvariantCulture));
|
|
||||||
}).ToList();
|
|
||||||
});
|
|
||||||
|
|
||||||
reader.ReadStartElement("polygon");
|
|
||||||
return new PolygonObject { Points = points };
|
|
||||||
}
|
|
||||||
|
|
||||||
private PolylineObject ReadPolylineObject(XmlReader reader)
|
|
||||||
{
|
|
||||||
// Attributes
|
|
||||||
var points = reader.GetRequiredAttributeParseable<List<Vector2>>("points", s =>
|
|
||||||
{
|
|
||||||
// Takes on format "x1,y1 x2,y2 x3,y3 ..."
|
|
||||||
var coords = s.Split(' ');
|
|
||||||
return coords.Select(c =>
|
|
||||||
{
|
|
||||||
var xy = c.Split(',');
|
|
||||||
return new Vector2(float.Parse(xy[0], CultureInfo.InvariantCulture), float.Parse(xy[1], CultureInfo.InvariantCulture));
|
|
||||||
}).ToList();
|
|
||||||
});
|
|
||||||
|
|
||||||
reader.ReadStartElement("polyline");
|
|
||||||
return new PolylineObject { Points = points };
|
|
||||||
}
|
|
||||||
|
|
||||||
private TextObject ReadTextObject(XmlReader reader)
|
|
||||||
{
|
|
||||||
// Attributes
|
|
||||||
var fontFamily = reader.GetOptionalAttribute("fontfamily") ?? "sans-serif";
|
|
||||||
var pixelSize = reader.GetOptionalAttributeParseable<int>("pixelsize") ?? 16;
|
|
||||||
var wrap = reader.GetOptionalAttributeParseable<bool>("wrap") ?? false;
|
|
||||||
var color = reader.GetOptionalAttributeClass<Color>("color") ?? Color.Parse("#000000", CultureInfo.InvariantCulture);
|
|
||||||
var bold = reader.GetOptionalAttributeParseable<bool>("bold") ?? false;
|
|
||||||
var italic = reader.GetOptionalAttributeParseable<bool>("italic") ?? false;
|
|
||||||
var underline = reader.GetOptionalAttributeParseable<bool>("underline") ?? false;
|
|
||||||
var strikeout = reader.GetOptionalAttributeParseable<bool>("strikeout") ?? false;
|
|
||||||
var kerning = reader.GetOptionalAttributeParseable<bool>("kerning") ?? true;
|
|
||||||
var hAlign = reader.GetOptionalAttributeEnum<TextHorizontalAlignment>("halign", s => s switch
|
|
||||||
{
|
|
||||||
"left" => TextHorizontalAlignment.Left,
|
|
||||||
"center" => TextHorizontalAlignment.Center,
|
|
||||||
"right" => TextHorizontalAlignment.Right,
|
|
||||||
"justify" => TextHorizontalAlignment.Justify,
|
|
||||||
_ => throw new Exception($"Unknown horizontal alignment '{s}'")
|
|
||||||
}) ?? TextHorizontalAlignment.Left;
|
|
||||||
var vAlign = reader.GetOptionalAttributeEnum<TextVerticalAlignment>("valign", s => s switch
|
|
||||||
{
|
|
||||||
"top" => TextVerticalAlignment.Top,
|
|
||||||
"center" => TextVerticalAlignment.Center,
|
|
||||||
"bottom" => TextVerticalAlignment.Bottom,
|
|
||||||
_ => throw new Exception($"Unknown vertical alignment '{s}'")
|
|
||||||
}) ?? TextVerticalAlignment.Top;
|
|
||||||
|
|
||||||
// Elements
|
|
||||||
var text = reader.ReadElementContentAsString("text", "");
|
|
||||||
|
|
||||||
return new TextObject
|
|
||||||
{
|
|
||||||
FontFamily = fontFamily,
|
|
||||||
PixelSize = pixelSize,
|
|
||||||
Wrap = wrap,
|
|
||||||
Color = color,
|
|
||||||
Bold = bold,
|
|
||||||
Italic = italic,
|
|
||||||
Underline = underline,
|
|
||||||
Strikeout = strikeout,
|
|
||||||
Kerning = kerning,
|
|
||||||
HorizontalAlignment = hAlign,
|
|
||||||
VerticalAlignment = vAlign,
|
|
||||||
Text = text
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private Template ReadTemplate(XmlReader reader)
|
|
||||||
{
|
|
||||||
// No attributes
|
|
||||||
|
|
||||||
// At most one of
|
|
||||||
Tileset? tileset = null;
|
|
||||||
|
|
||||||
// Should contain exactly one of
|
|
||||||
Object? obj = null;
|
|
||||||
|
|
||||||
reader.ProcessChildren("template", (r, elementName) => elementName switch
|
|
||||||
{
|
|
||||||
"tileset" => () => Helpers.SetAtMostOnce(ref tileset, ReadTileset(r), "Tileset"),
|
|
||||||
"object" => () => Helpers.SetAtMostOnce(ref obj, ReadObject(r), "Object"),
|
|
||||||
_ => r.Skip
|
|
||||||
});
|
|
||||||
|
|
||||||
if (obj is null)
|
|
||||||
throw new NotSupportedException("Template must contain exactly one object");
|
|
||||||
|
|
||||||
return new Template
|
|
||||||
{
|
|
||||||
Tileset = tileset,
|
|
||||||
Object = obj
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,54 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Xml;
|
|
||||||
|
|
||||||
namespace DotTiled;
|
|
||||||
|
|
||||||
public partial class TmxSerializer
|
|
||||||
{
|
|
||||||
private Dictionary<string, IProperty> ReadProperties(XmlReader reader)
|
|
||||||
{
|
|
||||||
return reader.ReadList("properties", "property", (r) =>
|
|
||||||
{
|
|
||||||
var name = r.GetRequiredAttribute("name");
|
|
||||||
var type = r.GetOptionalAttributeEnum<PropertyType>("type", (s) => s switch
|
|
||||||
{
|
|
||||||
"string" => PropertyType.String,
|
|
||||||
"int" => PropertyType.Int,
|
|
||||||
"float" => PropertyType.Float,
|
|
||||||
"bool" => PropertyType.Bool,
|
|
||||||
"color" => PropertyType.Color,
|
|
||||||
"file" => PropertyType.File,
|
|
||||||
"object" => PropertyType.Object,
|
|
||||||
"class" => PropertyType.Class,
|
|
||||||
_ => throw new XmlException("Invalid property type")
|
|
||||||
}) ?? PropertyType.String;
|
|
||||||
|
|
||||||
IProperty property = type switch
|
|
||||||
{
|
|
||||||
PropertyType.String => new StringProperty { Name = name, Value = r.GetRequiredAttribute("value") },
|
|
||||||
PropertyType.Int => new IntProperty { Name = name, Value = r.GetRequiredAttributeParseable<int>("value") },
|
|
||||||
PropertyType.Float => new FloatProperty { Name = name, Value = r.GetRequiredAttributeParseable<float>("value") },
|
|
||||||
PropertyType.Bool => new BoolProperty { Name = name, Value = r.GetRequiredAttributeParseable<bool>("value") },
|
|
||||||
PropertyType.Color => new ColorProperty { Name = name, Value = r.GetRequiredAttributeParseable<Color>("value") },
|
|
||||||
PropertyType.File => new FileProperty { Name = name, Value = r.GetRequiredAttribute("value") },
|
|
||||||
PropertyType.Object => new ObjectProperty { Name = name, Value = r.GetRequiredAttributeParseable<uint>("value") },
|
|
||||||
PropertyType.Class => ReadClassProperty(r),
|
|
||||||
_ => throw new XmlException("Invalid property type")
|
|
||||||
};
|
|
||||||
return (name, property);
|
|
||||||
}).ToDictionary(x => x.name, x => x.property);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ClassProperty ReadClassProperty(XmlReader reader)
|
|
||||||
{
|
|
||||||
var name = reader.GetRequiredAttribute("name");
|
|
||||||
var propertyType = reader.GetRequiredAttribute("propertytype");
|
|
||||||
|
|
||||||
reader.ReadStartElement("property");
|
|
||||||
var properties = ReadProperties(reader);
|
|
||||||
reader.ReadEndElement();
|
|
||||||
|
|
||||||
return new ClassProperty { Name = name, PropertyType = propertyType, Properties = properties };
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,148 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Xml;
|
|
||||||
|
|
||||||
namespace DotTiled;
|
|
||||||
|
|
||||||
public partial class TmxSerializer
|
|
||||||
{
|
|
||||||
private TileLayer ReadTileLayer(XmlReader reader, bool dataUsesChunks)
|
|
||||||
{
|
|
||||||
var id = reader.GetRequiredAttributeParseable<uint>("id");
|
|
||||||
var name = reader.GetOptionalAttribute("name") ?? "";
|
|
||||||
var @class = reader.GetOptionalAttribute("class") ?? "";
|
|
||||||
var x = reader.GetOptionalAttributeParseable<uint>("x") ?? 0;
|
|
||||||
var y = reader.GetOptionalAttributeParseable<uint>("y") ?? 0;
|
|
||||||
var width = reader.GetRequiredAttributeParseable<uint>("width");
|
|
||||||
var height = reader.GetRequiredAttributeParseable<uint>("height");
|
|
||||||
var opacity = reader.GetOptionalAttributeParseable<float>("opacity") ?? 1.0f;
|
|
||||||
var visible = reader.GetOptionalAttributeParseable<bool>("visible") ?? true;
|
|
||||||
var tintColor = reader.GetOptionalAttributeClass<Color>("tintcolor");
|
|
||||||
var offsetX = reader.GetOptionalAttributeParseable<float>("offsetx") ?? 0.0f;
|
|
||||||
var offsetY = reader.GetOptionalAttributeParseable<float>("offsety") ?? 0.0f;
|
|
||||||
var parallaxX = reader.GetOptionalAttributeParseable<float>("parallaxx") ?? 1.0f;
|
|
||||||
var parallaxY = reader.GetOptionalAttributeParseable<float>("parallaxy") ?? 1.0f;
|
|
||||||
|
|
||||||
Dictionary<string, IProperty>? properties = null;
|
|
||||||
Data? data = null;
|
|
||||||
|
|
||||||
reader.ProcessChildren("layer", (r, elementName) => elementName switch
|
|
||||||
{
|
|
||||||
"data" => () => Helpers.SetAtMostOnce(ref data, ReadData(r, dataUsesChunks), "Data"),
|
|
||||||
"properties" => () => Helpers.SetAtMostOnce(ref properties, ReadProperties(r), "Properties"),
|
|
||||||
_ => r.Skip
|
|
||||||
});
|
|
||||||
|
|
||||||
return new TileLayer
|
|
||||||
{
|
|
||||||
ID = id,
|
|
||||||
Name = name,
|
|
||||||
Class = @class,
|
|
||||||
X = x,
|
|
||||||
Y = y,
|
|
||||||
Width = width,
|
|
||||||
Height = height,
|
|
||||||
Opacity = opacity,
|
|
||||||
Visible = visible,
|
|
||||||
TintColor = tintColor,
|
|
||||||
OffsetX = offsetX,
|
|
||||||
OffsetY = offsetY,
|
|
||||||
ParallaxX = parallaxX,
|
|
||||||
ParallaxY = parallaxY,
|
|
||||||
Data = data,
|
|
||||||
Properties = properties
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private ImageLayer ReadImageLayer(XmlReader reader)
|
|
||||||
{
|
|
||||||
var id = reader.GetRequiredAttributeParseable<uint>("id");
|
|
||||||
var name = reader.GetOptionalAttribute("name") ?? "";
|
|
||||||
var @class = reader.GetOptionalAttribute("class") ?? "";
|
|
||||||
var x = reader.GetOptionalAttributeParseable<uint>("x") ?? 0;
|
|
||||||
var y = reader.GetOptionalAttributeParseable<uint>("y") ?? 0;
|
|
||||||
var opacity = reader.GetOptionalAttributeParseable<float>("opacity") ?? 1.0f;
|
|
||||||
var visible = reader.GetOptionalAttributeParseable<bool>("visible") ?? true;
|
|
||||||
var tintColor = reader.GetOptionalAttributeClass<Color>("tintcolor");
|
|
||||||
var offsetX = reader.GetOptionalAttributeParseable<float>("offsetx") ?? 0.0f;
|
|
||||||
var offsetY = reader.GetOptionalAttributeParseable<float>("offsety") ?? 0.0f;
|
|
||||||
var parallaxX = reader.GetOptionalAttributeParseable<float>("parallaxx") ?? 1.0f;
|
|
||||||
var parallaxY = reader.GetOptionalAttributeParseable<float>("parallaxy") ?? 1.0f;
|
|
||||||
var repeatX = reader.GetRequiredAttributeParseable<bool>("repeatx");
|
|
||||||
var repeatY = reader.GetRequiredAttributeParseable<bool>("repeaty");
|
|
||||||
|
|
||||||
Dictionary<string, IProperty>? properties = null;
|
|
||||||
Image? image = null;
|
|
||||||
|
|
||||||
reader.ProcessChildren("imagelayer", (r, elementName) => elementName switch
|
|
||||||
{
|
|
||||||
"image" => () => Helpers.SetAtMostOnce(ref image, ReadImage(r), "Image"),
|
|
||||||
"properties" => () => Helpers.SetAtMostOnce(ref properties, ReadProperties(r), "Properties"),
|
|
||||||
_ => r.Skip
|
|
||||||
});
|
|
||||||
|
|
||||||
return new ImageLayer
|
|
||||||
{
|
|
||||||
ID = id,
|
|
||||||
Name = name,
|
|
||||||
Class = @class,
|
|
||||||
X = x,
|
|
||||||
Y = y,
|
|
||||||
Opacity = opacity,
|
|
||||||
Visible = visible,
|
|
||||||
TintColor = tintColor,
|
|
||||||
OffsetX = offsetX,
|
|
||||||
OffsetY = offsetY,
|
|
||||||
ParallaxX = parallaxX,
|
|
||||||
ParallaxY = parallaxY,
|
|
||||||
Properties = properties,
|
|
||||||
Image = image,
|
|
||||||
RepeatX = repeatX,
|
|
||||||
RepeatY = repeatY
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private Group ReadGroup(XmlReader reader)
|
|
||||||
{
|
|
||||||
var id = reader.GetRequiredAttributeParseable<uint>("id");
|
|
||||||
var name = reader.GetOptionalAttribute("name") ?? "";
|
|
||||||
var @class = reader.GetOptionalAttribute("class") ?? "";
|
|
||||||
var opacity = reader.GetOptionalAttributeParseable<float>("opacity") ?? 1.0f;
|
|
||||||
var visible = reader.GetOptionalAttributeParseable<bool>("visible") ?? true;
|
|
||||||
var tintColor = reader.GetOptionalAttributeClass<Color>("tintcolor");
|
|
||||||
var offsetX = reader.GetOptionalAttributeParseable<float>("offsetx") ?? 0.0f;
|
|
||||||
var offsetY = reader.GetOptionalAttributeParseable<float>("offsety") ?? 0.0f;
|
|
||||||
var parallaxX = reader.GetOptionalAttributeParseable<float>("parallaxx") ?? 1.0f;
|
|
||||||
var parallaxY = reader.GetOptionalAttributeParseable<float>("parallaxy") ?? 1.0f;
|
|
||||||
|
|
||||||
Dictionary<string, IProperty>? properties = null;
|
|
||||||
List<BaseLayer> layers = [];
|
|
||||||
|
|
||||||
reader.ProcessChildren("group", (r, elementName) => elementName switch
|
|
||||||
{
|
|
||||||
"properties" => () => Helpers.SetAtMostOnce(ref properties, ReadProperties(r), "Properties"),
|
|
||||||
"layer" => () => layers.Add(ReadTileLayer(r, dataUsesChunks: false)),
|
|
||||||
"objectgroup" => () => layers.Add(ReadObjectLayer(r)),
|
|
||||||
"imagelayer" => () => layers.Add(ReadImageLayer(r)),
|
|
||||||
"group" => () => layers.Add(ReadGroup(r)),
|
|
||||||
_ => r.Skip
|
|
||||||
});
|
|
||||||
|
|
||||||
return new Group
|
|
||||||
{
|
|
||||||
ID = id,
|
|
||||||
Name = name,
|
|
||||||
Class = @class,
|
|
||||||
Opacity = opacity,
|
|
||||||
Visible = visible,
|
|
||||||
TintColor = tintColor,
|
|
||||||
OffsetX = offsetX,
|
|
||||||
OffsetY = offsetY,
|
|
||||||
ParallaxX = parallaxX,
|
|
||||||
ParallaxY = parallaxY,
|
|
||||||
Properties = properties,
|
|
||||||
Layers = layers
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,313 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Xml;
|
|
||||||
|
|
||||||
namespace DotTiled;
|
|
||||||
|
|
||||||
public partial class TmxSerializer
|
|
||||||
{
|
|
||||||
private Tileset ReadTileset(XmlReader reader)
|
|
||||||
{
|
|
||||||
// Attributes
|
|
||||||
var version = reader.GetOptionalAttribute("version");
|
|
||||||
var tiledVersion = reader.GetOptionalAttribute("tiledversion");
|
|
||||||
var firstGID = reader.GetOptionalAttributeParseable<uint>("firstgid");
|
|
||||||
var source = reader.GetOptionalAttribute("source");
|
|
||||||
var name = reader.GetOptionalAttribute("name");
|
|
||||||
var @class = reader.GetOptionalAttribute("class") ?? "";
|
|
||||||
var tileWidth = reader.GetOptionalAttributeParseable<uint>("tilewidth");
|
|
||||||
var tileHeight = reader.GetOptionalAttributeParseable<uint>("tileheight");
|
|
||||||
var spacing = reader.GetOptionalAttributeParseable<uint>("spacing");
|
|
||||||
var margin = reader.GetOptionalAttributeParseable<uint>("margin");
|
|
||||||
var tileCount = reader.GetOptionalAttributeParseable<uint>("tilecount");
|
|
||||||
var columns = reader.GetOptionalAttributeParseable<uint>("columns");
|
|
||||||
var objectAlignment = reader.GetOptionalAttributeEnum<ObjectAlignment>("objectalignment", s => s switch
|
|
||||||
{
|
|
||||||
"unspecified" => ObjectAlignment.Unspecified,
|
|
||||||
"topleft" => ObjectAlignment.TopLeft,
|
|
||||||
"top" => ObjectAlignment.Top,
|
|
||||||
"topright" => ObjectAlignment.TopRight,
|
|
||||||
"left" => ObjectAlignment.Left,
|
|
||||||
"center" => ObjectAlignment.Center,
|
|
||||||
"right" => ObjectAlignment.Right,
|
|
||||||
"bottomleft" => ObjectAlignment.BottomLeft,
|
|
||||||
"bottom" => ObjectAlignment.Bottom,
|
|
||||||
"bottomright" => ObjectAlignment.BottomRight,
|
|
||||||
_ => throw new Exception($"Unknown object alignment '{s}'")
|
|
||||||
}) ?? ObjectAlignment.Unspecified;
|
|
||||||
var renderSize = reader.GetOptionalAttributeEnum<TileRenderSize>("rendersize", s => s switch
|
|
||||||
{
|
|
||||||
"tile" => TileRenderSize.Tile,
|
|
||||||
"grid" => TileRenderSize.Grid,
|
|
||||||
_ => throw new Exception($"Unknown render size '{s}'")
|
|
||||||
}) ?? TileRenderSize.Tile;
|
|
||||||
var fillMode = reader.GetOptionalAttributeEnum<FillMode>("fillmode", s => s switch
|
|
||||||
{
|
|
||||||
"stretch" => FillMode.Stretch,
|
|
||||||
"preserve-aspect-fit" => FillMode.PreserveAspectFit,
|
|
||||||
_ => throw new Exception($"Unknown fill mode '{s}'")
|
|
||||||
}) ?? FillMode.Stretch;
|
|
||||||
|
|
||||||
// Elements
|
|
||||||
Image? image = null;
|
|
||||||
TileOffset? tileOffset = null;
|
|
||||||
Grid? grid = null;
|
|
||||||
Dictionary<string, IProperty>? properties = null;
|
|
||||||
List<Wangset>? wangsets = null;
|
|
||||||
Transformations? transformations = null;
|
|
||||||
List<Tile> tiles = [];
|
|
||||||
|
|
||||||
reader.ProcessChildren("tileset", (r, elementName) => elementName switch
|
|
||||||
{
|
|
||||||
"image" => () => Helpers.SetAtMostOnce(ref image, ReadImage(r), "Image"),
|
|
||||||
"tileoffset" => () => Helpers.SetAtMostOnce(ref tileOffset, ReadTileOffset(r), "TileOffset"),
|
|
||||||
"grid" => () => Helpers.SetAtMostOnce(ref grid, ReadGrid(r), "Grid"),
|
|
||||||
"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)),
|
|
||||||
_ => r.Skip
|
|
||||||
});
|
|
||||||
|
|
||||||
// Check if tileset is referring to external file
|
|
||||||
if (source is not null)
|
|
||||||
{
|
|
||||||
var resolvedTileset = _externalTilesetResolver(this, source);
|
|
||||||
resolvedTileset.FirstGID = firstGID;
|
|
||||||
resolvedTileset.Source = null;
|
|
||||||
return resolvedTileset;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Tileset
|
|
||||||
{
|
|
||||||
Version = version,
|
|
||||||
TiledVersion = tiledVersion,
|
|
||||||
FirstGID = firstGID,
|
|
||||||
Source = source,
|
|
||||||
Name = name,
|
|
||||||
Class = @class,
|
|
||||||
TileWidth = tileWidth,
|
|
||||||
TileHeight = tileHeight,
|
|
||||||
Spacing = spacing,
|
|
||||||
Margin = margin,
|
|
||||||
TileCount = tileCount,
|
|
||||||
Columns = columns,
|
|
||||||
ObjectAlignment = objectAlignment,
|
|
||||||
RenderSize = renderSize,
|
|
||||||
FillMode = fillMode,
|
|
||||||
Image = image,
|
|
||||||
TileOffset = tileOffset,
|
|
||||||
Grid = grid,
|
|
||||||
Properties = properties,
|
|
||||||
Wangsets = wangsets,
|
|
||||||
Transformations = transformations,
|
|
||||||
Tiles = tiles
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private Image ReadImage(XmlReader reader)
|
|
||||||
{
|
|
||||||
// Attributes
|
|
||||||
var format = reader.GetOptionalAttributeEnum<ImageFormat>("format", s => s switch
|
|
||||||
{
|
|
||||||
"png" => ImageFormat.Png,
|
|
||||||
"jpg" => ImageFormat.Jpg,
|
|
||||||
"bmp" => ImageFormat.Bmp,
|
|
||||||
"gif" => ImageFormat.Gif,
|
|
||||||
_ => throw new Exception($"Unknown image format '{s}'")
|
|
||||||
});
|
|
||||||
var source = reader.GetOptionalAttribute("source");
|
|
||||||
var transparentColor = reader.GetOptionalAttributeClass<Color>("trans");
|
|
||||||
var width = reader.GetOptionalAttributeParseable<uint>("width");
|
|
||||||
var height = reader.GetOptionalAttributeParseable<uint>("height");
|
|
||||||
|
|
||||||
reader.ProcessChildren("image", (r, elementName) => elementName switch
|
|
||||||
{
|
|
||||||
"data" => throw new NotSupportedException("Embedded image data is not supported."),
|
|
||||||
_ => r.Skip
|
|
||||||
});
|
|
||||||
|
|
||||||
return new Image
|
|
||||||
{
|
|
||||||
Format = format,
|
|
||||||
Source = source,
|
|
||||||
TransparentColor = transparentColor,
|
|
||||||
Width = width,
|
|
||||||
Height = height,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private TileOffset ReadTileOffset(XmlReader reader)
|
|
||||||
{
|
|
||||||
// Attributes
|
|
||||||
var x = reader.GetOptionalAttributeParseable<float>("x") ?? 0f;
|
|
||||||
var y = reader.GetOptionalAttributeParseable<float>("y") ?? 0f;
|
|
||||||
|
|
||||||
reader.ReadStartElement("tileoffset");
|
|
||||||
return new TileOffset { X = x, Y = y };
|
|
||||||
}
|
|
||||||
|
|
||||||
private Grid ReadGrid(XmlReader reader)
|
|
||||||
{
|
|
||||||
// Attributes
|
|
||||||
var orientation = reader.GetOptionalAttributeEnum<GridOrientation>("orientation", s => s switch
|
|
||||||
{
|
|
||||||
"orthogonal" => GridOrientation.Orthogonal,
|
|
||||||
"isometric" => GridOrientation.Isometric,
|
|
||||||
_ => throw new Exception($"Unknown orientation '{s}'")
|
|
||||||
}) ?? GridOrientation.Orthogonal;
|
|
||||||
var width = reader.GetRequiredAttributeParseable<uint>("width");
|
|
||||||
var height = reader.GetRequiredAttributeParseable<uint>("height");
|
|
||||||
|
|
||||||
reader.ReadStartElement("grid");
|
|
||||||
return new Grid { Orientation = orientation, Width = width, Height = height };
|
|
||||||
}
|
|
||||||
|
|
||||||
private Transformations ReadTransformations(XmlReader reader)
|
|
||||||
{
|
|
||||||
// Attributes
|
|
||||||
var hFlip = reader.GetOptionalAttributeParseable<bool>("hflip") ?? false;
|
|
||||||
var vFlip = reader.GetOptionalAttributeParseable<bool>("vflip") ?? false;
|
|
||||||
var rotate = reader.GetOptionalAttributeParseable<bool>("rotate") ?? false;
|
|
||||||
var preferUntransformed = reader.GetOptionalAttributeParseable<bool>("preferuntransformed") ?? false;
|
|
||||||
|
|
||||||
reader.ReadStartElement("transformations");
|
|
||||||
return new Transformations { HFlip = hFlip, VFlip = vFlip, Rotate = rotate, PreferUntransformed = preferUntransformed };
|
|
||||||
}
|
|
||||||
|
|
||||||
private Tile ReadTile(XmlReader reader)
|
|
||||||
{
|
|
||||||
// Attributes
|
|
||||||
var id = reader.GetRequiredAttributeParseable<uint>("id");
|
|
||||||
var type = reader.GetOptionalAttribute("type") ?? "";
|
|
||||||
var probability = reader.GetOptionalAttributeParseable<float>("probability") ?? 0f;
|
|
||||||
var x = reader.GetOptionalAttributeParseable<uint>("x") ?? 0;
|
|
||||||
var y = reader.GetOptionalAttributeParseable<uint>("y") ?? 0;
|
|
||||||
var width = reader.GetOptionalAttributeParseable<uint>("width");
|
|
||||||
var height = reader.GetOptionalAttributeParseable<uint>("height");
|
|
||||||
|
|
||||||
// Elements
|
|
||||||
Dictionary<string, IProperty>? properties = null;
|
|
||||||
Image? image = null;
|
|
||||||
ObjectLayer? objectLayer = null;
|
|
||||||
List<Frame>? animation = null;
|
|
||||||
|
|
||||||
reader.ProcessChildren("tile", (r, elementName) => elementName switch
|
|
||||||
{
|
|
||||||
"properties" => () => Helpers.SetAtMostOnce(ref properties, ReadProperties(r), "Properties"),
|
|
||||||
"image" => () => Helpers.SetAtMostOnce(ref image, ReadImage(r), "Image"),
|
|
||||||
"objectgroup" => () => Helpers.SetAtMostOnce(ref objectLayer, ReadObjectLayer(r), "ObjectLayer"),
|
|
||||||
"animation" => () => Helpers.SetAtMostOnce(ref animation, r.ReadList<Frame>("animation", "frame", (ar) =>
|
|
||||||
{
|
|
||||||
var tileID = ar.GetRequiredAttributeParseable<uint>("tileid");
|
|
||||||
var duration = ar.GetRequiredAttributeParseable<uint>("duration");
|
|
||||||
return new Frame { TileID = tileID, Duration = duration };
|
|
||||||
}), "Animation"),
|
|
||||||
_ => r.Skip
|
|
||||||
});
|
|
||||||
|
|
||||||
return new Tile
|
|
||||||
{
|
|
||||||
ID = id,
|
|
||||||
Type = type,
|
|
||||||
Probability = probability,
|
|
||||||
X = x,
|
|
||||||
Y = y,
|
|
||||||
Width = width ?? image?.Width ?? 0,
|
|
||||||
Height = height ?? image?.Height ?? 0,
|
|
||||||
Properties = properties,
|
|
||||||
Image = image,
|
|
||||||
ObjectLayer = objectLayer,
|
|
||||||
Animation = animation
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Wangset> ReadWangsets(XmlReader reader)
|
|
||||||
{
|
|
||||||
return reader.ReadList<Wangset>("wangsets", "wangset", ReadWangset);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Wangset ReadWangset(XmlReader reader)
|
|
||||||
{
|
|
||||||
// Attributes
|
|
||||||
var name = reader.GetRequiredAttribute("name");
|
|
||||||
var @class = reader.GetOptionalAttribute("class") ?? "";
|
|
||||||
var tile = reader.GetRequiredAttributeParseable<uint>("tile");
|
|
||||||
|
|
||||||
// Elements
|
|
||||||
Dictionary<string, IProperty>? properties = null;
|
|
||||||
List<WangColor> wangColors = [];
|
|
||||||
List<WangTile> wangTiles = [];
|
|
||||||
|
|
||||||
reader.ProcessChildren("wangset", (r, elementName) => elementName switch
|
|
||||||
{
|
|
||||||
"properties" => () => Helpers.SetAtMostOnce(ref properties, ReadProperties(r), "Properties"),
|
|
||||||
"wangcolor" => () => wangColors.Add(ReadWangColor(r)),
|
|
||||||
"wangtile" => () => wangTiles.Add(ReadWangTile(r)),
|
|
||||||
_ => r.Skip
|
|
||||||
});
|
|
||||||
|
|
||||||
if (wangColors.Count > 254)
|
|
||||||
throw new ArgumentException("Wangset can have at most 254 Wang colors.");
|
|
||||||
|
|
||||||
return new Wangset
|
|
||||||
{
|
|
||||||
Name = name,
|
|
||||||
Class = @class,
|
|
||||||
Tile = tile,
|
|
||||||
Properties = properties,
|
|
||||||
WangColors = wangColors,
|
|
||||||
WangTiles = wangTiles
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private WangColor ReadWangColor(XmlReader reader)
|
|
||||||
{
|
|
||||||
// Attributes
|
|
||||||
var name = reader.GetRequiredAttribute("name");
|
|
||||||
var @class = reader.GetOptionalAttribute("class") ?? "";
|
|
||||||
var color = reader.GetRequiredAttributeParseable<Color>("color");
|
|
||||||
var tile = reader.GetRequiredAttributeParseable<uint>("tile");
|
|
||||||
var probability = reader.GetOptionalAttributeParseable<float>("probability") ?? 0f;
|
|
||||||
|
|
||||||
// Elements
|
|
||||||
Dictionary<string, IProperty>? properties = null;
|
|
||||||
|
|
||||||
reader.ProcessChildren("wangcolor", (r, elementName) => elementName switch
|
|
||||||
{
|
|
||||||
"properties" => () => Helpers.SetAtMostOnce(ref properties, ReadProperties(r), "Properties"),
|
|
||||||
_ => r.Skip
|
|
||||||
});
|
|
||||||
|
|
||||||
return new WangColor
|
|
||||||
{
|
|
||||||
Name = name,
|
|
||||||
Class = @class,
|
|
||||||
Color = color,
|
|
||||||
Tile = tile,
|
|
||||||
Probability = probability,
|
|
||||||
Properties = properties
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private WangTile ReadWangTile(XmlReader reader)
|
|
||||||
{
|
|
||||||
// Attributes
|
|
||||||
var tileID = reader.GetRequiredAttributeParseable<uint>("tileid");
|
|
||||||
var wangID = reader.GetRequiredAttributeParseable<byte[]>("wangid", s =>
|
|
||||||
{
|
|
||||||
// Comma-separated list of indices (0-254)
|
|
||||||
var indices = s.Split(',').Select(i => byte.Parse(i)).ToArray();
|
|
||||||
if (indices.Length > 8)
|
|
||||||
throw new ArgumentException("Wang ID can have at most 8 indices.");
|
|
||||||
return indices;
|
|
||||||
});
|
|
||||||
|
|
||||||
return new WangTile
|
|
||||||
{
|
|
||||||
TileID = tileID,
|
|
||||||
WangID = wangID
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Xml;
|
|
||||||
|
|
||||||
namespace DotTiled;
|
|
||||||
|
|
||||||
public partial class TmxSerializer
|
|
||||||
{
|
|
||||||
private readonly Func<TmxSerializer, string, Tileset> _externalTilesetResolver;
|
|
||||||
private readonly Func<TmxSerializer, string, Template> _externalTemplateResolver;
|
|
||||||
|
|
||||||
public TmxSerializer(
|
|
||||||
Func<TmxSerializer, string, Tileset> externalTilesetResolver,
|
|
||||||
Func<TmxSerializer, string, Template> 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);
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Add table
Reference in a new issue