mirror of
https://github.com/dcronqvist/DotTiled.git
synced 2025-02-05 08:52:50 +02:00
Merge pull request #2 from dcronqvist/json-support
Add JSON map parsing support
This commit is contained in:
commit
33f1f4dcb7
62 changed files with 2599 additions and 210 deletions
|
@ -15,68 +15,54 @@ namespace MyBenchmarks
|
||||||
[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)]
|
[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)]
|
||||||
[CategoriesColumn]
|
[CategoriesColumn]
|
||||||
[Orderer(SummaryOrderPolicy.FastestToSlowest)]
|
[Orderer(SummaryOrderPolicy.FastestToSlowest)]
|
||||||
|
[HideColumns(["StdDev", "Error", "RatioSD"])]
|
||||||
public class MapLoading
|
public class MapLoading
|
||||||
{
|
{
|
||||||
private string _tmxPath = @"C:\Users\Daniel\winrepos\DotTiled\DotTiled.Tests\Serialization\Tmx\TestData\Map\empty-map-csv.tmx";
|
private string _tmxPath = @"C:\Users\Daniel\winrepos\DotTiled\DotTiled.Tests\Serialization\TestData\Map\empty-map-csv.tmx";
|
||||||
private string _tmxContents = "";
|
private string _tmxContents = "";
|
||||||
|
|
||||||
|
private string _tmjPath = @"C:\Users\Daniel\winrepos\DotTiled\DotTiled.Tests\Serialization\TestData\Map\empty-map-csv.tmj";
|
||||||
|
private string _tmjContents = "";
|
||||||
|
|
||||||
public MapLoading()
|
public MapLoading()
|
||||||
{
|
{
|
||||||
_tmxContents = System.IO.File.ReadAllText(_tmxPath);
|
_tmxContents = System.IO.File.ReadAllText(_tmxPath);
|
||||||
|
_tmjContents = System.IO.File.ReadAllText(_tmjPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
[BenchmarkCategory("MapFromInMemoryTmxString")]
|
[BenchmarkCategory("MapFromInMemoryTmxString")]
|
||||||
[Benchmark(Baseline = true, Description = "DotTiled")]
|
[Benchmark(Baseline = true, Description = "DotTiled")]
|
||||||
public DotTiled.Map LoadWithDotTiledFromInMemoryString()
|
public DotTiled.Map LoadWithDotTiledFromInMemoryTmxString()
|
||||||
{
|
{
|
||||||
using var stringReader = new StringReader(_tmxContents);
|
using var stringReader = new StringReader(_tmxContents);
|
||||||
using var xmlReader = XmlReader.Create(stringReader);
|
using var xmlReader = XmlReader.Create(stringReader);
|
||||||
using var mapReader = new DotTiled.TmxMapReader(xmlReader, _ => throw new Exception(), _ => throw new Exception());
|
using var mapReader = new DotTiled.TmxMapReader(xmlReader, _ => throw new Exception(), _ => throw new Exception(), []);
|
||||||
return mapReader.ReadMap();
|
return mapReader.ReadMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
[BenchmarkCategory("MapFromTmxFile")]
|
[BenchmarkCategory("MapFromInMemoryTmjString")]
|
||||||
[Benchmark(Baseline = true, Description = "DotTiled")]
|
[Benchmark(Baseline = true, Description = "DotTiled")]
|
||||||
public DotTiled.Map LoadWithDotTiledFromFile()
|
public DotTiled.Map LoadWithDotTiledFromInMemoryTmjString()
|
||||||
{
|
{
|
||||||
using var fileStream = System.IO.File.OpenRead(_tmxPath);
|
using var mapReader = new DotTiled.TmjMapReader(_tmjContents, _ => throw new Exception(), _ => throw new Exception(), []);
|
||||||
using var xmlReader = XmlReader.Create(fileStream);
|
|
||||||
using var mapReader = new DotTiled.TmxMapReader(xmlReader, _ => throw new Exception(), _ => throw new Exception());
|
|
||||||
return mapReader.ReadMap();
|
return mapReader.ReadMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
[BenchmarkCategory("MapFromInMemoryTmxString")]
|
[BenchmarkCategory("MapFromInMemoryTmxString")]
|
||||||
[Benchmark(Description = "TiledLib")]
|
[Benchmark(Description = "TiledLib")]
|
||||||
public TiledLib.Map LoadWithTiledLibFromInMemoryString()
|
public TiledLib.Map LoadWithTiledLibFromInMemoryTmxString()
|
||||||
{
|
{
|
||||||
using var memStream = new MemoryStream(Encoding.UTF8.GetBytes(_tmxContents));
|
using var memStream = new MemoryStream(Encoding.UTF8.GetBytes(_tmxContents));
|
||||||
return TiledLib.Map.FromStream(memStream);
|
return TiledLib.Map.FromStream(memStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
[BenchmarkCategory("MapFromTmxFile")]
|
|
||||||
[Benchmark(Description = "TiledLib")]
|
|
||||||
public TiledLib.Map LoadWithTiledLibFromFile()
|
|
||||||
{
|
|
||||||
using var fileStream = System.IO.File.OpenRead(_tmxPath);
|
|
||||||
var map = TiledLib.Map.FromStream(fileStream);
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
[BenchmarkCategory("MapFromInMemoryTmxString")]
|
[BenchmarkCategory("MapFromInMemoryTmxString")]
|
||||||
[Benchmark(Description = "TiledCSPlus")]
|
[Benchmark(Description = "TiledCSPlus")]
|
||||||
public TiledCSPlus.TiledMap LoadWithTiledCSPlusFromInMemoryString()
|
public TiledCSPlus.TiledMap LoadWithTiledCSPlusFromInMemoryTmxString()
|
||||||
{
|
{
|
||||||
using var memStream = new MemoryStream(Encoding.UTF8.GetBytes(_tmxContents));
|
using var memStream = new MemoryStream(Encoding.UTF8.GetBytes(_tmxContents));
|
||||||
return new TiledCSPlus.TiledMap(memStream);
|
return new TiledCSPlus.TiledMap(memStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
[BenchmarkCategory("MapFromTmxFile")]
|
|
||||||
[Benchmark(Description = "TiledCSPlus")]
|
|
||||||
public TiledCSPlus.TiledMap LoadWithTiledCSPlusFromFile()
|
|
||||||
{
|
|
||||||
using var fileStream = System.IO.File.OpenRead(_tmxPath);
|
|
||||||
return new TiledCSPlus.TiledMap(fileStream);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Program
|
public class Program
|
||||||
|
@ -84,6 +70,7 @@ namespace MyBenchmarks
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
var config = BenchmarkDotNet.Configs.DefaultConfig.Instance
|
var config = BenchmarkDotNet.Configs.DefaultConfig.Instance
|
||||||
|
.WithArtifactsPath(args[0])
|
||||||
.WithOptions(ConfigOptions.DisableOptimizationsValidator)
|
.WithOptions(ConfigOptions.DisableOptimizationsValidator)
|
||||||
.AddDiagnoser(BenchmarkDotNet.Diagnosers.MemoryDiagnoser.Default);
|
.AddDiagnoser(BenchmarkDotNet.Diagnosers.MemoryDiagnoser.Default);
|
||||||
var summary = BenchmarkRunner.Run<MapLoading>(config);
|
var summary = BenchmarkRunner.Run<MapLoading>(config);
|
||||||
|
|
|
@ -30,11 +30,11 @@ public static partial class DotTiledAssert
|
||||||
Assert.NotNull(actual.Tilesets);
|
Assert.NotNull(actual.Tilesets);
|
||||||
Assert.Equal(expected.Tilesets.Count, actual.Tilesets.Count);
|
Assert.Equal(expected.Tilesets.Count, actual.Tilesets.Count);
|
||||||
for (var i = 0; i < expected.Tilesets.Count; i++)
|
for (var i = 0; i < expected.Tilesets.Count; i++)
|
||||||
AssertTileset(actual.Tilesets[i], expected.Tilesets[i]);
|
AssertTileset(expected.Tilesets[i], actual.Tilesets[i]);
|
||||||
|
|
||||||
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]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ public static partial class DotTiledAssert
|
||||||
Assert.Equal(expected.Visible, actual.Visible);
|
Assert.Equal(expected.Visible, actual.Visible);
|
||||||
Assert.Equal(expected.Template, actual.Template);
|
Assert.Equal(expected.Template, actual.Template);
|
||||||
|
|
||||||
AssertProperties(actual.Properties, expected.Properties);
|
AssertProperties(expected.Properties, actual.Properties);
|
||||||
AssertObject((dynamic)expected, (dynamic)actual);
|
AssertObject((dynamic)expected, (dynamic)actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,51 +19,51 @@ public static partial class DotTiledAssert
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AssertProperty(IProperty actual, IProperty expected)
|
private static void AssertProperty(IProperty expected, IProperty actual)
|
||||||
{
|
{
|
||||||
Assert.Equal(expected.Type, actual.Type);
|
Assert.Equal(expected.Type, actual.Type);
|
||||||
Assert.Equal(expected.Name, actual.Name);
|
Assert.Equal(expected.Name, actual.Name);
|
||||||
AssertProperties((dynamic)actual, (dynamic)expected);
|
AssertProperties((dynamic)actual, (dynamic)expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AssertProperty(StringProperty actual, StringProperty expected)
|
private static void AssertProperty(StringProperty expected, StringProperty actual)
|
||||||
{
|
{
|
||||||
Assert.Equal(expected.Value, actual.Value);
|
Assert.Equal(expected.Value, actual.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AssertProperty(IntProperty actual, IntProperty expected)
|
private static void AssertProperty(IntProperty expected, IntProperty actual)
|
||||||
{
|
{
|
||||||
Assert.Equal(expected.Value, actual.Value);
|
Assert.Equal(expected.Value, actual.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AssertProperty(FloatProperty actual, FloatProperty expected)
|
private static void AssertProperty(FloatProperty expected, FloatProperty actual)
|
||||||
{
|
{
|
||||||
Assert.Equal(expected.Value, actual.Value);
|
Assert.Equal(expected.Value, actual.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AssertProperty(BoolProperty actual, BoolProperty expected)
|
private static void AssertProperty(BoolProperty expected, BoolProperty actual)
|
||||||
{
|
{
|
||||||
Assert.Equal(expected.Value, actual.Value);
|
Assert.Equal(expected.Value, actual.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AssertProperty(ColorProperty actual, ColorProperty expected)
|
private static void AssertProperty(ColorProperty expected, ColorProperty actual)
|
||||||
{
|
{
|
||||||
Assert.Equal(expected.Value, actual.Value);
|
Assert.Equal(expected.Value, actual.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AssertProperty(FileProperty actual, FileProperty expected)
|
private static void AssertProperty(FileProperty expected, FileProperty actual)
|
||||||
{
|
{
|
||||||
Assert.Equal(expected.Value, actual.Value);
|
Assert.Equal(expected.Value, actual.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AssertProperty(ObjectProperty actual, ObjectProperty expected)
|
private static void AssertProperty(ObjectProperty expected, ObjectProperty actual)
|
||||||
{
|
{
|
||||||
Assert.Equal(expected.Value, actual.Value);
|
Assert.Equal(expected.Value, actual.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AssertProperty(ClassProperty actual, ClassProperty expected)
|
private static void AssertProperty(ClassProperty expected, ClassProperty actual)
|
||||||
{
|
{
|
||||||
Assert.Equal(expected.PropertyType, actual.PropertyType);
|
Assert.Equal(expected.PropertyType, actual.PropertyType);
|
||||||
AssertProperties(actual.Properties, expected.Properties);
|
AssertProperties(expected.Properties, actual.Properties);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,8 +25,8 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<!-- TmxSerializer test data -->
|
<!-- Test data -->
|
||||||
<EmbeddedResource Include="Serialization/Tmx/TestData/**/*" />
|
<EmbeddedResource Include="Serialization/TestData/**/*" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -2,12 +2,12 @@ using System.Xml;
|
||||||
|
|
||||||
namespace DotTiled.Tests;
|
namespace DotTiled.Tests;
|
||||||
|
|
||||||
public static class TmxMapReaderTestData
|
public static partial class TestData
|
||||||
{
|
{
|
||||||
public static XmlReader GetXmlReaderFor(string testDataFile)
|
public static XmlReader GetXmlReaderFor(string testDataFile)
|
||||||
{
|
{
|
||||||
var fullyQualifiedTestDataFile = $"DotTiled.Tests.{testDataFile}";
|
var fullyQualifiedTestDataFile = $"DotTiled.Tests.{testDataFile}";
|
||||||
using var stream = typeof(TmxMapReaderTestData).Assembly.GetManifestResourceStream(fullyQualifiedTestDataFile)
|
using var stream = typeof(TestData).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 TmxMapReaderTestData
|
||||||
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(TmxMapReaderTestData).Assembly.GetManifestResourceStream(fullyQualifiedTestDataFile)
|
using var stream = typeof(TestData).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);
|
|
@ -0,0 +1,103 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"name": "Enum0String",
|
||||||
|
"storageType": "string",
|
||||||
|
"type": "enum",
|
||||||
|
"values": [
|
||||||
|
"Enum0_1",
|
||||||
|
"Enum0_2",
|
||||||
|
"Enum0_3"
|
||||||
|
],
|
||||||
|
"valuesAsFlags": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 5,
|
||||||
|
"name": "Enum1Num",
|
||||||
|
"storageType": "int",
|
||||||
|
"type": "enum",
|
||||||
|
"values": [
|
||||||
|
"Enum1Num_1",
|
||||||
|
"Enum1Num_2",
|
||||||
|
"Enum1Num_3",
|
||||||
|
"Enum1Num_4"
|
||||||
|
],
|
||||||
|
"valuesAsFlags": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 6,
|
||||||
|
"name": "Enum2StringFlags",
|
||||||
|
"storageType": "string",
|
||||||
|
"type": "enum",
|
||||||
|
"values": [
|
||||||
|
"Enum2StringFlags_1",
|
||||||
|
"Enum2StringFlags_2",
|
||||||
|
"Enum2StringFlags_3",
|
||||||
|
"Enum2StringFlags_4"
|
||||||
|
],
|
||||||
|
"valuesAsFlags": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 7,
|
||||||
|
"name": "Enum3NumFlags",
|
||||||
|
"storageType": "int",
|
||||||
|
"type": "enum",
|
||||||
|
"values": [
|
||||||
|
"Enum3NumFlags_1",
|
||||||
|
"Enum3NumFlags_2",
|
||||||
|
"Enum3NumFlags_3",
|
||||||
|
"Enum3NumFlags_4",
|
||||||
|
"Enum3NumFlags_5"
|
||||||
|
],
|
||||||
|
"valuesAsFlags": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "#ffa0a0a4",
|
||||||
|
"drawFill": true,
|
||||||
|
"id": 2,
|
||||||
|
"members": [
|
||||||
|
{
|
||||||
|
"name": "Yep",
|
||||||
|
"propertyType": "TestClass",
|
||||||
|
"type": "class",
|
||||||
|
"value": {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "Test",
|
||||||
|
"type": "class",
|
||||||
|
"useAs": [
|
||||||
|
"property",
|
||||||
|
"map",
|
||||||
|
"layer",
|
||||||
|
"object",
|
||||||
|
"tile",
|
||||||
|
"tileset",
|
||||||
|
"wangcolor",
|
||||||
|
"wangset",
|
||||||
|
"project"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "#ffa0a0a4",
|
||||||
|
"drawFill": true,
|
||||||
|
"id": 1,
|
||||||
|
"members": [
|
||||||
|
{
|
||||||
|
"name": "Amount",
|
||||||
|
"type": "float",
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Name",
|
||||||
|
"type": "string",
|
||||||
|
"value": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "TestClass",
|
||||||
|
"type": "class",
|
||||||
|
"useAs": [
|
||||||
|
"property"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
|
@ -0,0 +1,24 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"color": "#ffa0a0a4",
|
||||||
|
"drawFill": true,
|
||||||
|
"id": 1,
|
||||||
|
"members": [
|
||||||
|
{
|
||||||
|
"name": "Amount",
|
||||||
|
"type": "float",
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Name",
|
||||||
|
"type": "string",
|
||||||
|
"value": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "TestClass",
|
||||||
|
"type": "class",
|
||||||
|
"useAs": [
|
||||||
|
"property"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
|
@ -0,0 +1,30 @@
|
||||||
|
{ "compressionlevel":-1,
|
||||||
|
"height":5,
|
||||||
|
"infinite":false,
|
||||||
|
"layers":[
|
||||||
|
{
|
||||||
|
"compression":"gzip",
|
||||||
|
"data":"H4sIAAAAAAAACmNgoD0AAMrGiJlkAAAA",
|
||||||
|
"encoding":"base64",
|
||||||
|
"height":5,
|
||||||
|
"id":1,
|
||||||
|
"name":"Tile Layer 1",
|
||||||
|
"opacity":1,
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":5,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
}],
|
||||||
|
"nextlayerid":2,
|
||||||
|
"nextobjectid":1,
|
||||||
|
"orientation":"orthogonal",
|
||||||
|
"renderorder":"right-down",
|
||||||
|
"tiledversion":"1.11.0",
|
||||||
|
"tileheight":32,
|
||||||
|
"tilesets":[],
|
||||||
|
"tilewidth":32,
|
||||||
|
"type":"map",
|
||||||
|
"version":"1.10",
|
||||||
|
"width":5
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
{ "compressionlevel":-1,
|
||||||
|
"height":5,
|
||||||
|
"infinite":false,
|
||||||
|
"layers":[
|
||||||
|
{
|
||||||
|
"compression":"zlib",
|
||||||
|
"data":"eJxjYKA9AAAAZAAB",
|
||||||
|
"encoding":"base64",
|
||||||
|
"height":5,
|
||||||
|
"id":1,
|
||||||
|
"name":"Tile Layer 1",
|
||||||
|
"opacity":1,
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":5,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
}],
|
||||||
|
"nextlayerid":2,
|
||||||
|
"nextobjectid":1,
|
||||||
|
"orientation":"orthogonal",
|
||||||
|
"renderorder":"right-down",
|
||||||
|
"tiledversion":"1.11.0",
|
||||||
|
"tileheight":32,
|
||||||
|
"tilesets":[],
|
||||||
|
"tilewidth":32,
|
||||||
|
"type":"map",
|
||||||
|
"version":"1.10",
|
||||||
|
"width":5
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
{ "compressionlevel":-1,
|
||||||
|
"height":5,
|
||||||
|
"infinite":false,
|
||||||
|
"layers":[
|
||||||
|
{
|
||||||
|
"compression":"",
|
||||||
|
"data":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
|
||||||
|
"encoding":"base64",
|
||||||
|
"height":5,
|
||||||
|
"id":1,
|
||||||
|
"name":"Tile Layer 1",
|
||||||
|
"opacity":1,
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":5,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
}],
|
||||||
|
"nextlayerid":2,
|
||||||
|
"nextobjectid":1,
|
||||||
|
"orientation":"orthogonal",
|
||||||
|
"renderorder":"right-down",
|
||||||
|
"tiledversion":"1.11.0",
|
||||||
|
"tileheight":32,
|
||||||
|
"tilesets":[],
|
||||||
|
"tilewidth":32,
|
||||||
|
"type":"map",
|
||||||
|
"version":"1.10",
|
||||||
|
"width":5
|
||||||
|
}
|
32
DotTiled.Tests/Serialization/TestData/Map/empty-map-csv.tmj
Normal file
32
DotTiled.Tests/Serialization/TestData/Map/empty-map-csv.tmj
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
{ "compressionlevel":-1,
|
||||||
|
"height":5,
|
||||||
|
"infinite":false,
|
||||||
|
"layers":[
|
||||||
|
{
|
||||||
|
"data":[0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0],
|
||||||
|
"height":5,
|
||||||
|
"id":1,
|
||||||
|
"name":"Tile Layer 1",
|
||||||
|
"opacity":1,
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":5,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
}],
|
||||||
|
"nextlayerid":2,
|
||||||
|
"nextobjectid":1,
|
||||||
|
"orientation":"orthogonal",
|
||||||
|
"renderorder":"right-down",
|
||||||
|
"tiledversion":"1.11.0",
|
||||||
|
"tileheight":32,
|
||||||
|
"tilesets":[],
|
||||||
|
"tilewidth":32,
|
||||||
|
"type":"map",
|
||||||
|
"version":"1.10",
|
||||||
|
"width":5
|
||||||
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
namespace DotTiled.Tests;
|
namespace DotTiled.Tests;
|
||||||
|
|
||||||
public partial class TmxMapReaderTests
|
public partial class TestData
|
||||||
{
|
{
|
||||||
private static Map EmptyMapWithProperties() => new Map
|
public static Map EmptyMapWithProperties() => new Map
|
||||||
{
|
{
|
||||||
Version = "1.10",
|
Version = "1.10",
|
||||||
TiledVersion = "1.11.0",
|
TiledVersion = "1.11.0",
|
|
@ -0,0 +1,69 @@
|
||||||
|
{ "compressionlevel":-1,
|
||||||
|
"height":5,
|
||||||
|
"infinite":false,
|
||||||
|
"layers":[
|
||||||
|
{
|
||||||
|
"data":[0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0],
|
||||||
|
"height":5,
|
||||||
|
"id":1,
|
||||||
|
"name":"Tile Layer 1",
|
||||||
|
"opacity":1,
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":5,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
}],
|
||||||
|
"nextlayerid":2,
|
||||||
|
"nextobjectid":1,
|
||||||
|
"orientation":"orthogonal",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"MapBool",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"MapColor",
|
||||||
|
"type":"color",
|
||||||
|
"value":"#ffff0000"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"MapFile",
|
||||||
|
"type":"file",
|
||||||
|
"value":"file.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"MapFloat",
|
||||||
|
"type":"float",
|
||||||
|
"value":5.2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"MapInt",
|
||||||
|
"type":"int",
|
||||||
|
"value":42
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name":"MapObject",
|
||||||
|
"type":"object",
|
||||||
|
"value":5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"MapString",
|
||||||
|
"type":"string",
|
||||||
|
"value":"string in map"
|
||||||
|
}],
|
||||||
|
"renderorder":"right-down",
|
||||||
|
"tiledversion":"1.11.0",
|
||||||
|
"tileheight":32,
|
||||||
|
"tilesets":[],
|
||||||
|
"tilewidth":32,
|
||||||
|
"type":"map",
|
||||||
|
"version":"1.10",
|
||||||
|
"width":5
|
||||||
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
namespace DotTiled.Tests;
|
namespace DotTiled.Tests;
|
||||||
|
|
||||||
public partial class TmxMapReaderTests
|
public partial class TestData
|
||||||
{
|
{
|
||||||
private static Map EmptyMapWithEncodingAndCompression(DataEncoding dataEncoding, DataCompression? compression) => new Map
|
public static Map EmptyMapWithEncodingAndCompression(DataEncoding dataEncoding, DataCompression? compression) => new Map
|
||||||
{
|
{
|
||||||
Version = "1.10",
|
Version = "1.10",
|
||||||
TiledVersion = "1.11.0",
|
TiledVersion = "1.11.0",
|
|
@ -1,8 +1,8 @@
|
||||||
namespace DotTiled.Tests;
|
namespace DotTiled.Tests;
|
||||||
|
|
||||||
public partial class TmxMapReaderTests
|
public partial class TestData
|
||||||
{
|
{
|
||||||
private static Map MapWithGroup() => new Map
|
public static Map MapWithGroup() => new Map
|
||||||
{
|
{
|
||||||
Version = "1.10",
|
Version = "1.10",
|
||||||
TiledVersion = "1.11.0",
|
TiledVersion = "1.11.0",
|
80
DotTiled.Tests/Serialization/TestData/Map/map-with-group.tmj
Normal file
80
DotTiled.Tests/Serialization/TestData/Map/map-with-group.tmj
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
{ "compressionlevel":-1,
|
||||||
|
"height":5,
|
||||||
|
"infinite":false,
|
||||||
|
"layers":[
|
||||||
|
{
|
||||||
|
"data":[0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0],
|
||||||
|
"height":5,
|
||||||
|
"id":4,
|
||||||
|
"name":"Tile Layer 2",
|
||||||
|
"opacity":1,
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":5,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":3,
|
||||||
|
"layers":[
|
||||||
|
{
|
||||||
|
"data":[0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0],
|
||||||
|
"height":5,
|
||||||
|
"id":1,
|
||||||
|
"name":"Tile Layer 1",
|
||||||
|
"opacity":1,
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":5,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"draworder":"topdown",
|
||||||
|
"id":2,
|
||||||
|
"name":"Object Layer 1",
|
||||||
|
"objects":[
|
||||||
|
{
|
||||||
|
"height":64.5,
|
||||||
|
"id":1,
|
||||||
|
"name":"Name",
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":64.5,
|
||||||
|
"x":35.5,
|
||||||
|
"y":26
|
||||||
|
}],
|
||||||
|
"opacity":1,
|
||||||
|
"type":"objectgroup",
|
||||||
|
"visible":true,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
}],
|
||||||
|
"name":"Group 1",
|
||||||
|
"opacity":1,
|
||||||
|
"type":"group",
|
||||||
|
"visible":true,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
}],
|
||||||
|
"nextlayerid":5,
|
||||||
|
"nextobjectid":2,
|
||||||
|
"orientation":"orthogonal",
|
||||||
|
"renderorder":"right-down",
|
||||||
|
"tiledversion":"1.11.0",
|
||||||
|
"tileheight":32,
|
||||||
|
"tilesets":[],
|
||||||
|
"tilewidth":32,
|
||||||
|
"type":"map",
|
||||||
|
"version":"1.10",
|
||||||
|
"width":5
|
||||||
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
namespace DotTiled.Tests;
|
namespace DotTiled.Tests;
|
||||||
|
|
||||||
public partial class TmxMapReaderTests
|
public partial class TestData
|
||||||
{
|
{
|
||||||
private static Map MapWithObjectTemplate() => new Map
|
public static Map MapWithObjectTemplate(string templateExtension) => new Map
|
||||||
{
|
{
|
||||||
Version = "1.10",
|
Version = "1.10",
|
||||||
TiledVersion = "1.11.0",
|
TiledVersion = "1.11.0",
|
||||||
|
@ -49,7 +49,7 @@ public partial class TmxMapReaderTests
|
||||||
new RectangleObject
|
new RectangleObject
|
||||||
{
|
{
|
||||||
ID = 1,
|
ID = 1,
|
||||||
Template = "map-with-object-template.tx",
|
Template = $"map-with-object-template.{templateExtension}",
|
||||||
Name = "Thingy 2",
|
Name = "Thingy 2",
|
||||||
X = 94.5749f,
|
X = 94.5749f,
|
||||||
Y = 33.6842f,
|
Y = 33.6842f,
|
||||||
|
@ -73,7 +73,7 @@ public partial class TmxMapReaderTests
|
||||||
new RectangleObject
|
new RectangleObject
|
||||||
{
|
{
|
||||||
ID = 2,
|
ID = 2,
|
||||||
Template = "map-with-object-template.tx",
|
Template = $"map-with-object-template.{templateExtension}",
|
||||||
Name = "Thingy",
|
Name = "Thingy",
|
||||||
X = 29.7976f,
|
X = 29.7976f,
|
||||||
Y = 33.8693f,
|
Y = 33.8693f,
|
||||||
|
@ -97,7 +97,7 @@ public partial class TmxMapReaderTests
|
||||||
new RectangleObject
|
new RectangleObject
|
||||||
{
|
{
|
||||||
ID = 3,
|
ID = 3,
|
||||||
Template = "map-with-object-template.tx",
|
Template = $"map-with-object-template.{templateExtension}",
|
||||||
Name = "Thingy 3",
|
Name = "Thingy 3",
|
||||||
X = 5,
|
X = 5,
|
||||||
Y = 5,
|
Y = 5,
|
||||||
|
@ -112,7 +112,7 @@ public partial class TmxMapReaderTests
|
||||||
PropertyType = "TestClass",
|
PropertyType = "TestClass",
|
||||||
Properties = new Dictionary<string, IProperty>
|
Properties = new Dictionary<string, IProperty>
|
||||||
{
|
{
|
||||||
["Amount"] = new FloatProperty { Name = "Amount", Value = 4.2f },
|
["Amount"] = new FloatProperty { Name = "Amount", Value = 0.0f },
|
||||||
["Name"] = new StringProperty { Name = "Name", Value = "I am here 3" }
|
["Name"] = new StringProperty { Name = "Name", Value = "I am here 3" }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
{ "compressionlevel":-1,
|
||||||
|
"height":5,
|
||||||
|
"infinite":false,
|
||||||
|
"layers":[
|
||||||
|
{
|
||||||
|
"data":[0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0],
|
||||||
|
"height":5,
|
||||||
|
"id":1,
|
||||||
|
"name":"Tile Layer 1",
|
||||||
|
"opacity":1,
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":5,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"draworder":"topdown",
|
||||||
|
"id":2,
|
||||||
|
"name":"Object Layer 1",
|
||||||
|
"objects":[
|
||||||
|
{
|
||||||
|
"height":37.0156,
|
||||||
|
"id":1,
|
||||||
|
"template":"map-with-object-template.tj",
|
||||||
|
"name":"Thingy 2",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"Bool",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TestClassInTemplate",
|
||||||
|
"propertytype":"TestClass",
|
||||||
|
"type":"class",
|
||||||
|
"value":
|
||||||
|
{
|
||||||
|
"Amount":37,
|
||||||
|
"Name":"I am here"
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":37.0156,
|
||||||
|
"x":94.5749,
|
||||||
|
"y":33.6842
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":2,
|
||||||
|
"template":"map-with-object-template.tj",
|
||||||
|
"x":29.7976,
|
||||||
|
"y":33.8693
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"height":37.0156,
|
||||||
|
"id":3,
|
||||||
|
"template":"map-with-object-template.tj",
|
||||||
|
"name":"Thingy 3",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"Bool",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TestClassInTemplate",
|
||||||
|
"propertytype":"TestClass",
|
||||||
|
"type":"class",
|
||||||
|
"value":
|
||||||
|
{
|
||||||
|
"Name":"I am here 3"
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":37.0156,
|
||||||
|
"x":5,
|
||||||
|
"y":5
|
||||||
|
}],
|
||||||
|
"opacity":1,
|
||||||
|
"type":"objectgroup",
|
||||||
|
"visible":true,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
}],
|
||||||
|
"nextlayerid":3,
|
||||||
|
"nextobjectid":3,
|
||||||
|
"orientation":"orthogonal",
|
||||||
|
"renderorder":"right-down",
|
||||||
|
"tiledversion":"1.11.0",
|
||||||
|
"tileheight":32,
|
||||||
|
"tilesets":[],
|
||||||
|
"tilewidth":32,
|
||||||
|
"type":"map",
|
||||||
|
"version":"1.10",
|
||||||
|
"width":5
|
||||||
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
namespace DotTiled.Tests;
|
namespace DotTiled.Tests;
|
||||||
|
|
||||||
public partial class TmxMapReaderTests
|
public partial class TestData
|
||||||
{
|
{
|
||||||
private static Map SimpleMapWithEmbeddedTileset() => new Map
|
public static Map SimpleMapWithEmbeddedTileset() => new Map
|
||||||
{
|
{
|
||||||
Version = "1.10",
|
Version = "1.10",
|
||||||
TiledVersion = "1.11.0",
|
TiledVersion = "1.11.0",
|
||||||
|
@ -26,6 +26,7 @@ public partial class TmxMapReaderTests
|
||||||
Columns = 4,
|
Columns = 4,
|
||||||
Image = new Image
|
Image = new Image
|
||||||
{
|
{
|
||||||
|
Format = ImageFormat.Png,
|
||||||
Source = "tiles.png",
|
Source = "tiles.png",
|
||||||
Width = 128,
|
Width = 128,
|
||||||
Height = 64
|
Height = 64
|
|
@ -0,0 +1,45 @@
|
||||||
|
{ "compressionlevel":-1,
|
||||||
|
"height":5,
|
||||||
|
"infinite":false,
|
||||||
|
"layers":[
|
||||||
|
{
|
||||||
|
"data":[1, 1, 1, 1, 1,
|
||||||
|
1, 1, 1, 1, 1,
|
||||||
|
1, 1, 1, 1, 1,
|
||||||
|
2, 2, 2, 2, 2,
|
||||||
|
2, 2, 2, 2, 2],
|
||||||
|
"height":5,
|
||||||
|
"id":1,
|
||||||
|
"name":"Tile Layer 1",
|
||||||
|
"opacity":1,
|
||||||
|
"type":"tilelayer",
|
||||||
|
"visible":true,
|
||||||
|
"width":5,
|
||||||
|
"x":0,
|
||||||
|
"y":0
|
||||||
|
}],
|
||||||
|
"nextlayerid":2,
|
||||||
|
"nextobjectid":1,
|
||||||
|
"orientation":"orthogonal",
|
||||||
|
"renderorder":"right-down",
|
||||||
|
"tiledversion":"1.11.0",
|
||||||
|
"tileheight":32,
|
||||||
|
"tilesets":[
|
||||||
|
{
|
||||||
|
"columns":4,
|
||||||
|
"firstgid":1,
|
||||||
|
"image":"tiles.png",
|
||||||
|
"imageheight":64,
|
||||||
|
"imagewidth":128,
|
||||||
|
"margin":0,
|
||||||
|
"name":"Tileset 1",
|
||||||
|
"spacing":0,
|
||||||
|
"tilecount":8,
|
||||||
|
"tileheight":32,
|
||||||
|
"tilewidth":32
|
||||||
|
}],
|
||||||
|
"tilewidth":32,
|
||||||
|
"type":"map",
|
||||||
|
"version":"1.10",
|
||||||
|
"width":5
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
{ "object":
|
||||||
|
{
|
||||||
|
"height":37.0156,
|
||||||
|
"id":2,
|
||||||
|
"name":"Thingy",
|
||||||
|
"properties":[
|
||||||
|
{
|
||||||
|
"name":"Bool",
|
||||||
|
"type":"bool",
|
||||||
|
"value":true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"TestClassInTemplate",
|
||||||
|
"propertytype":"TestClass",
|
||||||
|
"type":"class",
|
||||||
|
"value":
|
||||||
|
{
|
||||||
|
"Amount":4.2,
|
||||||
|
"Name":"Hello there"
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":37.0156
|
||||||
|
},
|
||||||
|
"type":"template"
|
||||||
|
}
|
107
DotTiled.Tests/Serialization/Tmj/TmjMapReaderTests.cs
Normal file
107
DotTiled.Tests/Serialization/Tmj/TmjMapReaderTests.cs
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
namespace DotTiled.Tests;
|
||||||
|
|
||||||
|
public partial class TmjMapReaderTests
|
||||||
|
{
|
||||||
|
public static IEnumerable<object[]> DeserializeMap_ValidTmjNoExternalTilesets_ReturnsMapWithoutThrowing_Data =>
|
||||||
|
[
|
||||||
|
["Serialization.TestData.Map.empty-map-csv.tmj", TestData.EmptyMapWithEncodingAndCompression(DataEncoding.Csv, null)],
|
||||||
|
["Serialization.TestData.Map.empty-map-base64.tmj", TestData.EmptyMapWithEncodingAndCompression(DataEncoding.Base64, null)],
|
||||||
|
["Serialization.TestData.Map.empty-map-base64-gzip.tmj", TestData.EmptyMapWithEncodingAndCompression(DataEncoding.Base64, DataCompression.GZip)],
|
||||||
|
["Serialization.TestData.Map.empty-map-base64-zlib.tmj", TestData.EmptyMapWithEncodingAndCompression(DataEncoding.Base64, DataCompression.ZLib)],
|
||||||
|
["Serialization.TestData.Map.simple-tileset-embed.tmj", TestData.SimpleMapWithEmbeddedTileset()],
|
||||||
|
["Serialization.TestData.Map.empty-map-properties.tmj", TestData.EmptyMapWithProperties()],
|
||||||
|
];
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(DeserializeMap_ValidTmjNoExternalTilesets_ReturnsMapWithoutThrowing_Data))]
|
||||||
|
public void TmxMapReaderReadMap_ValidTmjNoExternalTilesets_ReturnsMapThatEqualsExpected(string testDataFile, Map expectedMap)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var json = TestData.GetRawStringFor(testDataFile);
|
||||||
|
static Template ResolveTemplate(string source)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("External templates are not supported in this test.");
|
||||||
|
}
|
||||||
|
static Tileset ResolveTileset(string source)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("External tilesets are not supported in this test.");
|
||||||
|
}
|
||||||
|
using var mapReader = new TmjMapReader(json, ResolveTileset, ResolveTemplate, []);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var map = mapReader.ReadMap();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(map);
|
||||||
|
DotTiledAssert.AssertMap(expectedMap, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> DeserializeMap_ValidTmjExternalTilesetsAndTemplates_ReturnsMapThatEqualsExpected_Data =>
|
||||||
|
[
|
||||||
|
["Serialization.TestData.Map.map-with-object-template.tmj", TestData.MapWithObjectTemplate("tj")],
|
||||||
|
["Serialization.TestData.Map.map-with-group.tmj", TestData.MapWithGroup()],
|
||||||
|
];
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(DeserializeMap_ValidTmjExternalTilesetsAndTemplates_ReturnsMapThatEqualsExpected_Data))]
|
||||||
|
public void TmxMapReaderReadMap_ValidTmjExternalTilesetsAndTemplates_ReturnsMapThatEqualsExpected(string testDataFile, Map expectedMap)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
CustomTypeDefinition[] customTypeDefinitions = [
|
||||||
|
new CustomClassDefinition
|
||||||
|
{
|
||||||
|
Name = "TestClass",
|
||||||
|
ID = 1,
|
||||||
|
UseAs = CustomClassUseAs.Property,
|
||||||
|
Members = [
|
||||||
|
new StringProperty
|
||||||
|
{
|
||||||
|
Name = "Name",
|
||||||
|
Value = ""
|
||||||
|
},
|
||||||
|
new FloatProperty
|
||||||
|
{
|
||||||
|
Name = "Amount",
|
||||||
|
Value = 0f
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
new CustomClassDefinition
|
||||||
|
{
|
||||||
|
Name = "Test",
|
||||||
|
ID = 2,
|
||||||
|
UseAs = CustomClassUseAs.All,
|
||||||
|
Members = [
|
||||||
|
new ClassProperty
|
||||||
|
{
|
||||||
|
Name = "Yep",
|
||||||
|
PropertyType = "TestClass",
|
||||||
|
Properties = []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
var json = TestData.GetRawStringFor(testDataFile);
|
||||||
|
Template ResolveTemplate(string source)
|
||||||
|
{
|
||||||
|
var templateJson = TestData.GetRawStringFor($"Serialization.TestData.Template.{source}");
|
||||||
|
using var templateReader = new TjTemplateReader(templateJson, ResolveTileset, ResolveTemplate, customTypeDefinitions);
|
||||||
|
return templateReader.ReadTemplate();
|
||||||
|
}
|
||||||
|
Tileset ResolveTileset(string source)
|
||||||
|
{
|
||||||
|
var tilesetJson = TestData.GetRawStringFor($"Serialization.TestData.Tileset.{source}");
|
||||||
|
using var tilesetReader = new TsjTilesetReader(tilesetJson, ResolveTemplate, customTypeDefinitions);
|
||||||
|
return tilesetReader.ReadTileset();
|
||||||
|
}
|
||||||
|
using var mapReader = new TmjMapReader(json, ResolveTileset, ResolveTemplate, customTypeDefinitions);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var map = mapReader.ReadMap();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(map);
|
||||||
|
DotTiledAssert.AssertMap(expectedMap, map);
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,7 +15,7 @@ public partial class TmxMapReaderTests
|
||||||
// Act
|
// Act
|
||||||
Action act = () =>
|
Action act = () =>
|
||||||
{
|
{
|
||||||
using var _ = new TmxMapReader(xmlReader, externalTilesetResolver, externalTemplateResolver);
|
using var _ = new TmxMapReader(xmlReader, externalTilesetResolver, externalTemplateResolver, []);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
@ -34,7 +34,7 @@ public partial class TmxMapReaderTests
|
||||||
// Act
|
// Act
|
||||||
Action act = () =>
|
Action act = () =>
|
||||||
{
|
{
|
||||||
using var _ = new TmxMapReader(xmlReader, externalTilesetResolver, externalTemplateResolver);
|
using var _ = new TmxMapReader(xmlReader, externalTilesetResolver, externalTemplateResolver, []);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
@ -53,7 +53,7 @@ public partial class TmxMapReaderTests
|
||||||
// Act
|
// Act
|
||||||
Action act = () =>
|
Action act = () =>
|
||||||
{
|
{
|
||||||
using var _ = new TmxMapReader(xmlReader, externalTilesetResolver, externalTemplateResolver);
|
using var _ = new TmxMapReader(xmlReader, externalTilesetResolver, externalTemplateResolver, []);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
@ -70,7 +70,7 @@ public partial class TmxMapReaderTests
|
||||||
Func<string, Template> externalTemplateResolver = (_) => new Template { Object = new RectangleObject { } };
|
Func<string, Template> externalTemplateResolver = (_) => new Template { Object = new RectangleObject { } };
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
using var tmxMapReader = new TmxMapReader(xmlReader, externalTilesetResolver, externalTemplateResolver);
|
using var tmxMapReader = new TmxMapReader(xmlReader, externalTilesetResolver, externalTemplateResolver, []);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.NotNull(tmxMapReader);
|
Assert.NotNull(tmxMapReader);
|
||||||
|
@ -78,12 +78,12 @@ public partial class TmxMapReaderTests
|
||||||
|
|
||||||
public static IEnumerable<object[]> DeserializeMap_ValidXmlNoExternalTilesets_ReturnsMapWithoutThrowing_Data =>
|
public static IEnumerable<object[]> DeserializeMap_ValidXmlNoExternalTilesets_ReturnsMapWithoutThrowing_Data =>
|
||||||
[
|
[
|
||||||
["Serialization.Tmx.TestData.Map.empty-map-csv.tmx", EmptyMapWithEncodingAndCompression(DataEncoding.Csv, null)],
|
["Serialization.TestData.Map.empty-map-csv.tmx", TestData.EmptyMapWithEncodingAndCompression(DataEncoding.Csv, null)],
|
||||||
["Serialization.Tmx.TestData.Map.empty-map-base64.tmx", EmptyMapWithEncodingAndCompression(DataEncoding.Base64, null)],
|
["Serialization.TestData.Map.empty-map-base64.tmx", TestData.EmptyMapWithEncodingAndCompression(DataEncoding.Base64, null)],
|
||||||
["Serialization.Tmx.TestData.Map.empty-map-base64-gzip.tmx", EmptyMapWithEncodingAndCompression(DataEncoding.Base64, DataCompression.GZip)],
|
["Serialization.TestData.Map.empty-map-base64-gzip.tmx", TestData.EmptyMapWithEncodingAndCompression(DataEncoding.Base64, DataCompression.GZip)],
|
||||||
["Serialization.Tmx.TestData.Map.empty-map-base64-zlib.tmx", EmptyMapWithEncodingAndCompression(DataEncoding.Base64, DataCompression.ZLib)],
|
["Serialization.TestData.Map.empty-map-base64-zlib.tmx", TestData.EmptyMapWithEncodingAndCompression(DataEncoding.Base64, DataCompression.ZLib)],
|
||||||
["Serialization.Tmx.TestData.Map.simple-tileset-embed.tmx", SimpleMapWithEmbeddedTileset()],
|
["Serialization.TestData.Map.simple-tileset-embed.tmx", TestData.SimpleMapWithEmbeddedTileset()],
|
||||||
["Serialization.Tmx.TestData.Map.empty-map-properties.tmx", EmptyMapWithProperties()],
|
["Serialization.TestData.Map.empty-map-properties.tmx", TestData.EmptyMapWithProperties()],
|
||||||
];
|
];
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
@ -91,20 +91,55 @@ public partial class TmxMapReaderTests
|
||||||
public void TmxMapReaderReadMap_ValidXmlNoExternalTilesets_ReturnsMapThatEqualsExpected(string testDataFile, Map expectedMap)
|
public void TmxMapReaderReadMap_ValidXmlNoExternalTilesets_ReturnsMapThatEqualsExpected(string testDataFile, Map expectedMap)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
using var reader = TmxMapReaderTestData.GetXmlReaderFor(testDataFile);
|
CustomTypeDefinition[] customTypeDefinitions = [
|
||||||
static Template ResolveTemplate(string source)
|
new CustomClassDefinition
|
||||||
|
{
|
||||||
|
Name = "TestClass",
|
||||||
|
ID = 1,
|
||||||
|
UseAs = CustomClassUseAs.Property,
|
||||||
|
Members = [
|
||||||
|
new StringProperty
|
||||||
|
{
|
||||||
|
Name = "Name",
|
||||||
|
Value = ""
|
||||||
|
},
|
||||||
|
new FloatProperty
|
||||||
|
{
|
||||||
|
Name = "Amount",
|
||||||
|
Value = 0f
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
new CustomClassDefinition
|
||||||
|
{
|
||||||
|
Name = "Test",
|
||||||
|
ID = 2,
|
||||||
|
UseAs = CustomClassUseAs.All,
|
||||||
|
Members = [
|
||||||
|
new ClassProperty
|
||||||
|
{
|
||||||
|
Name = "Yep",
|
||||||
|
PropertyType = "TestClass",
|
||||||
|
Properties = []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
using var reader = TestData.GetXmlReaderFor(testDataFile);
|
||||||
|
Template ResolveTemplate(string source)
|
||||||
{
|
{
|
||||||
using var xmlTemplateReader = TmxMapReaderTestData.GetXmlReaderFor($"Serialization.Tmx.TestData.Template.{source}");
|
using var xmlTemplateReader = TestData.GetXmlReaderFor($"Serialization.TestData.Template.{source}");
|
||||||
using var templateReader = new TxTemplateReader(xmlTemplateReader, ResolveTileset, ResolveTemplate);
|
using var templateReader = new TxTemplateReader(xmlTemplateReader, ResolveTileset, ResolveTemplate, customTypeDefinitions);
|
||||||
return templateReader.ReadTemplate();
|
return templateReader.ReadTemplate();
|
||||||
}
|
}
|
||||||
static Tileset ResolveTileset(string source)
|
Tileset ResolveTileset(string source)
|
||||||
{
|
{
|
||||||
using var xmlTilesetReader = TmxMapReaderTestData.GetXmlReaderFor($"Serialization.Tmx.TestData.Tileset.{source}");
|
using var xmlTilesetReader = TestData.GetXmlReaderFor($"Serialization.TestData.Tileset.{source}");
|
||||||
using var tilesetReader = new TsxTilesetReader(xmlTilesetReader, ResolveTemplate);
|
using var tilesetReader = new TsxTilesetReader(xmlTilesetReader, ResolveTemplate, customTypeDefinitions);
|
||||||
return tilesetReader.ReadTileset();
|
return tilesetReader.ReadTileset();
|
||||||
}
|
}
|
||||||
using var mapReader = new TmxMapReader(reader, ResolveTileset, ResolveTemplate);
|
using var mapReader = new TmxMapReader(reader, ResolveTileset, ResolveTemplate, customTypeDefinitions);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var map = mapReader.ReadMap();
|
var map = mapReader.ReadMap();
|
||||||
|
@ -116,8 +151,8 @@ public partial class TmxMapReaderTests
|
||||||
|
|
||||||
public static IEnumerable<object[]> DeserializeMap_ValidXmlExternalTilesetsAndTemplates_ReturnsMapThatEqualsExpected_Data =>
|
public static IEnumerable<object[]> DeserializeMap_ValidXmlExternalTilesetsAndTemplates_ReturnsMapThatEqualsExpected_Data =>
|
||||||
[
|
[
|
||||||
["Serialization.Tmx.TestData.Map.map-with-object-template.tmx", MapWithObjectTemplate()],
|
["Serialization.TestData.Map.map-with-object-template.tmx", TestData.MapWithObjectTemplate("tx")],
|
||||||
["Serialization.Tmx.TestData.Map.map-with-group.tmx", MapWithGroup()],
|
["Serialization.TestData.Map.map-with-group.tmx", TestData.MapWithGroup()],
|
||||||
];
|
];
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
@ -125,20 +160,54 @@ public partial class TmxMapReaderTests
|
||||||
public void TmxMapReaderReadMap_ValidXmlExternalTilesetsAndTemplates_ReturnsMapThatEqualsExpected(string testDataFile, Map expectedMap)
|
public void TmxMapReaderReadMap_ValidXmlExternalTilesetsAndTemplates_ReturnsMapThatEqualsExpected(string testDataFile, Map expectedMap)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
using var reader = TmxMapReaderTestData.GetXmlReaderFor(testDataFile);
|
CustomTypeDefinition[] customTypeDefinitions = [
|
||||||
static Template ResolveTemplate(string source)
|
new CustomClassDefinition
|
||||||
|
{
|
||||||
|
Name = "TestClass",
|
||||||
|
ID = 1,
|
||||||
|
UseAs = CustomClassUseAs.Property,
|
||||||
|
Members = [
|
||||||
|
new StringProperty
|
||||||
|
{
|
||||||
|
Name = "Name",
|
||||||
|
Value = ""
|
||||||
|
},
|
||||||
|
new FloatProperty
|
||||||
|
{
|
||||||
|
Name = "Amount",
|
||||||
|
Value = 0f
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
new CustomClassDefinition
|
||||||
|
{
|
||||||
|
Name = "Test",
|
||||||
|
ID = 2,
|
||||||
|
UseAs = CustomClassUseAs.All,
|
||||||
|
Members = [
|
||||||
|
new ClassProperty
|
||||||
|
{
|
||||||
|
Name = "Yep",
|
||||||
|
PropertyType = "TestClass",
|
||||||
|
Properties = []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
using var reader = TestData.GetXmlReaderFor(testDataFile);
|
||||||
|
Template ResolveTemplate(string source)
|
||||||
{
|
{
|
||||||
using var xmlTemplateReader = TmxMapReaderTestData.GetXmlReaderFor($"Serialization.Tmx.TestData.Template.{source}");
|
using var xmlTemplateReader = TestData.GetXmlReaderFor($"Serialization.TestData.Template.{source}");
|
||||||
using var templateReader = new TxTemplateReader(xmlTemplateReader, ResolveTileset, ResolveTemplate);
|
using var templateReader = new TxTemplateReader(xmlTemplateReader, ResolveTileset, ResolveTemplate, customTypeDefinitions);
|
||||||
return templateReader.ReadTemplate();
|
return templateReader.ReadTemplate();
|
||||||
}
|
}
|
||||||
static Tileset ResolveTileset(string source)
|
Tileset ResolveTileset(string source)
|
||||||
{
|
{
|
||||||
using var xmlTilesetReader = TmxMapReaderTestData.GetXmlReaderFor($"Serialization.Tmx.TestData.Tileset.{source}");
|
using var xmlTilesetReader = TestData.GetXmlReaderFor($"Serialization.TestData.Tileset.{source}");
|
||||||
using var tilesetReader = new TsxTilesetReader(xmlTilesetReader, ResolveTemplate);
|
using var tilesetReader = new TsxTilesetReader(xmlTilesetReader, ResolveTemplate, customTypeDefinitions);
|
||||||
return tilesetReader.ReadTileset();
|
return tilesetReader.ReadTileset();
|
||||||
}
|
}
|
||||||
using var mapReader = new TmxMapReader(reader, ResolveTileset, ResolveTemplate);
|
using var mapReader = new TmxMapReader(reader, ResolveTileset, ResolveTemplate, customTypeDefinitions);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var map = mapReader.ReadMap();
|
var map = mapReader.ReadMap();
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace DotTiled;
|
namespace DotTiled;
|
||||||
|
|
||||||
|
@ -18,6 +20,8 @@ public interface IProperty
|
||||||
{
|
{
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public PropertyType Type { get; }
|
public PropertyType Type { get; }
|
||||||
|
|
||||||
|
IProperty Clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class StringProperty : IProperty
|
public class StringProperty : IProperty
|
||||||
|
@ -25,6 +29,12 @@ public class StringProperty : IProperty
|
||||||
public required string Name { get; set; }
|
public required string Name { get; set; }
|
||||||
public PropertyType Type => PropertyType.String;
|
public PropertyType Type => PropertyType.String;
|
||||||
public required string Value { get; set; }
|
public required string Value { get; set; }
|
||||||
|
|
||||||
|
public IProperty Clone() => new StringProperty
|
||||||
|
{
|
||||||
|
Name = Name,
|
||||||
|
Value = Value
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public class IntProperty : IProperty
|
public class IntProperty : IProperty
|
||||||
|
@ -32,6 +42,12 @@ public class IntProperty : IProperty
|
||||||
public required string Name { get; set; }
|
public required string Name { get; set; }
|
||||||
public PropertyType Type => PropertyType.Int;
|
public PropertyType Type => PropertyType.Int;
|
||||||
public required int Value { get; set; }
|
public required int Value { get; set; }
|
||||||
|
|
||||||
|
public IProperty Clone() => new IntProperty
|
||||||
|
{
|
||||||
|
Name = Name,
|
||||||
|
Value = Value
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public class FloatProperty : IProperty
|
public class FloatProperty : IProperty
|
||||||
|
@ -39,6 +55,12 @@ public class FloatProperty : IProperty
|
||||||
public required string Name { get; set; }
|
public required string Name { get; set; }
|
||||||
public PropertyType Type => PropertyType.Float;
|
public PropertyType Type => PropertyType.Float;
|
||||||
public required float Value { get; set; }
|
public required float Value { get; set; }
|
||||||
|
|
||||||
|
public IProperty Clone() => new FloatProperty
|
||||||
|
{
|
||||||
|
Name = Name,
|
||||||
|
Value = Value
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public class BoolProperty : IProperty
|
public class BoolProperty : IProperty
|
||||||
|
@ -46,6 +68,12 @@ public class BoolProperty : IProperty
|
||||||
public required string Name { get; set; }
|
public required string Name { get; set; }
|
||||||
public PropertyType Type => PropertyType.Bool;
|
public PropertyType Type => PropertyType.Bool;
|
||||||
public required bool Value { get; set; }
|
public required bool Value { get; set; }
|
||||||
|
|
||||||
|
public IProperty Clone() => new BoolProperty
|
||||||
|
{
|
||||||
|
Name = Name,
|
||||||
|
Value = Value
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ColorProperty : IProperty
|
public class ColorProperty : IProperty
|
||||||
|
@ -53,6 +81,12 @@ public class ColorProperty : IProperty
|
||||||
public required string Name { get; set; }
|
public required string Name { get; set; }
|
||||||
public PropertyType Type => PropertyType.Color;
|
public PropertyType Type => PropertyType.Color;
|
||||||
public required Color Value { get; set; }
|
public required Color Value { get; set; }
|
||||||
|
|
||||||
|
public IProperty Clone() => new ColorProperty
|
||||||
|
{
|
||||||
|
Name = Name,
|
||||||
|
Value = Value
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public class FileProperty : IProperty
|
public class FileProperty : IProperty
|
||||||
|
@ -60,6 +94,12 @@ public class FileProperty : IProperty
|
||||||
public required string Name { get; set; }
|
public required string Name { get; set; }
|
||||||
public PropertyType Type => PropertyType.File;
|
public PropertyType Type => PropertyType.File;
|
||||||
public required string Value { get; set; }
|
public required string Value { get; set; }
|
||||||
|
|
||||||
|
public IProperty Clone() => new FileProperty
|
||||||
|
{
|
||||||
|
Name = Name,
|
||||||
|
Value = Value
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ObjectProperty : IProperty
|
public class ObjectProperty : IProperty
|
||||||
|
@ -67,6 +107,12 @@ public class ObjectProperty : IProperty
|
||||||
public required string Name { get; set; }
|
public required string Name { get; set; }
|
||||||
public PropertyType Type => PropertyType.Object;
|
public PropertyType Type => PropertyType.Object;
|
||||||
public required uint Value { get; set; }
|
public required uint Value { get; set; }
|
||||||
|
|
||||||
|
public IProperty Clone() => new ObjectProperty
|
||||||
|
{
|
||||||
|
Name = Name,
|
||||||
|
Value = Value
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ClassProperty : IProperty
|
public class ClassProperty : IProperty
|
||||||
|
@ -75,4 +121,53 @@ public class ClassProperty : IProperty
|
||||||
public PropertyType Type => DotTiled.PropertyType.Class;
|
public PropertyType Type => DotTiled.PropertyType.Class;
|
||||||
public required string PropertyType { get; set; }
|
public required string PropertyType { get; set; }
|
||||||
public required Dictionary<string, IProperty> Properties { get; set; }
|
public required Dictionary<string, IProperty> Properties { get; set; }
|
||||||
|
|
||||||
|
public IProperty Clone() => new ClassProperty
|
||||||
|
{
|
||||||
|
Name = Name,
|
||||||
|
PropertyType = PropertyType,
|
||||||
|
Properties = Properties.ToDictionary(p => p.Key, p => p.Value.Clone())
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class CustomTypeDefinition
|
||||||
|
{
|
||||||
|
public uint ID { get; set; }
|
||||||
|
public string Name { get; set; } = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum CustomClassUseAs
|
||||||
|
{
|
||||||
|
Property,
|
||||||
|
Map,
|
||||||
|
Layer,
|
||||||
|
Object,
|
||||||
|
Tile,
|
||||||
|
Tileset,
|
||||||
|
WangColor,
|
||||||
|
Wangset,
|
||||||
|
Project,
|
||||||
|
All = Property | Map | Layer | Object | Tile | Tileset | WangColor | Wangset | Project
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CustomClassDefinition : CustomTypeDefinition
|
||||||
|
{
|
||||||
|
public Color Color { get; set; }
|
||||||
|
public bool DrawFill { get; set; }
|
||||||
|
public CustomClassUseAs UseAs { get; set; }
|
||||||
|
public List<IProperty> Members { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum CustomEnumStorageType
|
||||||
|
{
|
||||||
|
Int,
|
||||||
|
String
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CustomEnumDefinition : CustomTypeDefinition
|
||||||
|
{
|
||||||
|
public CustomEnumStorageType StorageType { get; set; }
|
||||||
|
public List<string> Values { get; set; } = [];
|
||||||
|
public bool ValueAsFlags { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,8 +39,8 @@ public class Tileset
|
||||||
public string Class { get; set; } = "";
|
public string Class { get; set; } = "";
|
||||||
public uint? TileWidth { get; set; }
|
public uint? TileWidth { get; set; }
|
||||||
public uint? TileHeight { get; set; }
|
public uint? TileHeight { get; set; }
|
||||||
public float? Spacing { get; set; }
|
public float? Spacing { get; set; } = 0f;
|
||||||
public float? Margin { get; set; }
|
public float? Margin { get; set; } = 0f;
|
||||||
public uint? TileCount { get; set; }
|
public uint? TileCount { get; set; }
|
||||||
public uint? Columns { get; set; }
|
public uint? Columns { get; set; }
|
||||||
public ObjectAlignment ObjectAlignment { get; set; } = ObjectAlignment.Unspecified;
|
public ObjectAlignment ObjectAlignment { get; set; } = ObjectAlignment.Unspecified;
|
||||||
|
|
123
DotTiled/Serialization/Helpers.cs
Normal file
123
DotTiled/Serialization/Helpers.cs
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace DotTiled;
|
||||||
|
|
||||||
|
internal static partial class Helpers
|
||||||
|
{
|
||||||
|
internal static 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];
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static uint[] DecompressGZip(MemoryStream stream)
|
||||||
|
{
|
||||||
|
using var decompressedStream = new GZipStream(stream, CompressionMode.Decompress);
|
||||||
|
return ReadMemoryStreamAsInt32Array(decompressedStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static uint[] DecompressZLib(MemoryStream stream)
|
||||||
|
{
|
||||||
|
using var decompressedStream = new ZLibStream(stream, CompressionMode.Decompress);
|
||||||
|
return ReadMemoryStreamAsInt32Array(decompressedStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static uint[] ReadBytesAsInt32Array(byte[] bytes)
|
||||||
|
{
|
||||||
|
var intArray = new uint[bytes.Length / 4];
|
||||||
|
for (var i = 0; i < intArray.Length; i++)
|
||||||
|
{
|
||||||
|
intArray[i] = BitConverter.ToUInt32(bytes, i * 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
return intArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static (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);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static ImageFormat ParseImageFormatFromSource(string source)
|
||||||
|
{
|
||||||
|
var extension = Path.GetExtension(source).ToLowerInvariant();
|
||||||
|
return extension switch
|
||||||
|
{
|
||||||
|
".png" => ImageFormat.Png,
|
||||||
|
".gif" => ImageFormat.Gif,
|
||||||
|
".jpg" => ImageFormat.Jpg,
|
||||||
|
".jpeg" => ImageFormat.Jpg,
|
||||||
|
".bmp" => ImageFormat.Bmp,
|
||||||
|
_ => throw new NotSupportedException($"Unsupported image format '{extension}'")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static 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 = baseProperties.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.Clone());
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void SetAtMostOnce<T>(ref T? field, T value, string fieldName)
|
||||||
|
{
|
||||||
|
if (field is not null)
|
||||||
|
throw new InvalidOperationException($"{fieldName} already set");
|
||||||
|
|
||||||
|
field = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal 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++;
|
||||||
|
}
|
||||||
|
}
|
108
DotTiled/Serialization/Tmj/ExtensionsJsonElement.cs
Normal file
108
DotTiled/Serialization/Tmj/ExtensionsJsonElement.cs
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace DotTiled;
|
||||||
|
|
||||||
|
internal static class ExtensionsJsonElement
|
||||||
|
{
|
||||||
|
internal static T GetRequiredProperty<T>(this JsonElement element, string propertyName)
|
||||||
|
{
|
||||||
|
if (!element.TryGetProperty(propertyName, out var property))
|
||||||
|
throw new JsonException($"Missing required property '{propertyName}'.");
|
||||||
|
|
||||||
|
return property.GetValueAs<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static T GetOptionalProperty<T>(this JsonElement element, string propertyName, T defaultValue)
|
||||||
|
{
|
||||||
|
if (!element.TryGetProperty(propertyName, out var property))
|
||||||
|
return defaultValue;
|
||||||
|
|
||||||
|
if (property.ValueKind == JsonValueKind.Null)
|
||||||
|
return defaultValue;
|
||||||
|
|
||||||
|
return property.GetValueAs<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static T GetValueAs<T>(this JsonElement element)
|
||||||
|
{
|
||||||
|
bool isNullable = Nullable.GetUnderlyingType(typeof(T)) != null;
|
||||||
|
|
||||||
|
if (isNullable && element.ValueKind == JsonValueKind.Null)
|
||||||
|
return default!;
|
||||||
|
|
||||||
|
var realType = isNullable ? Nullable.GetUnderlyingType(typeof(T))! : typeof(T);
|
||||||
|
|
||||||
|
string val = realType switch
|
||||||
|
{
|
||||||
|
Type t when t == typeof(string) => element.GetString()!,
|
||||||
|
Type t when t == typeof(int) => element.GetInt32().ToString(CultureInfo.InvariantCulture),
|
||||||
|
Type t when t == typeof(uint) => element.GetUInt32().ToString(CultureInfo.InvariantCulture),
|
||||||
|
Type t when t == typeof(float) => element.GetSingle().ToString(CultureInfo.InvariantCulture),
|
||||||
|
Type t when t == typeof(bool) => element.GetBoolean().ToString(CultureInfo.InvariantCulture),
|
||||||
|
_ => throw new JsonException($"Unsupported type '{typeof(T)}'.")
|
||||||
|
};
|
||||||
|
|
||||||
|
return (T)Convert.ChangeType(val, realType, CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static T GetRequiredPropertyParseable<T>(this JsonElement element, string propertyName) where T : IParsable<T>
|
||||||
|
{
|
||||||
|
if (!element.TryGetProperty(propertyName, out var property))
|
||||||
|
throw new JsonException($"Missing required property '{propertyName}'.");
|
||||||
|
|
||||||
|
return T.Parse(property.GetString()!, CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static T GetRequiredPropertyParseable<T>(this JsonElement element, string propertyName, Func<string, T> parser)
|
||||||
|
{
|
||||||
|
if (!element.TryGetProperty(propertyName, out var property))
|
||||||
|
throw new JsonException($"Missing required property '{propertyName}'.");
|
||||||
|
|
||||||
|
return parser(property.GetString()!);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static T GetOptionalPropertyParseable<T>(this JsonElement element, string propertyName, T defaultValue) where T : IParsable<T>
|
||||||
|
{
|
||||||
|
if (!element.TryGetProperty(propertyName, out var property))
|
||||||
|
return defaultValue;
|
||||||
|
|
||||||
|
return T.Parse(property.GetString()!, CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static T GetOptionalPropertyParseable<T>(this JsonElement element, string propertyName, Func<string, T> parser, T defaultValue)
|
||||||
|
{
|
||||||
|
if (!element.TryGetProperty(propertyName, out var property))
|
||||||
|
return defaultValue;
|
||||||
|
|
||||||
|
return parser(property.GetString()!);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static T GetRequiredPropertyCustom<T>(this JsonElement element, string propertyName, Func<JsonElement, T> parser)
|
||||||
|
{
|
||||||
|
if (!element.TryGetProperty(propertyName, out var property))
|
||||||
|
throw new JsonException($"Missing required property '{propertyName}'.");
|
||||||
|
|
||||||
|
return parser(property);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static T GetOptionalPropertyCustom<T>(this JsonElement element, string propertyName, Func<JsonElement, T> parser, T defaultValue)
|
||||||
|
{
|
||||||
|
if (!element.TryGetProperty(propertyName, out var property))
|
||||||
|
return defaultValue;
|
||||||
|
|
||||||
|
return parser(property);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static List<T> GetValueAsList<T>(this JsonElement element, Func<JsonElement, T> parser)
|
||||||
|
{
|
||||||
|
var list = new List<T>();
|
||||||
|
|
||||||
|
foreach (var item in element.EnumerateArray())
|
||||||
|
list.Add(parser(item));
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
64
DotTiled/Serialization/Tmj/TjTemplateReader.cs
Normal file
64
DotTiled/Serialization/Tmj/TjTemplateReader.cs
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace DotTiled;
|
||||||
|
|
||||||
|
public class TjTemplateReader : ITemplateReader
|
||||||
|
{
|
||||||
|
// External resolvers
|
||||||
|
private readonly Func<string, Tileset> _externalTilesetResolver;
|
||||||
|
private readonly Func<string, Template> _externalTemplateResolver;
|
||||||
|
|
||||||
|
private readonly string _jsonString;
|
||||||
|
private bool disposedValue;
|
||||||
|
|
||||||
|
private readonly IReadOnlyCollection<CustomTypeDefinition> _customTypeDefinitions;
|
||||||
|
|
||||||
|
public TjTemplateReader(
|
||||||
|
string jsonString,
|
||||||
|
Func<string, Tileset> externalTilesetResolver,
|
||||||
|
Func<string, Template> externalTemplateResolver,
|
||||||
|
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions)
|
||||||
|
{
|
||||||
|
_jsonString = jsonString ?? throw new ArgumentNullException(nameof(jsonString));
|
||||||
|
_externalTilesetResolver = externalTilesetResolver ?? throw new ArgumentNullException(nameof(externalTilesetResolver));
|
||||||
|
_externalTemplateResolver = externalTemplateResolver ?? throw new ArgumentNullException(nameof(externalTemplateResolver));
|
||||||
|
_customTypeDefinitions = customTypeDefinitions ?? throw new ArgumentNullException(nameof(customTypeDefinitions));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Template ReadTemplate()
|
||||||
|
{
|
||||||
|
var jsonDoc = System.Text.Json.JsonDocument.Parse(_jsonString);
|
||||||
|
var rootElement = jsonDoc.RootElement;
|
||||||
|
return Tmj.ReadTemplate(rootElement, _externalTilesetResolver, _externalTemplateResolver, _customTypeDefinitions);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (!disposedValue)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
// TODO: dispose managed state (managed objects)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: free unmanaged resources (unmanaged objects) and override finalizer
|
||||||
|
// TODO: set large fields to null
|
||||||
|
disposedValue = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources
|
||||||
|
// ~TjTemplateReader()
|
||||||
|
// {
|
||||||
|
// // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
||||||
|
// Dispose(disposing: false);
|
||||||
|
// }
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
||||||
|
Dispose(disposing: true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
}
|
82
DotTiled/Serialization/Tmj/Tmj.Data.cs
Normal file
82
DotTiled/Serialization/Tmj/Tmj.Data.cs
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace DotTiled;
|
||||||
|
|
||||||
|
internal partial class Tmj
|
||||||
|
{
|
||||||
|
internal static Data ReadDataAsChunks(JsonElement element, DataCompression? compression, DataEncoding encoding)
|
||||||
|
{
|
||||||
|
var chunks = element.GetValueAsList<Chunk>(e => ReadChunk(e, compression, encoding)).ToArray();
|
||||||
|
return new Data
|
||||||
|
{
|
||||||
|
Chunks = chunks,
|
||||||
|
Compression = compression,
|
||||||
|
Encoding = encoding,
|
||||||
|
FlippingFlags = null,
|
||||||
|
GlobalTileIDs = null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static Chunk ReadChunk(JsonElement element, DataCompression? compression, DataEncoding encoding)
|
||||||
|
{
|
||||||
|
var data = ReadDataWithoutChunks(element, compression, encoding);
|
||||||
|
|
||||||
|
var x = element.GetRequiredProperty<int>("x");
|
||||||
|
var y = element.GetRequiredProperty<int>("y");
|
||||||
|
var width = element.GetRequiredProperty<uint>("width");
|
||||||
|
var height = element.GetRequiredProperty<uint>("height");
|
||||||
|
|
||||||
|
return new Chunk
|
||||||
|
{
|
||||||
|
X = x,
|
||||||
|
Y = y,
|
||||||
|
Width = width,
|
||||||
|
Height = height,
|
||||||
|
GlobalTileIDs = data.GlobalTileIDs!,
|
||||||
|
FlippingFlags = data.FlippingFlags!
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static Data ReadDataWithoutChunks(JsonElement element, DataCompression? compression, DataEncoding encoding)
|
||||||
|
{
|
||||||
|
if (encoding == DataEncoding.Csv)
|
||||||
|
{
|
||||||
|
// Array of uint
|
||||||
|
var data = element.GetValueAsList<uint>(e => e.GetValueAs<uint>()).ToArray();
|
||||||
|
var (globalTileIDs, flippingFlags) = Helpers.ReadAndClearFlippingFlagsFromGIDs(data);
|
||||||
|
return new Data { Encoding = encoding, Compression = compression, GlobalTileIDs = globalTileIDs, FlippingFlags = flippingFlags, Chunks = null };
|
||||||
|
}
|
||||||
|
else if (encoding == DataEncoding.Base64)
|
||||||
|
{
|
||||||
|
var base64Data = element.GetBytesFromBase64();
|
||||||
|
|
||||||
|
if (compression == null)
|
||||||
|
{
|
||||||
|
var data = Helpers.ReadBytesAsInt32Array(base64Data);
|
||||||
|
var (globalTileIDs, flippingFlags) = Helpers.ReadAndClearFlippingFlagsFromGIDs(data);
|
||||||
|
return new Data { Encoding = encoding, Compression = compression, GlobalTileIDs = globalTileIDs, FlippingFlags = flippingFlags, Chunks = null };
|
||||||
|
}
|
||||||
|
|
||||||
|
using var stream = new MemoryStream(base64Data);
|
||||||
|
var decompressed = compression switch
|
||||||
|
{
|
||||||
|
DataCompression.GZip => Helpers.DecompressGZip(stream),
|
||||||
|
DataCompression.ZLib => Helpers.DecompressZLib(stream),
|
||||||
|
_ => throw new JsonException($"Unsupported compression '{compression}'.")
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
var (globalTileIDs, flippingFlags) = Helpers.ReadAndClearFlippingFlagsFromGIDs(decompressed);
|
||||||
|
return new Data { Encoding = encoding, Compression = compression, GlobalTileIDs = globalTileIDs, FlippingFlags = flippingFlags, Chunks = null };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new JsonException($"Unsupported encoding '{encoding}'.");
|
||||||
|
}
|
||||||
|
}
|
45
DotTiled/Serialization/Tmj/Tmj.Group.cs
Normal file
45
DotTiled/Serialization/Tmj/Tmj.Group.cs
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace DotTiled;
|
||||||
|
|
||||||
|
internal partial class Tmj
|
||||||
|
{
|
||||||
|
internal static Group ReadGroup(
|
||||||
|
JsonElement element,
|
||||||
|
Func<string, Template> externalTemplateResolver,
|
||||||
|
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions)
|
||||||
|
{
|
||||||
|
var id = element.GetRequiredProperty<uint>("id");
|
||||||
|
var name = element.GetRequiredProperty<string>("name");
|
||||||
|
var @class = element.GetOptionalProperty<string>("class", "");
|
||||||
|
var opacity = element.GetOptionalProperty<float>("opacity", 1.0f);
|
||||||
|
var visible = element.GetOptionalProperty<bool>("visible", true);
|
||||||
|
var tintColor = element.GetOptionalPropertyParseable<Color?>("tintcolor", s => Color.Parse(s, CultureInfo.InvariantCulture), null);
|
||||||
|
var offsetX = element.GetOptionalProperty<float>("offsetx", 0.0f);
|
||||||
|
var offsetY = element.GetOptionalProperty<float>("offsety", 0.0f);
|
||||||
|
var parallaxX = element.GetOptionalProperty<float>("parallaxx", 1.0f);
|
||||||
|
var parallaxY = element.GetOptionalProperty<float>("parallaxy", 1.0f);
|
||||||
|
var properties = element.GetOptionalPropertyCustom<Dictionary<string, IProperty>?>("properties", e => ReadProperties(e, customTypeDefinitions), null);
|
||||||
|
var layers = element.GetOptionalPropertyCustom<List<BaseLayer>>("layers", e => e.GetValueAsList<BaseLayer>(el => ReadLayer(el, externalTemplateResolver, customTypeDefinitions)), []);
|
||||||
|
|
||||||
|
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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
63
DotTiled/Serialization/Tmj/Tmj.ImageLayer.cs
Normal file
63
DotTiled/Serialization/Tmj/Tmj.ImageLayer.cs
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace DotTiled;
|
||||||
|
|
||||||
|
internal partial class Tmj
|
||||||
|
{
|
||||||
|
internal static ImageLayer ReadImageLayer(
|
||||||
|
JsonElement element,
|
||||||
|
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions)
|
||||||
|
{
|
||||||
|
var id = element.GetRequiredProperty<uint>("id");
|
||||||
|
var name = element.GetRequiredProperty<string>("name");
|
||||||
|
var @class = element.GetOptionalProperty<string>("class", "");
|
||||||
|
var opacity = element.GetOptionalProperty<float>("opacity", 1.0f);
|
||||||
|
var visible = element.GetOptionalProperty<bool>("visible", true);
|
||||||
|
var tintColor = element.GetOptionalPropertyParseable<Color?>("tintcolor", s => Color.Parse(s, CultureInfo.InvariantCulture), null);
|
||||||
|
var offsetX = element.GetOptionalProperty<float>("offsetx", 0.0f);
|
||||||
|
var offsetY = element.GetOptionalProperty<float>("offsety", 0.0f);
|
||||||
|
var parallaxX = element.GetOptionalProperty<float>("parallaxx", 1.0f);
|
||||||
|
var parallaxY = element.GetOptionalProperty<float>("parallaxy", 1.0f);
|
||||||
|
var properties = element.GetOptionalPropertyCustom<Dictionary<string, IProperty>?>("properties", e => ReadProperties(e, customTypeDefinitions), null);
|
||||||
|
|
||||||
|
var image = element.GetRequiredProperty<string>("image");
|
||||||
|
var repeatX = element.GetRequiredProperty<bool>("repeatx");
|
||||||
|
var repeatY = element.GetRequiredProperty<bool>("repeaty");
|
||||||
|
var transparentColor = element.GetOptionalPropertyParseable<Color?>("transparentcolor", s => Color.Parse(s, CultureInfo.InvariantCulture), null);
|
||||||
|
var x = element.GetOptionalProperty<uint>("x", 0);
|
||||||
|
var y = element.GetOptionalProperty<uint>("y", 0);
|
||||||
|
|
||||||
|
var imgModel = new Image
|
||||||
|
{
|
||||||
|
Format = Helpers.ParseImageFormatFromSource(image),
|
||||||
|
Height = 0,
|
||||||
|
Width = 0,
|
||||||
|
Source = image,
|
||||||
|
TransparentColor = transparentColor
|
||||||
|
};
|
||||||
|
|
||||||
|
return new ImageLayer
|
||||||
|
{
|
||||||
|
ID = id,
|
||||||
|
Name = name,
|
||||||
|
Class = @class,
|
||||||
|
Opacity = opacity,
|
||||||
|
Visible = visible,
|
||||||
|
TintColor = tintColor,
|
||||||
|
OffsetX = offsetX,
|
||||||
|
OffsetY = offsetY,
|
||||||
|
ParallaxX = parallaxX,
|
||||||
|
ParallaxY = parallaxY,
|
||||||
|
Properties = properties,
|
||||||
|
Image = imgModel,
|
||||||
|
RepeatX = repeatX,
|
||||||
|
RepeatY = repeatY,
|
||||||
|
X = x,
|
||||||
|
Y = y
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
27
DotTiled/Serialization/Tmj/Tmj.Layer.cs
Normal file
27
DotTiled/Serialization/Tmj/Tmj.Layer.cs
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace DotTiled;
|
||||||
|
|
||||||
|
internal partial class Tmj
|
||||||
|
{
|
||||||
|
internal static BaseLayer ReadLayer(
|
||||||
|
JsonElement element,
|
||||||
|
Func<string, Template> externalTemplateResolver,
|
||||||
|
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions)
|
||||||
|
{
|
||||||
|
var type = element.GetRequiredProperty<string>("type");
|
||||||
|
|
||||||
|
return type switch
|
||||||
|
{
|
||||||
|
"tilelayer" => ReadTileLayer(element, customTypeDefinitions),
|
||||||
|
"objectgroup" => ReadObjectLayer(element, externalTemplateResolver, customTypeDefinitions),
|
||||||
|
"imagelayer" => ReadImageLayer(element, customTypeDefinitions),
|
||||||
|
"group" => ReadGroup(element, externalTemplateResolver, customTypeDefinitions),
|
||||||
|
_ => throw new JsonException($"Unsupported layer type '{type}'.")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
93
DotTiled/Serialization/Tmj/Tmj.Map.cs
Normal file
93
DotTiled/Serialization/Tmj/Tmj.Map.cs
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace DotTiled;
|
||||||
|
|
||||||
|
internal partial class Tmj
|
||||||
|
{
|
||||||
|
internal static Map ReadMap(
|
||||||
|
JsonElement element,
|
||||||
|
Func<string, Tileset>? externalTilesetResolver,
|
||||||
|
Func<string, Template> externalTemplateResolver,
|
||||||
|
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions)
|
||||||
|
{
|
||||||
|
var version = element.GetRequiredProperty<string>("version");
|
||||||
|
var tiledVersion = element.GetRequiredProperty<string>("tiledversion");
|
||||||
|
string @class = element.GetOptionalProperty<string>("class", "");
|
||||||
|
var orientation = element.GetRequiredPropertyParseable<MapOrientation>("orientation", s => s switch
|
||||||
|
{
|
||||||
|
"orthogonal" => MapOrientation.Orthogonal,
|
||||||
|
"isometric" => MapOrientation.Isometric,
|
||||||
|
"staggered" => MapOrientation.Staggered,
|
||||||
|
"hexagonal" => MapOrientation.Hexagonal,
|
||||||
|
_ => throw new JsonException($"Unknown orientation '{s}'")
|
||||||
|
});
|
||||||
|
var renderOrder = element.GetOptionalPropertyParseable<RenderOrder>("renderorder", s => s switch
|
||||||
|
{
|
||||||
|
"right-down" => RenderOrder.RightDown,
|
||||||
|
"right-up" => RenderOrder.RightUp,
|
||||||
|
"left-down" => RenderOrder.LeftDown,
|
||||||
|
"left-up" => RenderOrder.LeftUp,
|
||||||
|
_ => throw new JsonException($"Unknown render order '{s}'")
|
||||||
|
}, RenderOrder.RightDown);
|
||||||
|
var compressionLevel = element.GetOptionalProperty<int>("compressionlevel", -1);
|
||||||
|
var width = element.GetRequiredProperty<uint>("width");
|
||||||
|
var height = element.GetRequiredProperty<uint>("height");
|
||||||
|
var tileWidth = element.GetRequiredProperty<uint>("tilewidth");
|
||||||
|
var tileHeight = element.GetRequiredProperty<uint>("tileheight");
|
||||||
|
var hexSideLength = element.GetOptionalProperty<uint?>("hexsidelength", null);
|
||||||
|
var staggerAxis = element.GetOptionalPropertyParseable<StaggerAxis?>("staggeraxis", s => s switch
|
||||||
|
{
|
||||||
|
"x" => StaggerAxis.X,
|
||||||
|
"y" => StaggerAxis.Y,
|
||||||
|
_ => throw new JsonException($"Unknown stagger axis '{s}'")
|
||||||
|
}, null);
|
||||||
|
var staggerIndex = element.GetOptionalPropertyParseable<StaggerIndex?>("staggerindex", s => s switch
|
||||||
|
{
|
||||||
|
"odd" => StaggerIndex.Odd,
|
||||||
|
"even" => StaggerIndex.Even,
|
||||||
|
_ => throw new JsonException($"Unknown stagger index '{s}'")
|
||||||
|
}, null);
|
||||||
|
var parallaxOriginX = element.GetOptionalProperty<float>("parallaxoriginx", 0.0f);
|
||||||
|
var parallaxOriginY = element.GetOptionalProperty<float>("parallaxoriginy", 0.0f);
|
||||||
|
var backgroundColor = element.GetOptionalPropertyParseable<Color>("backgroundcolor", s => Color.Parse(s, CultureInfo.InvariantCulture), Color.Parse("#00000000", CultureInfo.InvariantCulture));
|
||||||
|
var nextLayerID = element.GetRequiredProperty<uint>("nextlayerid");
|
||||||
|
var nextObjectID = element.GetRequiredProperty<uint>("nextobjectid");
|
||||||
|
var infinite = element.GetOptionalProperty<bool>("infinite", false);
|
||||||
|
|
||||||
|
var properties = element.GetOptionalPropertyCustom<Dictionary<string, IProperty>?>("properties", el => ReadProperties(el, customTypeDefinitions), null);
|
||||||
|
|
||||||
|
List<BaseLayer> layers = element.GetOptionalPropertyCustom<List<BaseLayer>>("layers", e => e.GetValueAsList<BaseLayer>(el => ReadLayer(el, externalTemplateResolver, customTypeDefinitions)), []);
|
||||||
|
List<Tileset> tilesets = element.GetOptionalPropertyCustom<List<Tileset>>("tilesets", e => e.GetValueAsList<Tileset>(el => ReadTileset(el, externalTilesetResolver, externalTemplateResolver, customTypeDefinitions)), []);
|
||||||
|
|
||||||
|
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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
289
DotTiled/Serialization/Tmj/Tmj.ObjectLayer.cs
Normal file
289
DotTiled/Serialization/Tmj/Tmj.ObjectLayer.cs
Normal file
|
@ -0,0 +1,289 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace DotTiled;
|
||||||
|
|
||||||
|
internal partial class Tmj
|
||||||
|
{
|
||||||
|
internal static ObjectLayer ReadObjectLayer(
|
||||||
|
JsonElement element,
|
||||||
|
Func<string, Template> externalTemplateResolver,
|
||||||
|
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions)
|
||||||
|
{
|
||||||
|
var id = element.GetRequiredProperty<uint>("id");
|
||||||
|
var name = element.GetRequiredProperty<string>("name");
|
||||||
|
var @class = element.GetOptionalProperty<string>("class", "");
|
||||||
|
var opacity = element.GetOptionalProperty<float>("opacity", 1.0f);
|
||||||
|
var visible = element.GetOptionalProperty<bool>("visible", true);
|
||||||
|
var tintColor = element.GetOptionalPropertyParseable<Color?>("tintcolor", s => Color.Parse(s, CultureInfo.InvariantCulture), null);
|
||||||
|
var offsetX = element.GetOptionalProperty<float>("offsetx", 0.0f);
|
||||||
|
var offsetY = element.GetOptionalProperty<float>("offsety", 0.0f);
|
||||||
|
var parallaxX = element.GetOptionalProperty<float>("parallaxx", 1.0f);
|
||||||
|
var parallaxY = element.GetOptionalProperty<float>("parallaxy", 1.0f);
|
||||||
|
var properties = element.GetOptionalPropertyCustom<Dictionary<string, IProperty>?>("properties", e => ReadProperties(e, customTypeDefinitions), null);
|
||||||
|
|
||||||
|
var x = element.GetOptionalProperty<uint>("x", 0);
|
||||||
|
var y = element.GetOptionalProperty<uint>("y", 0);
|
||||||
|
var width = element.GetOptionalProperty<uint?>("width", null);
|
||||||
|
var height = element.GetOptionalProperty<uint?>("height", null);
|
||||||
|
var color = element.GetOptionalPropertyParseable<Color?>("color", s => Color.Parse(s, CultureInfo.InvariantCulture), null);
|
||||||
|
var drawOrder = element.GetOptionalPropertyParseable<DrawOrder>("draworder", s => s switch
|
||||||
|
{
|
||||||
|
"topdown" => DrawOrder.TopDown,
|
||||||
|
"index" => DrawOrder.Index,
|
||||||
|
_ => throw new JsonException($"Unknown draw order '{s}'.")
|
||||||
|
}, DrawOrder.TopDown);
|
||||||
|
|
||||||
|
var objects = element.GetOptionalPropertyCustom<List<Object>>("objects", e => e.GetValueAsList<Object>(el => ReadObject(el, externalTemplateResolver, customTypeDefinitions)), []);
|
||||||
|
|
||||||
|
return new ObjectLayer
|
||||||
|
{
|
||||||
|
ID = id,
|
||||||
|
Name = name,
|
||||||
|
Class = @class,
|
||||||
|
Opacity = opacity,
|
||||||
|
Visible = visible,
|
||||||
|
TintColor = tintColor,
|
||||||
|
OffsetX = offsetX,
|
||||||
|
OffsetY = offsetY,
|
||||||
|
ParallaxX = parallaxX,
|
||||||
|
ParallaxY = parallaxY,
|
||||||
|
Properties = properties,
|
||||||
|
X = x,
|
||||||
|
Y = y,
|
||||||
|
Width = width,
|
||||||
|
Height = height,
|
||||||
|
Color = color,
|
||||||
|
DrawOrder = drawOrder,
|
||||||
|
Objects = objects
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static Object ReadObject(
|
||||||
|
JsonElement element,
|
||||||
|
Func<string, Template> externalTemplateResolver,
|
||||||
|
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
bool ellipseDefault = false;
|
||||||
|
bool pointDefault = false;
|
||||||
|
List<Vector2>? polygonDefault = null;
|
||||||
|
List<Vector2>? polylineDefault = null;
|
||||||
|
Dictionary<string, IProperty>? propertiesDefault = null;
|
||||||
|
|
||||||
|
var template = element.GetOptionalProperty<string?>("template", null);
|
||||||
|
if (template is not null)
|
||||||
|
{
|
||||||
|
var resolvedTemplate = externalTemplateResolver(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;
|
||||||
|
ellipseDefault = templObj is EllipseObject;
|
||||||
|
pointDefault = templObj is PointObject;
|
||||||
|
polygonDefault = (templObj is PolygonObject polygonObj) ? polygonObj.Points : null;
|
||||||
|
polylineDefault = (templObj is PolylineObject polylineObj) ? polylineObj.Points : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ellipse = element.GetOptionalProperty<bool>("ellipse", ellipseDefault);
|
||||||
|
var gid = element.GetOptionalProperty<uint?>("gid", gidDefault);
|
||||||
|
var height = element.GetOptionalProperty<float>("height", heightDefault);
|
||||||
|
var id = element.GetOptionalProperty<uint?>("id", idDefault);
|
||||||
|
var name = element.GetOptionalProperty<string>("name", nameDefault);
|
||||||
|
var point = element.GetOptionalProperty<bool>("point", pointDefault);
|
||||||
|
var polygon = element.GetOptionalPropertyCustom<List<Vector2>?>("polygon", e => ReadPoints(e), polygonDefault);
|
||||||
|
var polyline = element.GetOptionalPropertyCustom<List<Vector2>?>("polyline", e => ReadPoints(e), polylineDefault);
|
||||||
|
var properties = element.GetOptionalPropertyCustom<Dictionary<string, IProperty>?>("properties", e => ReadProperties(e, customTypeDefinitions), propertiesDefault);
|
||||||
|
var rotation = element.GetOptionalProperty<float>("rotation", rotationDefault);
|
||||||
|
var text = element.GetOptionalPropertyCustom<TextObject?>("text", ReadText, null);
|
||||||
|
var type = element.GetOptionalProperty<string>("type", typeDefault);
|
||||||
|
var visible = element.GetOptionalProperty<bool>("visible", visibleDefault);
|
||||||
|
var width = element.GetOptionalProperty<float>("width", widthDefault);
|
||||||
|
var x = element.GetOptionalProperty<float>("x", xDefault);
|
||||||
|
var y = element.GetOptionalProperty<float>("y", yDefault);
|
||||||
|
|
||||||
|
if (ellipse)
|
||||||
|
{
|
||||||
|
return new EllipseObject
|
||||||
|
{
|
||||||
|
ID = id,
|
||||||
|
Name = name,
|
||||||
|
Type = type,
|
||||||
|
X = x,
|
||||||
|
Y = y,
|
||||||
|
Width = width,
|
||||||
|
Height = height,
|
||||||
|
Rotation = rotation,
|
||||||
|
GID = gid,
|
||||||
|
Visible = visible,
|
||||||
|
Template = template,
|
||||||
|
Properties = properties
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (point)
|
||||||
|
{
|
||||||
|
return new PointObject
|
||||||
|
{
|
||||||
|
ID = id,
|
||||||
|
Name = name,
|
||||||
|
Type = type,
|
||||||
|
X = x,
|
||||||
|
Y = y,
|
||||||
|
Width = width,
|
||||||
|
Height = height,
|
||||||
|
Rotation = rotation,
|
||||||
|
GID = gid,
|
||||||
|
Visible = visible,
|
||||||
|
Template = template,
|
||||||
|
Properties = properties
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (polygon is not null)
|
||||||
|
{
|
||||||
|
return new PolygonObject
|
||||||
|
{
|
||||||
|
ID = id,
|
||||||
|
Name = name,
|
||||||
|
Type = type,
|
||||||
|
X = x,
|
||||||
|
Y = y,
|
||||||
|
Width = width,
|
||||||
|
Height = height,
|
||||||
|
Rotation = rotation,
|
||||||
|
GID = gid,
|
||||||
|
Visible = visible,
|
||||||
|
Template = template,
|
||||||
|
Properties = properties,
|
||||||
|
Points = polygon
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (polyline is not null)
|
||||||
|
{
|
||||||
|
return new PolylineObject
|
||||||
|
{
|
||||||
|
ID = id,
|
||||||
|
Name = name,
|
||||||
|
Type = type,
|
||||||
|
X = x,
|
||||||
|
Y = y,
|
||||||
|
Width = width,
|
||||||
|
Height = height,
|
||||||
|
Rotation = rotation,
|
||||||
|
GID = gid,
|
||||||
|
Visible = visible,
|
||||||
|
Template = template,
|
||||||
|
Properties = properties,
|
||||||
|
Points = polyline
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text is not null)
|
||||||
|
{
|
||||||
|
text.ID = id;
|
||||||
|
text.Name = name;
|
||||||
|
text.Type = type;
|
||||||
|
text.X = x;
|
||||||
|
text.Y = y;
|
||||||
|
text.Width = width;
|
||||||
|
text.Height = height;
|
||||||
|
text.Rotation = rotation;
|
||||||
|
text.GID = gid;
|
||||||
|
text.Visible = visible;
|
||||||
|
text.Template = template;
|
||||||
|
text.Properties = properties;
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new RectangleObject
|
||||||
|
{
|
||||||
|
ID = id,
|
||||||
|
Name = name,
|
||||||
|
Type = type,
|
||||||
|
X = x,
|
||||||
|
Y = y,
|
||||||
|
Width = width,
|
||||||
|
Height = height,
|
||||||
|
Rotation = rotation,
|
||||||
|
GID = gid,
|
||||||
|
Visible = visible,
|
||||||
|
Template = template,
|
||||||
|
Properties = properties
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static List<Vector2> ReadPoints(JsonElement element) =>
|
||||||
|
element.GetValueAsList<Vector2>(e =>
|
||||||
|
{
|
||||||
|
var x = e.GetRequiredProperty<float>("x");
|
||||||
|
var y = e.GetRequiredProperty<float>("y");
|
||||||
|
return new Vector2(x, y);
|
||||||
|
});
|
||||||
|
|
||||||
|
internal static TextObject ReadText(JsonElement element)
|
||||||
|
{
|
||||||
|
var bold = element.GetOptionalProperty<bool>("bold", false);
|
||||||
|
var color = element.GetOptionalPropertyParseable<Color>("color", s => Color.Parse(s, CultureInfo.InvariantCulture), Color.Parse("#00000000", CultureInfo.InvariantCulture));
|
||||||
|
var fontfamily = element.GetOptionalProperty<string>("fontfamily", "sans-serif");
|
||||||
|
var halign = element.GetOptionalPropertyParseable<TextHorizontalAlignment>("halign", s => s switch
|
||||||
|
{
|
||||||
|
"left" => TextHorizontalAlignment.Left,
|
||||||
|
"center" => TextHorizontalAlignment.Center,
|
||||||
|
"right" => TextHorizontalAlignment.Right,
|
||||||
|
_ => throw new JsonException($"Unknown horizontal alignment '{s}'.")
|
||||||
|
}, TextHorizontalAlignment.Left);
|
||||||
|
var italic = element.GetOptionalProperty<bool>("italic", false);
|
||||||
|
var kerning = element.GetOptionalProperty<bool>("kerning", true);
|
||||||
|
var pixelsize = element.GetOptionalProperty<int>("pixelsize", 16);
|
||||||
|
var strikeout = element.GetOptionalProperty<bool>("strikeout", false);
|
||||||
|
var text = element.GetRequiredProperty<string>("text");
|
||||||
|
var underline = element.GetOptionalProperty<bool>("underline", false);
|
||||||
|
var valign = element.GetOptionalPropertyParseable<TextVerticalAlignment>("valign", s => s switch
|
||||||
|
{
|
||||||
|
"top" => TextVerticalAlignment.Top,
|
||||||
|
"center" => TextVerticalAlignment.Center,
|
||||||
|
"bottom" => TextVerticalAlignment.Bottom,
|
||||||
|
_ => throw new JsonException($"Unknown vertical alignment '{s}'.")
|
||||||
|
}, TextVerticalAlignment.Top);
|
||||||
|
var wrap = element.GetOptionalProperty<bool>("wrap", false);
|
||||||
|
|
||||||
|
return new TextObject
|
||||||
|
{
|
||||||
|
Bold = bold,
|
||||||
|
Color = color,
|
||||||
|
FontFamily = fontfamily,
|
||||||
|
HorizontalAlignment = halign,
|
||||||
|
Italic = italic,
|
||||||
|
Kerning = kerning,
|
||||||
|
PixelSize = pixelsize,
|
||||||
|
Strikeout = strikeout,
|
||||||
|
Text = text,
|
||||||
|
Underline = underline,
|
||||||
|
VerticalAlignment = valign,
|
||||||
|
Wrap = wrap
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
108
DotTiled/Serialization/Tmj/Tmj.Properties.cs
Normal file
108
DotTiled/Serialization/Tmj/Tmj.Properties.cs
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace DotTiled;
|
||||||
|
|
||||||
|
internal partial class Tmj
|
||||||
|
{
|
||||||
|
internal static Dictionary<string, IProperty> ReadProperties(
|
||||||
|
JsonElement element,
|
||||||
|
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions) =>
|
||||||
|
element.GetValueAsList<IProperty>(e =>
|
||||||
|
{
|
||||||
|
var name = e.GetRequiredProperty<string>("name");
|
||||||
|
var type = e.GetOptionalPropertyParseable<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 JsonException("Invalid property type")
|
||||||
|
}, PropertyType.String);
|
||||||
|
|
||||||
|
IProperty property = type switch
|
||||||
|
{
|
||||||
|
PropertyType.String => new StringProperty { Name = name, Value = e.GetRequiredProperty<string>("value") },
|
||||||
|
PropertyType.Int => new IntProperty { Name = name, Value = e.GetRequiredProperty<int>("value") },
|
||||||
|
PropertyType.Float => new FloatProperty { Name = name, Value = e.GetRequiredProperty<float>("value") },
|
||||||
|
PropertyType.Bool => new BoolProperty { Name = name, Value = e.GetRequiredProperty<bool>("value") },
|
||||||
|
PropertyType.Color => new ColorProperty { Name = name, Value = e.GetRequiredPropertyParseable<Color>("value") },
|
||||||
|
PropertyType.File => new FileProperty { Name = name, Value = e.GetRequiredProperty<string>("value") },
|
||||||
|
PropertyType.Object => new ObjectProperty { Name = name, Value = e.GetRequiredProperty<uint>("value") },
|
||||||
|
PropertyType.Class => ReadClassProperty(e, customTypeDefinitions),
|
||||||
|
_ => throw new JsonException("Invalid property type")
|
||||||
|
};
|
||||||
|
|
||||||
|
return property!;
|
||||||
|
}).ToDictionary(p => p.Name);
|
||||||
|
|
||||||
|
internal static ClassProperty ReadClassProperty(
|
||||||
|
JsonElement element,
|
||||||
|
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions)
|
||||||
|
{
|
||||||
|
var name = element.GetRequiredProperty<string>("name");
|
||||||
|
var propertyType = element.GetRequiredProperty<string>("propertytype");
|
||||||
|
|
||||||
|
var customTypeDef = customTypeDefinitions.FirstOrDefault(ctd => ctd.Name == propertyType);
|
||||||
|
|
||||||
|
if (customTypeDef is CustomClassDefinition ccd)
|
||||||
|
{
|
||||||
|
var propsInType = CreateInstanceOfCustomClass(ccd);
|
||||||
|
var props = element.GetOptionalPropertyCustom<Dictionary<string, IProperty>>("value", el => ReadCustomClassProperties(el, ccd, customTypeDefinitions), []);
|
||||||
|
|
||||||
|
var mergedProps = Helpers.MergeProperties(propsInType, props);
|
||||||
|
|
||||||
|
return new ClassProperty
|
||||||
|
{
|
||||||
|
Name = name,
|
||||||
|
PropertyType = propertyType,
|
||||||
|
Properties = mergedProps
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new JsonException($"Unknown custom class '{propertyType}'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static Dictionary<string, IProperty> ReadCustomClassProperties(
|
||||||
|
JsonElement element,
|
||||||
|
CustomClassDefinition customClassDefinition,
|
||||||
|
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions)
|
||||||
|
{
|
||||||
|
Dictionary<string, IProperty> resultingProps = [];
|
||||||
|
|
||||||
|
foreach (var prop in customClassDefinition.Members)
|
||||||
|
{
|
||||||
|
if (!element.TryGetProperty(prop.Name, out var propElement))
|
||||||
|
continue; // Property not present in element, therefore will use default value
|
||||||
|
|
||||||
|
IProperty property = prop.Type switch
|
||||||
|
{
|
||||||
|
PropertyType.String => new StringProperty { Name = prop.Name, Value = propElement.GetValueAs<string>() },
|
||||||
|
PropertyType.Int => new IntProperty { Name = prop.Name, Value = propElement.GetValueAs<int>() },
|
||||||
|
PropertyType.Float => new FloatProperty { Name = prop.Name, Value = propElement.GetValueAs<float>() },
|
||||||
|
PropertyType.Bool => new BoolProperty { Name = prop.Name, Value = propElement.GetValueAs<bool>() },
|
||||||
|
PropertyType.Color => new ColorProperty { Name = prop.Name, Value = Color.Parse(propElement.GetValueAs<string>(), CultureInfo.InvariantCulture) },
|
||||||
|
PropertyType.File => new FileProperty { Name = prop.Name, Value = propElement.GetValueAs<string>() },
|
||||||
|
PropertyType.Object => new ObjectProperty { Name = prop.Name, Value = propElement.GetValueAs<uint>() },
|
||||||
|
PropertyType.Class => ReadClassProperty(propElement, customTypeDefinitions),
|
||||||
|
_ => throw new JsonException("Invalid property type")
|
||||||
|
};
|
||||||
|
|
||||||
|
resultingProps[prop.Name] = property;
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultingProps;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static Dictionary<string, IProperty> CreateInstanceOfCustomClass(CustomClassDefinition customClassDefinition)
|
||||||
|
{
|
||||||
|
return customClassDefinition.Members.ToDictionary(m => m.Name, m => m.Clone());
|
||||||
|
}
|
||||||
|
}
|
27
DotTiled/Serialization/Tmj/Tmj.Template.cs
Normal file
27
DotTiled/Serialization/Tmj/Tmj.Template.cs
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace DotTiled;
|
||||||
|
|
||||||
|
internal partial class Tmj
|
||||||
|
{
|
||||||
|
internal static Template ReadTemplate(
|
||||||
|
JsonElement element,
|
||||||
|
Func<string, Tileset> externalTilesetResolver,
|
||||||
|
Func<string, Template> externalTemplateResolver,
|
||||||
|
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions)
|
||||||
|
{
|
||||||
|
var type = element.GetRequiredProperty<string>("type");
|
||||||
|
var tileset = element.GetOptionalPropertyCustom<Tileset?>("tileset", el => ReadTileset(el, externalTilesetResolver, externalTemplateResolver, customTypeDefinitions), null);
|
||||||
|
var @object = element.GetRequiredPropertyCustom<Object>("object", el => ReadObject(el, externalTemplateResolver, customTypeDefinitions));
|
||||||
|
|
||||||
|
return new Template
|
||||||
|
{
|
||||||
|
Tileset = tileset,
|
||||||
|
Object = @object
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
75
DotTiled/Serialization/Tmj/Tmj.TileLayer.cs
Normal file
75
DotTiled/Serialization/Tmj/Tmj.TileLayer.cs
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace DotTiled;
|
||||||
|
|
||||||
|
internal partial class Tmj
|
||||||
|
{
|
||||||
|
|
||||||
|
internal static TileLayer ReadTileLayer(
|
||||||
|
JsonElement element,
|
||||||
|
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions)
|
||||||
|
{
|
||||||
|
var compression = element.GetOptionalPropertyParseable<DataCompression?>("compression", s => s switch
|
||||||
|
{
|
||||||
|
"zlib" => DataCompression.ZLib,
|
||||||
|
"gzip" => DataCompression.GZip,
|
||||||
|
"" => null,
|
||||||
|
_ => throw new JsonException($"Unsupported compression '{s}'.")
|
||||||
|
}, null);
|
||||||
|
var encoding = element.GetOptionalPropertyParseable<DataEncoding>("encoding", s => s switch
|
||||||
|
{
|
||||||
|
"csv" => DataEncoding.Csv,
|
||||||
|
"base64" => DataEncoding.Base64,
|
||||||
|
_ => throw new JsonException($"Unsupported encoding '{s}'.")
|
||||||
|
}, DataEncoding.Csv);
|
||||||
|
var chunks = element.GetOptionalPropertyCustom<Data?>("chunks", e => ReadDataAsChunks(e, compression, encoding), null);
|
||||||
|
var @class = element.GetOptionalProperty<string>("class", "");
|
||||||
|
var data = element.GetOptionalPropertyCustom<Data?>("data", e => ReadDataWithoutChunks(e, compression, encoding), null);
|
||||||
|
var height = element.GetRequiredProperty<uint>("height");
|
||||||
|
var id = element.GetRequiredProperty<uint>("id");
|
||||||
|
var name = element.GetRequiredProperty<string>("name");
|
||||||
|
var offsetX = element.GetOptionalProperty<float>("offsetx", 0.0f);
|
||||||
|
var offsetY = element.GetOptionalProperty<float>("offsety", 0.0f);
|
||||||
|
var opacity = element.GetOptionalProperty<float>("opacity", 1.0f);
|
||||||
|
var parallaxx = element.GetOptionalProperty<float>("parallaxx", 1.0f);
|
||||||
|
var parallaxy = element.GetOptionalProperty<float>("parallaxy", 1.0f);
|
||||||
|
var properties = element.GetOptionalPropertyCustom<Dictionary<string, IProperty>?>("properties", e => ReadProperties(e, customTypeDefinitions), null);
|
||||||
|
var repeatX = element.GetOptionalProperty<bool>("repeatx", false);
|
||||||
|
var repeatY = element.GetOptionalProperty<bool>("repeaty", false);
|
||||||
|
var startX = element.GetOptionalProperty<int>("startx", 0);
|
||||||
|
var startY = element.GetOptionalProperty<int>("starty", 0);
|
||||||
|
var tintColor = element.GetOptionalPropertyParseable<Color?>("tintcolor", s => Color.Parse(s, CultureInfo.InvariantCulture), null);
|
||||||
|
var transparentColor = element.GetOptionalPropertyParseable<Color?>("transparentcolor", s => Color.Parse(s, CultureInfo.InvariantCulture), null);
|
||||||
|
var visible = element.GetOptionalProperty<bool>("visible", true);
|
||||||
|
var width = element.GetRequiredProperty<uint>("width");
|
||||||
|
var x = element.GetRequiredProperty<uint>("x");
|
||||||
|
var y = element.GetRequiredProperty<uint>("y");
|
||||||
|
|
||||||
|
if ((data ?? chunks) is null)
|
||||||
|
throw new JsonException("Tile layer does not contain data.");
|
||||||
|
|
||||||
|
return new TileLayer
|
||||||
|
{
|
||||||
|
ID = id,
|
||||||
|
Name = name,
|
||||||
|
Class = @class,
|
||||||
|
Opacity = opacity,
|
||||||
|
Visible = visible,
|
||||||
|
TintColor = tintColor,
|
||||||
|
OffsetX = offsetX,
|
||||||
|
OffsetY = offsetY,
|
||||||
|
ParallaxX = parallaxx,
|
||||||
|
ParallaxY = parallaxy,
|
||||||
|
Properties = properties,
|
||||||
|
X = x,
|
||||||
|
Y = y,
|
||||||
|
Width = width,
|
||||||
|
Height = height,
|
||||||
|
Data = data ?? chunks
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
259
DotTiled/Serialization/Tmj/Tmj.Tileset.cs
Normal file
259
DotTiled/Serialization/Tmj/Tmj.Tileset.cs
Normal file
|
@ -0,0 +1,259 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace DotTiled;
|
||||||
|
|
||||||
|
internal partial class Tmj
|
||||||
|
{
|
||||||
|
internal static Tileset ReadTileset(
|
||||||
|
JsonElement element,
|
||||||
|
Func<string, Tileset>? externalTilesetResolver,
|
||||||
|
Func<string, Template> externalTemplateResolver,
|
||||||
|
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions)
|
||||||
|
{
|
||||||
|
var backgroundColor = element.GetOptionalPropertyParseable<Color?>("backgroundcolor", s => Color.Parse(s, CultureInfo.InvariantCulture), null);
|
||||||
|
var @class = element.GetOptionalProperty<string>("class", "");
|
||||||
|
var columns = element.GetOptionalProperty<uint?>("columns", null);
|
||||||
|
var fillMode = element.GetOptionalPropertyParseable<FillMode>("fillmode", s => s switch
|
||||||
|
{
|
||||||
|
"stretch" => FillMode.Stretch,
|
||||||
|
"preserve-aspect-fit" => FillMode.PreserveAspectFit,
|
||||||
|
_ => throw new JsonException($"Unknown fill mode '{s}'")
|
||||||
|
}, FillMode.Stretch);
|
||||||
|
var firstGID = element.GetOptionalProperty<uint?>("firstgid", null);
|
||||||
|
var grid = element.GetOptionalPropertyCustom<Grid?>("grid", ReadGrid, null);
|
||||||
|
var image = element.GetOptionalProperty<string?>("image", null);
|
||||||
|
var imageHeight = element.GetOptionalProperty<uint?>("imageheight", null);
|
||||||
|
var imageWidth = element.GetOptionalProperty<uint?>("imagewidth", null);
|
||||||
|
var margin = element.GetOptionalProperty<uint?>("margin", null);
|
||||||
|
var name = element.GetOptionalProperty<string?>("name", null);
|
||||||
|
var objectAlignment = element.GetOptionalPropertyParseable<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 JsonException($"Unknown object alignment '{s}'")
|
||||||
|
}, ObjectAlignment.Unspecified);
|
||||||
|
var properties = element.GetOptionalPropertyCustom<Dictionary<string, IProperty>?>("properties", el => ReadProperties(el, customTypeDefinitions), null);
|
||||||
|
var source = element.GetOptionalProperty<string?>("source", null);
|
||||||
|
var spacing = element.GetOptionalProperty<uint?>("spacing", null);
|
||||||
|
var tileCount = element.GetOptionalProperty<uint?>("tilecount", null);
|
||||||
|
var tiledVersion = element.GetOptionalProperty<string?>("tiledversion", null);
|
||||||
|
var tileHeight = element.GetOptionalProperty<uint?>("tileheight", null);
|
||||||
|
var tileOffset = element.GetOptionalPropertyCustom<TileOffset?>("tileoffset", ReadTileOffset, null);
|
||||||
|
var tileRenderSize = element.GetOptionalPropertyParseable<TileRenderSize>("tilerendersize", s => s switch
|
||||||
|
{
|
||||||
|
"tile" => TileRenderSize.Tile,
|
||||||
|
"grid" => TileRenderSize.Grid,
|
||||||
|
_ => throw new JsonException($"Unknown tile render size '{s}'")
|
||||||
|
}, TileRenderSize.Tile);
|
||||||
|
var tiles = element.GetOptionalPropertyCustom<List<Tile>>("tiles", el => ReadTiles(el, externalTemplateResolver, customTypeDefinitions), []);
|
||||||
|
var tileWidth = element.GetOptionalProperty<uint?>("tilewidth", null);
|
||||||
|
var transparentColor = element.GetOptionalPropertyParseable<Color?>("transparentcolor", s => Color.Parse(s, CultureInfo.InvariantCulture), null);
|
||||||
|
var type = element.GetOptionalProperty<string?>("type", null);
|
||||||
|
var version = element.GetOptionalProperty<string?>("version", null);
|
||||||
|
//var wangsets = element.GetOptionalPropertyCustom<List<Wangset>?>("wangsets", ReadWangSets, null);
|
||||||
|
|
||||||
|
if (source is not null)
|
||||||
|
{
|
||||||
|
if (externalTilesetResolver is null)
|
||||||
|
throw new JsonException("External tileset resolver is required to resolve external tilesets.");
|
||||||
|
|
||||||
|
var resolvedTileset = externalTilesetResolver(source);
|
||||||
|
resolvedTileset.FirstGID = firstGID;
|
||||||
|
resolvedTileset.Source = source;
|
||||||
|
return resolvedTileset;
|
||||||
|
}
|
||||||
|
|
||||||
|
var imageModel = new Image
|
||||||
|
{
|
||||||
|
Format = Helpers.ParseImageFormatFromSource(image!),
|
||||||
|
Source = image,
|
||||||
|
Height = imageHeight,
|
||||||
|
Width = imageWidth,
|
||||||
|
TransparentColor = transparentColor
|
||||||
|
};
|
||||||
|
|
||||||
|
return new Tileset
|
||||||
|
{
|
||||||
|
Class = @class,
|
||||||
|
Columns = columns,
|
||||||
|
FillMode = fillMode,
|
||||||
|
FirstGID = firstGID,
|
||||||
|
Grid = grid,
|
||||||
|
Image = imageModel,
|
||||||
|
Margin = margin,
|
||||||
|
Name = name,
|
||||||
|
ObjectAlignment = objectAlignment,
|
||||||
|
Properties = properties,
|
||||||
|
Source = source,
|
||||||
|
Spacing = spacing,
|
||||||
|
TileCount = tileCount,
|
||||||
|
TiledVersion = tiledVersion,
|
||||||
|
TileHeight = tileHeight,
|
||||||
|
TileOffset = tileOffset,
|
||||||
|
RenderSize = tileRenderSize,
|
||||||
|
Tiles = tiles,
|
||||||
|
TileWidth = tileWidth,
|
||||||
|
Version = version,
|
||||||
|
//Wangsets = wangsets
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static Grid ReadGrid(JsonElement element)
|
||||||
|
{
|
||||||
|
var orientation = element.GetOptionalPropertyParseable<GridOrientation>("orientation", s => s switch
|
||||||
|
{
|
||||||
|
"orthogonal" => GridOrientation.Orthogonal,
|
||||||
|
"isometric" => GridOrientation.Isometric,
|
||||||
|
_ => throw new JsonException($"Unknown grid orientation '{s}'")
|
||||||
|
}, GridOrientation.Orthogonal);
|
||||||
|
var height = element.GetRequiredProperty<uint>("height");
|
||||||
|
var width = element.GetRequiredProperty<uint>("width");
|
||||||
|
|
||||||
|
return new Grid
|
||||||
|
{
|
||||||
|
Orientation = orientation,
|
||||||
|
Height = height,
|
||||||
|
Width = width
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static TileOffset ReadTileOffset(JsonElement element)
|
||||||
|
{
|
||||||
|
var x = element.GetRequiredProperty<int>("x");
|
||||||
|
var y = element.GetRequiredProperty<int>("y");
|
||||||
|
|
||||||
|
return new TileOffset
|
||||||
|
{
|
||||||
|
X = x,
|
||||||
|
Y = y
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static List<Tile> ReadTiles(
|
||||||
|
JsonElement element,
|
||||||
|
Func<string, Template> externalTemplateResolver,
|
||||||
|
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions) =>
|
||||||
|
element.GetValueAsList<Tile>(e =>
|
||||||
|
{
|
||||||
|
var animation = e.GetOptionalPropertyCustom<List<Frame>?>("animation", e => e.GetValueAsList<Frame>(ReadFrame), null);
|
||||||
|
var id = e.GetRequiredProperty<uint>("id");
|
||||||
|
var image = e.GetOptionalProperty<string?>("image", null);
|
||||||
|
var imageHeight = e.GetOptionalProperty<uint?>("imageheight", null);
|
||||||
|
var imageWidth = e.GetOptionalProperty<uint?>("imagewidth", null);
|
||||||
|
var x = e.GetOptionalProperty<uint>("x", 0);
|
||||||
|
var y = e.GetOptionalProperty<uint>("y", 0);
|
||||||
|
var width = e.GetOptionalProperty<uint>("width", imageWidth ?? 0);
|
||||||
|
var height = e.GetOptionalProperty<uint>("height", imageHeight ?? 0);
|
||||||
|
var objectGroup = e.GetOptionalPropertyCustom<ObjectLayer?>("objectgroup", e => ReadObjectLayer(e, externalTemplateResolver, customTypeDefinitions), null);
|
||||||
|
var probability = e.GetOptionalProperty<float>("probability", 1.0f);
|
||||||
|
var properties = e.GetOptionalPropertyCustom<Dictionary<string, IProperty>?>("properties", el => ReadProperties(el, customTypeDefinitions), null);
|
||||||
|
// var terrain, replaced by wangsets
|
||||||
|
var type = e.GetOptionalProperty<string>("type", "");
|
||||||
|
|
||||||
|
var imageModel = image != null ? new Image
|
||||||
|
{
|
||||||
|
Format = Helpers.ParseImageFormatFromSource(image),
|
||||||
|
Source = image,
|
||||||
|
Height = imageHeight ?? 0,
|
||||||
|
Width = imageWidth ?? 0
|
||||||
|
} : null;
|
||||||
|
|
||||||
|
return new Tile
|
||||||
|
{
|
||||||
|
Animation = animation,
|
||||||
|
ID = id,
|
||||||
|
Image = imageModel,
|
||||||
|
X = x,
|
||||||
|
Y = y,
|
||||||
|
Width = width,
|
||||||
|
Height = height,
|
||||||
|
ObjectLayer = objectGroup,
|
||||||
|
Probability = probability,
|
||||||
|
Properties = properties,
|
||||||
|
Type = type
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
internal static Frame ReadFrame(JsonElement element)
|
||||||
|
{
|
||||||
|
var duration = element.GetRequiredProperty<uint>("duration");
|
||||||
|
var tileID = element.GetRequiredProperty<uint>("tileid");
|
||||||
|
|
||||||
|
return new Frame
|
||||||
|
{
|
||||||
|
Duration = duration,
|
||||||
|
TileID = tileID
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static Wangset ReadWangset(
|
||||||
|
JsonElement element,
|
||||||
|
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions)
|
||||||
|
{
|
||||||
|
var @clalss = element.GetOptionalProperty<string>("class", "");
|
||||||
|
var colors = element.GetOptionalPropertyCustom<List<WangColor>>("colors", e => e.GetValueAsList<WangColor>(el => ReadWangColor(el, customTypeDefinitions)), []);
|
||||||
|
var name = element.GetRequiredProperty<string>("name");
|
||||||
|
var properties = element.GetOptionalPropertyCustom<Dictionary<string, IProperty>?>("properties", e => ReadProperties(e, customTypeDefinitions), null);
|
||||||
|
var tile = element.GetOptionalProperty<uint>("tile", 0);
|
||||||
|
var type = element.GetOptionalProperty<string>("type", "");
|
||||||
|
var wangTiles = element.GetOptionalPropertyCustom<List<WangTile>>("wangtiles", e => e.GetValueAsList<WangTile>(ReadWangTile), []);
|
||||||
|
|
||||||
|
return new Wangset
|
||||||
|
{
|
||||||
|
Class = @clalss,
|
||||||
|
WangColors = colors,
|
||||||
|
Name = name,
|
||||||
|
Properties = properties,
|
||||||
|
Tile = tile,
|
||||||
|
WangTiles = wangTiles
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static WangColor ReadWangColor(
|
||||||
|
JsonElement element,
|
||||||
|
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions)
|
||||||
|
{
|
||||||
|
var @class = element.GetOptionalProperty<string>("class", "");
|
||||||
|
var color = element.GetRequiredPropertyParseable<Color>("color", s => Color.Parse(s, CultureInfo.InvariantCulture));
|
||||||
|
var name = element.GetRequiredProperty<string>("name");
|
||||||
|
var probability = element.GetOptionalProperty<float>("probability", 1.0f);
|
||||||
|
var properties = element.GetOptionalPropertyCustom<Dictionary<string, IProperty>?>("properties", e => ReadProperties(e, customTypeDefinitions), null);
|
||||||
|
var tile = element.GetOptionalProperty<uint>("tile", 0);
|
||||||
|
|
||||||
|
return new WangColor
|
||||||
|
{
|
||||||
|
Class = @class,
|
||||||
|
Color = color,
|
||||||
|
Name = name,
|
||||||
|
Probability = probability,
|
||||||
|
Properties = properties,
|
||||||
|
Tile = tile
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static WangTile ReadWangTile(JsonElement element)
|
||||||
|
{
|
||||||
|
var tileID = element.GetRequiredProperty<uint>("tileid");
|
||||||
|
var wangID = element.GetOptionalPropertyCustom<List<byte>>("wangid", e => e.GetValueAsList<byte>(el => (byte)el.GetUInt32()), []);
|
||||||
|
|
||||||
|
return new WangTile
|
||||||
|
{
|
||||||
|
TileID = tileID,
|
||||||
|
WangID = [.. wangID]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
13
DotTiled/Serialization/Tmj/Tmj.Wangset.cs
Normal file
13
DotTiled/Serialization/Tmj/Tmj.Wangset.cs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace DotTiled;
|
||||||
|
|
||||||
|
internal partial class Tmj
|
||||||
|
{
|
||||||
|
}
|
67
DotTiled/Serialization/Tmj/TmjMapReader.cs
Normal file
67
DotTiled/Serialization/Tmj/TmjMapReader.cs
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace DotTiled;
|
||||||
|
|
||||||
|
public class TmjMapReader : IMapReader
|
||||||
|
{
|
||||||
|
// External resolvers
|
||||||
|
private readonly Func<string, Tileset> _externalTilesetResolver;
|
||||||
|
private readonly Func<string, Template> _externalTemplateResolver;
|
||||||
|
|
||||||
|
private string _jsonString;
|
||||||
|
private bool disposedValue;
|
||||||
|
|
||||||
|
private readonly IReadOnlyCollection<CustomTypeDefinition> _customTypeDefinitions;
|
||||||
|
|
||||||
|
public TmjMapReader(
|
||||||
|
string jsonString,
|
||||||
|
Func<string, Tileset> externalTilesetResolver,
|
||||||
|
Func<string, Template> externalTemplateResolver,
|
||||||
|
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions)
|
||||||
|
{
|
||||||
|
_jsonString = jsonString ?? throw new ArgumentNullException(nameof(jsonString));
|
||||||
|
_externalTilesetResolver = externalTilesetResolver ?? throw new ArgumentNullException(nameof(externalTilesetResolver));
|
||||||
|
_externalTemplateResolver = externalTemplateResolver ?? throw new ArgumentNullException(nameof(externalTemplateResolver));
|
||||||
|
_customTypeDefinitions = customTypeDefinitions ?? throw new ArgumentNullException(nameof(customTypeDefinitions));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map ReadMap()
|
||||||
|
{
|
||||||
|
var jsonDoc = JsonDocument.Parse(_jsonString);
|
||||||
|
var rootElement = jsonDoc.RootElement;
|
||||||
|
return Tmj.ReadMap(rootElement, _externalTilesetResolver, _externalTemplateResolver, _customTypeDefinitions);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (!disposedValue)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
// TODO: dispose managed state (managed objects)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: free unmanaged resources (unmanaged objects) and override finalizer
|
||||||
|
// TODO: set large fields to null
|
||||||
|
disposedValue = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources
|
||||||
|
// ~TmjMapReader()
|
||||||
|
// {
|
||||||
|
// // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
||||||
|
// Dispose(disposing: false);
|
||||||
|
// }
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
||||||
|
Dispose(disposing: true);
|
||||||
|
System.GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
}
|
65
DotTiled/Serialization/Tmj/TsjTilesetReader.cs
Normal file
65
DotTiled/Serialization/Tmj/TsjTilesetReader.cs
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace DotTiled;
|
||||||
|
|
||||||
|
public class TsjTilesetReader : ITilesetReader
|
||||||
|
{
|
||||||
|
// External resolvers
|
||||||
|
private readonly Func<string, Template> _externalTemplateResolver;
|
||||||
|
|
||||||
|
private readonly string _jsonString;
|
||||||
|
private bool disposedValue;
|
||||||
|
|
||||||
|
private readonly IReadOnlyCollection<CustomTypeDefinition> _customTypeDefinitions;
|
||||||
|
|
||||||
|
public TsjTilesetReader(
|
||||||
|
string jsonString,
|
||||||
|
Func<string, Template> externalTemplateResolver,
|
||||||
|
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions)
|
||||||
|
{
|
||||||
|
_jsonString = jsonString ?? throw new ArgumentNullException(nameof(jsonString));
|
||||||
|
_externalTemplateResolver = externalTemplateResolver ?? throw new ArgumentNullException(nameof(externalTemplateResolver));
|
||||||
|
_customTypeDefinitions = customTypeDefinitions ?? throw new ArgumentNullException(nameof(customTypeDefinitions));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Tileset ReadTileset()
|
||||||
|
{
|
||||||
|
var jsonDoc = System.Text.Json.JsonDocument.Parse(_jsonString);
|
||||||
|
var rootElement = jsonDoc.RootElement;
|
||||||
|
return Tmj.ReadTileset(
|
||||||
|
rootElement,
|
||||||
|
_ => throw new NotSupportedException("External tilesets cannot refer to other external tilesets."),
|
||||||
|
_externalTemplateResolver,
|
||||||
|
_customTypeDefinitions);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (!disposedValue)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
// TODO: dispose managed state (managed objects)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: free unmanaged resources (unmanaged objects) and override finalizer
|
||||||
|
// TODO: set large fields to null
|
||||||
|
disposedValue = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources
|
||||||
|
// ~TsjTilesetReader()
|
||||||
|
// {
|
||||||
|
// // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
||||||
|
// Dispose(disposing: false);
|
||||||
|
// }
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
||||||
|
Dispose(disposing: true);
|
||||||
|
System.GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,26 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace DotTiled;
|
|
||||||
|
|
||||||
internal partial class Tmx
|
|
||||||
{
|
|
||||||
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++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -8,7 +8,11 @@ namespace DotTiled;
|
||||||
|
|
||||||
internal partial class Tmx
|
internal partial class Tmx
|
||||||
{
|
{
|
||||||
internal static Map ReadMap(XmlReader reader, Func<string, Tileset> externalTilesetResolver, Func<string, Template> externalTemplateResolver)
|
internal static Map ReadMap(
|
||||||
|
XmlReader reader,
|
||||||
|
Func<string, Tileset> externalTilesetResolver,
|
||||||
|
Func<string, Template> externalTemplateResolver,
|
||||||
|
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions)
|
||||||
{
|
{
|
||||||
// Attributes
|
// Attributes
|
||||||
var version = reader.GetRequiredAttribute("version");
|
var version = reader.GetRequiredAttribute("version");
|
||||||
|
@ -64,12 +68,12 @@ internal partial class Tmx
|
||||||
|
|
||||||
reader.ProcessChildren("map", (r, elementName) => elementName switch
|
reader.ProcessChildren("map", (r, elementName) => elementName switch
|
||||||
{
|
{
|
||||||
"properties" => () => Helpers.SetAtMostOnce(ref properties, ReadProperties(r), "Properties"),
|
"properties" => () => Helpers.SetAtMostOnce(ref properties, ReadProperties(r, customTypeDefinitions), "Properties"),
|
||||||
"tileset" => () => tilesets.Add(ReadTileset(r, externalTilesetResolver, externalTemplateResolver)),
|
"tileset" => () => tilesets.Add(ReadTileset(r, externalTilesetResolver, externalTemplateResolver, customTypeDefinitions)),
|
||||||
"layer" => () => layers.Add(ReadTileLayer(r, dataUsesChunks: infinite)),
|
"layer" => () => layers.Add(ReadTileLayer(r, infinite, customTypeDefinitions)),
|
||||||
"objectgroup" => () => layers.Add(ReadObjectLayer(r, externalTemplateResolver)),
|
"objectgroup" => () => layers.Add(ReadObjectLayer(r, externalTemplateResolver, customTypeDefinitions)),
|
||||||
"imagelayer" => () => layers.Add(ReadImageLayer(r)),
|
"imagelayer" => () => layers.Add(ReadImageLayer(r, customTypeDefinitions)),
|
||||||
"group" => () => layers.Add(ReadGroup(r, externalTemplateResolver)),
|
"group" => () => layers.Add(ReadGroup(r, externalTemplateResolver, customTypeDefinitions)),
|
||||||
_ => r.Skip
|
_ => r.Skip
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,10 @@ namespace DotTiled;
|
||||||
|
|
||||||
internal partial class Tmx
|
internal partial class Tmx
|
||||||
{
|
{
|
||||||
internal static ObjectLayer ReadObjectLayer(XmlReader reader, Func<string, Template> externalTemplateResolver)
|
internal static ObjectLayer ReadObjectLayer(
|
||||||
|
XmlReader reader,
|
||||||
|
Func<string, Template> externalTemplateResolver,
|
||||||
|
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions)
|
||||||
{
|
{
|
||||||
// Attributes
|
// Attributes
|
||||||
var id = reader.GetRequiredAttributeParseable<uint>("id");
|
var id = reader.GetRequiredAttributeParseable<uint>("id");
|
||||||
|
@ -40,8 +43,8 @@ internal partial class Tmx
|
||||||
|
|
||||||
reader.ProcessChildren("objectgroup", (r, elementName) => elementName switch
|
reader.ProcessChildren("objectgroup", (r, elementName) => elementName switch
|
||||||
{
|
{
|
||||||
"properties" => () => Helpers.SetAtMostOnce(ref properties, ReadProperties(r), "Properties"),
|
"properties" => () => Helpers.SetAtMostOnce(ref properties, ReadProperties(r, customTypeDefinitions), "Properties"),
|
||||||
"object" => () => objects.Add(ReadObject(r, externalTemplateResolver)),
|
"object" => () => objects.Add(ReadObject(r, externalTemplateResolver, customTypeDefinitions)),
|
||||||
_ => r.Skip
|
_ => r.Skip
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -68,7 +71,10 @@ internal partial class Tmx
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static Object ReadObject(XmlReader reader, Func<string, Template> externalTemplateResolver)
|
internal static Object ReadObject(
|
||||||
|
XmlReader reader,
|
||||||
|
Func<string, Template> externalTemplateResolver,
|
||||||
|
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions)
|
||||||
{
|
{
|
||||||
// Attributes
|
// Attributes
|
||||||
var template = reader.GetOptionalAttribute("template");
|
var template = reader.GetOptionalAttribute("template");
|
||||||
|
@ -122,7 +128,7 @@ internal partial class Tmx
|
||||||
|
|
||||||
reader.ProcessChildren("object", (r, elementName) => elementName switch
|
reader.ProcessChildren("object", (r, elementName) => elementName switch
|
||||||
{
|
{
|
||||||
"properties" => () => Helpers.SetAtMostOnceUsingCounter(ref properties, MergeProperties(properties, ReadProperties(r)), "Properties", ref propertiesCounter),
|
"properties" => () => Helpers.SetAtMostOnceUsingCounter(ref properties, Helpers.MergeProperties(properties, ReadProperties(r, customTypeDefinitions)), "Properties", ref propertiesCounter),
|
||||||
"ellipse" => () => Helpers.SetAtMostOnce(ref obj, ReadEllipseObject(r), "Object marker"),
|
"ellipse" => () => Helpers.SetAtMostOnce(ref obj, ReadEllipseObject(r), "Object marker"),
|
||||||
"point" => () => Helpers.SetAtMostOnce(ref obj, ReadPointObject(r), "Object marker"),
|
"point" => () => Helpers.SetAtMostOnce(ref obj, ReadPointObject(r), "Object marker"),
|
||||||
"polygon" => () => Helpers.SetAtMostOnce(ref obj, ReadPolygonObject(r), "Object marker"),
|
"polygon" => () => Helpers.SetAtMostOnce(ref obj, ReadPolygonObject(r), "Object marker"),
|
||||||
|
@ -152,38 +158,6 @@ internal partial class Tmx
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static EllipseObject ReadEllipseObject(XmlReader reader)
|
internal static EllipseObject ReadEllipseObject(XmlReader reader)
|
||||||
{
|
{
|
||||||
reader.Skip();
|
reader.Skip();
|
||||||
|
@ -280,7 +254,11 @@ internal partial class Tmx
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static Template ReadTemplate(XmlReader reader, Func<string, Tileset> externalTilesetResolver, Func<string, Template> externalTemplateResolver)
|
internal static Template ReadTemplate(
|
||||||
|
XmlReader reader,
|
||||||
|
Func<string, Tileset> externalTilesetResolver,
|
||||||
|
Func<string, Template> externalTemplateResolver,
|
||||||
|
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions)
|
||||||
{
|
{
|
||||||
// No attributes
|
// No attributes
|
||||||
|
|
||||||
|
@ -292,8 +270,8 @@ internal partial class Tmx
|
||||||
|
|
||||||
reader.ProcessChildren("template", (r, elementName) => elementName switch
|
reader.ProcessChildren("template", (r, elementName) => elementName switch
|
||||||
{
|
{
|
||||||
"tileset" => () => Helpers.SetAtMostOnce(ref tileset, ReadTileset(r, externalTilesetResolver, externalTemplateResolver), "Tileset"),
|
"tileset" => () => Helpers.SetAtMostOnce(ref tileset, ReadTileset(r, externalTilesetResolver, externalTemplateResolver, customTypeDefinitions), "Tileset"),
|
||||||
"object" => () => Helpers.SetAtMostOnce(ref obj, ReadObject(r, externalTemplateResolver), "Object"),
|
"object" => () => Helpers.SetAtMostOnce(ref obj, ReadObject(r, externalTemplateResolver, customTypeDefinitions), "Object"),
|
||||||
_ => r.Skip
|
_ => r.Skip
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,9 @@ namespace DotTiled;
|
||||||
|
|
||||||
internal partial class Tmx
|
internal partial class Tmx
|
||||||
{
|
{
|
||||||
internal static Dictionary<string, IProperty> ReadProperties(XmlReader reader)
|
internal static Dictionary<string, IProperty> ReadProperties(
|
||||||
|
XmlReader reader,
|
||||||
|
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions)
|
||||||
{
|
{
|
||||||
return reader.ReadList("properties", "property", (r) =>
|
return reader.ReadList("properties", "property", (r) =>
|
||||||
{
|
{
|
||||||
|
@ -33,22 +35,38 @@ internal partial class Tmx
|
||||||
PropertyType.Color => new ColorProperty { Name = name, Value = r.GetRequiredAttributeParseable<Color>("value") },
|
PropertyType.Color => new ColorProperty { Name = name, Value = r.GetRequiredAttributeParseable<Color>("value") },
|
||||||
PropertyType.File => new FileProperty { Name = name, Value = r.GetRequiredAttribute("value") },
|
PropertyType.File => new FileProperty { Name = name, Value = r.GetRequiredAttribute("value") },
|
||||||
PropertyType.Object => new ObjectProperty { Name = name, Value = r.GetRequiredAttributeParseable<uint>("value") },
|
PropertyType.Object => new ObjectProperty { Name = name, Value = r.GetRequiredAttributeParseable<uint>("value") },
|
||||||
PropertyType.Class => ReadClassProperty(r),
|
PropertyType.Class => ReadClassProperty(r, customTypeDefinitions),
|
||||||
_ => throw new XmlException("Invalid property type")
|
_ => throw new XmlException("Invalid property type")
|
||||||
};
|
};
|
||||||
return (name, property);
|
return (name, property);
|
||||||
}).ToDictionary(x => x.name, x => x.property);
|
}).ToDictionary(x => x.name, x => x.property);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static ClassProperty ReadClassProperty(XmlReader reader)
|
internal static ClassProperty ReadClassProperty(
|
||||||
|
XmlReader reader,
|
||||||
|
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions)
|
||||||
{
|
{
|
||||||
var name = reader.GetRequiredAttribute("name");
|
var name = reader.GetRequiredAttribute("name");
|
||||||
var propertyType = reader.GetRequiredAttribute("propertytype");
|
var propertyType = reader.GetRequiredAttribute("propertytype");
|
||||||
|
|
||||||
reader.ReadStartElement("property");
|
var customTypeDef = customTypeDefinitions.FirstOrDefault(ctd => ctd.Name == propertyType);
|
||||||
var properties = ReadProperties(reader);
|
if (customTypeDef is CustomClassDefinition ccd)
|
||||||
reader.ReadEndElement();
|
{
|
||||||
|
reader.ReadStartElement("property");
|
||||||
|
var propsInType = CreateInstanceOfCustomClass(ccd);
|
||||||
|
var props = ReadProperties(reader, customTypeDefinitions);
|
||||||
|
|
||||||
return new ClassProperty { Name = name, PropertyType = propertyType, Properties = properties };
|
var mergedProps = Helpers.MergeProperties(propsInType, props);
|
||||||
|
|
||||||
|
reader.ReadEndElement();
|
||||||
|
return new ClassProperty { Name = name, PropertyType = propertyType, Properties = mergedProps };
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new XmlException($"Unkonwn custom class definition: {propertyType}");
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static Dictionary<string, IProperty> CreateInstanceOfCustomClass(CustomClassDefinition customClassDefinition)
|
||||||
|
{
|
||||||
|
return customClassDefinition.Members.ToDictionary(m => m.Name, m => m.Clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,10 @@ namespace DotTiled;
|
||||||
|
|
||||||
internal partial class Tmx
|
internal partial class Tmx
|
||||||
{
|
{
|
||||||
internal static TileLayer ReadTileLayer(XmlReader reader, bool dataUsesChunks)
|
internal static TileLayer ReadTileLayer(
|
||||||
|
XmlReader reader,
|
||||||
|
bool dataUsesChunks,
|
||||||
|
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions)
|
||||||
{
|
{
|
||||||
var id = reader.GetRequiredAttributeParseable<uint>("id");
|
var id = reader.GetRequiredAttributeParseable<uint>("id");
|
||||||
var name = reader.GetOptionalAttribute("name") ?? "";
|
var name = reader.GetOptionalAttribute("name") ?? "";
|
||||||
|
@ -30,7 +33,7 @@ internal partial class Tmx
|
||||||
reader.ProcessChildren("layer", (r, elementName) => elementName switch
|
reader.ProcessChildren("layer", (r, elementName) => elementName switch
|
||||||
{
|
{
|
||||||
"data" => () => Helpers.SetAtMostOnce(ref data, ReadData(r, dataUsesChunks), "Data"),
|
"data" => () => Helpers.SetAtMostOnce(ref data, ReadData(r, dataUsesChunks), "Data"),
|
||||||
"properties" => () => Helpers.SetAtMostOnce(ref properties, ReadProperties(r), "Properties"),
|
"properties" => () => Helpers.SetAtMostOnce(ref properties, ReadProperties(r, customTypeDefinitions), "Properties"),
|
||||||
_ => r.Skip
|
_ => r.Skip
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -55,7 +58,9 @@ internal partial class Tmx
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static ImageLayer ReadImageLayer(XmlReader reader)
|
internal static ImageLayer ReadImageLayer(
|
||||||
|
XmlReader reader,
|
||||||
|
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions)
|
||||||
{
|
{
|
||||||
var id = reader.GetRequiredAttributeParseable<uint>("id");
|
var id = reader.GetRequiredAttributeParseable<uint>("id");
|
||||||
var name = reader.GetOptionalAttribute("name") ?? "";
|
var name = reader.GetOptionalAttribute("name") ?? "";
|
||||||
|
@ -78,7 +83,7 @@ internal partial class Tmx
|
||||||
reader.ProcessChildren("imagelayer", (r, elementName) => elementName switch
|
reader.ProcessChildren("imagelayer", (r, elementName) => elementName switch
|
||||||
{
|
{
|
||||||
"image" => () => Helpers.SetAtMostOnce(ref image, ReadImage(r), "Image"),
|
"image" => () => Helpers.SetAtMostOnce(ref image, ReadImage(r), "Image"),
|
||||||
"properties" => () => Helpers.SetAtMostOnce(ref properties, ReadProperties(r), "Properties"),
|
"properties" => () => Helpers.SetAtMostOnce(ref properties, ReadProperties(r, customTypeDefinitions), "Properties"),
|
||||||
_ => r.Skip
|
_ => r.Skip
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -103,7 +108,10 @@ internal partial class Tmx
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static Group ReadGroup(XmlReader reader, Func<string, Template> externalTemplateResolver)
|
internal static Group ReadGroup(
|
||||||
|
XmlReader reader,
|
||||||
|
Func<string, Template> externalTemplateResolver,
|
||||||
|
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions)
|
||||||
{
|
{
|
||||||
var id = reader.GetRequiredAttributeParseable<uint>("id");
|
var id = reader.GetRequiredAttributeParseable<uint>("id");
|
||||||
var name = reader.GetOptionalAttribute("name") ?? "";
|
var name = reader.GetOptionalAttribute("name") ?? "";
|
||||||
|
@ -121,11 +129,11 @@ internal partial class Tmx
|
||||||
|
|
||||||
reader.ProcessChildren("group", (r, elementName) => elementName switch
|
reader.ProcessChildren("group", (r, elementName) => elementName switch
|
||||||
{
|
{
|
||||||
"properties" => () => Helpers.SetAtMostOnce(ref properties, ReadProperties(r), "Properties"),
|
"properties" => () => Helpers.SetAtMostOnce(ref properties, ReadProperties(r, customTypeDefinitions), "Properties"),
|
||||||
"layer" => () => layers.Add(ReadTileLayer(r, dataUsesChunks: false)),
|
"layer" => () => layers.Add(ReadTileLayer(r, false, customTypeDefinitions)),
|
||||||
"objectgroup" => () => layers.Add(ReadObjectLayer(r, externalTemplateResolver)),
|
"objectgroup" => () => layers.Add(ReadObjectLayer(r, externalTemplateResolver, customTypeDefinitions)),
|
||||||
"imagelayer" => () => layers.Add(ReadImageLayer(r)),
|
"imagelayer" => () => layers.Add(ReadImageLayer(r, customTypeDefinitions)),
|
||||||
"group" => () => layers.Add(ReadGroup(r, externalTemplateResolver)),
|
"group" => () => layers.Add(ReadGroup(r, externalTemplateResolver, customTypeDefinitions)),
|
||||||
_ => r.Skip
|
_ => r.Skip
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
|
|
||||||
|
@ -7,7 +8,11 @@ namespace DotTiled;
|
||||||
|
|
||||||
internal partial class Tmx
|
internal partial class Tmx
|
||||||
{
|
{
|
||||||
internal static Tileset ReadTileset(XmlReader reader, Func<string, Tileset>? externalTilesetResolver, Func<string, Template> externalTemplateResolver)
|
internal static Tileset ReadTileset(
|
||||||
|
XmlReader reader,
|
||||||
|
Func<string, Tileset>? externalTilesetResolver,
|
||||||
|
Func<string, Template> externalTemplateResolver,
|
||||||
|
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions)
|
||||||
{
|
{
|
||||||
// Attributes
|
// Attributes
|
||||||
var version = reader.GetOptionalAttribute("version");
|
var version = reader.GetOptionalAttribute("version");
|
||||||
|
@ -18,8 +23,8 @@ internal partial class Tmx
|
||||||
var @class = reader.GetOptionalAttribute("class") ?? "";
|
var @class = reader.GetOptionalAttribute("class") ?? "";
|
||||||
var tileWidth = reader.GetOptionalAttributeParseable<uint>("tilewidth");
|
var tileWidth = reader.GetOptionalAttributeParseable<uint>("tilewidth");
|
||||||
var tileHeight = reader.GetOptionalAttributeParseable<uint>("tileheight");
|
var tileHeight = reader.GetOptionalAttributeParseable<uint>("tileheight");
|
||||||
var spacing = reader.GetOptionalAttributeParseable<uint>("spacing");
|
var spacing = reader.GetOptionalAttributeParseable<uint>("spacing") ?? 0;
|
||||||
var margin = reader.GetOptionalAttributeParseable<uint>("margin");
|
var margin = reader.GetOptionalAttributeParseable<uint>("margin") ?? 0;
|
||||||
var tileCount = reader.GetOptionalAttributeParseable<uint>("tilecount");
|
var tileCount = reader.GetOptionalAttributeParseable<uint>("tilecount");
|
||||||
var columns = reader.GetOptionalAttributeParseable<uint>("columns");
|
var columns = reader.GetOptionalAttributeParseable<uint>("columns");
|
||||||
var objectAlignment = reader.GetOptionalAttributeEnum<ObjectAlignment>("objectalignment", s => s switch
|
var objectAlignment = reader.GetOptionalAttributeEnum<ObjectAlignment>("objectalignment", s => s switch
|
||||||
|
@ -63,10 +68,10 @@ internal partial class Tmx
|
||||||
"image" => () => Helpers.SetAtMostOnce(ref image, ReadImage(r), "Image"),
|
"image" => () => Helpers.SetAtMostOnce(ref image, ReadImage(r), "Image"),
|
||||||
"tileoffset" => () => Helpers.SetAtMostOnce(ref tileOffset, ReadTileOffset(r), "TileOffset"),
|
"tileoffset" => () => Helpers.SetAtMostOnce(ref tileOffset, ReadTileOffset(r), "TileOffset"),
|
||||||
"grid" => () => Helpers.SetAtMostOnce(ref grid, ReadGrid(r), "Grid"),
|
"grid" => () => Helpers.SetAtMostOnce(ref grid, ReadGrid(r), "Grid"),
|
||||||
"properties" => () => Helpers.SetAtMostOnce(ref properties, ReadProperties(r), "Properties"),
|
"properties" => () => Helpers.SetAtMostOnce(ref properties, ReadProperties(r, customTypeDefinitions), "Properties"),
|
||||||
"wangsets" => () => Helpers.SetAtMostOnce(ref wangsets, ReadWangsets(r), "Wangsets"),
|
"wangsets" => () => Helpers.SetAtMostOnce(ref wangsets, ReadWangsets(r, customTypeDefinitions), "Wangsets"),
|
||||||
"transformations" => () => Helpers.SetAtMostOnce(ref transformations, ReadTransformations(r), "Transformations"),
|
"transformations" => () => Helpers.SetAtMostOnce(ref transformations, ReadTransformations(r), "Transformations"),
|
||||||
"tile" => () => tiles.Add(ReadTile(r, externalTemplateResolver)),
|
"tile" => () => tiles.Add(ReadTile(r, externalTemplateResolver, customTypeDefinitions)),
|
||||||
_ => r.Skip
|
_ => r.Skip
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -131,6 +136,9 @@ internal partial class Tmx
|
||||||
_ => r.Skip
|
_ => r.Skip
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (format is null && source is not null)
|
||||||
|
format = ParseImageFormatFromSource(source);
|
||||||
|
|
||||||
return new Image
|
return new Image
|
||||||
{
|
{
|
||||||
Format = format,
|
Format = format,
|
||||||
|
@ -141,6 +149,21 @@ internal partial class Tmx
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static ImageFormat ParseImageFormatFromSource(string source)
|
||||||
|
{
|
||||||
|
var extension = Path.GetExtension(source).ToLowerInvariant();
|
||||||
|
return extension switch
|
||||||
|
{
|
||||||
|
".png" => ImageFormat.Png,
|
||||||
|
".gif" => ImageFormat.Gif,
|
||||||
|
".jpg" => ImageFormat.Jpg,
|
||||||
|
".jpeg" => ImageFormat.Jpg,
|
||||||
|
".bmp" => ImageFormat.Bmp,
|
||||||
|
_ => throw new XmlException($"Unsupported image format '{extension}'")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
internal static TileOffset ReadTileOffset(XmlReader reader)
|
internal static TileOffset ReadTileOffset(XmlReader reader)
|
||||||
{
|
{
|
||||||
// Attributes
|
// Attributes
|
||||||
|
@ -179,7 +202,10 @@ internal partial class Tmx
|
||||||
return new Transformations { HFlip = hFlip, VFlip = vFlip, Rotate = rotate, PreferUntransformed = preferUntransformed };
|
return new Transformations { HFlip = hFlip, VFlip = vFlip, Rotate = rotate, PreferUntransformed = preferUntransformed };
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static Tile ReadTile(XmlReader reader, Func<string, Template> externalTemplateResolver)
|
internal static Tile ReadTile(
|
||||||
|
XmlReader reader,
|
||||||
|
Func<string, Template> externalTemplateResolver,
|
||||||
|
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions)
|
||||||
{
|
{
|
||||||
// Attributes
|
// Attributes
|
||||||
var id = reader.GetRequiredAttributeParseable<uint>("id");
|
var id = reader.GetRequiredAttributeParseable<uint>("id");
|
||||||
|
@ -198,9 +224,9 @@ internal partial class Tmx
|
||||||
|
|
||||||
reader.ProcessChildren("tile", (r, elementName) => elementName switch
|
reader.ProcessChildren("tile", (r, elementName) => elementName switch
|
||||||
{
|
{
|
||||||
"properties" => () => Helpers.SetAtMostOnce(ref properties, ReadProperties(r), "Properties"),
|
"properties" => () => Helpers.SetAtMostOnce(ref properties, ReadProperties(r, customTypeDefinitions), "Properties"),
|
||||||
"image" => () => Helpers.SetAtMostOnce(ref image, ReadImage(r), "Image"),
|
"image" => () => Helpers.SetAtMostOnce(ref image, ReadImage(r), "Image"),
|
||||||
"objectgroup" => () => Helpers.SetAtMostOnce(ref objectLayer, ReadObjectLayer(r, externalTemplateResolver), "ObjectLayer"),
|
"objectgroup" => () => Helpers.SetAtMostOnce(ref objectLayer, ReadObjectLayer(r, externalTemplateResolver, customTypeDefinitions), "ObjectLayer"),
|
||||||
"animation" => () => Helpers.SetAtMostOnce(ref animation, r.ReadList<Frame>("animation", "frame", (ar) =>
|
"animation" => () => Helpers.SetAtMostOnce(ref animation, r.ReadList<Frame>("animation", "frame", (ar) =>
|
||||||
{
|
{
|
||||||
var tileID = ar.GetRequiredAttributeParseable<uint>("tileid");
|
var tileID = ar.GetRequiredAttributeParseable<uint>("tileid");
|
||||||
|
@ -226,12 +252,16 @@ internal partial class Tmx
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static List<Wangset> ReadWangsets(XmlReader reader)
|
internal static List<Wangset> ReadWangsets(
|
||||||
|
XmlReader reader,
|
||||||
|
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions)
|
||||||
{
|
{
|
||||||
return reader.ReadList<Wangset>("wangsets", "wangset", ReadWangset);
|
return reader.ReadList<Wangset>("wangsets", "wangset", r => ReadWangset(r, customTypeDefinitions));
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static Wangset ReadWangset(XmlReader reader)
|
internal static Wangset ReadWangset(
|
||||||
|
XmlReader reader,
|
||||||
|
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions)
|
||||||
{
|
{
|
||||||
// Attributes
|
// Attributes
|
||||||
var name = reader.GetRequiredAttribute("name");
|
var name = reader.GetRequiredAttribute("name");
|
||||||
|
@ -245,8 +275,8 @@ internal partial class Tmx
|
||||||
|
|
||||||
reader.ProcessChildren("wangset", (r, elementName) => elementName switch
|
reader.ProcessChildren("wangset", (r, elementName) => elementName switch
|
||||||
{
|
{
|
||||||
"properties" => () => Helpers.SetAtMostOnce(ref properties, ReadProperties(r), "Properties"),
|
"properties" => () => Helpers.SetAtMostOnce(ref properties, ReadProperties(r, customTypeDefinitions), "Properties"),
|
||||||
"wangcolor" => () => wangColors.Add(ReadWangColor(r)),
|
"wangcolor" => () => wangColors.Add(ReadWangColor(r, customTypeDefinitions)),
|
||||||
"wangtile" => () => wangTiles.Add(ReadWangTile(r)),
|
"wangtile" => () => wangTiles.Add(ReadWangTile(r)),
|
||||||
_ => r.Skip
|
_ => r.Skip
|
||||||
});
|
});
|
||||||
|
@ -265,7 +295,9 @@ internal partial class Tmx
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static WangColor ReadWangColor(XmlReader reader)
|
internal static WangColor ReadWangColor(
|
||||||
|
XmlReader reader,
|
||||||
|
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions)
|
||||||
{
|
{
|
||||||
// Attributes
|
// Attributes
|
||||||
var name = reader.GetRequiredAttribute("name");
|
var name = reader.GetRequiredAttribute("name");
|
||||||
|
@ -279,7 +311,7 @@ internal partial class Tmx
|
||||||
|
|
||||||
reader.ProcessChildren("wangcolor", (r, elementName) => elementName switch
|
reader.ProcessChildren("wangcolor", (r, elementName) => elementName switch
|
||||||
{
|
{
|
||||||
"properties" => () => Helpers.SetAtMostOnce(ref properties, ReadProperties(r), "Properties"),
|
"properties" => () => Helpers.SetAtMostOnce(ref properties, ReadProperties(r, customTypeDefinitions), "Properties"),
|
||||||
_ => r.Skip
|
_ => r.Skip
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -13,11 +13,18 @@ public class TmxMapReader : IMapReader
|
||||||
private readonly XmlReader _reader;
|
private readonly XmlReader _reader;
|
||||||
private bool disposedValue;
|
private bool disposedValue;
|
||||||
|
|
||||||
public TmxMapReader(XmlReader reader, Func<string, Tileset> externalTilesetResolver, Func<string, Template> externalTemplateResolver)
|
private readonly IReadOnlyCollection<CustomTypeDefinition> _customTypeDefinitions;
|
||||||
|
|
||||||
|
public TmxMapReader(
|
||||||
|
XmlReader reader,
|
||||||
|
Func<string, Tileset> externalTilesetResolver,
|
||||||
|
Func<string, Template> externalTemplateResolver,
|
||||||
|
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions)
|
||||||
{
|
{
|
||||||
_reader = reader ?? throw new ArgumentNullException(nameof(reader));
|
_reader = reader ?? throw new ArgumentNullException(nameof(reader));
|
||||||
_externalTilesetResolver = externalTilesetResolver ?? throw new ArgumentNullException(nameof(externalTilesetResolver));
|
_externalTilesetResolver = externalTilesetResolver ?? throw new ArgumentNullException(nameof(externalTilesetResolver));
|
||||||
_externalTemplateResolver = externalTemplateResolver ?? throw new ArgumentNullException(nameof(externalTemplateResolver));
|
_externalTemplateResolver = externalTemplateResolver ?? throw new ArgumentNullException(nameof(externalTemplateResolver));
|
||||||
|
_customTypeDefinitions = customTypeDefinitions ?? throw new ArgumentNullException(nameof(customTypeDefinitions));
|
||||||
|
|
||||||
// Prepare reader
|
// Prepare reader
|
||||||
_reader.MoveToContent();
|
_reader.MoveToContent();
|
||||||
|
@ -25,7 +32,7 @@ public class TmxMapReader : IMapReader
|
||||||
|
|
||||||
public Map ReadMap()
|
public Map ReadMap()
|
||||||
{
|
{
|
||||||
return Tmx.ReadMap(_reader, _externalTilesetResolver, _externalTemplateResolver);
|
return Tmx.ReadMap(_reader, _externalTilesetResolver, _externalTemplateResolver, _customTypeDefinitions);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
|
|
||||||
namespace DotTiled;
|
namespace DotTiled;
|
||||||
|
@ -11,13 +12,22 @@ public class TsxTilesetReader : ITilesetReader
|
||||||
private readonly XmlReader _reader;
|
private readonly XmlReader _reader;
|
||||||
private bool disposedValue;
|
private bool disposedValue;
|
||||||
|
|
||||||
public TsxTilesetReader(XmlReader reader, Func<string, Template> externalTemplateResolver)
|
private readonly IReadOnlyCollection<CustomTypeDefinition> _customTypeDefinitions;
|
||||||
|
|
||||||
|
public TsxTilesetReader(
|
||||||
|
XmlReader reader,
|
||||||
|
Func<string, Template> externalTemplateResolver,
|
||||||
|
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions)
|
||||||
{
|
{
|
||||||
_reader = reader ?? throw new ArgumentNullException(nameof(reader));
|
_reader = reader ?? throw new ArgumentNullException(nameof(reader));
|
||||||
_externalTemplateResolver = externalTemplateResolver ?? throw new ArgumentNullException(nameof(externalTemplateResolver));
|
_externalTemplateResolver = externalTemplateResolver ?? throw new ArgumentNullException(nameof(externalTemplateResolver));
|
||||||
|
_customTypeDefinitions = customTypeDefinitions ?? throw new ArgumentNullException(nameof(customTypeDefinitions));
|
||||||
|
|
||||||
|
// Prepare reader
|
||||||
|
_reader.MoveToContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Tileset ReadTileset() => Tmx.ReadTileset(_reader, null, _externalTemplateResolver);
|
public Tileset ReadTileset() => Tmx.ReadTileset(_reader, null, _externalTemplateResolver, _customTypeDefinitions);
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
|
|
||||||
namespace DotTiled;
|
namespace DotTiled;
|
||||||
|
@ -12,17 +13,24 @@ public class TxTemplateReader : ITemplateReader
|
||||||
private readonly XmlReader _reader;
|
private readonly XmlReader _reader;
|
||||||
private bool disposedValue;
|
private bool disposedValue;
|
||||||
|
|
||||||
public TxTemplateReader(XmlReader reader, Func<string, Tileset> externalTilesetResolver, Func<string, Template> externalTemplateResolver)
|
private readonly IReadOnlyCollection<CustomTypeDefinition> _customTypeDefinitions;
|
||||||
|
|
||||||
|
public TxTemplateReader(
|
||||||
|
XmlReader reader,
|
||||||
|
Func<string, Tileset> externalTilesetResolver,
|
||||||
|
Func<string, Template> externalTemplateResolver,
|
||||||
|
IReadOnlyCollection<CustomTypeDefinition> customTypeDefinitions)
|
||||||
{
|
{
|
||||||
_reader = reader ?? throw new ArgumentNullException(nameof(reader));
|
_reader = reader ?? throw new ArgumentNullException(nameof(reader));
|
||||||
_externalTilesetResolver = externalTilesetResolver ?? throw new ArgumentNullException(nameof(externalTilesetResolver));
|
_externalTilesetResolver = externalTilesetResolver ?? throw new ArgumentNullException(nameof(externalTilesetResolver));
|
||||||
_externalTemplateResolver = externalTemplateResolver ?? throw new ArgumentNullException(nameof(externalTemplateResolver));
|
_externalTemplateResolver = externalTemplateResolver ?? throw new ArgumentNullException(nameof(externalTemplateResolver));
|
||||||
|
_customTypeDefinitions = customTypeDefinitions ?? throw new ArgumentNullException(nameof(customTypeDefinitions));
|
||||||
|
|
||||||
// Prepare reader
|
// Prepare reader
|
||||||
_reader.MoveToContent();
|
_reader.MoveToContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Template ReadTemplate() => Tmx.ReadTemplate(_reader, _externalTilesetResolver, _externalTemplateResolver);
|
public Template ReadTemplate() => Tmx.ReadTemplate(_reader, _externalTilesetResolver, _externalTemplateResolver, _customTypeDefinitions);
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
|
|
10
Makefile
10
Makefile
|
@ -0,0 +1,10 @@
|
||||||
|
test:
|
||||||
|
dotnet test
|
||||||
|
|
||||||
|
BENCHMARK_SOURCES = DotTiled.Benchmark/Program.cs DotTiled.Benchmark/DotTiled.Benchmark.csproj
|
||||||
|
BENCHMARK_OUTPUTDIR = DotTiled.Benchmark/BenchmarkDotNet.Artifacts
|
||||||
|
.PHONY: benchmark
|
||||||
|
benchmark: $(BENCHMARK_OUTPUTDIR)/results/MyBenchmarks.MapLoading-report-github.md
|
||||||
|
|
||||||
|
$(BENCHMARK_OUTPUTDIR)/results/MyBenchmarks.MapLoading-report-github.md: $(BENCHMARK_SOURCES)
|
||||||
|
dotnet run --project DotTiled.Benchmark/DotTiled.Benchmark.csproj -c Release -- $(BENCHMARK_OUTPUTDIR)
|
16
README.md
16
README.md
|
@ -60,15 +60,13 @@ BenchmarkDotNet v0.13.12, Windows 10 (10.0.19045.4651/22H2/2022Update)
|
||||||
[Host] : .NET 8.0.3 (8.0.324.11423), X64 RyuJIT AVX2
|
[Host] : .NET 8.0.3 (8.0.324.11423), X64 RyuJIT AVX2
|
||||||
DefaultJob : .NET 8.0.3 (8.0.324.11423), X64 RyuJIT AVX2
|
DefaultJob : .NET 8.0.3 (8.0.324.11423), X64 RyuJIT AVX2
|
||||||
```
|
```
|
||||||
| Method | Categories | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Gen1 | Allocated | Alloc Ratio |
|
| Method | Categories | Mean | Ratio | Gen0 | Gen1 | Allocated | Alloc Ratio |
|
||||||
|------------ |------------------------- |----------:|----------:|----------:|------:|--------:|-------:|-------:|----------:|------------:|
|
|------------ |------------------------- |---------:|------:|-------:|-------:|----------:|------------:|
|
||||||
| DotTiled | MapFromInMemoryTmxString | 2.991 μs | 0.0266 μs | 0.0236 μs | 1.00 | 0.00 | 1.2817 | 0.0610 | 16.37 KB | 1.00 |
|
| DotTiled | MapFromInMemoryTmjString | 4.292 μs | 1.00 | 0.4349 | - | 5.62 KB | 1.00 |
|
||||||
| TiledLib | MapFromInMemoryTmxString | 5.405 μs | 0.0466 μs | 0.0413 μs | 1.81 | 0.02 | 1.8158 | 0.1068 | 23.32 KB | 1.42 |
|
| | | | | | | | |
|
||||||
| TiledCSPlus | MapFromInMemoryTmxString | 6.354 μs | 0.0703 μs | 0.0587 μs | 2.12 | 0.03 | 2.5940 | 0.1831 | 33.23 KB | 2.03 |
|
| DotTiled | MapFromInMemoryTmxString | 3.075 μs | 1.00 | 1.2817 | 0.0610 | 16.4 KB | 1.00 |
|
||||||
| | | | | | | | | | | |
|
| TiledLib | MapFromInMemoryTmxString | 5.574 μs | 1.81 | 1.8005 | 0.0916 | 23.32 KB | 1.42 |
|
||||||
| DotTiled | MapFromTmxFile | 28.570 μs | 0.1216 μs | 0.1137 μs | 1.00 | 0.00 | 1.0376 | - | 13.88 KB | 1.00 |
|
| TiledCSPlus | MapFromInMemoryTmxString | 6.546 μs | 2.13 | 2.5940 | 0.1831 | 33.16 KB | 2.02 |
|
||||||
| TiledCSPlus | MapFromTmxFile | 33.377 μs | 0.1086 μs | 0.1016 μs | 1.17 | 0.01 | 2.8076 | 0.1221 | 36.93 KB | 2.66 |
|
|
||||||
| TiledLib | MapFromTmxFile | 36.077 μs | 0.1900 μs | 0.1777 μs | 1.26 | 0.01 | 2.0752 | 0.1221 | 27.1 KB | 1.95 |
|
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue