diff --git a/assets/tilesets/testLoadTileset.tsx b/assets/tilesets/testLoadTileset.tsx new file mode 100644 index 0000000..6d4eeb9 --- /dev/null +++ b/assets/tilesets/testLoadTileset.tsx @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/assets/tilesets/testLoadTilesetTile.tsx b/assets/tilesets/testLoadTilesetTile.tsx new file mode 100644 index 0000000..2867ede --- /dev/null +++ b/assets/tilesets/testLoadTilesetTile.tsx @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/go.sum b/go.sum index 32e3988..e47987d 100644 --- a/go.sum +++ b/go.sum @@ -4,10 +4,7 @@ github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1 github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 h1:hVwzHzIUGRjiF7EcUjqNxk3NCfkPxbDKRdnNE1Rpg0U= diff --git a/tiled.go b/tiled.go index 4a912b0..6d1c0f5 100644 --- a/tiled.go +++ b/tiled.go @@ -86,3 +86,29 @@ func (l *Loader) LoadFromFile(fileName string) (*Map, error) { dir := filepath.Dir(fileName) return l.LoadFromReader(dir, f) } + +// LoadTilesetFromReader loads a .tsx file into a Tileset structure +func LoadTilesetFromReader(baseDir string, r io.Reader) (*Tileset, error) { + d := xml.NewDecoder(r) + + m := &Tileset{ + baseDir: baseDir, + SourceLoaded: true, + } + if err := d.Decode(m); err != nil { + return nil, err + } + + return m, nil +} + +// SaveTilesetToWriter saves a Tileset structure into a given writer +func SaveTilesetToWriter(tileset *Tileset, w io.Writer) error { + encoder := xml.NewEncoder(w) + encoder.Indent("", " ") + return encoder.Encode(tileset) +} + +func b(v bool) *bool { + return &v +} diff --git a/tiled_test.go b/tiled_test.go index 5468bce..e4ec294 100644 --- a/tiled_test.go +++ b/tiled_test.go @@ -85,12 +85,12 @@ func TestLoadFromFile(t *testing.T) { // Test ObjectGroups.Visible defaults to true assert.Len(t, m.ObjectGroups, 1) assert.Equal(t, uint32(2), m.ObjectGroups[0].ID) - assert.Equal(t, true, m.ObjectGroups[0].Visible) + assert.Equal(t, true, *m.ObjectGroups[0].Visible) // Test Object.Visible defaults to true assert.Len(t, m.ObjectGroups[0].Objects, 1) assert.Equal(t, uint32(2), m.ObjectGroups[0].Objects[0].ID) - assert.Equal(t, true, m.ObjectGroups[0].Objects[0].Visible) + assert.Equal(t, true, *m.ObjectGroups[0].Objects[0].Visible) } func TestLoadFromFileError(t *testing.T) { @@ -197,7 +197,7 @@ func TestFont(t *testing.T) { assert.Equal(t, false, text.Italic) assert.Equal(t, false, text.Underline) assert.Equal(t, false, text.Strikethrough) - assert.Equal(t, true, text.Kerning) + assert.Equal(t, true, *text.Kerning) assert.Equal(t, "left", text.HAlign) assert.Equal(t, "top", text.VAlign) } diff --git a/tmx_defaults.go b/tmx_defaults.go index cf20e5a..ef6c684 100644 --- a/tmx_defaults.go +++ b/tmx_defaults.go @@ -57,12 +57,12 @@ func (a *aliasMap) SetDefaults() { // SetDefaults provides default values for Object. func (a *aliasObject) SetDefaults() { - a.Visible = true + a.Visible = b(true) } // SetDefaults provides default values for ObjectGroup. func (a *aliasObjectGroup) SetDefaults() { - a.Visible = true + a.Visible = b(true) a.Opacity = 1 } @@ -70,7 +70,7 @@ func (a *aliasObjectGroup) SetDefaults() { func (a *aliasText) SetDefaults() { a.FontFamily = "sans-serif" a.Size = 16 - a.Kerning = true + a.Kerning = b(true) a.HAlign = "left" a.VAlign = "top" a.Color = &HexColor{} diff --git a/tmx_group.go b/tmx_group.go index 2d05ae2..fd84537 100644 --- a/tmx_group.go +++ b/tmx_group.go @@ -43,7 +43,7 @@ type Group struct { // Whether the layer is shown (1) or hidden (0). Defaults to 1. Visible bool `xml:"visible,attr"` // Custom properties - Properties Properties `xml:"properties>property"` + Properties *Properties `xml:"properties"` // Map layers Layers []*Layer `xml:"layer"` // Map object groups diff --git a/tmx_image.go b/tmx_image.go index 933c204..50d1720 100644 --- a/tmx_image.go +++ b/tmx_image.go @@ -47,7 +47,7 @@ type ImageLayer struct { // Whether the layer is shown (1) or hidden (0). Defaults to 1. Visible bool `xml:"visible,attr"` // Custom properties - Properties Properties `xml:"properties>property"` + Properties *Properties `xml:"properties"` // The group image Image *Image `xml:"image"` } @@ -69,16 +69,16 @@ func (l *ImageLayer) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error // Image source type Image struct { // Used for embedded images, in combination with a data child element. Valid values are file extensions like png, gif, jpg, bmp, etc. - Format string `xml:"format,attr"` + Format string `xml:"format,attr,omitempty"` // The reference to the tileset image file Source string `xml:"source,attr"` // Defines a specific color that is treated as transparent (example value: "#FF00FF" for magenta). // Up until Tiled 0.12, this value is written out without a # but this is planned to change. - Trans *HexColor `xml:"trans,attr"` + Trans *HexColor `xml:"trans,attr,omitempty"` // The image width in pixels (optional, used for tile index correction when the image changes) - Width int `xml:"width,attr"` + Width int `xml:"width,attr,omitempty"` // The image height in pixels (optional) - Height int `xml:"height,attr"` + Height int `xml:"height,attr,omitempty"` // Embedded image content - Data *Data `xml:"data,attr"` + Data *Data `xml:"data,attr,omitempty"` } diff --git a/tmx_layer.go b/tmx_layer.go index 432f7e0..b96216e 100644 --- a/tmx_layer.go +++ b/tmx_layer.go @@ -79,7 +79,7 @@ type Layer struct { // Rendering offset for this layer in pixels. Defaults to 0. (since 0.14) OffsetY int `xml:"offsety,attr"` // Custom properties - Properties Properties `xml:"properties>property"` + Properties *Properties `xml:"properties"` // This is the attribute you'd like to use, not Data. Tile entry at (x,y) is obtained using l.DecodedTiles[y*map.Width+x]. Tiles []*LayerTile // Data diff --git a/tmx_map.go b/tmx_map.go index 7045095..b4e0031 100644 --- a/tmx_map.go +++ b/tmx_map.go @@ -79,7 +79,7 @@ type Map struct { // Stores the next available ID for new objects. This number is stored to prevent reuse of the same ID after objects have been removed. (since 0.11) NextObjectID uint32 `xml:"nextobjectid,attr"` // Custom properties - Properties *Properties `xml:"properties>property"` + Properties *Properties `xml:"properties"` // Map tilesets Tilesets []*Tileset `xml:"tileset"` // Map layers diff --git a/tmx_object.go b/tmx_object.go index 78c9ded..26fb545 100644 --- a/tmx_object.go +++ b/tmx_object.go @@ -40,25 +40,25 @@ type ObjectGroup struct { // Unique ID of the layer. // Each layer that added to a map gets a unique id. Even if a layer is deleted, // no layer ever gets the same ID. Can not be changed in Tiled. (since Tiled 1.2) - ID uint32 `xml:"id,attr"` + ID uint32 `xml:"id,attr,omitempty"` // The name of the object group. - Name string `xml:"name,attr"` + Name string `xml:"name,attr,omitempty"` // The color used to display the objects in this group. - Color *HexColor `xml:"color,attr"` + Color *HexColor `xml:"color,attr,omitempty"` // The opacity of the layer as a value from 0 to 1. Defaults to 1. - Opacity float32 `xml:"opacity,attr"` + Opacity float32 `xml:"opacity,attr,omitempty"` // Whether the layer is shown (1) or hidden (0). Defaults to 1. - Visible bool `xml:"visible,attr"` + Visible *bool `xml:"visible,attr"` // Rendering offset for this layer in pixels. Defaults to 0. (since 0.14) - OffsetX int `xml:"offsetx,attr"` + OffsetX int `xml:"offsetx,attr,omitempty"` // Rendering offset for this layer in pixels. Defaults to 0. (since 0.14) - OffsetY int `xml:"offsety,attr"` + OffsetY int `xml:"offsety,attr,omitempty"` // Whether the objects are drawn according to the order of appearance ("index") or sorted by their y-coordinate ("topdown"). Defaults to "topdown". - DrawOrder string `xml:"draworder,attr"` + DrawOrder string `xml:"draworder,attr,omitempty"` // Custom properties - Properties Properties `xml:"properties>property"` + Properties *Properties `xml:"properties,omitempty"` // Group objects - Objects []*Object `xml:"object"` + Objects []*Object `xml:"object,omitempty"` } // DecodeObjectGroup decodes object group data @@ -94,37 +94,37 @@ func (g *ObjectGroup) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error type Object struct { // Unique ID of the object. Each object that is placed on a map gets a unique id. Even if an object was deleted, no object gets the same ID. // Can not be changed in Tiled Qt. (since Tiled 0.11) - ID uint32 `xml:"id,attr"` + ID uint32 `xml:"id,attr,omitempty"` // The name of the object. An arbitrary string. - Name string `xml:"name,attr"` + Name string `xml:"name,attr,omitempty"` // The type of the object. An arbitrary string. - Type string `xml:"type,attr"` + Type string `xml:"type,attr,omitempty"` // The x coordinate of the object. - X float64 `xml:"x,attr"` + X float64 `xml:"x,attr,omitempty"` // The y coordinate of the object. - Y float64 `xml:"y,attr"` + Y float64 `xml:"y,attr,omitempty"` // The width of the object (defaults to 0). - Width float64 `xml:"width,attr"` + Width float64 `xml:"width,attr,omitempty"` // The height of the object (defaults to 0). - Height float64 `xml:"height,attr"` + Height float64 `xml:"height,attr,omitempty"` // The rotation of the object in degrees clockwise (defaults to 0). (since 0.10) - Rotation float64 `xml:"rotation,attr"` + Rotation float64 `xml:"rotation,attr,omitempty"` // An reference to a tile (optional). - GID uint32 `xml:"gid,attr"` + GID uint32 `xml:"gid,attr,omitempty"` // Whether the object is shown (1) or hidden (0). Defaults to 1. (since 0.9) - Visible bool `xml:"visible,attr"` + Visible *bool `xml:"visible,attr"` // Custom properties - Properties Properties `xml:"properties>property"` + Properties *Properties `xml:"properties,omitempty"` // Used to mark an object as an ellipse. The existing x, y, width and height attributes are used to determine the size of the ellipse. - Ellipses []*Ellipse `xml:"ellipse"` + Ellipses []*Ellipse `xml:"ellipse,omitempty"` // Polygons - Polygons []*Polygon `xml:"polygon"` + Polygons []*Polygon `xml:"polygon,omitempty"` // Poly lines - PolyLines []*PolyLine `xml:"polyline"` + PolyLines []*PolyLine `xml:"polyline,omitempty"` // Text - Text *Text `xml:"text"` + Text *Text `xml:"text,omitempty"` // Template - TemplateSource string `xml:"template,attr"` + TemplateSource string `xml:"template,attr,omitempty"` TemplateLoaded bool `xml:"-"` Template *Template } @@ -241,31 +241,32 @@ type Text struct { // The actual text Text string `xml:",chardata"` // The font family used (default: "sans-serif") - FontFamily string `xml:"fontfamily,attr"` + FontFamily string `xml:"fontfamily,attr,omitempty"` // The size of the font in pixels (not using points, because other sizes in the TMX format are also using pixels) (default: 16) - Size int `xml:"pixelsize,attr"` + Size int `xml:"pixelsize,attr,omitempty"` // Whether word wrapping is enabled (1) or disabled (0). Defaults to 0. - Wrap bool `xml:"wrap,attr"` + Wrap bool `xml:"wrap,attr,omitempty"` // Color of the text in #AARRGGBB or #RRGGBB format (default: #000000) - Color *HexColor `xml:"color,attr"` + Color *HexColor `xml:"color,attr,omitempty"` // Whether the font is bold (1) or not (0). Defaults to 0. - Bold bool `xml:"bold,attr"` + Bold bool `xml:"bold,attr,omitempty"` // Whether the font is italic (1) or not (0). Defaults to 0. - Italic bool `xml:"italic,attr"` + Italic bool `xml:"italic,attr,omitempty"` // Whether a line should be drawn below the text (1) or not (0). Defaults to 0. - Underline bool `xml:"underline,attr"` + Underline bool `xml:"underline,attr,omitempty"` // Whether a line should be drawn through the text (1) or not (0). Defaults to 0. - Strikethrough bool `xml:"strikeout,attr"` + Strikethrough bool `xml:"strikeout,attr,omitempty"` // Whether kerning should be used while rendering the text (1) or not (0). Default to 1. - Kerning bool `xml:"kerning,attr"` + Kerning *bool `xml:"kerning,attr,omitempty"` // Horizontal alignment of the text within the object (left (default), center, right or justify (since Tiled 1.2.1)) - HAlign string `xml:"halign,attr"` + HAlign string `xml:"halign,attr,omitempty"` // Vertical alignment of the text within the object (top (default), center or bottom) - VAlign string `xml:"valign,attr"` + VAlign string `xml:"valign,attr,omitempty"` } // UnmarshalXML decodes a single XML element beginning with the given start element. func (t *Text) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + item := aliasText{} item.SetDefaults() diff --git a/tmx_property.go b/tmx_property.go index 836c930..e4a8aef 100644 --- a/tmx_property.go +++ b/tmx_property.go @@ -25,7 +25,9 @@ package tiled import "strconv" // Properties wraps any number of custom properties -type Properties []*Property +type Properties struct { + Property []*Property `xml:"property"` +} // Property is used for custom properties type Property struct { @@ -43,7 +45,7 @@ type Property struct { // Get finds all properties by specified name func (p Properties) Get(name string) []string { var values []string - for _, property := range p { + for _, property := range p.Property { if property.Name == name { values = append(values, property.Value) } @@ -54,7 +56,7 @@ func (p Properties) Get(name string) []string { // GetString finds first string property by specified name func (p Properties) GetString(name string) string { var v string - for _, property := range p { + for _, property := range p.Property { if property.Name == name { if property.Type == "" { return property.Value @@ -68,7 +70,7 @@ func (p Properties) GetString(name string) string { // GetBool finds first bool property by specified name func (p Properties) GetBool(name string) bool { - for _, property := range p { + for _, property := range p.Property { if property.Name == name && property.Type == "boolean" { return property.Value == "true" } @@ -78,7 +80,7 @@ func (p Properties) GetBool(name string) bool { // GetInt finds first int property by specified name func (p Properties) GetInt(name string) int { - for _, property := range p { + for _, property := range p.Property { if property.Name == name && property.Type == "int" { v, err := strconv.Atoi(property.Value) if err != nil { @@ -92,7 +94,7 @@ func (p Properties) GetInt(name string) int { // GetFloat finds first float property by specified name func (p Properties) GetFloat(name string) float64 { - for _, property := range p { + for _, property := range p.Property { if property.Name == name && property.Type == "float" { v, err := strconv.ParseFloat(property.Value, 64) if err != nil { diff --git a/tmx_property_test.go b/tmx_property_test.go index 074246f..4271ba4 100644 --- a/tmx_property_test.go +++ b/tmx_property_test.go @@ -31,25 +31,27 @@ import ( func TestGetProperty(t *testing.T) { props := Properties{ - { - Name: "string-name", - Type: "string", - Value: "string-value", - }, - { - Name: "int-name", - Type: "int", - Value: "123", - }, - { - Name: "float-name", - Type: "float", - Value: "1.23", - }, - { - Name: "bool-name", - Type: "boolean", - Value: "true", + Property: []*Property{ + { + Name: "string-name", + Type: "string", + Value: "string-value", + }, + { + Name: "int-name", + Type: "int", + Value: "123", + }, + { + Name: "float-name", + Type: "float", + Value: "1.23", + }, + { + Name: "bool-name", + Type: "boolean", + Value: "true", + }, }, } @@ -57,4 +59,5 @@ func TestGetProperty(t *testing.T) { assert.Equal(t, 123, props.GetInt("int-name")) assert.Equal(t, 1.23, props.GetFloat("float-name")) assert.Equal(t, true, props.GetBool("bool-name")) + } diff --git a/tmx_tileset.go b/tmx_tileset.go index 7f62017..1e2352b 100644 --- a/tmx_tileset.go +++ b/tmx_tileset.go @@ -10,43 +10,46 @@ type Tileset struct { // Base directory baseDir string + // XMLName holds the xml name for this struct + XMLName struct{} `xml:"tileset"` + // The TMX format version, generally 1.0. - Version string `xml:"version,attr"` + Version string `xml:"version,attr,omitempty"` // The Tiled version used to generate this file - TiledVersion string `xml:"tiledversion,attr"` + TiledVersion string `xml:"tiledversion,attr,omitempty"` // The first global tile ID of this tileset (this global ID maps to the first tile in this tileset). - FirstGID uint32 `xml:"firstgid,attr"` + FirstGID uint32 `xml:"firstgid,attr,omitempty"` // If this tileset is stored in an external TSX (Tile Set XML) file, this attribute refers to that file. // That TSX file has the same structure as the element described here. (There is the firstgid // attribute missing and this source attribute is also not there. These two attributes are kept in the // TMX map, since they are map specific.) - Source string `xml:"source,attr"` + Source string `xml:"source,attr,omitempty"` // External TSX source loaded. SourceLoaded bool `xml:"-"` // The name of this tileset. - Name string `xml:"name,attr"` + Name string `xml:"name,attr,omitempty"` // The (maximum) width of the tiles in this tileset. - TileWidth int `xml:"tilewidth,attr"` + TileWidth int `xml:"tilewidth,attr,omitempty"` // The (maximum) height of the tiles in this tileset. - TileHeight int `xml:"tileheight,attr"` + TileHeight int `xml:"tileheight,attr,omitempty"` // The spacing in pixels between the tiles in this tileset (applies to the tileset image). - Spacing int `xml:"spacing,attr"` + Spacing int `xml:"spacing,attr,omitempty"` // The margin around the tiles in this tileset (applies to the tileset image). - Margin int `xml:"margin,attr"` + Margin int `xml:"margin,attr,omitempty"` // The number of tiles in this tileset (since 0.13) - TileCount int `xml:"tilecount,attr"` + TileCount int `xml:"tilecount,attr,omitempty"` // The number of tile columns in the tileset. For image collection tilesets it is editable and is used when displaying the tileset. (since 0.15) - Columns int `xml:"columns,attr"` + Columns int `xml:"columns,attr,omitempty"` // Offset in pixels, to be applied when drawing a tile from the related tileset. When not present, no offset is applied. - TileOffset *TilesetTileOffset `xml:"tileoffset"` + TileOffset *TilesetTileOffset `xml:"tileoffset,omitempty"` // Custom properties - Properties Properties `xml:"properties>property"` + Properties *Properties `xml:"properties,omitempty"` // Embedded image - Image *Image `xml:"image"` + Image *Image `xml:"image,omitempty"` // Defines an array of terrain types, which can be referenced from the terrain of the tile element. - TerrainTypes []*Terrain `xml:"terraintypes>terrain"` + TerrainTypes *TerrainTypes `xml:"terraintypes,omitempty"` // Tiles in tileset - Tiles []*TilesetTile `xml:"tile"` + Tiles []*TilesetTile `xml:"tile,omitempty"` } // GetFileFullPath returns path to file relative to tileset file @@ -62,6 +65,11 @@ type TilesetTileOffset struct { Y int `xml:"y,attr"` } +// TerrainTypes represent a list of Terrains +type TerrainTypes struct { + Terrain []*Terrain +} + // Terrain type type Terrain struct { // The name of the terrain type. @@ -69,7 +77,7 @@ type Terrain struct { // The local tile-id of the tile that represents the terrain visually. Tile uint32 `xml:"tile,attr"` // Custom properties - Properties Properties `xml:"properties>property"` + Properties *Properties `xml:"properties"` } // TilesetTile information @@ -77,21 +85,26 @@ type TilesetTile struct { // The local tile ID within its tileset. ID uint32 `xml:"id,attr"` // The type of the tile. Refers to an object type and is used by tile objects. (optional) (since 1.0) - Type string `xml:"type,attr"` + Type string `xml:"type,attr,omitempty"` // Defines the terrain type of each corner of the tile, given as comma-separated indexes in the terrain types // array in the order top-left, top-right, bottom-left, bottom-right. // Leaving out a value means that corner has no terrain. (optional) (since 0.9) - Terrain string `xml:"terrain,attr"` + Terrain string `xml:"terrain,attr,omitempty"` // A percentage indicating the probability that this tile is chosen when it competes with others while editing with the terrain tool. (optional) (since 0.9) - Probability float32 `xml:"probability,attr"` + Probability float32 `xml:"probability,attr,omitempty"` // Custom properties - Properties Properties `xml:"properties>property"` + Properties *Properties `xml:"properties,omitempty"` // Embedded image Image *Image `xml:"image"` // Tile object groups - ObjectGroups []*ObjectGroup `xml:"objectgroup"` + ObjectGroups []*ObjectGroup `xml:"objectgroup,omitempty"` // List of animation frames - Animation []*AnimationFrame `xml:"animation>frame"` + Animation *Animation `xml:"animation,omitempty"` +} + +// Animation represents a list of AnimationFrames +type Animation struct { + Frame []*AnimationFrame `xml:"frame"` } // AnimationFrame is single frame of animation diff --git a/tmx_tileset_test.go b/tmx_tileset_test.go index 2a4ea68..b426225 100644 --- a/tmx_tileset_test.go +++ b/tmx_tileset_test.go @@ -23,7 +23,12 @@ SOFTWARE. package tiled import ( + "bytes" + "encoding/xml" "image" + "io" + "os" + "path/filepath" "testing" "github.com/stretchr/testify/assert" @@ -165,5 +170,179 @@ func TestGetTileRect(t *testing.T) { } }) } +} + +var testLoadTilesetFile = &Tileset{ + baseDir: ".", + Columns: 64, + FirstGID: 0, + Image: &Image{ + Format: "", + Data: nil, + Height: 3040, + Width: 2048, + Source: "ProjectUtumno_full.png", + Trans: nil, + }, + Margin: 0, + Name: "ProjectUtumno_full", + Properties: nil, + Source: "", + SourceLoaded: true, + Spacing: 0, + TerrainTypes: nil, + TileCount: 6080, + TileHeight: 32, + TileOffset: nil, + TileWidth: 32, + TiledVersion: "1.2.3", + Tiles: []*TilesetTile{ + { + ID: 116, + Type: "door", + Animation: nil, + Image: nil, + ObjectGroups: nil, + Probability: 0, + Properties: nil, + Terrain: "", + }, + }, + Version: "1.2", +} + +var testLoadTilesetTileFile = &TilesetTile{ + ID: 464, + Animation: &Animation{ + Frame: []*AnimationFrame{ + { + Duration: 500, + TileID: 75, + }, + { + Duration: 500, + TileID: 76, + }, + }, + }, + Image: nil, + ObjectGroups: []*ObjectGroup{ + { + ID: 0, + Color: nil, + DrawOrder: "index", + Name: "", + Objects: []*Object{ + { + GID: 0, + Ellipses: nil, + Height: 6.125, + ID: 1, + Name: "", + PolyLines: nil, + Polygons: nil, + Properties: nil, + Rotation: 0, + Text: nil, + Type: "", + Visible: b(true), + Width: 32.375, + X: -0.25, + Y: 17.75, + }, + }, + OffsetX: 0, + OffsetY: 0, + Opacity: 1, + Properties: nil, + Visible: b(true), + }, + }, +} + +func TestLoadTileset(t *testing.T) { + tsxFile, err := os.Open(filepath.Join(GetAssetsDirectory(), "tilesets/testLoadTileset.tsx")) + assert.Nil(t, err) + defer tsxFile.Close() + + tsx, err := LoadTilesetFromReader(".", tsxFile) + assert.Nil(t, err) + assert.Equal(t, testLoadTilesetFile, tsx) +} + +func TestSaveTileset(t *testing.T) { + tsxFile, err := os.Open(filepath.Join(GetAssetsDirectory(), "tilesets/testLoadTileset.tsx")) + assert.Nil(t, err) + defer tsxFile.Close() + + buffer := &bytes.Buffer{} + err = SaveTilesetToWriter(testLoadTilesetFile, buffer) + assert.Nil(t, err) + + assertXMLEqual(t, tsxFile, buffer) +} + +func TestLoadTile(t *testing.T) { + tsxFile, err := os.Open(filepath.Join(GetAssetsDirectory(), "tilesets/testLoadTilesetTile.tsx")) + assert.Nil(t, err) + defer tsxFile.Close() + + tsx, err := LoadTilesetFromReader(".", tsxFile) + assert.Nil(t, err) + assert.Len(t, tsx.Tiles, 1) + + tile := tsx.Tiles[0] + assert.Equal(t, testLoadTilesetTileFile, tile) +} + +func TestSaveTile(t *testing.T) { + tsxFile, err := os.Open(filepath.Join(GetAssetsDirectory(), "tilesets/testLoadTilesetTile.tsx")) + assert.Nil(t, err) + defer tsxFile.Close() + + tsx, err := LoadTilesetFromReader(".", tsxFile) + assert.Nil(t, err) + + buffer := &bytes.Buffer{} + xml.NewEncoder(buffer).Encode(tsx) + + tsxFile.Seek(0, 0) + assertXMLEqual(t, tsxFile, buffer) + +} + +func assertXMLEqual(t *testing.T, expected io.Reader, obtained io.Reader) { + var expec node + var obt node + var err error + + err = xml.NewDecoder(expected).Decode(&expec) + assert.Nil(t, err) + err = xml.NewDecoder(obtained).Decode(&obt) + assert.Nil(t, err) + + assert.Equal(t, expec, obt) +} + +type node struct { + XMLName xml.Name + Attrs []xml.Attr `xml:",any,attr"` + Content string `xml:",innerxml"` + Nodes []node `xml:",any"` +} + +func (n *node) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + type inNode node + + err := d.DecodeElement((*inNode)(n), &start) + if err != nil { + return err + } + + //Discard content if there are child nodes + if len(n.Nodes) > 0 { + n.Content = "" + } + return nil }