Skip to content

Commit

Permalink
tmx parser files added
Browse files Browse the repository at this point in the history
  • Loading branch information
Noofbiz committed Sep 2, 2018
1 parent a09abe7 commit 12e8460
Show file tree
Hide file tree
Showing 9 changed files with 793 additions and 0 deletions.
170 changes: 170 additions & 0 deletions data.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
package tmx

import (
"bytes"
"compress/gzip"
"compress/zlib"
"encoding/base64"
"encoding/binary"
"encoding/csv"
"encoding/xml"
"errors"
"io"
"strconv"
"strings"
)

// Data contains the tile data for a map
type Data struct {
// Encoding is the encoding used for the data. It can either be "base64"
// or "csv"
Encoding string `xml:"encoding,attr"`
// Compression is the compression used for the data. It can either be
// "gzip" or "zlib"
Compression string `xml:"compression,attr"`
// Tiles are the tiles in the data. Not the same as TMXTiles from the Tileset.
Tiles []TileData `xml:"tile"`
// Chunks are sets of tiles over an area. Used for randomly generated maps.
Chunks []Chunk `xml:"chunk"`
// Inner is the inner data
Inner string `xml:",innerxml"`
}

// Chunk contains chunk data for a map. A chunk is a set of more than one
// tile that goes together, so when the map is set to randomly generate, these
// tiles are generated together.
type Chunk struct {
// X is the x coordinate of the chunk in tiles
X int `xml:"x,attr"`
// Y is the y coordinate of the chunk in tiles
Y int `xml:"y,attr"`
// Width is the width of the chunk in tiles
Width int `xml:"width,attr"`
// Height is the height of the chunk in tiles
Height int `xml:"height,attr"`
// Tiles are the tiles in the chunk
Tiles []TileData `xml:"tile"`
}

// TileData contains the gid that maps a tile to the sprite
type TileData struct {
// RawGID is the global tile ID given in the map
RawGID uint32 `xml:"gid,attr"`
// GID is the global tile ID with the flipping bits removed
GID uint32
// Flipping is the flipping flags present
// You can & this with the constants HorizontalFlipFlag, VerticalFlipFlag, and
// DiagonalFlipFlag to find out if the flag was present on the tile.
Flipping uint32
}

const (
// HorizontalFlipFlag is a flag for a horizontally flipped tile
HorizontalFlipFlag uint32 = 0x80000000
// VerticalFlipFlag is a flag for a vertically flipped tile
VerticalFlipFlag uint32 = 0x40000000
// DiagonalFlipFlag is a flag for a diagonally flipped tile
DiagonalFlipFlag uint32 = 0x20000000
)

// UnmarshalXML implements the encoding/xml Unmarshaler interface
func (da *Data) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
type data Data
dat := data{}
if err := d.DecodeElement(&dat, &start); err != nil {
return err
}
*da = (Data)(dat)
if len(da.Tiles) > 0 {
return nil
}
if da.Encoding == "csv" {
b := strings.NewReader(strings.TrimSpace(da.Inner))
cr := csv.NewReader(b)
// We allow variable number of fields per record to allow line ending commas and then
// empty strings appearing as a field. Later, we filter empty strings. This trick is
// needed to allow TilEd-style CSVs with line-ending commas but no comma at the end
// of last line.
cr.FieldsPerRecord = -1
if recs, err := cr.ReadAll(); err == nil {
if len(recs) < 1 {
return errors.New("No csv records found")
}
for _, rec := range recs {
for i, id := range rec {
// An empty string appearing after last comma. We filter it.
if id == "" && i == len(rec)-1 {
continue
}
if nextInt, err2 := strconv.ParseUint(id, 10, 32); err == nil {
da.Tiles = append(da.Tiles, TileData{GID: uint32(nextInt)})
} else {
return err2
}
}
}
if len(da.Tiles) < 1 {
return errors.New("No Data Returned")
}
} else {
return err
}
return nil
}
var breader io.Reader
if da.Encoding == "base64" {
buff, err := base64.StdEncoding.DecodeString(strings.TrimSpace(da.Inner))
if err != nil {
return err
}
breader = bytes.NewReader(buff)
} else {
return errors.New("Unknown Encoding")
}
// Setup decompression if needed
var zreader io.Reader
if da.Compression == "" {
zreader = breader
} else if da.Compression == "zlib" {
z, err := zlib.NewReader(breader)
if err != nil {
return err
}
defer z.Close()
zreader = z
} else if da.Compression == "gzip" {
z, err := gzip.NewReader(breader)
if err != nil {
return err
}
defer z.Close()
zreader = z
} else {
return errors.New("Unknown Compression")
}
var nextInt uint32
for {
err := binary.Read(zreader, binary.LittleEndian, &nextInt)
if err != nil {
if err == io.EOF {
break
}
return err
}
g, f := decodeGID(nextInt)
da.Tiles = append(da.Tiles, TileData{
RawGID: nextInt,
GID: g,
Flipping: f,
})
}
return nil
}

func decodeGID(u uint32) (uint32, uint32) {
h := u & HorizontalFlipFlag
v := u & VerticalFlipFlag
d := u & DiagonalFlipFlag
ret := u & ^(HorizontalFlipFlag | VerticalFlipFlag | DiagonalFlipFlag)
return ret, h | v | d
}
45 changes: 45 additions & 0 deletions layer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package tmx

import "encoding/xml"

// Layer is a layer of the map
type Layer struct {
// Name is the name of the layer
Name string `xml:"name,attr"`
// X is the x coordinate of the layer in tiles
X int `xml:"x,attr"`
// Y is the y coordinate of the layer in tiles
Y int `xml:"y,attr"`
// Width is the width of the layer in tiles. Always the same as the map
// width for fixed-size maps.
Width int `xml:"width,attr"`
// Height is the height of the layer in tiles. Always the same as the map
// height for fixed-size maps.
Height int `xml:"height,attr"`
// Opacity is the opacity of the layer as a value from 0 to 1. Defaults to 1.
Opacity int `xml:"opacity,attr"`
// Visible is whether the layer is shown(1) or hidden(0). Defaults to 1.
Visible int `xml:"visible,attr"`
// OffsetX is the rendering offset for this layer in pixels.
OffsetX float64 `xml:"offsetx,attr"`
// OffsetY is the rendering offset for this layer in pixels.
OffsetY float64 `xml:"offsety,attr"`
// Properties are the properties of the layer
Properties []Property `xml:"properties>property"`
// Data is any data for the layer
Data []Data `xml:"data"`
}

// UnmarshalXML implements the encoding/xml Unmarshaler interface
func (l *Layer) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
type layer Layer
la := layer{
Opacity: 1,
Visible: 1,
}
if err := d.DecodeElement(&la, &start); err != nil {
return err
}
*l = (Layer)(la)
return nil
}
65 changes: 65 additions & 0 deletions map.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package tmx

import "encoding/xml"

// Map is the root element of a TMX map
type Map struct {
// Version is the TMX format version
Version string `xml:"version,attr"`
// TiledVersion is the Version of Tiled Map Editor used to generate the TMX
TiledVersion string `xml:"tiledversion,attr"`
// Orientation is the orientation of the map. Tiled supports “orthogonal”,
// “isometric”, “staggered” and “hexagonal”
Orientation string `xml:"orientation,attr"`
// RenderOrder is The order in which tiles on tile layers are rendered.
// Valid values are right-down (the default), right-up, left-down and left-up.
// In all cases, the map is drawn row-by-row.
// (only supported for orthogonal maps at the moment)
RenderOrder string `xml:"renderorder,attr"`
// Width is the map width in tiles
Width int `xml:"width,attr"`
// Height is the map height in tiles
Height int `xml:"height,attr"`
// TileWidth is the width of each tile in pixels
TileWidth int `xml:"tilewidth,attr"`
// TileHeight is the height of each tile in pixels
TileHeight int `xml:"tileheight,attr"`
// HexSideLength determines the width or height (depending on the staggered
// axis) of the tile’s edge, in pixels. Only for hexagonal maps.
HexSideLength int `xml:"hexsidelength,attr"`
// StaggerAxis is for staggered and hexagonal maps, determines which axis
// (“x” or “y”) is staggered.
StaggerAxis string `xml:"staggeraxis,attr"`
// StaggerIndex is for staggered and hexagonal maps, determines whether the
// “even” or “odd” indexes along the staggered axis are shifted.
StaggerIndex string `xml:"staggerindex,attr"`
// BackgroundColor is the background color of the map. Is of the form #AARRGGBB
BackgroundColor string `xml:"backgroundcolor,attr"`
// NextObjectID stores the next object id available for new objects.
NextObjectID int `xml:"nextobjectid,attr"`
// Properties are the properties of the map
Properties []Property `xml:"properties>property"`
// Tilesets are the tilesets of the map
Tilesets []Tileset `xml:"tileset"`
// Layers are the layers of the map
Layers []Layer `xml:"layer"`
// ObjectGroups are the object groups of the map
ObjectGroups []ObjectGroup `xml:"objectgroup"`
// ImageLayers are the image layers of the map
ImageLayers []ImageLayer `xml:"imagelayer"`
// Groups are the groups of the map
Groups []Group `xml:"group"`
}

// UnmarshalXML implements the encoding/xml Unmarshaler interface
func (m *Map) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
type maph Map
ma := maph{
RenderOrder: "right-down",
}
if err := d.DecodeElement(&ma, &start); err != nil {
return err
}
*m = (Map)(ma)
return nil
}
Loading

0 comments on commit 12e8460

Please sign in to comment.