diff options
author | sadbeast <sadbeast@sadbeast.com> | 2024-04-15 18:08:28 -0700 |
---|---|---|
committer | sadbeast <sadbeast@sadbeast.com> | 2024-05-18 17:23:35 -0700 |
commit | a4711cad923d6c7480e596685d9dcaefa241fe3b (patch) | |
tree | f0c36edea9b32b8a1825270436868bb020689657 /src | |
download | tmz-a4711cad923d6c7480e596685d9dcaefa241fe3b.tar.gz tmz-a4711cad923d6c7480e596685d9dcaefa241fe3b.tar.bz2 |
Diffstat (limited to 'src')
-rw-r--r-- | src/test/map-base64-gzip.tmj | 35 | ||||
-rw-r--r-- | src/test/map-base64-none.tmj | 35 | ||||
-rw-r--r-- | src/test/map-base64-zlib.tmj | 35 | ||||
-rw-r--r-- | src/test/map-base64-zstd.tmj | 35 | ||||
-rw-r--r-- | src/test/map-csv.tmj | 37 | ||||
-rw-r--r-- | src/test/map-infinite-base64-zstd.tmj | 137 | ||||
-rw-r--r-- | src/test/map-infinite-csv.tmj | 195 | ||||
-rw-r--r-- | src/test/map.tmj | 107 | ||||
-rw-r--r-- | src/test/tiles.png | bin | 0 -> 3572 bytes | |||
-rw-r--r-- | src/test/tiles.tsj | 126 | ||||
-rw-r--r-- | src/tmz.zig | 551 | ||||
-rw-r--r-- | src/tmz_test.zig | 117 |
12 files changed, 1410 insertions, 0 deletions
diff --git a/src/test/map-base64-gzip.tmj b/src/test/map-base64-gzip.tmj new file mode 100644 index 0000000..3fa4154 --- /dev/null +++ b/src/test/map-base64-gzip.tmj @@ -0,0 +1,35 @@ +{ "compressionlevel":-1, + "height":5, + "infinite":false, + "layers":[ + { + "class":"bar", + "compression":"gzip", + "data":"H4sIAAAAAAAAA+NiYGDgB2JWIGZjYDgAxA6sUD5UrAGIGZiBcsxockB+AzOUDTJDAEmOFcrnBmIAadKM8GQAAAA=", + "encoding":"base64", + "height":5, + "id":1, + "name":"ground", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":5, + "x":0, + "y":0 + }], + "nextlayerid":2, + "nextobjectid":1, + "orientation":"orthogonal", + "renderorder":"right-down", + "tiledversion":"1.10.2", + "tileheight":8, + "tilesets":[ + { + "firstgid":1, + "source":"src/test/tiles.tsj" + }], + "tilewidth":8, + "type":"map", + "version":"1.10", + "width":5 +} diff --git a/src/test/map-base64-none.tmj b/src/test/map-base64-none.tmj new file mode 100644 index 0000000..a4a92d6 --- /dev/null +++ b/src/test/map-base64-none.tmj @@ -0,0 +1,35 @@ +{ "compressionlevel":-1, + "height":5, + "infinite":false, + "layers":[ + { + "class":"bar", + "compression":"", + "data":"CgAAAA8AAAAFAAAABgAAwAYAAEAFAAAABQAAAAUAAAAGAACABgAAAAMAAMADAABABQAAAAUAAAAFAAAAAwAAgAMAAAAFAAAADwAAABAAAAAFAAAABQAAAAUAAAAQAAAACwAAAA==", + "encoding":"base64", + "height":5, + "id":1, + "name":"base64-ground", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":5, + "x":0, + "y":0 + }], + "nextlayerid":2, + "nextobjectid":1, + "orientation":"orthogonal", + "renderorder":"right-down", + "tiledversion":"1.10.2", + "tileheight":8, + "tilesets":[ + { + "firstgid":1, + "source":"src/test/tiles.tsj" + }], + "tilewidth":8, + "type":"map", + "version":"1.10", + "width":5 +} diff --git a/src/test/map-base64-zlib.tmj b/src/test/map-base64-zlib.tmj new file mode 100644 index 0000000..698dabd --- /dev/null +++ b/src/test/map-base64-zlib.tmj @@ -0,0 +1,35 @@ +{ "compressionlevel":-1, + "height":5, + "infinite":false, + "layers":[ + { + "class":"bar", + "compression":"zlib", + "data":"eJzjYmBg4AdiViBmY2A4AMQOrFA+VKwBiBmYgXLMaHJAfgMzlA0yQwBJjhXK5wZiAOC8A68=", + "encoding":"base64", + "height":5, + "id":1, + "name":"base64-ground", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":5, + "x":0, + "y":0 + }], + "nextlayerid":2, + "nextobjectid":1, + "orientation":"orthogonal", + "renderorder":"right-down", + "tiledversion":"1.10.2", + "tileheight":8, + "tilesets":[ + { + "firstgid":1, + "source":"src/test/tiles.tsj" + }], + "tilewidth":8, + "type":"map", + "version":"1.10", + "width":5 +} diff --git a/src/test/map-base64-zstd.tmj b/src/test/map-base64-zstd.tmj new file mode 100644 index 0000000..70be286 --- /dev/null +++ b/src/test/map-base64-zstd.tmj @@ -0,0 +1,35 @@ +{ "compressionlevel":-1, + "height":5, + "infinite":false, + "layers":[ + { + "class":"bar", + "compression":"zstd", + "data":"KLUv\/SBkFQIA1AIKAAAADwAAAAUAAAAGAADABgAAQAWABgAAAAMAAMADAwAAgAMQEAAAAAsAAAAHACDAj92BQ3jJ9M+ONzM42WQC", + "encoding":"base64", + "height":5, + "id":1, + "name":"base64-ground", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":5, + "x":0, + "y":0 + }], + "nextlayerid":2, + "nextobjectid":1, + "orientation":"orthogonal", + "renderorder":"right-down", + "tiledversion":"1.10.2", + "tileheight":8, + "tilesets":[ + { + "firstgid":1, + "source":"src/test/tiles.tsj" + }], + "tilewidth":8, + "type":"map", + "version":"1.10", + "width":5 +} diff --git a/src/test/map-csv.tmj b/src/test/map-csv.tmj new file mode 100644 index 0000000..4241017 --- /dev/null +++ b/src/test/map-csv.tmj @@ -0,0 +1,37 @@ +{ "compressionlevel":-1, + "height":5, + "infinite":false, + "layers":[ + { + "class":"bar", + "data":[10, 15, 5, 3221225478, 1073741830, + 5, 5, 5, 2147483654, 6, + 3221225475, 1073741827, 5, 5, 5, + 2147483651, 3, 5, 15, 16, + 5, 5, 5, 16, 11], + "height":5, + "id":1, + "name":"base64-ground", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":5, + "x":0, + "y":0 + }], + "nextlayerid":2, + "nextobjectid":1, + "orientation":"orthogonal", + "renderorder":"right-down", + "tiledversion":"1.10.2", + "tileheight":8, + "tilesets":[ + { + "firstgid":1, + "source":"src/test/tiles.tsj" + }], + "tilewidth":8, + "type":"map", + "version":"1.10", + "width":5 +} diff --git a/src/test/map-infinite-base64-zstd.tmj b/src/test/map-infinite-base64-zstd.tmj new file mode 100644 index 0000000..ea864c6 --- /dev/null +++ b/src/test/map-infinite-base64-zstd.tmj @@ -0,0 +1,137 @@ +{ "class":"bar", + "compressionlevel":-1, + "height":30, + "infinite":true, + "layers":[ + { + "chunks":[ + { + "data":"KLUv\/WAAA9UGAPLFDxPAJR3\/v+v\/N\/1v7iZkS5mLRlcKP\/0tQiBieEGG8GMICH4MPzYOmGJNQOVq5aaGbEmZttVhSeHUaB2RrH7+FVeooLhlDoCCGaoyDxGQhJYSEFVFbbUcm4S4\/zC+1IopmU\/wPW1YdH8THhx4e8zYoeYLWfd+\/yFv\/Fod+L5mxuF\/X6oPaz\/UozixnBL+ZPfLGts0fY52QV\/AjX2rjgjX+Ags9rs7voEFYm2abNBRqMnswvxgNq6UC\/co\/efu+5ldPHd43rLv+aGhPPgAz\/QczylL5tdg5gqn", + "height":16, + "width":16, + "x":0, + "y":0 + }, + { + "data":"KLUv\/WAAA80GAHKECw\/gaQwAIABQYJJJpjQkngI88BOQ6uvWSdXibJAo7pq11qIPts0JBskYK0OSJXlhqNC44XCCkak7DxGgiECShKgWxJY5v59BPzPfmxfuVoU9TdRy8Onzd4QOmw7JvPYnCmAY3ZAvRZcH43ZHDPllarkdzvPQg4a5IfFJc5yOEIec8Ui+mg18NPthM60tgTyyd6XeXz19Hsz0eFAHIF4XPAn3d53gj0\/2bCHcHrv7MC\/3xqNSfs4D3aTNOiAP8HstjnS45vrC+84HcMRY4rNb04PsbNJQkAE=", + "height":16, + "width":16, + "x":16, + "y":0 + }, + { + "data":"KLUv\/WAAA7UGACKECw\/gGQ4AoKAGHsqUpEw3Hyk2RxbCkZhs\/pVpXz7QjKkRbIyqLUWs7b5MgfDYRgFYqND4YlCCIURpHhIAEkBgrGEAUTCvuf5\/B5070Y5yrvUw3SjPgje6q9+wPTUdHb0F+YTtaAbG9wB0xUUOtxfrNUk70nPsnA68ilkrdudLbPKZD3DTlEGP95KHzQb+oqO9twvrmY+UwTEjHyg3iGboEx+7k5zzR34v2tHz6Cl2t\/t8Ocx1t99FTcWJ\/PfMzOi7dFT++cH9Yk8+0zsXT11wAPgMmCk=", + "height":16, + "width":16, + "x":0, + "y":16 + }, + { + "data":"KLUv\/WAAAzUHACLFDhWwJekMwwAkSTKLw58Dcsu99w5V21Itkg0GR6izwFzHX1GZFDnSivECOgrOJZIOO\/z98gOGn79+0m8BW6jh7QrtvwFwgkJTVg8SYEQEeI0QlhFJStWy\/3edmqHGgenHjtXxDFEaOO52o4Q\/TH4P4gXD5umLBjn5zpEvE+4M\/cTB5J0wq0YzgG5jY\/38IqFB7xncopxvbGDnj+5rHhDOsvza1k8aXHGBH5QXkkwc5M\/vzYcO1\/e2tPk\/8Qytk82H9osw+rbcw\/o9VKZjvvNxLTsNR7AEzRxy0VH2rQ8A+H3PnmA1", + "height":16, + "width":16, + "x":16, + "y":16 + }], + "class":"bar", + "compression":"zstd", + "encoding":"base64", + "height":32, + "id":1, + "name":"ground", + "opacity":1, + "startx":0, + "starty":0, + "type":"tilelayer", + "visible":true, + "width":32, + "x":0, + "y":0 + }, + { + "draworder":"topdown", + "id":2, + "name":"objects", + "objects":[ + { + "height":16, + "id":1, + "name":"pool", + "rotation":0, + "type":"", + "visible":true, + "width":16, + "x":88, + "y":96 + }, + { + "height":20, + "id":2, + "name":"", + "rotation":0, + "text": + { + "color":"#241f31", + "fontfamily":"Serif", + "text":"tmz", + "wrap":true + }, + "type":"", + "visible":true, + "width":30, + "x":70, + "y":16 + }], + "opacity":1, + "type":"objectgroup", + "visible":true, + "x":0, + "y":0 + }, + { + "id":3, + "image":"tiles.png", + "name":"tile_image", + "offsetx":1, + "offsety":1, + "opacity":1, + "type":"imagelayer", + "visible":true, + "x":0, + "y":0 + }], + "nextlayerid":4, + "nextobjectid":3, + "orientation":"orthogonal", + "properties":[ + { + "name":"beautiful", + "type":"bool", + "value":true + }], + "renderorder":"right-down", + "tiledversion":"1.10.2", + "tileheight":8, + "tilesets":[ + { + "firstgid":1, + "source":"src/test/tiles.tsj" + }, + { + "columns":4, + "firstgid":17, + "image":"tiles.png", + "imageheight":32, + "imagewidth":32, + "margin":0, + "name":"embedded_tiles", + "spacing":0, + "tilecount":16, + "tileheight":8, + "tilewidth":8 + }], + "tilewidth":8, + "type":"map", + "version":"1.10", + "width":32 +} diff --git a/src/test/map-infinite-csv.tmj b/src/test/map-infinite-csv.tmj new file mode 100644 index 0000000..1889fee --- /dev/null +++ b/src/test/map-infinite-csv.tmj @@ -0,0 +1,195 @@ +{ "class":"bar", + "compressionlevel":-1, + "height":30, + "infinite":true, + "layers":[ + { + "chunks":[ + { + "data":[16, 11, 16, 16, 16, 16, 16, 16, 10, 10, 16, 14, 16, 16, 10, 16, + 16, 11, 11, 13, 14, 10, 11, 12, 16, 11, 11, 10, 13, 14, 13, 16, + 16, 14, 13, 10, 12, 16, 16, 11, 10, 16, 9, 13, 11, 11, 10, 16, + 10, 16, 16, 9, 13, 9, 15, 9, 14, 15, 9, 12, 13, 11, 13, 14, + 11, 9, 14, 9, 9, 9, 13, 12, 9, 13, 10, 16, 9, 16, 9, 12, + 13, 16, 12, 9, 12, 9, 12, 9, 9, 9, 12, 11, 16, 9, 16, 15, + 12, 11, 14, 9, 16, 11, 10, 16, 12, 16, 9, 16, 12, 14, 16, 11, + 14, 11, 14, 15, 11, 9, 9, 13, 9, 13, 16, 16, 14, 16, 13, 16, + 9, 11, 9, 10, 14, 9, 9, 11, 13, 9, 13, 13, 16, 16, 9, 12, + 12, 9, 10, 10, 11, 16, 16, 10, 9, 10, 10, 12, 9, 13, 10, 11, + 11, 16, 12, 16, 14, 13, 16, 1073741834, 9, 15, 9, 12, 16, 12, 9, 9, + 11, 11, 15, 16, 13, 9, 9, 13, 11, 13, 5, 5, 5, 5, 10, 13, + 16, 11, 9, 13, 10, 10, 16, 14, 10, 15, 5, 3221225478, 1073741830, 5, 13, 10, + 16, 13, 14, 13, 16, 9, 14, 5, 5, 5, 5, 2147483654, 6, 5, 10, 12, + 16, 9, 9, 14, 16, 9, 14, 5, 3221225475, 1073741827, 5, 5, 5, 5, 12, 11, + 16, 9, 14, 11, 9, 13, 10, 5, 2147483651, 3, 5, 15, 16, 10, 12, 13], + "height":16, + "width":16, + "x":0, + "y":0 + }, + { + "data":[16, 16, 16, 16, 9, 10, 16, 16, 16, 11, 11, 10, 12, 16, 11, 9, + 16, 16, 16, 11, 15, 12, 16, 16, 9, 16, 3221225487, 3221225487, 16, 16, 16, 9, + 9, 16, 11, 16, 13, 15, 15, 15, 9, 3221225487, 3221225487, 16, 3221225487, 11, 16, 16, + 14, 10, 13, 15, 14, 11, 14, 3221225487, 14, 16, 16, 10, 10, 12, 16, 16, + 11, 14, 12, 9, 12, 9, 13, 16, 16, 10, 14, 16, 9, 9, 11, 16, + 13, 12, 13, 16, 12, 14, 9, 9, 12, 2, 2, 2, 9, 9, 14, 16, + 12, 9, 13, 12, 2, 2, 2, 2, 2, 2, 16, 16, 2, 9, 16, 9, + 10, 12, 11, 13, 2, 14, 12, 2, 2, 11, 16, 11, 2, 9, 15, 9, + 12, 16, 14, 2, 2, 9, 10, 2, 2, 16, 10, 13, 2, 12, 15, 16, + 10, 10, 11, 14, 2, 16, 9, 14, 13, 14, 14, 2, 2, 14, 9, 16, + 9, 12, 16, 13, 2, 14, 9, 9, 11, 11, 16, 2, 9, 9, 11, 16, + 9, 16, 14, 15, 2, 2, 13, 12, 14, 16, 2, 2, 16, 14, 9, 11, + 14, 11, 10, 10, 11, 2, 9, 13, 16, 2, 2, 9, 14, 14, 9, 16, + 9, 13, 12, 9, 9, 2, 2, 9, 9, 2, 10, 13, 9, 16, 15, 12, + 13, 10, 16, 2147483663, 11, 15, 2, 2, 2, 2, 13, 16, 16, 12, 16, 14, + 16, 9, 16, 2147483663, 16, 9, 11, 2, 2, 11, 15, 12, 14, 9, 16, 9], + "height":16, + "width":16, + "x":16, + "y":0 + }, + { + "data":[16, 16, 9, 9, 15, 3221225487, 16, 5, 5, 5, 5, 16, 11, 11, 13, 15, + 12, 12, 15, 13, 14, 3221225487, 2147483663, 16, 16, 12, 10, 16, 16, 2147483663, 12, 9, + 14, 10, 14, 9, 12, 12, 16, 2147483663, 11, 13, 14, 12, 2147483663, 9, 16, 1073741834, + 12, 10, 12, 12, 9, 16, 9, 2147483663, 15, 16, 10, 14, 10, 11, 16, 16, + 16, 12, 16, 10, 16, 16, 9, 12, 14, 12, 14, 9, 14, 10, 14, 9, + 13, 9, 16, 9, 16, 16, 9, 13, 11, 11, 16, 14, 10, 3221225483, 15, 9, + 13, 10, 10, 9, 16, 9, 16, 11, 10, 13, 12, 16, 2147483663, 11, 15, 11, + 16, 11, 9, 9, 12, 14, 11, 16, 9, 16, 9, 10, 13, 10, 13, 13, + 16, 11, 12, 11, 10, 13, 13, 10, 10, 9, 12, 15, 16, 12, 9, 11, + 16, 11, 10, 9, 11, 15, 10, 15, 10, 11, 16, 10, 13, 15, 14, 3221225487, + 16, 9, 13, 11, 9, 11, 14, 14, 9, 16, 16, 16, 16, 12, 11, 14, + 16, 11, 9, 12, 16, 16, 9, 9, 14, 16, 16, 14, 15, 10, 10, 15, + 12, 15, 11, 16, 9, 13, 16, 15, 14, 15, 12, 13, 13, 13, 14, 10, + 16, 16, 16, 10, 12, 16, 16, 10, 13, 10, 14, 10, 10, 14, 13, 16, + 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, 0, 0, 0, 0, 0, 0, 0], + "height":16, + "width":16, + "x":0, + "y":16 + }, + { + "data":[13, 11, 11, 13, 14, 12, 2147483658, 2, 9, 2147483663, 2147483663, 11, 16, 9, 15, 9, + 14, 9, 10, 11, 16, 11, 9, 12, 14, 9, 9, 11, 11, 11, 16, 9, + 12, 14, 11, 12, 11, 16, 10, 2147483658, 2147483658, 9, 15, 10, 16, 9, 14, 16, + 34, 34, 34, 34, 34, 14, 10, 2147483663, 2147483658, 14, 11, 10, 11, 9, 15, 9, + 34, 3221225511, 3221225511, 3221225511, 34, 16, 2147483663, 10, 2147483658, 16, 11, 16, 16, 16, 11, 9, + 34, 3221225511, 34, 3221225511, 34, 9, 9, 10, 9, 11, 16, 16, 16, 16, 9, 10, + 34, 3221225511, 3221225511, 3221225511, 34, 14, 16, 9, 14, 14, 16, 16, 16, 11, 12, 13, + 34, 34, 34, 34, 34, 9, 13, 13, 9, 9, 12, 9, 13, 16, 12, 11, + 16, 10, 11, 14, 11, 15, 10, 12, 16, 16, 16, 3221225487, 3221225487, 16, 13, 15, + 13, 15, 12, 9, 13, 10, 16, 10, 9, 9, 16, 8, 8, 8, 8, 8, + 13, 14, 13, 12, 15, 13, 12, 12, 16, 16, 9, 8, 1, 1, 1, 1, + 15, 10, 13, 12, 14, 15, 16, 16, 3221225483, 3221225483, 3221225483, 8, 1, 1073741825, 1073741825, 1, + 9, 15, 9, 13, 11, 13, 9, 10, 3221225487, 3221225487, 11, 8, 1, 1073741825, 1073741825, 1, + 14, 9, 14, 12, 16, 13, 16, 16, 14, 9, 9, 8, 1, 1073741825, 1073741825, 1, + 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, 0, 0, 0, 0, 0, 0, 0], + "height":16, + "width":16, + "x":16, + "y":16 + }], + "class":"bar", + "height":32, + "id":1, + "name":"ground", + "opacity":1, + "startx":0, + "starty":0, + "type":"tilelayer", + "visible":true, + "width":32, + "x":0, + "y":0 + }, + { + "draworder":"topdown", + "id":2, + "name":"objects", + "objects":[ + { + "height":16, + "id":1, + "name":"pool", + "rotation":0, + "type":"", + "visible":true, + "width":16, + "x":88, + "y":96 + }, + { + "height":20, + "id":2, + "name":"", + "rotation":0, + "text": + { + "color":"#241f31", + "fontfamily":"Serif", + "text":"tmz", + "wrap":true + }, + "type":"", + "visible":true, + "width":30, + "x":70, + "y":16 + }], + "opacity":1, + "type":"objectgroup", + "visible":true, + "x":0, + "y":0 + }, + { + "id":3, + "image":"tiles.png", + "name":"tile_image", + "offsetx":1, + "offsety":1, + "opacity":1, + "type":"imagelayer", + "visible":true, + "x":0, + "y":0 + }], + "nextlayerid":4, + "nextobjectid":3, + "orientation":"orthogonal", + "properties":[ + { + "name":"beautiful", + "type":"bool", + "value":true + }], + "renderorder":"right-down", + "tiledversion":"1.10.2", + "tileheight":8, + "tilesets":[ + { + "firstgid":1, + "source":"src/test/tiles.tsj" + }, + { + "columns":4, + "firstgid":17, + "image":"tiles.png", + "imageheight":32, + "imagewidth":32, + "margin":0, + "name":"embedded_tiles", + "spacing":0, + "tilecount":16, + "tileheight":8, + "tilewidth":8 + }], + "tilewidth":8, + "type":"map", + "version":"1.10", + "width":32 +} diff --git a/src/test/map.tmj b/src/test/map.tmj new file mode 100644 index 0000000..4414387 --- /dev/null +++ b/src/test/map.tmj @@ -0,0 +1,107 @@ +{ "class":"bar", + "compressionlevel":-1, + "height":30, + "infinite":false, + "layers":[ + { + "class":"bar", + "compression":"zstd", + "data":"KLUv\/WAADo0WAOLOIhewJekMw4AmSQqb4zwHkkxSSmknYWS3FL3tKaVakYricx+o1U0H6HiOrIpawkr20Ux75XaikAGvO8JVvSFVRWuob50v1ZNByLH9iAJCBkQmKGTQgZDD\/WGhIIdCDjO2j9CPWlvYpqHXVGnohm1sxA6qOnn4WvVpVV3oXLbp3WZeyPtZjbWonpMkkg+BU6jB6QvNAaEEyChHMWcHEZCIwJGEiChKBs0ag\/WoKoS5gp76\/p+G5cawdf2AkXsGPwUnt+jgPJ7zhrn26D9Aeus6Hyyf6BNbJaeIEfzi8uPDXHvFbzPJALzjJyR7H9sz5+4BuY89HqnnOJ6YyUnYVfZjDPgrOc29sHaf911o19ny2Yo58M+3Ee1LTfmVdy836PBu\/4kzFZMp\/4Y0PPPvTF9qH4AaMV9IHKfF7C2h4vr\/SDtjE30QBqzZ3iPh7svCD7wNB\/2ny34\/E8yAS8tQezmPVwbhvqPAQdCy4pCyFt6Tdp9r2v4Xx2CcloneN8Qv9sWDW9z+N3sUXpk9skLP2xL3T7JzZv3wBHjSlj3P3Ptr6QNxfwVWFFdkMJVZ5IPo31F8cD\/0\/vp5+nuMePDMtP1JJ\/6K\/14vYucn32yTSW8XH7kc787uXO+RwXmDw4ryMw+TVHYe\/\/3sQO2OeB0nPV0PBm1YpvDFOnri3bbBlrnF4cD\/aPaKOOeHpJ\/t5aFunfuFD57AhmcPMaptd5vEwfQL6LJOfBdHuN5czo+tQLzpP4r9aQ1dBmj0+7vxZ3IDeW4W\/uHxA0krKXi2Bj6Eo32cd0qD1yicts8yzwZU9uivOzf9+26Mwzc6j7d0dCsWYEWJXcZk8QCbKY3OH8Po\/SavNPGeOx8sxt3JgHPU20x24EAb\/MJ3YD8BjZhLnBznCu\/3v3Y\/g6PwQb+W8hNPenxROT6V3WnwE5\/PoXjgT9hhuOqtPPNXuvlotZ1rLng1VAE=", + "encoding":"base64", + "height":30, + "id":1, + "name":"ground", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":32, + "x":0, + "y":0 + }, + { + "draworder":"topdown", + "id":2, + "name":"objects", + "objects":[ + { + "height":16, + "id":1, + "name":"pool", + "rotation":0, + "type":"", + "visible":true, + "width":16, + "x":88, + "y":96 + }, + { + "height":20, + "id":2, + "name":"", + "rotation":0, + "text": + { + "color":"#241f31", + "fontfamily":"Serif", + "text":"tmz", + "wrap":true + }, + "type":"", + "visible":true, + "width":30, + "x":70, + "y":16 + }], + "opacity":1, + "type":"objectgroup", + "visible":true, + "x":0, + "y":0 + }, + { + "id":3, + "image":"tiles.png", + "name":"tile_image", + "offsetx":1, + "offsety":1, + "opacity":1, + "type":"imagelayer", + "visible":true, + "x":0, + "y":0 + }], + "nextlayerid":4, + "nextobjectid":3, + "orientation":"orthogonal", + "properties":[ + { + "name":"beautiful", + "type":"bool", + "value":true + }], + "renderorder":"right-down", + "tiledversion":"1.10.2", + "tileheight":8, + "tilesets":[ + { + "firstgid":1, + "source":"src/test/tiles.tsj" + }, + { + "columns":4, + "firstgid":17, + "image":"tiles.png", + "imageheight":32, + "imagewidth":32, + "margin":0, + "name":"embedded_tiles", + "spacing":0, + "tilecount":16, + "tileheight":8, + "tilewidth":8 + }], + "tilewidth":8, + "type":"map", + "version":"1.10", + "width":32 +} diff --git a/src/test/tiles.png b/src/test/tiles.png Binary files differnew file mode 100644 index 0000000..499259c --- /dev/null +++ b/src/test/tiles.png diff --git a/src/test/tiles.tsj b/src/test/tiles.tsj new file mode 100644 index 0000000..53b9476 --- /dev/null +++ b/src/test/tiles.tsj @@ -0,0 +1,126 @@ +{ "columns":4, + "image":"tiles.png", + "imageheight":32, + "imagewidth":32, + "margin":0, + "name":"tiles", + "spacing":0, + "tilecount":16, + "tiledversion":"1.10.2", + "tileheight":8, + "tiles":[ + { + "id":6, + "objectgroup": + { + "draworder":"index", + "name":"", + "objects":[ + { + "height":7, + "id":1, + "name":"", + "rotation":0, + "type":"", + "visible":true, + "width":7, + "x":0, + "y":0 + }], + "opacity":1, + "type":"objectgroup", + "visible":true, + "x":0, + "y":0 + } + }, + { + "id":7, + "objectgroup": + { + "draworder":"index", + "name":"", + "objects":[ + { + "height":8, + "id":1, + "name":"", + "rotation":0, + "type":"", + "visible":true, + "width":8, + "x":0, + "y":0 + }], + "opacity":1, + "type":"objectgroup", + "visible":true, + "x":0, + "y":0 + } + }, + { + "animation":[ + { + "duration":200, + "tileid":8 + }, + { + "duration":200, + "tileid":10 + }, + { + "duration":200, + "tileid":9 + }, + { + "duration":200, + "tileid":11 + }], + "id":8 + }], + "tilewidth":8, + "transformations": + { + "hflip":true, + "preferuntransformed":false, + "rotate":true, + "vflip":false + }, + "type":"tileset", + "version":"1.10", + "wangsets":[ + { + "colors":[ + { + "color":"#ff0000", + "name":"", + "probability":1, + "tile":-1 + }], + "name":"grass", + "tile":-1, + "type":"corner", + "wangtiles":[ + { + "tileid":5, + "wangid":[0, 0, 0, 1, 0, 1, 0, 1] + }, + { + "tileid":9, + "wangid":[0, 0, 0, 0, 0, 1, 0, 1] + }, + { + "tileid":10, + "wangid":[0, 1, 0, 1, 0, 0, 0, 1] + }, + { + "tileid":13, + "wangid":[0, 1, 0, 0, 0, 1, 0, 1] + }, + { + "tileid":14, + "wangid":[0, 0, 0, 0, 0, 0, 0, 1] + }] + }] +}
\ No newline at end of file diff --git a/src/tmz.zig b/src/tmz.zig new file mode 100644 index 0000000..b743e58 --- /dev/null +++ b/src/tmz.zig @@ -0,0 +1,551 @@ +//! Tiled Map parser +//! +//! This is essentially a thin wrapper around std.json to parse the contents of JSON Map files (.tmj). +//! +//! Supports both CSV and Base64 encoded Layer and Chunk data. If Layer is compressed, data will be +//! automatically decompressed, using all supported compression methods in Tiled (as of 1.10.2): +//! - gzip +//! - zlib +//! - zstd +//! +//! Use `parseMap` to parse Tiled JSON Map string +//! +//! Use `parseTileset` to parse Tiled JSON Tileset string + +const std = @import("std"); +const base64_decoder = std.base64.standard.Decoder; +const equals = std.testing.expectEqual; +const stringEquals = std.testing.expectEqualStrings; +const fixedBufferStream = std.io.fixedBufferStream; +const ParseOptions = std.json.ParseOptions; +const Value = std.json.Value; +const Allocator = std.mem.Allocator; + +/// https://doc.mapeditor.org/en/stable/reference/json-map-format/#map +pub const Map = struct { + /// Hex-formatted color (#RRGGBB or #AARRGGBB) + background_color: ?[]const u8 = null, + class: ?[]const u8 = null, + /// The compression level to use for tile layer data (defaults to -1, which means to use the algorithm default) + // TODO: actually support this? + compression_level: i32 = -1, + /// Number of tile rows + height: u32, + /// Length of the side of a hex tile in pixels (hexagonal maps only) + hex_side_length: ?u32 = null, + infinite: bool, + layers: []Layer, + /// Auto-increments for each layer + next_layer_id: u32, + /// Auto-increments for each placed object + next_object_id: u32, + orientation: Orientation, + parallax_origin_x: ?f32 = 0, + parallax_origin_y: ?f32 = 0, + properties: ?[]const Property = null, + /// currently only supported for orthogonal maps + render_order: RenderOrder, + /// staggered / hexagonal maps only + stagger_axis: ?StaggerAxis = null, + /// staggered / hexagonal maps only + stagger_index: ?StaggerIndex = null, + tiled_version: []const u8, + /// Map grid height + tile_height: u32, + tilesets: []Tileset, + /// Map grid width + tile_width: u32, + version: []const u8, + /// Number of tile columns + width: u32, + + pub const Orientation = enum { orthogonal, isometric, staggered, hexagonal }; + pub const RenderOrder = enum { @"right-down", @"right-up", @"left-down", @"left-up" }; + pub const StaggerAxis = enum { x, y }; + pub const StaggerIndex = enum { odd, even }; + + pub fn jsonParseFromValue(allocator: Allocator, source: Value, options: ParseOptions) !@This() { + return try jsonParser(@This(), allocator, source, options); + } +}; + +/// https://doc.mapeditor.org/en/stable/reference/json-map-format/#layer +pub const Layer = struct { + chunks: ?[]Chunk = null, + class: ?[]const u8 = null, + compression: ?Compression = null, + data: ?[]u32 = null, + draw_order: ?DrawOrder = .topdown, + encoding: ?Encoding = .csv, + height: ?u32 = null, + id: u32, + image: ?[]const u8 = null, + layers: ?[]Layer = null, + locked: bool = false, + name: []const u8, + objects: ?[]Object = null, + offset_x: f32 = 0, + offset_y: f32 = 0, + opacity: f32, + parallax_x: f32 = 1, + parallax_y: f32 = 1, + properties: ?[]Property = null, + repeat_x: ?bool = null, + repeat_y: ?bool = null, + start_x: ?i32 = null, + start_y: ?i32 = null, + tint_color: ?[]const u8 = null, + transparent_color: ?[]const u8 = null, + type: Type, + visible: bool, + width: ?u32 = null, + x: i32, + y: i32, + + pub const DrawOrder = enum { topdown, index }; + pub const Encoding = enum { csv, base64 }; + pub const Type = enum { tilelayer, objectgroup, imagelayer, group }; + + pub const Compression = enum { + none, + zlib, + gzip, + zstd, + + pub fn jsonParseFromValue(allocator: Allocator, source: Value, options: ParseOptions) !@This() { + _ = allocator; + _ = options; + return switch (source) { + .string, .number_string => |value| cmp: { + if (value.len == 0) { + break :cmp .none; + } else { + break :cmp std.meta.stringToEnum(Compression, value) orelse .none; + } + }, + else => .none, + }; + } + }; + + pub fn jsonParseFromValue(allocator: Allocator, source: Value, options: ParseOptions) !@This() { + var layer = try jsonParser(@This(), allocator, source, options); + + if (layer.type == .tilelayer) { + if (source.object.get("data")) |data| { + if (layer.encoding == .csv) { + layer.data = try std.json.parseFromValueLeaky([]u32, allocator, data, options); + if (source.object.get("chunks")) |chunks| { + layer.chunks = try std.json.parseFromValueLeaky([]Chunk, allocator, chunks, options); + } + } else { + const base64_data = try std.json.parseFromValueLeaky([]const u8, allocator, data, options); + const layer_size: usize = (layer.width orelse 0) * (layer.height orelse 0); + + layer.data = parseBase64(allocator, base64_data, layer_size, layer.compression orelse .none); + + if (layer.chunks) |chunks| { + for (chunks) |*chunk| { + const chunk_size: usize = chunk.width * chunk.height; + chunk.tiles = parseBase64(allocator, chunk.data.base64, chunk_size, layer.compression orelse .none); + } + } + } + } + } + return layer; + } +}; + +/// https://doc.mapeditor.org/en/stable/reference/json-map-format/#chunk +pub const Chunk = struct { + data: DataEncoding, + tiles: []u32, + height: u32, + width: u32, + x: u32, + y: u32, + + const DataEncoding = union(enum) { + csv: []const u32, + base64: []const u8, + }; + + pub fn jsonParseFromValue(allocator: Allocator, source: Value, options: ParseOptions) !@This() { + var chunk = try jsonParser(@This(), allocator, source, options); + + if (source.object.get("data")) |data| { + switch (data) { + .array => { + chunk.data = .{ .csv = try std.json.parseFromValueLeaky([]const u32, allocator, data, options) }; + }, + .string => { + chunk.data = .{ .base64 = try std.json.parseFromValueLeaky([]const u8, allocator, data, options) }; + }, + else => return error.UnexpectedToken, + } + } + return chunk; + } +}; + +/// https://doc.mapeditor.org/en/stable/reference/json-map-format/#object +pub const Object = struct { + ellipse: ?bool = null, + gid: ?u32 = null, + height: f32, + id: u32, + name: []const u8, + point: ?bool = null, + polygon: ?[]Point = null, + polyline: ?[]Point = null, + properties: ?[]Property = null, + rotation: f32, + template: ?[]const u8 = null, + text: ?Text = null, + type: ?[]const u8 = null, + visible: bool, + width: f32, + x: f32, + y: f32, +}; + +/// https://doc.mapeditor.org/en/stable/reference/json-map-format/#point +pub const Point = struct { + x: f32, + y: f32, +}; + +/// https://doc.mapeditor.org/en/stable/reference/json-map-format/#text +pub const Text = struct { + bold: bool, + color: []const u8, + font_family: []const u8 = "sans-serif", + h_align: enum { center, right, justify, left } = .left, + italic: bool = false, + kerning: bool = true, + pixel_size: usize = 16, + strikeout: bool = false, + text: []const u8, + underline: bool = false, + v_align: enum { center, bottom, top } = .top, + wrap: bool = false, + + pub fn jsonParseFromValue(allocator: Allocator, source: Value, options: ParseOptions) !@This() { + return try jsonParser(@This(), allocator, source, options); + } +}; + +/// https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#properties +pub const Property = struct { + name: []const u8, + type: Type = .string, + property_type: ?[]const u8 = null, + + pub const Type = enum { + string, + int, + float, + bool, + color, + file, + object, + class, + }; + + pub fn jsonParseFromValue(allocator: Allocator, source: Value, options: ParseOptions) !@This() { + return try jsonParser(@This(), allocator, source, options); + } +}; + +/// https://doc.mapeditor.org/en/stable/reference/json-map-format/#tileset +pub const Tileset = struct { + background_color: ?[]const u8 = null, + class: ?[]const u8 = null, + columns: ?u32 = null, + fill_mode: ?FillMode = .stretch, + first_gid: u32, + grid: ?Grid = null, + image: ?[]const u8 = null, + image_height: ?u32 = null, + image_width: ?u32 = null, + margin: ?u32 = null, + name: ?[]const u8 = null, + object_alignment: ?ObjectAlignment = .unspecified, + properties: ?[]Property = null, + source: ?[]const u8 = null, + spacing: ?i32 = null, + terrains: ?[]Terrain = null, + tile_count: ?u32 = null, + tile_version: ?[]const u8 = null, + tile_height: ?u32 = null, + tile_offset: ?Point = null, + tile_render_size: ?TileRenderSize = .tile, + tiles: ?[]Tile = null, + tile_width: ?u32 = null, + transformations: ?Transformations = null, + transparent_color: ?[]const u8 = null, + type: ?Type = .tileset, + version: ?[]const u8 = null, + wang_sets: ?[]WangSet = null, + + pub const FillMode = enum { stretch, @"preserve-aspect-fit" }; + pub const TileRenderSize = enum { tile, grid }; + pub const Type = enum { tileset }; + + pub const ObjectAlignment = enum { + unspecified, + topleft, + top, + topright, + left, + center, + right, + bottomleft, + bottom, + bottomright, + }; + + pub fn jsonParseFromValue(allocator: Allocator, source: Value, options: ParseOptions) !@This() { + const tileset = try jsonParser(@This(), allocator, source, options); + + if (tileset.source) |s| { + const file = std.fs.cwd().openFile(s, .{}) catch @panic("couldn't open tileset"); + defer file.close(); + const tileset_bytes = file.readToEndAlloc(allocator, std.math.maxInt(usize)) catch @panic("couldn't read tileset file "); + return parseJsonLeaky(Tileset, allocator, tileset_bytes) catch @panic("could not parse tileset"); + } + return tileset; + } +}; + +/// https://doc.mapeditor.org/en/stable/reference/json-map-format/#grid +pub const Grid = struct { + orientation: enum { orthogonal, isometric } = .orthogonal, + height: i32, + width: i32, +}; + +/// https://doc.mapeditor.org/en/stable/reference/json-map-format/#terrain +pub const Terrain = struct { + name: []const u8, + properties: []Property, + tile: i32, +}; + +/// https://doc.mapeditor.org/en/stable/reference/json-map-format/#tile-definition +pub const Tile = struct { + animation: []Frame, + id: i32, + name: ?[]const u8, + image_height: u32, + image_width: u32, + x: i32, + y: i32, + width: u32, + height: u32, + objectgroup: ?Layer, + probability: f32, + properties: []Property, + terrain: []i32, + type: []const u8, + + pub fn jsonParseFromValue(allocator: Allocator, source: Value, options: ParseOptions) !@This() { + return try jsonParser(@This(), allocator, source, options); + } +}; + +/// https://doc.mapeditor.org/en/stable/reference/json-map-format/#frame +pub const Frame = struct { + duration: i32, + tile_id: i32, + + pub fn jsonParseFromValue(allocator: Allocator, source: Value, options: ParseOptions) !@This() { + return try jsonParser(@This(), allocator, source, options); + } +}; + +/// https://doc.mapeditor.org/en/stable/reference/json-map-format/#transformations +pub const Transformations = struct { + h_flip: bool, + v_flip: bool, + rotate: bool, + prefer_untransformed: bool, + + pub fn jsonParseFromValue(allocator: Allocator, source: Value, options: ParseOptions) !@This() { + return try jsonParser(@This(), allocator, source, options); + } +}; + +/// https://doc.mapeditor.org/en/stable/reference/json-map-format/#wang-set +pub const WangSet = struct { + class: ?[]const u8 = null, + colors: ?[]WangColor = null, + name: []const u8, + properties: ?[]Property = null, + tile: i32, + type: Type, + wang_tiles: []WangTile, + + pub const Type = enum { corner, edge, mixed }; + + pub fn jsonParseFromValue(allocator: Allocator, source: Value, options: ParseOptions) !@This() { + return try jsonParser(@This(), allocator, source, options); + } +}; + +/// https://doc.mapeditor.org/en/stable/reference/json-map-format/#wang-color +pub const WangColor = struct { + class: ?[]const u8 = null, + color: []const u8, + name: []const u8, + probability: f32, + properties: ?[]Property = null, + tile: i32, +}; + +/// https://doc.mapeditor.org/en/stable/reference/json-map-format/#wang-tile +pub const WangTile = struct { + tile_id: i32, + wang_id: [8]u8, + + pub fn jsonParseFromValue(allocator: Allocator, source: Value, options: ParseOptions) !@This() { + return try jsonParser(@This(), allocator, source, options); + } +}; + +/// From https://www.openmymind.net/Zigs-std-json-Parsed/ +pub fn Managed(comptime T: type) type { + return struct { + value: T, + arena: *std.heap.ArenaAllocator, + + const Self = @This(); + + pub fn fromJson(parsed: std.json.Parsed(T)) Self { + return .{ + .arena = parsed.arena, + .value = parsed.value, + }; + } + + pub fn deinit(self: Self) void { + const arena = self.arena; + const allocator = arena.child_allocator; + arena.deinit(); + allocator.destroy(arena); + } + }; +} + +/// Parse a JSON Map string +/// You must call `deinit()` on the returned `Managed(Map)` to clean up allocated resources. +/// If you are using a `std.heap.ArenaAllocator` or similar, consider calling `parseMapLeaky` instead. +pub fn parseMap(allocator: Allocator, bytes: []const u8) !Managed(Map) { + return Managed(Map).fromJson(try parse(Map, allocator, bytes)); +} + +pub fn parseMapLeaky(allocator: Allocator, bytes: []const u8) !Map { + return try parseJsonLeaky(Map, allocator, bytes); +} + +/// Parse a JSON Tileset string +/// You must call `deinit()` on the returned `Managed(Tileset)` to clean up allocated resources. +/// If you are using a `std.heap.ArenaAllocator` or similar, consider calling `parseTilesetLeaky` instead. +pub fn parseTileset(allocator: Allocator, bytes: []const u8) !Managed(Tileset) { + return Managed(Tileset).fromJson(try parse(Tileset, allocator, bytes)); +} + +pub fn parseTilesetLeaky(allocator: Allocator, bytes: []const u8) !Tileset { + return try parseJsonLeaky(Tileset, allocator, bytes); +} + +fn parse(comptime T: type, allocator: Allocator, bytes: []const u8) !std.json.Parsed(T) { + var parsed = std.json.Parsed(T){ + .arena = try allocator.create(std.heap.ArenaAllocator), + .value = undefined, + }; + errdefer allocator.destroy(parsed.arena); + + parsed.arena.* = std.heap.ArenaAllocator.init(allocator); + errdefer parsed.arena.deinit(); + + parsed.value = try parseJsonLeaky(T, parsed.arena.allocator(), bytes); + + return parsed; +} + +fn parseJsonLeaky(comptime T: type, allocator: Allocator, bytes: []const u8) !T { + const json_value = try std.json.parseFromSliceLeaky(Value, allocator, bytes, .{}); + return try std.json.parseFromValueLeaky(T, allocator, json_value, .{ .ignore_unknown_fields = true }); +} + +// converts masheduppropertynames to Zig style snake_case field names and ignores data +fn jsonParser(T: type, allocator: Allocator, source: Value, options: ParseOptions) !T { + var t: T = undefined; + + inline for (@typeInfo(T).Struct.fields) |field| { + if (comptime std.mem.eql(u8, field.name, "data")) continue; + const size = comptime std.mem.replacementSize(u8, field.name, "_", ""); + var tiled_name: [size]u8 = undefined; + _ = std.mem.replace(u8, field.name, "_", "", &tiled_name); + + const source_field = source.object.get(&tiled_name); + if (source_field) |s| { + @field(t, field.name) = try std.json.innerParseFromValue(field.type, allocator, s, options); + } else { + if (field.default_value) |val| { + @field(t, field.name) = @as(*align(1) const field.type, @ptrCast(val)).*; + } + } + } + + return t; +} + +fn parseBase64(allocator: Allocator, base64_data: []const u8, chunk_size: usize, compression: Layer.Compression) []u32 { + const size = base64_decoder.calcSizeForSlice(base64_data) catch @panic("Unable to decode base64 data"); + + var decoded = allocator.alloc(u8, size) catch @panic("OOM"); + defer allocator.free(decoded); + base64_decoder.decode(decoded, base64_data) catch @panic("Unable to decode base64 data"); + + const data = allocator.alloc(u32, chunk_size) catch @panic("OOM"); + const alignment = @alignOf(u32); + const buf = allocator.alloc(u8, chunk_size * alignment) catch @panic("OOM"); + var decompressed = fixedBufferStream(buf); + var compressed = fixedBufferStream(decoded); + switch (compression) { + .gzip => { + std.compress.gzip.decompress(compressed.reader(), decompressed.writer()) catch @panic("Unable to decompress gzip"); + decoded = decompressed.getWritten(); + }, + .zlib => { + std.compress.zlib.decompress(compressed.reader(), decompressed.writer()) catch @panic("Unable to decompress zlib"); + decoded = decompressed.getWritten(); + }, + .zstd => { + const window_buffer = allocator.alloc(u8, 1 << 23) catch @panic("OOM"); + var zstd_stream = std.compress.zstd.decompressor(compressed.reader(), .{ .window_buffer = window_buffer }); + _ = zstd_stream.reader().readAll(buf) catch @panic("Unable to decompress zstd"); + decoded = buf; + }, + .none => {}, + } + + if (buf.len != decoded.len) { + @panic("data size does not match Layer dimensions"); + } + + var tile_index: usize = 0; + for (data) |*tile| { + const end = tile_index + alignment; + tile.* = std.mem.readInt(u32, decoded[tile_index..end][0..alignment], .little); + tile_index += alignment; + } + return data; +} + +test { + _ = @import("./tmz_test.zig"); +} diff --git a/src/tmz_test.zig b/src/tmz_test.zig new file mode 100644 index 0000000..221a871 --- /dev/null +++ b/src/tmz_test.zig @@ -0,0 +1,117 @@ +const std = @import("std"); +const equals = std.testing.expectEqual; +const stringEquals = std.testing.expectEqualStrings; +const tmz = @import("./tmz.zig"); +const Layer = tmz.Layer; +const Map = tmz.Map; + +const full_map = @embedFile("test/map.tmj"); + +test "parseMap works" { + const parsed_map = try tmz.parseMap(std.testing.allocator, full_map); + defer parsed_map.deinit(); + + const map = parsed_map.value; + try regularMapTests(map); + try equals(false, map.infinite); +} + +test "infinite map with base64 chunks" { + const infinite_map = @embedFile("test/map-infinite-base64-zstd.tmj"); + const parsed_map = try tmz.parseMap(std.testing.allocator, infinite_map); + defer parsed_map.deinit(); + + const map = parsed_map.value; + try equals(true, map.infinite); +} + +test "infinite map with csv chunks" { + const infinite_map = @embedFile("test/map-infinite-csv.tmj"); + const parsed_map = try tmz.parseMap(std.testing.allocator, infinite_map); + defer parsed_map.deinit(); + + const map = parsed_map.value; + try equals(true, map.infinite); +} + +test "parseTileset works" { + const tileset_data = @embedFile("test/tiles.tsj"); + const parsed_tileset = try tmz.parseTileset(std.testing.allocator, tileset_data); + defer parsed_tileset.deinit(); + + const tileset = parsed_tileset.value; + + try stringEquals("tiles.png", tileset.image.?); +} + +test "parseMapLeaky works" { + var arena = std.heap.ArenaAllocator.init(std.testing.allocator); + defer arena.deinit(); + const map = try tmz.parseMapLeaky(arena.allocator(), full_map); + try regularMapTests(map); +} + +fn regularMapTests(map: Map) !void { + try equals(null, map.background_color); + try stringEquals("bar", map.class.?); + try equals(-1, map.compression_level); + try equals(30, map.height); + try equals(null, map.hex_side_length); + try equals(false, map.infinite); + try equals(4, map.next_layer_id); + try equals(3, map.next_object_id); + try equals(.orthogonal, map.orientation); + try equals(0, map.parallax_origin_x); + try equals(0, map.parallax_origin_y); + + try equals(Map.RenderOrder.@"right-down", map.render_order); + + try stringEquals("1.10", map.version); + + try equals(3, map.layers.len); + const layer = map.layers[0]; + + try stringEquals("bar", layer.class.?); + try equals(Layer.Type.tilelayer, layer.type); + try equals(32, layer.width.?); + try equals(0, layer.x); + try equals(0, layer.y); + + try equals(16, layer.data.?[0]); + try equals(11, layer.data.?[1]); +} + +test "Map with CSV encoded layer data parses correctly" { + const parsed_map = try tmz.parseMap(std.testing.allocator, @embedFile("test/map-csv.tmj")); + defer parsed_map.deinit(); + + const map = parsed_map.value; + + try equals(1, map.layers.len); + + const layer = map.layers[0]; + try equals(null, layer.compression); + + try equals(10, layer.data.?[0]); + try equals(15, layer.data.?[1]); + try equals(5, layer.data.?[2]); +} + +test "Map with Base64 encoded layer data parses correctly" { + inline for (@typeInfo(Layer.Compression).Enum.fields) |field| { + const map_file = @embedFile("test/map-base64-" ++ field.name ++ ".tmj"); + const parsed_map = try tmz.parseMap(std.testing.allocator, map_file); + defer parsed_map.deinit(); + + const map = parsed_map.value; + + try equals(1, map.layers.len); + + const layer = map.layers[0]; + try equals(@as(Layer.Compression, @enumFromInt(field.value)), layer.compression); + + try equals(10, layer.data.?[0]); + try equals(15, layer.data.?[1]); + try equals(5, layer.data.?[2]); + } +} |