mirror of
https://github.com/dcronqvist/DotTiled.git
synced 2025-02-05 08:52:50 +02:00
Getting further with the json support
This commit is contained in:
parent
5626614acd
commit
1168917c23
38 changed files with 1072 additions and 269 deletions
|
@ -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]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,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() => new Map
|
||||||
{
|
{
|
||||||
Version = "1.10",
|
Version = "1.10",
|
||||||
TiledVersion = "1.11.0",
|
TiledVersion = "1.11.0",
|
|
@ -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"
|
||||||
|
}
|
|
@ -2,64 +2,75 @@ namespace DotTiled.Tests;
|
||||||
|
|
||||||
public partial class TmjMapReaderTests
|
public partial class TmjMapReaderTests
|
||||||
{
|
{
|
||||||
[Fact]
|
public static IEnumerable<object[]> DeserializeMap_ValidTmjNoExternalTilesets_ReturnsMapWithoutThrowing_Data =>
|
||||||
public void Test1()
|
[
|
||||||
|
["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
|
// Arrange
|
||||||
var jsonString =
|
var json = TestData.GetRawStringFor(testDataFile);
|
||||||
"""
|
static Template ResolveTemplate(string source)
|
||||||
{
|
{
|
||||||
"backgroundcolor":"#656667",
|
var templateJson = TestData.GetRawStringFor($"Serialization.TestData.Template.{source}");
|
||||||
"height":4,
|
//var templateReader = new TmjTemplateReader(templateJson, ResolveTemplate);
|
||||||
"nextobjectid":1,
|
return null;
|
||||||
"nextlayerid":1,
|
|
||||||
"orientation":"orthogonal",
|
|
||||||
"properties": [
|
|
||||||
{
|
|
||||||
"name":"mapProperty1",
|
|
||||||
"type":"string",
|
|
||||||
"value":"one"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name":"mapProperty3",
|
|
||||||
"type":"string",
|
|
||||||
"value":"twoeee"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"renderorder":"right-down",
|
|
||||||
"tileheight":32,
|
|
||||||
"tilewidth":32,
|
|
||||||
"version":"1",
|
|
||||||
"tiledversion":"1.0.3",
|
|
||||||
"width":4,
|
|
||||||
"tilesets": [
|
|
||||||
{
|
|
||||||
"columns":19,
|
|
||||||
"firstgid":1,
|
|
||||||
"image":"image/fishbaddie_parts.png",
|
|
||||||
"imageheight":480,
|
|
||||||
"imagewidth":640,
|
|
||||||
"margin":3,
|
|
||||||
"name":"",
|
|
||||||
"properties":[
|
|
||||||
{
|
|
||||||
"name":"myProperty1",
|
|
||||||
"type":"string",
|
|
||||||
"value":"myProperty1_value"
|
|
||||||
}],
|
|
||||||
"spacing":1,
|
|
||||||
"tilecount":266,
|
|
||||||
"tileheight":32,
|
|
||||||
"tilewidth":32
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
""";
|
static Tileset ResolveTileset(string source)
|
||||||
|
{
|
||||||
|
var tilesetJson = TestData.GetXmlReaderFor($"Serialization.TestData.Tileset.{source}");
|
||||||
|
//var tilesetReader = new TmjTilesetReader(tilesetJson, ResolveTileset, ResolveTemplate);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
using var mapReader = new TmjMapReader(json, ResolveTileset, ResolveTemplate);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
using var tmjMapReader = new TmjMapReader(jsonString);
|
var map = mapReader.ReadMap();
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var map = tmjMapReader.ReadMap();
|
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()],
|
||||||
|
["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
|
||||||
|
var json = TestData.GetRawStringFor(testDataFile);
|
||||||
|
static Template ResolveTemplate(string source)
|
||||||
|
{
|
||||||
|
var templateJson = TestData.GetRawStringFor($"Serialization.TestData.Template.{source}");
|
||||||
|
//var templateReader = new TmjTemplateReader(templateJson, ResolveTemplate);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
static Tileset ResolveTileset(string source)
|
||||||
|
{
|
||||||
|
var tilesetJson = TestData.GetXmlReaderFor($"Serialization.TestData.Tileset.{source}");
|
||||||
|
//var tilesetReader = new TmjTilesetReader(tilesetJson, ResolveTileset, ResolveTemplate);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
using var mapReader = new TmjMapReader(json, ResolveTileset, ResolveTemplate);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var map = mapReader.ReadMap();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(map);
|
||||||
|
DotTiledAssert.AssertMap(expectedMap, map);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,16 +91,16 @@ 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);
|
using var reader = TestData.GetXmlReaderFor(testDataFile);
|
||||||
static Template ResolveTemplate(string source)
|
static 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);
|
||||||
return templateReader.ReadTemplate();
|
return templateReader.ReadTemplate();
|
||||||
}
|
}
|
||||||
static Tileset ResolveTileset(string source)
|
static 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);
|
||||||
return tilesetReader.ReadTileset();
|
return tilesetReader.ReadTileset();
|
||||||
}
|
}
|
||||||
|
@ -116,8 +116,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()],
|
||||||
["Serialization.Tmx.TestData.Map.map-with-group.tmx", MapWithGroup()],
|
["Serialization.TestData.Map.map-with-group.tmx", TestData.MapWithGroup()],
|
||||||
];
|
];
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
@ -125,16 +125,16 @@ 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);
|
using var reader = TestData.GetXmlReaderFor(testDataFile);
|
||||||
static Template ResolveTemplate(string source)
|
static 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);
|
||||||
return templateReader.ReadTemplate();
|
return templateReader.ReadTemplate();
|
||||||
}
|
}
|
||||||
static Tileset ResolveTileset(string source)
|
static 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);
|
||||||
return tilesetReader.ReadTileset();
|
return tilesetReader.ReadTileset();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -20,21 +20,32 @@ internal static class ExtensionsJsonElement
|
||||||
if (!element.TryGetProperty(propertyName, out var property))
|
if (!element.TryGetProperty(propertyName, out var property))
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
|
|
||||||
|
if (property.ValueKind == JsonValueKind.Null)
|
||||||
|
return defaultValue;
|
||||||
|
|
||||||
return property.GetValueAs<T>();
|
return property.GetValueAs<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static T GetValueAs<T>(this JsonElement element)
|
internal static T GetValueAs<T>(this JsonElement element)
|
||||||
{
|
{
|
||||||
string val = typeof(T) switch
|
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(string) => element.GetString()!,
|
||||||
Type t when t == typeof(int) => element.GetInt32().ToString(CultureInfo.InvariantCulture),
|
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(uint) => element.GetUInt32().ToString(CultureInfo.InvariantCulture),
|
||||||
Type t when t == typeof(float) => element.GetSingle().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)}'.")
|
_ => throw new JsonException($"Unsupported type '{typeof(T)}'.")
|
||||||
};
|
};
|
||||||
|
|
||||||
return (T)Convert.ChangeType(val, typeof(T), CultureInfo.InvariantCulture);
|
return (T)Convert.ChangeType(val, realType, CultureInfo.InvariantCulture);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static T GetRequiredPropertyParseable<T>(this JsonElement element, string propertyName) where T : IParsable<T>
|
internal static T GetRequiredPropertyParseable<T>(this JsonElement element, string propertyName) where T : IParsable<T>
|
||||||
|
|
132
DotTiled/Serialization/Tmj/Tmj.Data.cs
Normal file
132
DotTiled/Serialization/Tmj/Tmj.Data.cs
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
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) = 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 = ReadBytesAsInt32Array(base64Data);
|
||||||
|
var (globalTileIDs, flippingFlags) = 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 => DecompressGZip(stream),
|
||||||
|
DataCompression.ZLib => DecompressZLib(stream),
|
||||||
|
_ => throw new JsonException($"Unsupported compression '{compression}'.")
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
var (globalTileIDs, flippingFlags) = ReadAndClearFlippingFlagsFromGIDs(decompressed);
|
||||||
|
return new Data { Encoding = encoding, Compression = compression, GlobalTileIDs = globalTileIDs, FlippingFlags = flippingFlags, Chunks = null };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new JsonException($"Unsupported encoding '{encoding}'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
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.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
88
DotTiled/Serialization/Tmj/Tmj.Layer.cs
Normal file
88
DotTiled/Serialization/Tmj/Tmj.Layer.cs
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
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 BaseLayer ReadLayer(JsonElement element)
|
||||||
|
{
|
||||||
|
var type = element.GetRequiredProperty<string>("type");
|
||||||
|
|
||||||
|
return type switch
|
||||||
|
{
|
||||||
|
"tilelayer" => ReadTileLayer(element),
|
||||||
|
// "objectgroup" => ReadObjectGroup(element),
|
||||||
|
// "imagelayer" => ReadImageLayer(element),
|
||||||
|
// "group" => ReadGroup(element),
|
||||||
|
_ => throw new JsonException($"Unsupported layer type '{type}'.")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static TileLayer ReadTileLayer(JsonElement element)
|
||||||
|
{
|
||||||
|
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), 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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,13 +1,15 @@
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
|
||||||
namespace DotTiled;
|
namespace DotTiled;
|
||||||
|
|
||||||
internal partial class Tmj
|
internal partial class Tmj
|
||||||
{
|
{
|
||||||
internal static Map ReadMap(JsonElement element)
|
internal static Map ReadMap(JsonElement element, Func<string, Tileset>? externalTilesetResolver, Func<string, Template> externalTemplateResolver)
|
||||||
{
|
{
|
||||||
var version = element.GetRequiredProperty<string>("version");
|
var version = element.GetRequiredProperty<string>("version");
|
||||||
var tiledVersion = element.GetRequiredProperty<string>("tiledversion");
|
var tiledVersion = element.GetRequiredProperty<string>("tiledversion");
|
||||||
|
@ -51,14 +53,12 @@ internal partial class Tmj
|
||||||
var backgroundColor = element.GetOptionalPropertyParseable<Color>("backgroundcolor", s => Color.Parse(s, CultureInfo.InvariantCulture), Color.Parse("#00000000", CultureInfo.InvariantCulture));
|
var backgroundColor = element.GetOptionalPropertyParseable<Color>("backgroundcolor", s => Color.Parse(s, CultureInfo.InvariantCulture), Color.Parse("#00000000", CultureInfo.InvariantCulture));
|
||||||
var nextLayerID = element.GetRequiredProperty<uint>("nextlayerid");
|
var nextLayerID = element.GetRequiredProperty<uint>("nextlayerid");
|
||||||
var nextObjectID = element.GetRequiredProperty<uint>("nextobjectid");
|
var nextObjectID = element.GetRequiredProperty<uint>("nextobjectid");
|
||||||
var infinite = element.GetOptionalProperty<int>("infinite", 0) == 1;
|
var infinite = element.GetOptionalProperty<bool>("infinite", false);
|
||||||
|
|
||||||
// At most one of
|
var properties = element.GetOptionalPropertyCustom<Dictionary<string, IProperty>?>("properties", ReadProperties, null);
|
||||||
Dictionary<string, IProperty>? properties = element.GetOptionalPropertyCustom<Dictionary<string, IProperty>>("properties", ReadProperties, null);
|
|
||||||
|
|
||||||
// Any number of
|
List<BaseLayer> layers = element.GetOptionalPropertyCustom<List<BaseLayer>>("layers", e => e.GetValueAsList<BaseLayer>(ReadLayer), []);
|
||||||
List<BaseLayer> layers = [];
|
List<Tileset> tilesets = element.GetOptionalPropertyCustom<List<Tileset>>("tilesets", e => e.GetValueAsList<Tileset>(el => ReadTileset(el, externalTilesetResolver, externalTemplateResolver)), []);
|
||||||
List<Tileset> tilesets = [];
|
|
||||||
|
|
||||||
return new Map
|
return new Map
|
||||||
{
|
{
|
||||||
|
@ -86,176 +86,4 @@ internal partial class Tmj
|
||||||
Layers = layers
|
Layers = layers
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static Dictionary<string, IProperty> ReadProperties(JsonElement element)
|
|
||||||
{
|
|
||||||
var properties = new Dictionary<string, IProperty>();
|
|
||||||
|
|
||||||
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),
|
|
||||||
_ => throw new JsonException("Invalid property type")
|
|
||||||
};
|
|
||||||
|
|
||||||
return property!;
|
|
||||||
}).ForEach(p => properties.Add(p.Name, p));
|
|
||||||
|
|
||||||
return properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static ClassProperty ReadClassProperty(JsonElement element)
|
|
||||||
{
|
|
||||||
var name = element.GetRequiredProperty<string>("name");
|
|
||||||
var propertyType = element.GetRequiredProperty<string>("propertytype");
|
|
||||||
|
|
||||||
var properties = element.GetRequiredPropertyCustom<Dictionary<string, IProperty>>("properties", ReadProperties);
|
|
||||||
|
|
||||||
return new ClassProperty { Name = name, PropertyType = propertyType, Properties = properties };
|
|
||||||
}
|
|
||||||
|
|
||||||
// internal static List<Tileset> ReadTilesets(ref Utf8JsonReader reader)
|
|
||||||
// {
|
|
||||||
// var tilesets = new List<Tileset>();
|
|
||||||
|
|
||||||
// reader.ProcessJsonArray((ref Utf8JsonReader reader) =>
|
|
||||||
// {
|
|
||||||
// var tileset = ReadTileset(ref reader);
|
|
||||||
// tilesets.Add(tileset);
|
|
||||||
// });
|
|
||||||
|
|
||||||
// return tilesets;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// internal static Tileset ReadTileset(ref Utf8JsonReader reader)
|
|
||||||
// {
|
|
||||||
// string? version = null;
|
|
||||||
// string? tiledVersion = null;
|
|
||||||
// uint? firstGID = null;
|
|
||||||
// string? source = null;
|
|
||||||
// string? name = null;
|
|
||||||
// string @class = "";
|
|
||||||
// uint? tileWidth = null;
|
|
||||||
// uint? tileHeight = null;
|
|
||||||
// uint? spacing = null;
|
|
||||||
// uint? margin = null;
|
|
||||||
// uint? tileCount = null;
|
|
||||||
// uint? columns = null;
|
|
||||||
// ObjectAlignment objectAlignment = ObjectAlignment.Unspecified;
|
|
||||||
// FillMode fillMode = FillMode.Stretch;
|
|
||||||
|
|
||||||
// string? image = null;
|
|
||||||
// uint? imageWidth = null;
|
|
||||||
// uint? imageHeight = null;
|
|
||||||
|
|
||||||
// Dictionary<string, IProperty>? properties = null;
|
|
||||||
|
|
||||||
// reader.ProcessJsonObject([
|
|
||||||
// new OptionalProperty("version", (ref Utf8JsonReader reader) => version = reader.Progress(reader.GetString())),
|
|
||||||
// new OptionalProperty("tiledversion", (ref Utf8JsonReader reader) => tiledVersion = reader.Progress(reader.GetString())),
|
|
||||||
// new OptionalProperty("firstgid", (ref Utf8JsonReader reader) => firstGID = reader.Progress(reader.GetUInt32())),
|
|
||||||
// new OptionalProperty("source", (ref Utf8JsonReader reader) => source = reader.Progress(reader.GetString())),
|
|
||||||
// new OptionalProperty("name", (ref Utf8JsonReader reader) => name = reader.Progress(reader.GetString())),
|
|
||||||
// new OptionalProperty("class", (ref Utf8JsonReader reader) => @class = reader.Progress(reader.GetString() ?? ""), allowNull: true),
|
|
||||||
// new OptionalProperty("tilewidth", (ref Utf8JsonReader reader) => tileWidth = reader.Progress(reader.GetUInt32())),
|
|
||||||
// new OptionalProperty("tileheight", (ref Utf8JsonReader reader) => tileHeight = reader.Progress(reader.GetUInt32())),
|
|
||||||
// new OptionalProperty("spacing", (ref Utf8JsonReader reader) => spacing = reader.Progress(reader.GetUInt32())),
|
|
||||||
// new OptionalProperty("margin", (ref Utf8JsonReader reader) => margin = reader.Progress(reader.GetUInt32())),
|
|
||||||
// new OptionalProperty("tilecount", (ref Utf8JsonReader reader) => tileCount = reader.Progress(reader.GetUInt32())),
|
|
||||||
// new OptionalProperty("columns", (ref Utf8JsonReader reader) => columns = reader.Progress(reader.GetUInt32())),
|
|
||||||
// new OptionalProperty("objectalignment", (ref Utf8JsonReader reader) => objectAlignment = reader.Progress(reader.GetString()) 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("Invalid object alignment.")
|
|
||||||
// }),
|
|
||||||
// new OptionalProperty("fillmode", (ref Utf8JsonReader reader) => fillMode = reader.Progress(reader.GetString()) switch
|
|
||||||
// {
|
|
||||||
// "stretch" => FillMode.Stretch,
|
|
||||||
// "preserve-aspect-fit" => FillMode.PreserveAspectFit,
|
|
||||||
// _ => throw new JsonException("Invalid fill mode.")
|
|
||||||
// }),
|
|
||||||
|
|
||||||
// new OptionalProperty("image", (ref Utf8JsonReader reader) => image = reader.Progress(reader.GetString())),
|
|
||||||
// new OptionalProperty("imagewidth", (ref Utf8JsonReader reader) => imageWidth = reader.Progress(reader.GetUInt32())),
|
|
||||||
// new OptionalProperty("imageheight", (ref Utf8JsonReader reader) => imageHeight = reader.Progress(reader.GetUInt32())),
|
|
||||||
|
|
||||||
// new OptionalProperty("properties", (ref Utf8JsonReader reader) => properties = ReadProperties(ref reader))
|
|
||||||
// ], "tileset");
|
|
||||||
|
|
||||||
// Image? imageInstance = image is not null ? new Image
|
|
||||||
// {
|
|
||||||
// Format = ParseImageFormatFromSource(image),
|
|
||||||
// Width = imageWidth,
|
|
||||||
// Height = imageHeight,
|
|
||||||
// Source = image
|
|
||||||
// } : null;
|
|
||||||
|
|
||||||
// return new Tileset
|
|
||||||
// {
|
|
||||||
// Version = version,
|
|
||||||
// TiledVersion = tiledVersion,
|
|
||||||
// FirstGID = firstGID,
|
|
||||||
// Source = source,
|
|
||||||
// Name = name,
|
|
||||||
// Class = @class,
|
|
||||||
// TileWidth = tileWidth,
|
|
||||||
// TileHeight = tileHeight,
|
|
||||||
// Spacing = spacing,
|
|
||||||
// Margin = margin,
|
|
||||||
// TileCount = tileCount,
|
|
||||||
// Columns = columns,
|
|
||||||
// ObjectAlignment = objectAlignment,
|
|
||||||
// FillMode = fillMode,
|
|
||||||
// Image = imageInstance,
|
|
||||||
// Properties = properties
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
|
|
||||||
// private static ImageFormat ParseImageFormatFromSource(string? source)
|
|
||||||
// {
|
|
||||||
// if (source is null)
|
|
||||||
// throw new JsonException("Image source is required to determine image format.");
|
|
||||||
|
|
||||||
// var extension = Path.GetExtension(source);
|
|
||||||
// return extension switch
|
|
||||||
// {
|
|
||||||
// ".png" => ImageFormat.Png,
|
|
||||||
// ".jpg" => ImageFormat.Jpg,
|
|
||||||
// ".jpeg" => ImageFormat.Jpg,
|
|
||||||
// ".gif" => ImageFormat.Gif,
|
|
||||||
// ".bmp" => ImageFormat.Bmp,
|
|
||||||
// _ => throw new JsonException("Invalid image format.")
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
53
DotTiled/Serialization/Tmj/Tmj.Properties.cs
Normal file
53
DotTiled/Serialization/Tmj/Tmj.Properties.cs
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
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) =>
|
||||||
|
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),
|
||||||
|
_ => throw new JsonException("Invalid property type")
|
||||||
|
};
|
||||||
|
|
||||||
|
return property!;
|
||||||
|
}).ToDictionary(p => p.Name);
|
||||||
|
|
||||||
|
internal static ClassProperty ReadClassProperty(JsonElement element)
|
||||||
|
{
|
||||||
|
var name = element.GetRequiredProperty<string>("name");
|
||||||
|
var propertyType = element.GetRequiredProperty<string>("propertytype");
|
||||||
|
|
||||||
|
var properties = element.GetRequiredPropertyCustom<Dictionary<string, IProperty>>("properties", ReadProperties);
|
||||||
|
|
||||||
|
return new ClassProperty { Name = name, PropertyType = propertyType, Properties = properties };
|
||||||
|
}
|
||||||
|
}
|
197
DotTiled/Serialization/Tmj/Tmj.Tileset.cs
Normal file
197
DotTiled/Serialization/Tmj/Tmj.Tileset.cs
Normal file
|
@ -0,0 +1,197 @@
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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", ReadProperties, 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", ReadTiles, []);
|
||||||
|
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 = 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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
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 JsonException($"Unsupported image format '{extension}'")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
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) =>
|
||||||
|
element.GetValueAsList<Tile>(e =>
|
||||||
|
{
|
||||||
|
//var animation = e.GetOptionalPropertyCustom<List<Frame>>("animation", ReadFrames, 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", ReadObjectLayer, null);
|
||||||
|
var probability = e.GetOptionalProperty<float>("probability", 1.0f);
|
||||||
|
var properties = e.GetOptionalPropertyCustom<Dictionary<string, IProperty>?>("properties", ReadProperties, null);
|
||||||
|
// var terrain, replaced by wangsets
|
||||||
|
var type = e.GetOptionalProperty<string>("type", "");
|
||||||
|
|
||||||
|
var imageModel = image != null ? new Image
|
||||||
|
{
|
||||||
|
Format = 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
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
|
@ -7,19 +7,25 @@ namespace DotTiled;
|
||||||
|
|
||||||
public class TmjMapReader : IMapReader
|
public class TmjMapReader : IMapReader
|
||||||
{
|
{
|
||||||
|
// External resolvers
|
||||||
|
private readonly Func<string, Tileset> _externalTilesetResolver;
|
||||||
|
private readonly Func<string, Template> _externalTemplateResolver;
|
||||||
|
|
||||||
private string _jsonString;
|
private string _jsonString;
|
||||||
private bool disposedValue;
|
private bool disposedValue;
|
||||||
|
|
||||||
public TmjMapReader(string jsonString)
|
public TmjMapReader(string jsonString, Func<string, Tileset> externalTilesetResolver, Func<string, Template> externalTemplateResolver)
|
||||||
{
|
{
|
||||||
_jsonString = jsonString ?? throw new ArgumentNullException(nameof(jsonString));
|
_jsonString = jsonString ?? throw new ArgumentNullException(nameof(jsonString));
|
||||||
|
_externalTilesetResolver = externalTilesetResolver ?? throw new ArgumentNullException(nameof(externalTilesetResolver));
|
||||||
|
_externalTemplateResolver = externalTemplateResolver ?? throw new ArgumentNullException(nameof(externalTemplateResolver));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map ReadMap()
|
public Map ReadMap()
|
||||||
{
|
{
|
||||||
var jsonDoc = JsonDocument.Parse(_jsonString);
|
var jsonDoc = JsonDocument.Parse(_jsonString);
|
||||||
var rootElement = jsonDoc.RootElement;
|
var rootElement = jsonDoc.RootElement;
|
||||||
return Tmj.ReadMap(rootElement);
|
return Tmj.ReadMap(rootElement, _externalTilesetResolver, _externalTemplateResolver);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
@ -18,8 +19,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
|
||||||
|
@ -131,6 +132,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 +145,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
|
||||||
|
|
9
Makefile
9
Makefile
|
@ -0,0 +1,9 @@
|
||||||
|
test:
|
||||||
|
dotnet test
|
||||||
|
|
||||||
|
.PHONY: benchmark
|
||||||
|
benchmark: DotTiled.Benchmark/BenchmarkDotNet.Artifacts/results/MyBenchmarks.MapLoading-report-github.md
|
||||||
|
|
||||||
|
BENCHMARK_SOURCES = DotTiled.Benchmark/Program.cs
|
||||||
|
DotTiled.Benchmark/BenchmarkDotNet.Artifacts/results/MyBenchmarks.MapLoading-report-github.md: $(BENCHMARK_SOURCES)
|
||||||
|
dotnet run --project DotTiled.Benchmark/DotTiled.Benchmark.csproj -c Release
|
Loading…
Add table
Reference in a new issue