diff --git a/.gitignore b/.gitignore
index cc6f63bf21caf612dc88be30e057ca23d338cdcd..6cd496af8ddd31e1e7419371d9ff55647c49eb5f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,4 @@ target
 Cargo.lock
 *.swp
 *.dll
+.vscode
\ No newline at end of file
diff --git a/src/animation.rs b/src/animation.rs
new file mode 100644
index 0000000000000000000000000000000000000000..1ffe8802ac24a87e8a05f30c2bb30c4e587bf796
--- /dev/null
+++ b/src/animation.rs
@@ -0,0 +1,27 @@
+use xml::attribute::OwnedAttribute;
+
+use crate::{error::TiledError, util::get_attrs};
+
+#[derive(Debug, PartialEq, Clone)]
+pub struct Frame {
+    pub tile_id: u32,
+    pub duration: u32,
+}
+
+impl Frame {
+    pub(crate) fn new(attrs: Vec<OwnedAttribute>) -> Result<Frame, TiledError> {
+        let ((), (tile_id, duration)) = get_attrs!(
+            attrs,
+            optionals: [],
+            required: [
+                ("tileid", tile_id, |v:String| v.parse().ok()),
+                ("duration", duration, |v:String| v.parse().ok()),
+            ],
+            TiledError::MalformedAttributes("A frame must have tileid and duration".to_string())
+        );
+        Ok(Frame {
+            tile_id: tile_id,
+            duration: duration,
+        })
+    }
+}
diff --git a/src/error.rs b/src/error.rs
new file mode 100644
index 0000000000000000000000000000000000000000..6a502c21c01edaa186ad2a9cca205ee7c963e74f
--- /dev/null
+++ b/src/error.rs
@@ -0,0 +1,49 @@
+use std::fmt;
+
+#[derive(Debug, Copy, Clone)]
+pub enum ParseTileError {
+    ColourError,
+    OrientationError,
+}
+
+/// Errors which occured when parsing the file
+#[derive(Debug)]
+pub enum TiledError {
+    /// A attribute was missing, had the wrong type of wasn't formated
+    /// correctly.
+    MalformedAttributes(String),
+    /// An error occured when decompressing using the
+    /// [flate2](https://github.com/alexcrichton/flate2-rs) crate.
+    DecompressingError(std::io::Error),
+    Base64DecodingError(base64::DecodeError),
+    XmlDecodingError(xml::reader::Error),
+    PrematureEnd(String),
+    Other(String),
+}
+
+impl fmt::Display for TiledError {
+    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+        match *self {
+            TiledError::MalformedAttributes(ref s) => write!(fmt, "{}", s),
+            TiledError::DecompressingError(ref e) => write!(fmt, "{}", e),
+            TiledError::Base64DecodingError(ref e) => write!(fmt, "{}", e),
+            TiledError::XmlDecodingError(ref e) => write!(fmt, "{}", e),
+            TiledError::PrematureEnd(ref e) => write!(fmt, "{}", e),
+            TiledError::Other(ref s) => write!(fmt, "{}", s),
+        }
+    }
+}
+
+// This is a skeleton implementation, which should probably be extended in the future.
+impl std::error::Error for TiledError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        match *self {
+            TiledError::MalformedAttributes(_) => None,
+            TiledError::DecompressingError(ref e) => Some(e as &dyn std::error::Error),
+            TiledError::Base64DecodingError(ref e) => Some(e as &dyn std::error::Error),
+            TiledError::XmlDecodingError(ref e) => Some(e as &dyn std::error::Error),
+            TiledError::PrematureEnd(_) => None,
+            TiledError::Other(_) => None,
+        }
+    }
+}
diff --git a/src/image.rs b/src/image.rs
new file mode 100644
index 0000000000000000000000000000000000000000..351cc5bf61ebf59f25543a9ed454780bb4fecc0b
--- /dev/null
+++ b/src/image.rs
@@ -0,0 +1,42 @@
+use std::io::Read;
+
+use xml::{attribute::OwnedAttribute, EventReader};
+
+use crate::{error::TiledError, properties::Colour, util::*};
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub struct Image {
+    /// The filepath of the image
+    pub source: String,
+    pub width: i32,
+    pub height: i32,
+    pub transparent_colour: Option<Colour>,
+}
+
+impl Image {
+    pub(crate) fn new<R: Read>(
+        parser: &mut EventReader<R>,
+        attrs: Vec<OwnedAttribute>,
+    ) -> Result<Image, TiledError> {
+        let (c, (s, w, h)) = get_attrs!(
+            attrs,
+            optionals: [
+                ("trans", trans, |v:String| v.parse().ok()),
+            ],
+            required: [
+                ("source", source, |v| Some(v)),
+                ("width", width, |v:String| v.parse().ok()),
+                ("height", height, |v:String| v.parse().ok()),
+            ],
+            TiledError::MalformedAttributes("image must have a source, width and height with correct types".to_string())
+        );
+
+        parse_tag!(parser, "image", { "" => |_| Ok(()) });
+        Ok(Image {
+            source: s,
+            width: w,
+            height: h,
+            transparent_colour: c,
+        })
+    }
+}
diff --git a/src/layers.rs b/src/layers.rs
new file mode 100644
index 0000000000000000000000000000000000000000..b802c569add9753496fd1fb37a210bf5ce313eb5
--- /dev/null
+++ b/src/layers.rs
@@ -0,0 +1,208 @@
+use std::{collections::HashMap, io::Read};
+
+use xml::{attribute::OwnedAttribute, EventReader};
+
+use crate::{
+    error::TiledError,
+    image::Image,
+    properties::{parse_properties, Properties},
+    util::*,
+};
+
+/// Stores the proper tile gid, along with how it is flipped.
+// Maybe PartialEq and Eq should be custom, so that it ignores tile-flipping?
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct LayerTile {
+    pub gid: u32,
+    pub flip_h: bool,
+    pub flip_v: bool,
+    pub flip_d: bool,
+}
+
+const FLIPPED_HORIZONTALLY_FLAG: u32 = 0x80000000;
+const FLIPPED_VERTICALLY_FLAG: u32 = 0x40000000;
+const FLIPPED_DIAGONALLY_FLAG: u32 = 0x20000000;
+const ALL_FLIP_FLAGS: u32 =
+    FLIPPED_HORIZONTALLY_FLAG | FLIPPED_VERTICALLY_FLAG | FLIPPED_DIAGONALLY_FLAG;
+
+impl LayerTile {
+    pub fn new(id: u32) -> LayerTile {
+        let flags = id & ALL_FLIP_FLAGS;
+        let gid = id & !ALL_FLIP_FLAGS;
+        let flip_d = flags & FLIPPED_DIAGONALLY_FLAG == FLIPPED_DIAGONALLY_FLAG; // Swap x and y axis (anti-diagonally) [flips over y = -x line]
+        let flip_h = flags & FLIPPED_HORIZONTALLY_FLAG == FLIPPED_HORIZONTALLY_FLAG; // Flip tile over y axis
+        let flip_v = flags & FLIPPED_VERTICALLY_FLAG == FLIPPED_VERTICALLY_FLAG; // Flip tile over x axis
+
+        LayerTile {
+            gid,
+            flip_h,
+            flip_v,
+            flip_d,
+        }
+    }
+}
+
+#[derive(Debug, PartialEq, Clone)]
+pub struct Layer {
+    pub name: String,
+    pub opacity: f32,
+    pub visible: bool,
+    pub offset_x: f32,
+    pub offset_y: f32,
+    /// The tiles are arranged in rows. Each tile is a number which can be used
+    ///  to find which tileset it belongs to and can then be rendered.
+    pub tiles: LayerData,
+    pub properties: Properties,
+    pub layer_index: u32,
+}
+
+impl Layer {
+    pub(crate) fn new<R: Read>(
+        parser: &mut EventReader<R>,
+        attrs: Vec<OwnedAttribute>,
+        width: u32,
+        layer_index: u32,
+        infinite: bool,
+    ) -> Result<Layer, TiledError> {
+        let ((o, v, ox, oy), n) = get_attrs!(
+            attrs,
+            optionals: [
+                ("opacity", opacity, |v:String| v.parse().ok()),
+                ("visible", visible, |v:String| v.parse().ok().map(|x:i32| x == 1)),
+                ("offsetx", offset_x, |v:String| v.parse().ok()),
+                ("offsety", offset_y, |v:String| v.parse().ok()),
+            ],
+            required: [
+                ("name", name, |v| Some(v)),
+            ],
+            TiledError::MalformedAttributes("layer must have a name".to_string())
+        );
+        let mut tiles: LayerData = LayerData::Finite(Default::default());
+        let mut properties = HashMap::new();
+        parse_tag!(parser, "layer", {
+            "data" => |attrs| {
+                if infinite {
+                    tiles = parse_infinite_data(parser, attrs, width)?;
+                } else {
+                    tiles = parse_data(parser, attrs, width)?;
+                }
+                Ok(())
+            },
+            "properties" => |_| {
+                properties = parse_properties(parser)?;
+                Ok(())
+            },
+        });
+
+        Ok(Layer {
+            name: n,
+            opacity: o.unwrap_or(1.0),
+            visible: v.unwrap_or(true),
+            offset_x: ox.unwrap_or(0.0),
+            offset_y: oy.unwrap_or(0.0),
+            tiles: tiles,
+            properties: properties,
+            layer_index,
+        })
+    }
+}
+#[derive(Debug, PartialEq, Clone)]
+pub enum LayerData {
+    Finite(Vec<Vec<LayerTile>>),
+    Infinite(HashMap<(i32, i32), Chunk>),
+}
+
+#[derive(Debug, PartialEq, Clone)]
+pub struct ImageLayer {
+    pub name: String,
+    pub opacity: f32,
+    pub visible: bool,
+    pub offset_x: f32,
+    pub offset_y: f32,
+    pub image: Option<Image>,
+    pub properties: Properties,
+    pub layer_index: u32,
+}
+
+impl ImageLayer {
+    pub(crate) fn new<R: Read>(
+        parser: &mut EventReader<R>,
+        attrs: Vec<OwnedAttribute>,
+        layer_index: u32,
+    ) -> Result<ImageLayer, TiledError> {
+        let ((o, v, ox, oy), n) = get_attrs!(
+            attrs,
+            optionals: [
+                ("opacity", opacity, |v:String| v.parse().ok()),
+                ("visible", visible, |v:String| v.parse().ok().map(|x:i32| x == 1)),
+                ("offsetx", offset_x, |v:String| v.parse().ok()),
+                ("offsety", offset_y, |v:String| v.parse().ok()),
+            ],
+            required: [
+                ("name", name, |v| Some(v)),
+            ],
+            TiledError::MalformedAttributes("layer must have a name".to_string()));
+        let mut properties = HashMap::new();
+        let mut image: Option<Image> = None;
+        parse_tag!(parser, "imagelayer", {
+            "image" => |attrs| {
+                image = Some(Image::new(parser, attrs)?);
+                Ok(())
+            },
+            "properties" => |_| {
+                properties = parse_properties(parser)?;
+                Ok(())
+            },
+        });
+        Ok(ImageLayer {
+            name: n,
+            opacity: o.unwrap_or(1.0),
+            visible: v.unwrap_or(true),
+            offset_x: ox.unwrap_or(0.0),
+            offset_y: oy.unwrap_or(0.0),
+            image,
+            properties,
+            layer_index,
+        })
+    }
+}
+
+#[derive(Debug, PartialEq, Clone)]
+pub struct Chunk {
+    pub x: i32,
+    pub y: i32,
+    pub width: u32,
+    pub height: u32,
+    pub tiles: Vec<Vec<LayerTile>>,
+}
+
+impl Chunk {
+    pub(crate) fn new<R: Read>(
+        parser: &mut EventReader<R>,
+        attrs: Vec<OwnedAttribute>,
+        encoding: Option<String>,
+        compression: Option<String>,
+    ) -> Result<Chunk, TiledError> {
+        let ((), (x, y, width, height)) = get_attrs!(
+            attrs,
+            optionals: [],
+            required: [
+                ("x", x, |v: String| v.parse().ok()),
+                ("y", y, |v: String| v.parse().ok()),
+                ("width", width, |v: String| v.parse().ok()),
+                ("height", height, |v: String| v.parse().ok()),
+            ],
+            TiledError::MalformedAttributes("layer must have a name".to_string())
+        );
+
+        let tiles = parse_data_line(encoding, compression, parser, width)?;
+
+        Ok(Chunk {
+            x,
+            y,
+            width,
+            height,
+            tiles,
+        })
+    }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 33364a369526de0613d013ed1943304d61590235..06805e70b8daa4ce9e7102a1f65b57bc57d54e91 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,1295 +1,34 @@
+pub mod animation;
+pub mod error;
+pub mod image;
+pub mod layers;
+pub mod map;
+pub mod objects;
+pub mod properties;
+pub mod tile;
+pub mod tileset;
+mod util;
+
 use base64;
 
+use error::*;
+use image::*;
+use layers::*;
+use map::*;
 use std::collections::HashMap;
 use std::fmt;
 use std::fs::File;
-use std::io::{BufReader, Error, Read};
-use std::path::Path;
+use std::io::Read;
+use std::path::{Path, PathBuf};
 use std::str::FromStr;
+use tile::*;
+use tileset::*;
+use util::*;
 use xml::attribute::OwnedAttribute;
 use xml::reader::XmlEvent;
 use xml::reader::{Error as XmlError, EventReader};
 
-#[derive(Debug, Copy, Clone)]
-pub enum ParseTileError {
-    ColourError,
-    OrientationError,
-}
-
-// Loops through the attributes once and pulls out the ones we ask it to. It
-// will check that the required ones are there. This could have been done with
-// attrs.find but that would be inefficient.
-//
-// This is probably a really terrible way to do this. It does cut down on lines
-// though which is nice.
-macro_rules! get_attrs {
-    ($attrs:expr, optionals: [$(($oName:pat, $oVar:ident, $oMethod:expr)),* $(,)*],
-     required: [$(($name:pat, $var:ident, $method:expr)),* $(,)*], $err:expr) => {
-        {
-            $(let mut $oVar = None;)*
-            $(let mut $var = None;)*
-            for attr in $attrs.iter() {
-                match attr.name.local_name.as_ref() {
-                    $($oName => $oVar = $oMethod(attr.value.clone()),)*
-                    $($name => $var = $method(attr.value.clone()),)*
-                    _ => {}
-                }
-            }
-            if !(true $(&& $var.is_some())*) {
-                return Err($err);
-            }
-            (($($oVar),*), ($($var.unwrap()),*))
-        }
-    }
-}
-
-// Goes through the children of the tag and will call the correct function for
-// that child. Closes the tag
-//
-// Not quite as bad.
-macro_rules! parse_tag {
-    ($parser:expr, $close_tag:expr, {$($open_tag:expr => $open_method:expr),* $(,)*}) => {
-        loop {
-            match $parser.next().map_err(TiledError::XmlDecodingError)? {
-                XmlEvent::StartElement {name, attributes, ..} => {
-                    if false {}
-                    $(else if name.local_name == $open_tag {
-                        match $open_method(attributes) {
-                            Ok(()) => {},
-                            Err(e) => return Err(e)
-                        };
-                    })*
-                }
-                XmlEvent::EndElement {name, ..} => {
-                    if name.local_name == $close_tag {
-                        break;
-                    }
-                }
-                XmlEvent::EndDocument => return Err(TiledError::PrematureEnd("Document ended before we expected.".to_string())),
-                _ => {}
-            }
-        }
-    }
-}
-
-#[derive(Debug, PartialEq, Eq, Copy, Clone)]
-pub struct Colour {
-    pub red: u8,
-    pub green: u8,
-    pub blue: u8,
-}
-
-impl FromStr for Colour {
-    type Err = ParseTileError;
-
-    fn from_str(s: &str) -> Result<Colour, ParseTileError> {
-        let s = if s.starts_with("#") { &s[1..] } else { s };
-        if s.len() != 6 {
-            return Err(ParseTileError::ColourError);
-        }
-        let r = u8::from_str_radix(&s[0..2], 16);
-        let g = u8::from_str_radix(&s[2..4], 16);
-        let b = u8::from_str_radix(&s[4..6], 16);
-        if r.is_ok() && g.is_ok() && b.is_ok() {
-            return Ok(Colour {
-                red: r.unwrap(),
-                green: g.unwrap(),
-                blue: b.unwrap(),
-            });
-        }
-        Err(ParseTileError::ColourError)
-    }
-}
-
-/// Errors which occured when parsing the file
-#[derive(Debug)]
-pub enum TiledError {
-    /// A attribute was missing, had the wrong type of wasn't formated
-    /// correctly.
-    MalformedAttributes(String),
-    /// An error occured when decompressing using the
-    /// [flate2](https://github.com/alexcrichton/flate2-rs) crate.
-    DecompressingError(Error),
-    Base64DecodingError(base64::DecodeError),
-    XmlDecodingError(XmlError),
-    PrematureEnd(String),
-    Other(String),
-}
-
-impl fmt::Display for TiledError {
-    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
-        match *self {
-            TiledError::MalformedAttributes(ref s) => write!(fmt, "{}", s),
-            TiledError::DecompressingError(ref e) => write!(fmt, "{}", e),
-            TiledError::Base64DecodingError(ref e) => write!(fmt, "{}", e),
-            TiledError::XmlDecodingError(ref e) => write!(fmt, "{}", e),
-            TiledError::PrematureEnd(ref e) => write!(fmt, "{}", e),
-            TiledError::Other(ref s) => write!(fmt, "{}", s),
-        }
-    }
-}
-
-// This is a skeleton implementation, which should probably be extended in the future.
-impl std::error::Error for TiledError {
-    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
-        match *self {
-            TiledError::MalformedAttributes(_) => None,
-            TiledError::DecompressingError(ref e) => Some(e as &dyn std::error::Error),
-            TiledError::Base64DecodingError(ref e) => Some(e as &dyn std::error::Error),
-            TiledError::XmlDecodingError(ref e) => Some(e as &dyn std::error::Error),
-            TiledError::PrematureEnd(_) => None,
-            TiledError::Other(_) => None,
-        }
-    }
-}
-
-#[derive(Debug, PartialEq, Clone)]
-pub enum PropertyValue {
-    BoolValue(bool),
-    FloatValue(f32),
-    IntValue(i32),
-    ColorValue(u32),
-    StringValue(String),
-    /// Holds the path relative to the map or tileset
-    FileValue(String),
-}
-
-impl PropertyValue {
-    fn new(property_type: String, value: String) -> Result<PropertyValue, TiledError> {
-        // Check the property type against the value.
-        match property_type.as_str() {
-            "bool" => match value.parse() {
-                Ok(val) => Ok(PropertyValue::BoolValue(val)),
-                Err(err) => Err(TiledError::Other(err.to_string())),
-            },
-            "float" => match value.parse() {
-                Ok(val) => Ok(PropertyValue::FloatValue(val)),
-                Err(err) => Err(TiledError::Other(err.to_string())),
-            },
-            "int" => match value.parse() {
-                Ok(val) => Ok(PropertyValue::IntValue(val)),
-                Err(err) => Err(TiledError::Other(err.to_string())),
-            },
-            "color" if value.len() > 1 => match u32::from_str_radix(&value[1..], 16) {
-                Ok(color) => Ok(PropertyValue::ColorValue(color)),
-                Err(_) => Err(TiledError::Other(format!(
-                    "Improperly formatted color property"
-                ))),
-            },
-            "string" => Ok(PropertyValue::StringValue(value)),
-            "file" => Ok(PropertyValue::FileValue(value)),
-            _ => Err(TiledError::Other(format!(
-                "Unknown property type \"{}\"",
-                property_type
-            ))),
-        }
-    }
-}
-
-pub type Properties = HashMap<String, PropertyValue>;
-
-fn parse_properties<R: Read>(parser: &mut EventReader<R>) -> Result<Properties, TiledError> {
-    let mut p = HashMap::new();
-    parse_tag!(parser, "properties", {
-        "property" => |attrs:Vec<OwnedAttribute>| {
-            let (t, (k, v)) = get_attrs!(
-                attrs,
-                optionals: [
-                    ("type", property_type, |v| Some(v)),
-                ],
-                required: [
-                    ("name", key, |v| Some(v)),
-                    ("value", value, |v| Some(v)),
-                ],
-                TiledError::MalformedAttributes("property must have a name and a value".to_string())
-            );
-            let t = t.unwrap_or("string".into());
-
-            p.insert(k, PropertyValue::new(t, v)?);
-            Ok(())
-        },
-    });
-    Ok(p)
-}
-
-/// All Tiled files will be parsed into this. Holds all the layers and tilesets
-#[derive(Debug, PartialEq, Clone)]
-pub struct Map {
-    pub version: String,
-    pub orientation: Orientation,
-    /// Width of the map, in tiles
-    pub width: u32,
-    /// Height of the map, in tiles
-    pub height: u32,
-    pub tile_width: u32,
-    pub tile_height: u32,
-    pub tilesets: Vec<Tileset>,
-    pub layers: Vec<Layer>,
-    pub image_layers: Vec<ImageLayer>,
-    pub object_groups: Vec<ObjectGroup>,
-    pub properties: Properties,
-    pub background_colour: Option<Colour>,
-    pub infinite: bool,
-}
-
-impl Map {
-    fn new<R: Read>(
-        parser: &mut EventReader<R>,
-        attrs: Vec<OwnedAttribute>,
-        map_path: Option<&Path>,
-    ) -> Result<Map, TiledError> {
-        let ((c, infinite), (v, o, w, h, tw, th)) = get_attrs!(
-            attrs,
-            optionals: [
-                ("backgroundcolor", colour, |v:String| v.parse().ok()),
-                ("infinite", infinite, |v:String| Some(v == "1")),
-            ],
-            required: [
-                ("version", version, |v| Some(v)),
-                ("orientation", orientation, |v:String| v.parse().ok()),
-                ("width", width, |v:String| v.parse().ok()),
-                ("height", height, |v:String| v.parse().ok()),
-                ("tilewidth", tile_width, |v:String| v.parse().ok()),
-                ("tileheight", tile_height, |v:String| v.parse().ok()),
-            ],
-            TiledError::MalformedAttributes("map must have a version, width and height with correct types".to_string())
-        );
-
-        let mut tilesets = Vec::new();
-        let mut layers = Vec::new();
-        let mut image_layers = Vec::new();
-        let mut properties = HashMap::new();
-        let mut object_groups = Vec::new();
-        let mut layer_index = 0;
-        parse_tag!(parser, "map", {
-            "tileset" => | attrs| {
-                tilesets.push(Tileset::new(parser, attrs, map_path)?);
-                Ok(())
-            },
-            "layer" => |attrs| {
-                layers.push(Layer::new(parser, attrs, w, layer_index, infinite.unwrap_or(false))?);
-                layer_index += 1;
-                Ok(())
-            },
-            "imagelayer" => |attrs| {
-                image_layers.push(ImageLayer::new(parser, attrs, layer_index)?);
-                layer_index += 1;
-                Ok(())
-            },
-            "properties" => |_| {
-                properties = parse_properties(parser)?;
-                Ok(())
-            },
-            "objectgroup" => |attrs| {
-                object_groups.push(ObjectGroup::new(parser, attrs, Some(layer_index))?);
-                layer_index += 1;
-                Ok(())
-            },
-        });
-        Ok(Map {
-            version: v,
-            orientation: o,
-            width: w,
-            height: h,
-            tile_width: tw,
-            tile_height: th,
-            tilesets,
-            layers,
-            image_layers,
-            object_groups,
-            properties,
-            background_colour: c,
-            infinite: infinite.unwrap_or(false),
-        })
-    }
-
-    /// This function will return the correct Tileset given a GID.
-    pub fn get_tileset_by_gid(&self, gid: u32) -> Option<&Tileset> {
-        let mut maximum_gid: i32 = -1;
-        let mut maximum_ts = None;
-        for tileset in self.tilesets.iter() {
-            if tileset.first_gid as i32 > maximum_gid && tileset.first_gid <= gid {
-                maximum_gid = tileset.first_gid as i32;
-                maximum_ts = Some(tileset);
-            }
-        }
-        maximum_ts
-    }
-}
-
-#[derive(Debug, PartialEq, Eq, Copy, Clone)]
-pub enum Orientation {
-    Orthogonal,
-    Isometric,
-    Staggered,
-    Hexagonal,
-}
-
-impl FromStr for Orientation {
-    type Err = ParseTileError;
-
-    fn from_str(s: &str) -> Result<Orientation, ParseTileError> {
-        match s {
-            "orthogonal" => Ok(Orientation::Orthogonal),
-            "isometric" => Ok(Orientation::Isometric),
-            "staggered" => Ok(Orientation::Staggered),
-            "hexagonal" => Ok(Orientation::Hexagonal),
-            _ => Err(ParseTileError::OrientationError),
-        }
-    }
-}
-
-impl fmt::Display for Orientation {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            Orientation::Orthogonal => write!(f, "orthogonal"),
-            Orientation::Isometric => write!(f, "isometric"),
-            Orientation::Staggered => write!(f, "staggered"),
-            Orientation::Hexagonal => write!(f, "hexagonal"),
-        }
-    }
-}
-
-/// A tileset, usually the tilesheet image.
-#[derive(Debug, PartialEq, Clone)]
-pub struct Tileset {
-    /// The GID of the first tile stored
-    pub first_gid: u32,
-    pub name: String,
-    pub tile_width: u32,
-    pub tile_height: u32,
-    pub spacing: u32,
-    pub margin: u32,
-    pub tilecount: Option<u32>,
-    /// The Tiled spec says that a tileset can have mutliple images so a `Vec`
-    /// is used. Usually you will only use one.
-    pub images: Vec<Image>,
-    pub tiles: Vec<Tile>,
-    pub properties: Properties,
-}
-
-impl Tileset {
-    fn new<R: Read>(
-        parser: &mut EventReader<R>,
-        attrs: Vec<OwnedAttribute>,
-        map_path: Option<&Path>,
-    ) -> Result<Tileset, TiledError> {
-        Tileset::new_internal(parser, &attrs).or_else(|_| Tileset::new_reference(&attrs, map_path))
-    }
-
-    fn new_internal<R: Read>(
-        parser: &mut EventReader<R>,
-        attrs: &Vec<OwnedAttribute>,
-    ) -> Result<Tileset, TiledError> {
-        let ((spacing, margin, tilecount), (first_gid, name, width, height)) = get_attrs!(
-           attrs,
-           optionals: [
-                ("spacing", spacing, |v:String| v.parse().ok()),
-                ("margin", margin, |v:String| v.parse().ok()),
-                ("tilecount", tilecount, |v:String| v.parse().ok()),
-            ],
-           required: [
-                ("firstgid", first_gid, |v:String| v.parse().ok()),
-                ("name", name, |v| Some(v)),
-                ("tilewidth", width, |v:String| v.parse().ok()),
-                ("tileheight", height, |v:String| v.parse().ok()),
-            ],
-            TiledError::MalformedAttributes("tileset must have a firstgid, name tile width and height with correct types".to_string())
-        );
-
-        let mut images = Vec::new();
-        let mut tiles = Vec::new();
-        let mut properties = HashMap::new();
-        parse_tag!(parser, "tileset", {
-            "image" => |attrs| {
-                images.push(Image::new(parser, attrs)?);
-                Ok(())
-            },
-            "properties" => |_| {
-                properties = parse_properties(parser)?;
-                Ok(())
-            },
-            "tile" => |attrs| {
-                tiles.push(Tile::new(parser, attrs)?);
-                Ok(())
-            },
-        });
-
-        Ok(Tileset {
-            tile_width: width,
-            tile_height: height,
-            spacing: spacing.unwrap_or(0),
-            margin: margin.unwrap_or(0),
-            first_gid,
-            name,
-            tilecount,
-            images,
-            tiles,
-            properties,
-        })
-    }
-
-    fn new_reference(
-        attrs: &Vec<OwnedAttribute>,
-        map_path: Option<&Path>,
-    ) -> Result<Tileset, TiledError> {
-        let ((), (first_gid, source)) = get_attrs!(
-            attrs,
-            optionals: [],
-            required: [
-                ("firstgid", first_gid, |v:String| v.parse().ok()),
-                ("source", name, |v| Some(v)),
-            ],
-            TiledError::MalformedAttributes("tileset must have a firstgid, name tile width and height with correct types".to_string())
-        );
-
-        let tileset_path = map_path.ok_or(TiledError::Other("Maps with external tilesets must know their file location.  See parse_with_path(Path).".to_string()))?.with_file_name(source);
-        let file = File::open(&tileset_path).map_err(|_| {
-            TiledError::Other(format!(
-                "External tileset file not found: {:?}",
-                tileset_path
-            ))
-        })?;
-        Tileset::new_external(file, first_gid)
-    }
-
-    fn new_external<R: Read>(file: R, first_gid: u32) -> Result<Tileset, TiledError> {
-        let mut tileset_parser = EventReader::new(file);
-        loop {
-            match tileset_parser
-                .next()
-                .map_err(TiledError::XmlDecodingError)?
-            {
-                XmlEvent::StartElement {
-                    name, attributes, ..
-                } => {
-                    if name.local_name == "tileset" {
-                        return Tileset::parse_external_tileset(
-                            first_gid,
-                            &mut tileset_parser,
-                            &attributes,
-                        );
-                    }
-                }
-                XmlEvent::EndDocument => {
-                    return Err(TiledError::PrematureEnd(
-                        "Tileset Document ended before map was parsed".to_string(),
-                    ))
-                }
-                _ => {}
-            }
-        }
-    }
-
-    fn parse_external_tileset<R: Read>(
-        first_gid: u32,
-        parser: &mut EventReader<R>,
-        attrs: &Vec<OwnedAttribute>,
-    ) -> Result<Tileset, TiledError> {
-        let ((spacing, margin, tilecount), (name, width, height)) = get_attrs!(
-            attrs,
-            optionals: [
-                ("spacing", spacing, |v:String| v.parse().ok()),
-                ("margin", margin, |v:String| v.parse().ok()),
-                ("tilecount", tilecount, |v:String| v.parse().ok()),
-            ],
-            required: [
-                ("name", name, |v| Some(v)),
-                ("tilewidth", width, |v:String| v.parse().ok()),
-                ("tileheight", height, |v:String| v.parse().ok()),
-            ],
-            TiledError::MalformedAttributes("tileset must have a firstgid, name tile width and height with correct types".to_string())
-        );
-
-        let mut images = Vec::new();
-        let mut tiles = Vec::new();
-        let mut properties = HashMap::new();
-        parse_tag!(parser, "tileset", {
-            "image" => |attrs| {
-                images.push(Image::new(parser, attrs)?);
-                Ok(())
-            },
-            "tile" => |attrs| {
-                tiles.push(Tile::new(parser, attrs)?);
-                Ok(())
-            },
-            "properties" => |_| {
-                properties = parse_properties(parser)?;
-                Ok(())
-            },
-        });
-
-        Ok(Tileset {
-            first_gid: first_gid,
-            name: name,
-            tile_width: width,
-            tile_height: height,
-            spacing: spacing.unwrap_or(0),
-            margin: margin.unwrap_or(0),
-            tilecount: tilecount,
-            images: images,
-            tiles: tiles,
-            properties,
-        })
-    }
-}
-
-#[derive(Debug, PartialEq, Clone)]
-pub struct Tile {
-    pub id: u32,
-    pub images: Vec<Image>,
-    pub properties: Properties,
-    pub objectgroup: Option<ObjectGroup>,
-    pub animation: Option<Vec<Frame>>,
-    pub tile_type: Option<String>,
-    pub probability: f32,
-}
-
-impl Tile {
-    fn new<R: Read>(
-        parser: &mut EventReader<R>,
-        attrs: Vec<OwnedAttribute>,
-    ) -> Result<Tile, TiledError> {
-        let ((tile_type, probability), id) = get_attrs!(
-            attrs,
-            optionals: [
-                ("type", tile_type, |v:String| v.parse().ok()),
-                ("probability", probability, |v:String| v.parse().ok()),
-            ],
-            required: [
-                ("id", id, |v:String| v.parse::<u32>().ok()),
-            ],
-            TiledError::MalformedAttributes("tile must have an id with the correct type".to_string())
-        );
-
-        let mut images = Vec::new();
-        let mut properties = HashMap::new();
-        let mut objectgroup = None;
-        let mut animation = None;
-        parse_tag!(parser, "tile", {
-            "image" => |attrs| {
-                images.push(Image::new(parser, attrs)?);
-                Ok(())
-            },
-            "properties" => |_| {
-                properties = parse_properties(parser)?;
-                Ok(())
-            },
-            "objectgroup" => |attrs| {
-                objectgroup = Some(ObjectGroup::new(parser, attrs, None)?);
-                Ok(())
-            },
-            "animation" => |_| {
-                animation = Some(parse_animation(parser)?);
-                Ok(())
-            },
-        });
-        Ok(Tile {
-            id,
-            images,
-            properties,
-            objectgroup,
-            animation,
-            tile_type,
-            probability: probability.unwrap_or(1.0),
-        })
-    }
-}
-
-#[derive(Debug, PartialEq, Eq, Clone)]
-pub struct Image {
-    /// The filepath of the image
-    pub source: String,
-    pub width: i32,
-    pub height: i32,
-    pub transparent_colour: Option<Colour>,
-}
-
-impl Image {
-    fn new<R: Read>(
-        parser: &mut EventReader<R>,
-        attrs: Vec<OwnedAttribute>,
-    ) -> Result<Image, TiledError> {
-        let (c, (s, w, h)) = get_attrs!(
-            attrs,
-            optionals: [
-                ("trans", trans, |v:String| v.parse().ok()),
-            ],
-            required: [
-                ("source", source, |v| Some(v)),
-                ("width", width, |v:String| v.parse().ok()),
-                ("height", height, |v:String| v.parse().ok()),
-            ],
-            TiledError::MalformedAttributes("image must have a source, width and height with correct types".to_string())
-        );
-
-        parse_tag!(parser, "image", { "" => |_| Ok(()) });
-        Ok(Image {
-            source: s,
-            width: w,
-            height: h,
-            transparent_colour: c,
-        })
-    }
-}
-
-/// Stores the proper tile gid, along with how it is flipped.
-// Maybe PartialEq and Eq should be custom, so that it ignores tile-flipping?
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub struct LayerTile {
-    pub gid: u32,
-    pub flip_h: bool,
-    pub flip_v: bool,
-    pub flip_d: bool,
-}
-
-const FLIPPED_HORIZONTALLY_FLAG: u32 = 0x80000000;
-const FLIPPED_VERTICALLY_FLAG: u32 = 0x40000000;
-const FLIPPED_DIAGONALLY_FLAG: u32 = 0x20000000;
-const ALL_FLIP_FLAGS: u32 =
-    FLIPPED_HORIZONTALLY_FLAG | FLIPPED_VERTICALLY_FLAG | FLIPPED_DIAGONALLY_FLAG;
-
-impl LayerTile {
-    pub fn new(id: u32) -> LayerTile {
-        let flags = id & ALL_FLIP_FLAGS;
-        let gid = id & !ALL_FLIP_FLAGS;
-        let flip_d = flags & FLIPPED_DIAGONALLY_FLAG == FLIPPED_DIAGONALLY_FLAG; // Swap x and y axis (anti-diagonally) [flips over y = -x line]
-        let flip_h = flags & FLIPPED_HORIZONTALLY_FLAG == FLIPPED_HORIZONTALLY_FLAG; // Flip tile over y axis
-        let flip_v = flags & FLIPPED_VERTICALLY_FLAG == FLIPPED_VERTICALLY_FLAG; // Flip tile over x axis
-
-        LayerTile {
-            gid,
-            flip_h,
-            flip_v,
-            flip_d,
-        }
-    }
-}
-
-#[derive(Debug, PartialEq, Clone)]
-pub struct Layer {
-    pub name: String,
-    pub opacity: f32,
-    pub visible: bool,
-    pub offset_x: f32,
-    pub offset_y: f32,
-    /// The tiles are arranged in rows. Each tile is a number which can be used
-    ///  to find which tileset it belongs to and can then be rendered.
-    pub tiles: LayerData,
-    pub properties: Properties,
-    pub layer_index: u32,
-}
-
-impl Layer {
-    fn new<R: Read>(
-        parser: &mut EventReader<R>,
-        attrs: Vec<OwnedAttribute>,
-        width: u32,
-        layer_index: u32,
-        infinite: bool,
-    ) -> Result<Layer, TiledError> {
-        let ((o, v, ox, oy), n) = get_attrs!(
-            attrs,
-            optionals: [
-                ("opacity", opacity, |v:String| v.parse().ok()),
-                ("visible", visible, |v:String| v.parse().ok().map(|x:i32| x == 1)),
-                ("offsetx", offset_x, |v:String| v.parse().ok()),
-                ("offsety", offset_y, |v:String| v.parse().ok()),
-            ],
-            required: [
-                ("name", name, |v| Some(v)),
-            ],
-            TiledError::MalformedAttributes("layer must have a name".to_string())
-        );
-        let mut tiles: LayerData = LayerData::Finite(Default::default());
-        let mut properties = HashMap::new();
-        parse_tag!(parser, "layer", {
-            "data" => |attrs| {
-                if infinite {
-                    tiles = parse_infinite_data(parser, attrs, width)?;
-                } else {
-                    tiles = parse_data(parser, attrs, width)?;
-                }
-                Ok(())
-            },
-            "properties" => |_| {
-                properties = parse_properties(parser)?;
-                Ok(())
-            },
-        });
-
-        Ok(Layer {
-            name: n,
-            opacity: o.unwrap_or(1.0),
-            visible: v.unwrap_or(true),
-            offset_x: ox.unwrap_or(0.0),
-            offset_y: oy.unwrap_or(0.0),
-            tiles: tiles,
-            properties: properties,
-            layer_index,
-        })
-    }
-}
-#[derive(Debug, PartialEq, Clone)]
-pub enum LayerData {
-    Finite(Vec<Vec<LayerTile>>),
-    Infinite(HashMap<(i32, i32), Chunk>),
-}
-
-#[derive(Debug, PartialEq, Clone)]
-pub struct Chunk {
-    pub x: i32,
-    pub y: i32,
-    pub width: u32,
-    pub height: u32,
-    pub tiles: Vec<Vec<LayerTile>>,
-}
-
-impl Chunk {
-    pub(crate) fn new<R: Read>(
-        parser: &mut EventReader<R>,
-        attrs: Vec<OwnedAttribute>,
-        encoding: Option<String>,
-        compression: Option<String>,
-    ) -> Result<Chunk, TiledError> {
-        let ((), (x, y, width, height)) = get_attrs!(
-            attrs,
-            optionals: [],
-            required: [
-                ("x", x, |v: String| v.parse().ok()),
-                ("y", y, |v: String| v.parse().ok()),
-                ("width", width, |v: String| v.parse().ok()),
-                ("height", height, |v: String| v.parse().ok()),
-            ],
-            TiledError::MalformedAttributes("layer must have a name".to_string())
-        );
-
-        let tiles = parse_data_line(encoding, compression, parser, width)?;
-
-        Ok(Chunk {
-            x,
-            y,
-            width,
-            height,
-            tiles,
-        })
-    }
-}
-
-#[derive(Debug, PartialEq, Clone)]
-pub struct ImageLayer {
-    pub name: String,
-    pub opacity: f32,
-    pub visible: bool,
-    pub offset_x: f32,
-    pub offset_y: f32,
-    pub image: Option<Image>,
-    pub properties: Properties,
-    pub layer_index: u32,
-}
-
-impl ImageLayer {
-    fn new<R: Read>(
-        parser: &mut EventReader<R>,
-        attrs: Vec<OwnedAttribute>,
-        layer_index: u32,
-    ) -> Result<ImageLayer, TiledError> {
-        let ((o, v, ox, oy), n) = get_attrs!(
-            attrs,
-            optionals: [
-                ("opacity", opacity, |v:String| v.parse().ok()),
-                ("visible", visible, |v:String| v.parse().ok().map(|x:i32| x == 1)),
-                ("offsetx", offset_x, |v:String| v.parse().ok()),
-                ("offsety", offset_y, |v:String| v.parse().ok()),
-            ],
-            required: [
-                ("name", name, |v| Some(v)),
-            ],
-            TiledError::MalformedAttributes("layer must have a name".to_string()));
-        let mut properties = HashMap::new();
-        let mut image: Option<Image> = None;
-        parse_tag!(parser, "imagelayer", {
-            "image" => |attrs| {
-                image = Some(Image::new(parser, attrs)?);
-                Ok(())
-            },
-            "properties" => |_| {
-                properties = parse_properties(parser)?;
-                Ok(())
-            },
-        });
-        Ok(ImageLayer {
-            name: n,
-            opacity: o.unwrap_or(1.0),
-            visible: v.unwrap_or(true),
-            offset_x: ox.unwrap_or(0.0),
-            offset_y: oy.unwrap_or(0.0),
-            image,
-            properties,
-            layer_index,
-        })
-    }
-}
-
-#[derive(Debug, PartialEq, Clone)]
-pub struct ObjectGroup {
-    pub name: String,
-    pub opacity: f32,
-    pub visible: bool,
-    pub objects: Vec<Object>,
-    pub colour: Option<Colour>,
-    /**
-     * Layer index is not preset for tile collision boxes
-     */
-    pub layer_index: Option<u32>,
-    pub properties: Properties,
-}
-
-impl ObjectGroup {
-    fn new<R: Read>(
-        parser: &mut EventReader<R>,
-        attrs: Vec<OwnedAttribute>,
-        layer_index: Option<u32>,
-    ) -> Result<ObjectGroup, TiledError> {
-        let ((o, v, c, n), ()) = get_attrs!(
-            attrs,
-            optionals: [
-                ("opacity", opacity, |v:String| v.parse().ok()),
-                ("visible", visible, |v:String| v.parse().ok().map(|x:i32| x == 1)),
-                ("color", colour, |v:String| v.parse().ok()),
-                ("name", name, |v:String| v.into()),
-            ],
-            required: [],
-            TiledError::MalformedAttributes("object groups must have a name".to_string())
-        );
-        let mut objects = Vec::new();
-        let mut properties = HashMap::new();
-        parse_tag!(parser, "objectgroup", {
-            "object" => |attrs| {
-                objects.push(Object::new(parser, attrs)?);
-                Ok(())
-            },
-            "properties" => |_| {
-                properties = parse_properties(parser)?;
-                Ok(())
-            },
-        });
-        Ok(ObjectGroup {
-            name: n.unwrap_or(String::new()),
-            opacity: o.unwrap_or(1.0),
-            visible: v.unwrap_or(true),
-            objects: objects,
-            colour: c,
-            layer_index,
-            properties,
-        })
-    }
-}
-
-#[derive(Debug, PartialEq, Clone)]
-pub enum ObjectShape {
-    Rect { width: f32, height: f32 },
-    Ellipse { width: f32, height: f32 },
-    Polyline { points: Vec<(f32, f32)> },
-    Polygon { points: Vec<(f32, f32)> },
-    Point(f32, f32),
-}
-
-#[derive(Debug, PartialEq, Clone)]
-pub struct Object {
-    pub id: u32,
-    pub gid: u32,
-    pub name: String,
-    pub obj_type: String,
-    pub width: f32,
-    pub height: f32,
-    pub x: f32,
-    pub y: f32,
-    pub rotation: f32,
-    pub visible: bool,
-    pub shape: ObjectShape,
-    pub properties: Properties,
-}
-
-impl Object {
-    fn new<R: Read>(
-        parser: &mut EventReader<R>,
-        attrs: Vec<OwnedAttribute>,
-    ) -> Result<Object, TiledError> {
-        let ((id, gid, n, t, w, h, v, r), (x, y)) = get_attrs!(
-            attrs,
-            optionals: [
-                ("id", id, |v:String| v.parse().ok()),
-                ("gid", gid, |v:String| v.parse().ok()),
-                ("name", name, |v:String| v.parse().ok()),
-                ("type", obj_type, |v:String| v.parse().ok()),
-                ("width", width, |v:String| v.parse().ok()),
-                ("height", height, |v:String| v.parse().ok()),
-                ("visible", visible, |v:String| v.parse().ok().map(|x:i32| x == 1)),
-                ("rotation", rotation, |v:String| v.parse().ok()),
-            ],
-            required: [
-                ("x", x, |v:String| v.parse().ok()),
-                ("y", y, |v:String| v.parse().ok()),
-            ],
-            TiledError::MalformedAttributes("objects must have an x and a y number".to_string())
-        );
-        let v = v.unwrap_or(true);
-        let w = w.unwrap_or(0f32);
-        let h = h.unwrap_or(0f32);
-        let r = r.unwrap_or(0f32);
-        let id = id.unwrap_or(0u32);
-        let gid = gid.unwrap_or(0u32);
-        let n = n.unwrap_or(String::new());
-        let t = t.unwrap_or(String::new());
-        let mut shape = None;
-        let mut properties = HashMap::new();
-
-        parse_tag!(parser, "object", {
-            "ellipse" => |_| {
-                shape = Some(ObjectShape::Ellipse {
-                    width: w,
-                    height: h,
-                });
-                Ok(())
-            },
-            "polyline" => |attrs| {
-                shape = Some(Object::new_polyline(attrs)?);
-                Ok(())
-            },
-            "polygon" => |attrs| {
-                shape = Some(Object::new_polygon(attrs)?);
-                Ok(())
-            },
-            "point" => |_| {
-                shape = Some(Object::new_point(x, y)?);
-                Ok(())
-            },
-            "properties" => |_| {
-                properties = parse_properties(parser)?;
-                Ok(())
-            },
-        });
-
-        let shape = shape.unwrap_or(ObjectShape::Rect {
-            width: w,
-            height: h,
-        });
-
-        Ok(Object {
-            id: id,
-            gid: gid,
-            name: n.clone(),
-            obj_type: t.clone(),
-            width: w,
-            height: h,
-            x: x,
-            y: y,
-            rotation: r,
-            visible: v,
-            shape: shape,
-            properties: properties,
-        })
-    }
-
-    fn new_polyline(attrs: Vec<OwnedAttribute>) -> Result<ObjectShape, TiledError> {
-        let ((), s) = get_attrs!(
-            attrs,
-            optionals: [],
-            required: [
-                ("points", points, |v| Some(v)),
-            ],
-            TiledError::MalformedAttributes("A polyline must have points".to_string())
-        );
-        let points = Object::parse_points(s)?;
-        Ok(ObjectShape::Polyline { points: points })
-    }
-
-    fn new_polygon(attrs: Vec<OwnedAttribute>) -> Result<ObjectShape, TiledError> {
-        let ((), s) = get_attrs!(
-            attrs,
-            optionals: [],
-            required: [
-                ("points", points, |v| Some(v)),
-            ],
-            TiledError::MalformedAttributes("A polygon must have points".to_string())
-        );
-        let points = Object::parse_points(s)?;
-        Ok(ObjectShape::Polygon { points: points })
-    }
-
-    fn new_point(x: f32, y: f32) -> Result<ObjectShape, TiledError> {
-        Ok(ObjectShape::Point(x, y))
-    }
-
-    fn parse_points(s: String) -> Result<Vec<(f32, f32)>, TiledError> {
-        let pairs = s.split(' ');
-        let mut points = Vec::new();
-        for v in pairs.map(|p| p.split(',')) {
-            let v: Vec<&str> = v.collect();
-            if v.len() != 2 {
-                return Err(TiledError::MalformedAttributes(
-                    "one of a polyline's points does not have an x and y coordinate".to_string(),
-                ));
-            }
-            let (x, y) = (v[0].parse().ok(), v[1].parse().ok());
-            if x.is_none() || y.is_none() {
-                return Err(TiledError::MalformedAttributes(
-                    "one of polyline's points does not have i32eger coordinates".to_string(),
-                ));
-            }
-            points.push((x.unwrap(), y.unwrap()));
-        }
-        Ok(points)
-    }
-}
-
-#[derive(Debug, PartialEq, Clone)]
-pub struct Frame {
-    pub tile_id: u32,
-    pub duration: u32,
-}
-
-impl Frame {
-    fn new(attrs: Vec<OwnedAttribute>) -> Result<Frame, TiledError> {
-        let ((), (tile_id, duration)) = get_attrs!(
-            attrs,
-            optionals: [],
-            required: [
-                ("tileid", tile_id, |v:String| v.parse().ok()),
-                ("duration", duration, |v:String| v.parse().ok()),
-            ],
-            TiledError::MalformedAttributes("A frame must have tileid and duration".to_string())
-        );
-        Ok(Frame {
-            tile_id: tile_id,
-            duration: duration,
-        })
-    }
-}
-
-fn parse_animation<R: Read>(parser: &mut EventReader<R>) -> Result<Vec<Frame>, TiledError> {
-    let mut animation = Vec::new();
-    parse_tag!(parser, "animation", {
-        "frame" => |attrs| {
-            animation.push(Frame::new(attrs)?);
-            Ok(())
-        },
-    });
-    Ok(animation)
-}
-
-fn parse_infinite_data<R: Read>(
-    parser: &mut EventReader<R>,
-    attrs: Vec<OwnedAttribute>,
-    width: u32,
-) -> Result<LayerData, TiledError> {
-    let ((e, c), ()) = get_attrs!(
-        attrs,
-        optionals: [
-            ("encoding", encoding, |v| Some(v)),
-            ("compression", compression, |v| Some(v)),
-        ],
-        required: [],
-        TiledError::MalformedAttributes("data must have an encoding and a compression".to_string())
-    );
-
-    let mut chunks = HashMap::<(i32, i32), Chunk>::new();
-    parse_tag!(parser, "data", {
-        "chunk" => |attrs| {
-            let chunk = Chunk::new(parser, attrs, e.clone(), c.clone())?;
-            chunks.insert((chunk.x, chunk.y), chunk);
-            Ok(())
-        }
-    });
-
-    Ok(LayerData::Infinite(chunks))
-}
-
-fn parse_data<R: Read>(
-    parser: &mut EventReader<R>,
-    attrs: Vec<OwnedAttribute>,
-    width: u32,
-) -> Result<LayerData, TiledError> {
-    let ((e, c), ()) = get_attrs!(
-        attrs,
-        optionals: [
-            ("encoding", encoding, |v| Some(v)),
-            ("compression", compression, |v| Some(v)),
-        ],
-        required: [],
-        TiledError::MalformedAttributes("data must have an encoding and a compression".to_string())
-    );
-
-    let tiles = parse_data_line(e, c, parser, width)?;
-
-    Ok(LayerData::Finite(tiles))
-}
-
-fn parse_data_line<R: Read>(
-    encoding: Option<String>,
-    compression: Option<String>,
-    parser: &mut EventReader<R>,
-    width: u32,
-) -> Result<Vec<Vec<LayerTile>>, TiledError> {
-    match (encoding, compression) {
-        (None, None) => {
-            return Err(TiledError::Other(
-                "XML format is currently not supported".to_string(),
-            ))
-        }
-        (Some(e), None) => match e.as_ref() {
-            "base64" => return parse_base64(parser).map(|v| convert_to_tile(&v, width)),
-            "csv" => return decode_csv(width, parser),
-            e => return Err(TiledError::Other(format!("Unknown encoding format {}", e))),
-        },
-        (Some(e), Some(c)) => match (e.as_ref(), c.as_ref()) {
-            ("base64", "zlib") => {
-                return parse_base64(parser)
-                    .and_then(decode_zlib)
-                    .map(|v| convert_to_tile(&v, width))
-            }
-            ("base64", "gzip") => {
-                return parse_base64(parser)
-                    .and_then(decode_gzip)
-                    .map(|v| convert_to_tile(&v, width))
-            }
-            #[cfg(feature = "zstd")]
-            ("base64", "zstd") => {
-                return parse_base64(parser)
-                    .and_then(decode_zstd)
-                    .map(|v| convert_to_tile(&v, width))
-            }
-            (e, c) => {
-                return Err(TiledError::Other(format!(
-                    "Unknown combination of {} encoding and {} compression",
-                    e, c
-                )))
-            }
-        },
-        _ => return Err(TiledError::Other("Missing encoding format".to_string())),
-    };
-}
-
-fn parse_base64<R: Read>(parser: &mut EventReader<R>) -> Result<Vec<u8>, TiledError> {
-    loop {
-        match parser.next().map_err(TiledError::XmlDecodingError)? {
-            XmlEvent::Characters(s) => {
-                return base64::decode(s.trim().as_bytes()).map_err(TiledError::Base64DecodingError)
-            }
-            XmlEvent::EndElement { name, .. } => {
-                if name.local_name == "data" {
-                    return Ok(Vec::new());
-                }
-            }
-            _ => {}
-        }
-    }
-}
-
-fn decode_zlib(data: Vec<u8>) -> Result<Vec<u8>, TiledError> {
-    use libflate::zlib::Decoder;
-    let mut zd =
-        Decoder::new(BufReader::new(&data[..])).map_err(|e| TiledError::DecompressingError(e))?;
-    let mut data = Vec::new();
-    match zd.read_to_end(&mut data) {
-        Ok(_v) => {}
-        Err(e) => return Err(TiledError::DecompressingError(e)),
-    }
-    Ok(data)
-}
-
-fn decode_gzip(data: Vec<u8>) -> Result<Vec<u8>, TiledError> {
-    use libflate::gzip::Decoder;
-    let mut zd =
-        Decoder::new(BufReader::new(&data[..])).map_err(|e| TiledError::DecompressingError(e))?;
-
-    let mut data = Vec::new();
-    zd.read_to_end(&mut data)
-        .map_err(|e| TiledError::DecompressingError(e))?;
-    Ok(data)
-}
-
-#[cfg(feature = "zstd")]
-fn decode_zstd(data: Vec<u8>) -> Result<Vec<u8>, TiledError> {
-    use std::io::Cursor;
-    use zstd::stream::read::Decoder;
-
-    let buff = Cursor::new(&data);
-    let mut zd = Decoder::with_buffer(buff).map_err(|e| TiledError::DecompressingError(e))?;
-
-    let mut data = Vec::new();
-    zd.read_to_end(&mut data)
-        .map_err(|e| TiledError::DecompressingError(e))?;
-    Ok(data)
-}
-
-fn decode_csv<R: Read>(width: u32, parser: &mut EventReader<R>) -> Result<Vec<Vec<LayerTile>>, TiledError> {
-    loop {
-        match parser.next().map_err(TiledError::XmlDecodingError)? {
-            XmlEvent::Characters(s) => {
-                let mut tiles_it = s
-                    .split(&['\n', '\r', ','][0..])
-                    .filter(|v| v.trim() != "")
-                    .map(|v| v.parse().unwrap())
-                    .map(LayerTile::new)
-                    .peekable();
-                let mut rows = Vec::new();
-                while tiles_it.peek().is_some() {
-                    let row = tiles_it.by_ref().take(width as usize).collect();
-                    rows.push(row);
-                }
-                return Ok(rows);
-            }
-            XmlEvent::EndElement { name, .. } => {
-                if name.local_name == "data" {
-                    return Ok(Vec::new());
-                }
-            }
-            _ => {}
-        }
-    }
-}
-
-fn convert_to_tile(all: &Vec<u8>, width: u32) -> Vec<Vec<LayerTile>> {
-    let mut data = Vec::new();
-    for chunk in all.chunks((width * 4) as usize) {
-        let mut row = Vec::new();
-        for i in 0..width {
-            let start: usize = i as usize * 4;
-            let n = ((chunk[start + 3] as u32) << 24)
-                + ((chunk[start + 2] as u32) << 16)
-                + ((chunk[start + 1] as u32) << 8)
-                + chunk[start] as u32;
-            let n = LayerTile::new(n);
-            row.push(n);
-        }
-        data.push(row);
-    }
-    data
-}
-
-fn parse_impl<R: Read>(reader: R, map_path: Option<&Path>) -> Result<Map, TiledError> {
-    let mut parser = EventReader::new(reader);
-    loop {
-        match parser.next().map_err(TiledError::XmlDecodingError)? {
-            XmlEvent::StartElement {
-                name, attributes, ..
-            } => {
-                if name.local_name == "map" {
-                    return Map::new(&mut parser, attributes, map_path);
-                }
-            }
-            XmlEvent::EndDocument => {
-                return Err(TiledError::PrematureEnd(
-                    "Document ended before map was parsed".to_string(),
-                ))
-            }
-            _ => {}
-        }
-    }
-}
-
+// TODO move these
 /// Parse a buffer hopefully containing the contents of a Tiled file and try to
 /// parse it. This augments `parse` with a file location: some engines
 /// (e.g. Amethyst) simply hand over a byte stream (and file location) for parsing,
diff --git a/src/map.rs b/src/map.rs
new file mode 100644
index 0000000000000000000000000000000000000000..f9271fa2dd1234210ab2dcb27662a1b17190750f
--- /dev/null
+++ b/src/map.rs
@@ -0,0 +1,150 @@
+use std::{collections::HashMap, fmt, io::Read, path::Path, str::FromStr};
+
+use xml::{attribute::OwnedAttribute, EventReader};
+
+use crate::{
+    error::{ParseTileError, TiledError},
+    layers::{ImageLayer, Layer},
+    objects::ObjectGroup,
+    properties::{parse_properties, Colour, Properties},
+    tileset::Tileset,
+    util::*,
+};
+
+/// All Tiled files will be parsed into this. Holds all the layers and tilesets
+#[derive(Debug, PartialEq, Clone)]
+pub struct Map {
+    pub version: String,
+    pub orientation: Orientation,
+    /// Width of the map, in tiles
+    pub width: u32,
+    /// Height of the map, in tiles
+    pub height: u32,
+    pub tile_width: u32,
+    pub tile_height: u32,
+    pub tilesets: Vec<Tileset>,
+    pub layers: Vec<Layer>,
+    pub image_layers: Vec<ImageLayer>,
+    pub object_groups: Vec<ObjectGroup>,
+    pub properties: Properties,
+    pub background_colour: Option<Colour>,
+    pub infinite: bool,
+}
+
+impl Map {
+    pub(crate) fn new<R: Read>(
+        parser: &mut EventReader<R>,
+        attrs: Vec<OwnedAttribute>,
+        map_path: Option<&Path>,
+    ) -> Result<Map, TiledError> {
+        let ((c, infinite), (v, o, w, h, tw, th)) = get_attrs!(
+            attrs,
+            optionals: [
+                ("backgroundcolor", colour, |v:String| v.parse().ok()),
+                ("infinite", infinite, |v:String| Some(v == "1")),
+            ],
+            required: [
+                ("version", version, |v| Some(v)),
+                ("orientation", orientation, |v:String| v.parse().ok()),
+                ("width", width, |v:String| v.parse().ok()),
+                ("height", height, |v:String| v.parse().ok()),
+                ("tilewidth", tile_width, |v:String| v.parse().ok()),
+                ("tileheight", tile_height, |v:String| v.parse().ok()),
+            ],
+            TiledError::MalformedAttributes("map must have a version, width and height with correct types".to_string())
+        );
+
+        let mut tilesets = Vec::new();
+        let mut layers = Vec::new();
+        let mut image_layers = Vec::new();
+        let mut properties = HashMap::new();
+        let mut object_groups = Vec::new();
+        let mut layer_index = 0;
+        parse_tag!(parser, "map", {
+            "tileset" => |attrs| {
+                tilesets.push(Tileset::new(parser, attrs, map_path)?);
+                Ok(())
+            },
+            "layer" => |attrs| {
+                layers.push(Layer::new(parser, attrs, w, layer_index, infinite.unwrap_or(false))?);
+                layer_index += 1;
+                Ok(())
+            },
+            "imagelayer" => |attrs| {
+                image_layers.push(ImageLayer::new(parser, attrs, layer_index)?);
+                layer_index += 1;
+                Ok(())
+            },
+            "properties" => |_| {
+                properties = parse_properties(parser)?;
+                Ok(())
+            },
+            "objectgroup" => |attrs| {
+                object_groups.push(ObjectGroup::new(parser, attrs, Some(layer_index))?);
+                layer_index += 1;
+                Ok(())
+            },
+        });
+        Ok(Map {
+            version: v,
+            orientation: o,
+            width: w,
+            height: h,
+            tile_width: tw,
+            tile_height: th,
+            tilesets,
+            layers,
+            image_layers,
+            object_groups,
+            properties,
+            background_colour: c,
+            infinite: infinite.unwrap_or(false),
+        })
+    }
+
+    /// This function will return the correct Tileset given a GID.
+    pub fn get_tileset_by_gid(&self, gid: u32) -> Option<&Tileset> {
+        let mut maximum_gid: i32 = -1;
+        let mut maximum_ts = None;
+        for tileset in self.tilesets.iter() {
+            if tileset.first_gid as i32 > maximum_gid && tileset.first_gid <= gid {
+                maximum_gid = tileset.first_gid as i32;
+                maximum_ts = Some(tileset);
+            }
+        }
+        maximum_ts
+    }
+}
+
+#[derive(Debug, PartialEq, Eq, Copy, Clone)]
+pub enum Orientation {
+    Orthogonal,
+    Isometric,
+    Staggered,
+    Hexagonal,
+}
+
+impl FromStr for Orientation {
+    type Err = ParseTileError;
+
+    fn from_str(s: &str) -> Result<Orientation, ParseTileError> {
+        match s {
+            "orthogonal" => Ok(Orientation::Orthogonal),
+            "isometric" => Ok(Orientation::Isometric),
+            "staggered" => Ok(Orientation::Staggered),
+            "hexagonal" => Ok(Orientation::Hexagonal),
+            _ => Err(ParseTileError::OrientationError),
+        }
+    }
+}
+
+impl fmt::Display for Orientation {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            Orientation::Orthogonal => write!(f, "orthogonal"),
+            Orientation::Isometric => write!(f, "isometric"),
+            Orientation::Staggered => write!(f, "staggered"),
+            Orientation::Hexagonal => write!(f, "hexagonal"),
+        }
+    }
+}
diff --git a/src/objects.rs b/src/objects.rs
new file mode 100644
index 0000000000000000000000000000000000000000..3e5edf919dea4938bee8111f7f77ce5597455203
--- /dev/null
+++ b/src/objects.rs
@@ -0,0 +1,222 @@
+use std::{collections::HashMap, io::Read};
+
+use xml::{attribute::OwnedAttribute, EventReader};
+
+use crate::{
+    error::TiledError,
+    properties::{parse_properties, Colour, Properties},
+    util::{get_attrs, parse_tag},
+};
+
+#[derive(Debug, PartialEq, Clone)]
+pub struct ObjectGroup {
+    pub name: String,
+    pub opacity: f32,
+    pub visible: bool,
+    pub objects: Vec<Object>,
+    pub colour: Option<Colour>,
+    /**
+     * Layer index is not preset for tile collision boxes
+     */
+    pub layer_index: Option<u32>,
+    pub properties: Properties,
+}
+
+impl ObjectGroup {
+    pub(crate) fn new<R: Read>(
+        parser: &mut EventReader<R>,
+        attrs: Vec<OwnedAttribute>,
+        layer_index: Option<u32>,
+    ) -> Result<ObjectGroup, TiledError> {
+        let ((o, v, c, n), ()) = get_attrs!(
+            attrs,
+            optionals: [
+                ("opacity", opacity, |v:String| v.parse().ok()),
+                ("visible", visible, |v:String| v.parse().ok().map(|x:i32| x == 1)),
+                ("color", colour, |v:String| v.parse().ok()),
+                ("name", name, |v:String| v.into()),
+            ],
+            required: [],
+            TiledError::MalformedAttributes("object groups must have a name".to_string())
+        );
+        let mut objects = Vec::new();
+        let mut properties = HashMap::new();
+        parse_tag!(parser, "objectgroup", {
+            "object" => |attrs| {
+                objects.push(Object::new(parser, attrs)?);
+                Ok(())
+            },
+            "properties" => |_| {
+                properties = parse_properties(parser)?;
+                Ok(())
+            },
+        });
+        Ok(ObjectGroup {
+            name: n.unwrap_or(String::new()),
+            opacity: o.unwrap_or(1.0),
+            visible: v.unwrap_or(true),
+            objects: objects,
+            colour: c,
+            layer_index,
+            properties,
+        })
+    }
+}
+
+#[derive(Debug, PartialEq, Clone)]
+pub enum ObjectShape {
+    Rect { width: f32, height: f32 },
+    Ellipse { width: f32, height: f32 },
+    Polyline { points: Vec<(f32, f32)> },
+    Polygon { points: Vec<(f32, f32)> },
+    Point(f32, f32),
+}
+
+#[derive(Debug, PartialEq, Clone)]
+pub struct Object {
+    pub id: u32,
+    pub gid: u32,
+    pub name: String,
+    pub obj_type: String,
+    pub width: f32,
+    pub height: f32,
+    pub x: f32,
+    pub y: f32,
+    pub rotation: f32,
+    pub visible: bool,
+    pub shape: ObjectShape,
+    pub properties: Properties,
+}
+
+impl Object {
+    fn new<R: Read>(
+        parser: &mut EventReader<R>,
+        attrs: Vec<OwnedAttribute>,
+    ) -> Result<Object, TiledError> {
+        let ((id, gid, n, t, w, h, v, r), (x, y)) = get_attrs!(
+            attrs,
+            optionals: [
+                ("id", id, |v:String| v.parse().ok()),
+                ("gid", gid, |v:String| v.parse().ok()),
+                ("name", name, |v:String| v.parse().ok()),
+                ("type", obj_type, |v:String| v.parse().ok()),
+                ("width", width, |v:String| v.parse().ok()),
+                ("height", height, |v:String| v.parse().ok()),
+                ("visible", visible, |v:String| v.parse().ok().map(|x:i32| x == 1)),
+                ("rotation", rotation, |v:String| v.parse().ok()),
+            ],
+            required: [
+                ("x", x, |v:String| v.parse().ok()),
+                ("y", y, |v:String| v.parse().ok()),
+            ],
+            TiledError::MalformedAttributes("objects must have an x and a y number".to_string())
+        );
+        let v = v.unwrap_or(true);
+        let w = w.unwrap_or(0f32);
+        let h = h.unwrap_or(0f32);
+        let r = r.unwrap_or(0f32);
+        let id = id.unwrap_or(0u32);
+        let gid = gid.unwrap_or(0u32);
+        let n = n.unwrap_or(String::new());
+        let t = t.unwrap_or(String::new());
+        let mut shape = None;
+        let mut properties = HashMap::new();
+
+        parse_tag!(parser, "object", {
+            "ellipse" => |_| {
+                shape = Some(ObjectShape::Ellipse {
+                    width: w,
+                    height: h,
+                });
+                Ok(())
+            },
+            "polyline" => |attrs| {
+                shape = Some(Object::new_polyline(attrs)?);
+                Ok(())
+            },
+            "polygon" => |attrs| {
+                shape = Some(Object::new_polygon(attrs)?);
+                Ok(())
+            },
+            "point" => |_| {
+                shape = Some(Object::new_point(x, y)?);
+                Ok(())
+            },
+            "properties" => |_| {
+                properties = parse_properties(parser)?;
+                Ok(())
+            },
+        });
+
+        let shape = shape.unwrap_or(ObjectShape::Rect {
+            width: w,
+            height: h,
+        });
+
+        Ok(Object {
+            id: id,
+            gid: gid,
+            name: n.clone(),
+            obj_type: t.clone(),
+            width: w,
+            height: h,
+            x: x,
+            y: y,
+            rotation: r,
+            visible: v,
+            shape: shape,
+            properties: properties,
+        })
+    }
+
+    fn new_polyline(attrs: Vec<OwnedAttribute>) -> Result<ObjectShape, TiledError> {
+        let ((), s) = get_attrs!(
+            attrs,
+            optionals: [],
+            required: [
+                ("points", points, |v| Some(v)),
+            ],
+            TiledError::MalformedAttributes("A polyline must have points".to_string())
+        );
+        let points = Object::parse_points(s)?;
+        Ok(ObjectShape::Polyline { points: points })
+    }
+
+    fn new_polygon(attrs: Vec<OwnedAttribute>) -> Result<ObjectShape, TiledError> {
+        let ((), s) = get_attrs!(
+            attrs,
+            optionals: [],
+            required: [
+                ("points", points, |v| Some(v)),
+            ],
+            TiledError::MalformedAttributes("A polygon must have points".to_string())
+        );
+        let points = Object::parse_points(s)?;
+        Ok(ObjectShape::Polygon { points: points })
+    }
+
+    fn new_point(x: f32, y: f32) -> Result<ObjectShape, TiledError> {
+        Ok(ObjectShape::Point(x, y))
+    }
+
+    fn parse_points(s: String) -> Result<Vec<(f32, f32)>, TiledError> {
+        let pairs = s.split(' ');
+        let mut points = Vec::new();
+        for v in pairs.map(|p| p.split(',')) {
+            let v: Vec<&str> = v.collect();
+            if v.len() != 2 {
+                return Err(TiledError::MalformedAttributes(
+                    "one of a polyline's points does not have an x and y coordinate".to_string(),
+                ));
+            }
+            let (x, y) = (v[0].parse().ok(), v[1].parse().ok());
+            if x.is_none() || y.is_none() {
+                return Err(TiledError::MalformedAttributes(
+                    "one of polyline's points does not have i32eger coordinates".to_string(),
+                ));
+            }
+            points.push((x.unwrap(), y.unwrap()));
+        }
+        Ok(points)
+    }
+}
diff --git a/src/properties.rs b/src/properties.rs
new file mode 100644
index 0000000000000000000000000000000000000000..6d9ae472c87c84a822a08ad963bf5a1ef0c4ecbd
--- /dev/null
+++ b/src/properties.rs
@@ -0,0 +1,108 @@
+use std::{collections::HashMap, io::Read, str::FromStr};
+
+use xml::{attribute::OwnedAttribute, EventReader};
+
+use crate::{
+    error::{ParseTileError, TiledError},
+    util::{get_attrs, parse_tag},
+};
+
+#[derive(Debug, PartialEq, Eq, Copy, Clone)]
+pub struct Colour {
+    pub red: u8,
+    pub green: u8,
+    pub blue: u8,
+}
+
+impl FromStr for Colour {
+    type Err = ParseTileError;
+
+    fn from_str(s: &str) -> Result<Colour, ParseTileError> {
+        let s = if s.starts_with("#") { &s[1..] } else { s };
+        if s.len() != 6 {
+            return Err(ParseTileError::ColourError);
+        }
+        let r = u8::from_str_radix(&s[0..2], 16);
+        let g = u8::from_str_radix(&s[2..4], 16);
+        let b = u8::from_str_radix(&s[4..6], 16);
+        if r.is_ok() && g.is_ok() && b.is_ok() {
+            return Ok(Colour {
+                red: r.unwrap(),
+                green: g.unwrap(),
+                blue: b.unwrap(),
+            });
+        }
+        Err(ParseTileError::ColourError)
+    }
+}
+
+#[derive(Debug, PartialEq, Clone)]
+pub enum PropertyValue {
+    BoolValue(bool),
+    FloatValue(f32),
+    IntValue(i32),
+    ColorValue(u32),
+    StringValue(String),
+    /// Holds the path relative to the map or tileset
+    FileValue(String),
+}
+
+impl PropertyValue {
+    fn new(property_type: String, value: String) -> Result<PropertyValue, TiledError> {
+        // Check the property type against the value.
+        match property_type.as_str() {
+            "bool" => match value.parse() {
+                Ok(val) => Ok(PropertyValue::BoolValue(val)),
+                Err(err) => Err(TiledError::Other(err.to_string())),
+            },
+            "float" => match value.parse() {
+                Ok(val) => Ok(PropertyValue::FloatValue(val)),
+                Err(err) => Err(TiledError::Other(err.to_string())),
+            },
+            "int" => match value.parse() {
+                Ok(val) => Ok(PropertyValue::IntValue(val)),
+                Err(err) => Err(TiledError::Other(err.to_string())),
+            },
+            "color" if value.len() > 1 => match u32::from_str_radix(&value[1..], 16) {
+                Ok(color) => Ok(PropertyValue::ColorValue(color)),
+                Err(_) => Err(TiledError::Other(format!(
+                    "Improperly formatted color property"
+                ))),
+            },
+            "string" => Ok(PropertyValue::StringValue(value)),
+            "file" => Ok(PropertyValue::FileValue(value)),
+            _ => Err(TiledError::Other(format!(
+                "Unknown property type \"{}\"",
+                property_type
+            ))),
+        }
+    }
+}
+
+pub type Properties = HashMap<String, PropertyValue>;
+
+pub(crate) fn parse_properties<R: Read>(
+    parser: &mut EventReader<R>,
+) -> Result<Properties, TiledError> {
+    let mut p = HashMap::new();
+    parse_tag!(parser, "properties", {
+        "property" => |attrs:Vec<OwnedAttribute>| {
+            let (t, (k, v)) = get_attrs!(
+                attrs,
+                optionals: [
+                    ("type", property_type, |v| Some(v)),
+                ],
+                required: [
+                    ("name", key, |v| Some(v)),
+                    ("value", value, |v| Some(v)),
+                ],
+                TiledError::MalformedAttributes("property must have a name and a value".to_string())
+            );
+            let t = t.unwrap_or("string".into());
+
+            p.insert(k, PropertyValue::new(t, v)?);
+            Ok(())
+        },
+    });
+    Ok(p)
+}
diff --git a/src/tile.rs b/src/tile.rs
new file mode 100644
index 0000000000000000000000000000000000000000..80d6f979ea1c89f462b013c3f5ef7c4b20369aae
--- /dev/null
+++ b/src/tile.rs
@@ -0,0 +1,74 @@
+use std::{collections::HashMap, io::Read};
+
+use xml::{attribute::OwnedAttribute, EventReader};
+
+use crate::{
+    animation::Frame,
+    error::TiledError,
+    image::Image,
+    objects::ObjectGroup,
+    properties::{parse_properties, Properties},
+    util::{get_attrs, parse_animation, parse_tag},
+};
+
+#[derive(Debug, PartialEq, Clone)]
+pub struct Tile {
+    pub id: u32,
+    pub images: Vec<Image>,
+    pub properties: Properties,
+    pub objectgroup: Option<ObjectGroup>,
+    pub animation: Option<Vec<Frame>>,
+    pub tile_type: Option<String>,
+    pub probability: f32,
+}
+
+impl Tile {
+    pub(crate) fn new<R: Read>(
+        parser: &mut EventReader<R>,
+        attrs: Vec<OwnedAttribute>,
+    ) -> Result<Tile, TiledError> {
+        let ((tile_type, probability), id) = get_attrs!(
+            attrs,
+            optionals: [
+                ("type", tile_type, |v:String| v.parse().ok()),
+                ("probability", probability, |v:String| v.parse().ok()),
+            ],
+            required: [
+                ("id", id, |v:String| v.parse::<u32>().ok()),
+            ],
+            TiledError::MalformedAttributes("tile must have an id with the correct type".to_string())
+        );
+
+        let mut images = Vec::new();
+        let mut properties = HashMap::new();
+        let mut objectgroup = None;
+        let mut animation = None;
+        parse_tag!(parser, "tile", {
+            "image" => |attrs| {
+                images.push(Image::new(parser, attrs)?);
+                Ok(())
+            },
+            "properties" => |_| {
+                properties = parse_properties(parser)?;
+                Ok(())
+            },
+            "objectgroup" => |attrs| {
+                objectgroup = Some(ObjectGroup::new(parser, attrs, None)?);
+                Ok(())
+            },
+            "animation" => |_| {
+                animation = Some(parse_animation(parser)?);
+                Ok(())
+            },
+        });
+        Ok(Tile {
+            id,
+            images,
+            properties,
+            objectgroup,
+            animation,
+            tile_type,
+            probability: probability.unwrap_or(1.0),
+        })
+    }
+}
diff --git a/src/tileset.rs b/src/tileset.rs
new file mode 100644
index 0000000000000000000000000000000000000000..fe45353523aea6176296d6718ccd682e0688b647
--- /dev/null
+++ b/src/tileset.rs
@@ -0,0 +1,187 @@
+use crate::properties::{parse_properties, Properties};
+use crate::util::*;
+use crate::*; // FIXME
+
+/// A tileset, usually the tilesheet image.
+#[derive(Debug, PartialEq, Clone)]
+pub struct Tileset {
+    /// The GID of the first tile stored
+    pub first_gid: u32,
+    pub name: String,
+    pub tile_width: u32,
+    pub tile_height: u32,
+    pub spacing: u32,
+    pub margin: u32,
+    pub tilecount: Option<u32>,
+    /// The Tiled spec says that a tileset can have mutliple images so a `Vec`
+    /// is used. Usually you will only use one.
+    pub images: Vec<Image>,
+    pub tiles: Vec<Tile>,
+    pub properties: Properties,
+}
+
+impl Tileset {
+    pub(crate) fn new<R: Read>(
+        parser: &mut EventReader<R>,
+        attrs: Vec<OwnedAttribute>,
+        map_path: Option<&Path>,
+    ) -> Result<Tileset, TiledError> {
+        Tileset::new_internal(parser, &attrs).or_else(|_| Tileset::new_reference(&attrs, map_path))
+    }
+
+    fn new_internal<R: Read>(
+        parser: &mut EventReader<R>,
+        attrs: &Vec<OwnedAttribute>,
+    ) -> Result<Tileset, TiledError> {
+        let ((spacing, margin, tilecount), (first_gid, name, width, height)) = get_attrs!(
+           attrs,
+           optionals: [
+                ("spacing", spacing, |v:String| v.parse().ok()),
+                ("margin", margin, |v:String| v.parse().ok()),
+                ("tilecount", tilecount, |v:String| v.parse().ok()),
+            ],
+           required: [
+                ("firstgid", first_gid, |v:String| v.parse().ok()),
+                ("name", name, |v| Some(v)),
+                ("tilewidth", width, |v:String| v.parse().ok()),
+                ("tileheight", height, |v:String| v.parse().ok()),
+            ],
+            TiledError::MalformedAttributes("tileset must have a firstgid, name tile width and height with correct types".to_string())
+        );
+
+        let mut images = Vec::new();
+        let mut tiles = Vec::new();
+        let mut properties = HashMap::new();
+        parse_tag!(parser, "tileset", {
+            "image" => |attrs| {
+                images.push(Image::new(parser, attrs)?);
+                Ok(())
+            },
+            "properties" => |_| {
+                properties = parse_properties(parser)?;
+                Ok(())
+            },
+            "tile" => |attrs| {
+                tiles.push(Tile::new(parser, attrs)?);
+                Ok(())
+            },
+        });
+
+        Ok(Tileset {
+            tile_width: width,
+            tile_height: height,
+            spacing: spacing.unwrap_or(0),
+            margin: margin.unwrap_or(0),
+            first_gid,
+            name,
+            tilecount,
+            images,
+            tiles,
+            properties,
+        })
+    }
+
+    fn new_reference(
+        attrs: &Vec<OwnedAttribute>,
+        map_path: Option<&Path>,
+    ) -> Result<Tileset, TiledError> {
+        let ((), (first_gid, source)) = get_attrs!(
+            attrs,
+            optionals: [],
+            required: [
+                ("firstgid", first_gid, |v:String| v.parse().ok()),
+                ("source", name, |v| Some(v)),
+            ],
+            TiledError::MalformedAttributes("tileset must have a firstgid, name tile width and height with correct types".to_string())
+        );
+
+        let tileset_path = map_path.ok_or(TiledError::Other("Maps with external tilesets must know their file location.  See parse_with_path(Path).".to_string()))?.with_file_name(source);
+        let file = File::open(&tileset_path).map_err(|_| {
+            TiledError::Other(format!(
+                "External tileset file not found: {:?}",
+                tileset_path
+            ))
+        })?;
+        Tileset::new_external(file, first_gid)
+    }
+
+    pub(crate) fn new_external<R: Read>(file: R, first_gid: u32) -> Result<Tileset, TiledError> {
+        let mut tileset_parser = EventReader::new(file);
+        loop {
+            match tileset_parser
+                .next()
+                .map_err(TiledError::XmlDecodingError)?
+            {
+                XmlEvent::StartElement {
+                    name, attributes, ..
+                } => {
+                    if name.local_name == "tileset" {
+                        return Tileset::parse_external_tileset(
+                            first_gid,
+                            &mut tileset_parser,
+                            &attributes,
+                        );
+                    }
+                }
+                XmlEvent::EndDocument => {
+                    return Err(TiledError::PrematureEnd(
+                        "Tileset Document ended before map was parsed".to_string(),
+                    ))
+                }
+                _ => {}
+            }
+        }
+    }
+
+    fn parse_external_tileset<R: Read>(
+        first_gid: u32,
+        parser: &mut EventReader<R>,
+        attrs: &Vec<OwnedAttribute>,
+    ) -> Result<Tileset, TiledError> {
+        let ((spacing, margin, tilecount), (name, width, height)) = get_attrs!(
+            attrs,
+            optionals: [
+                ("spacing", spacing, |v:String| v.parse().ok()),
+                ("margin", margin, |v:String| v.parse().ok()),
+                ("tilecount", tilecount, |v:String| v.parse().ok()),
+            ],
+            required: [
+                ("name", name, |v| Some(v)),
+                ("tilewidth", width, |v:String| v.parse().ok()),
+                ("tileheight", height, |v:String| v.parse().ok()),
+            ],
+            TiledError::MalformedAttributes("tileset must have a firstgid, name tile width and height with correct types".to_string())
+        );
+
+        let mut images = Vec::new();
+        let mut tiles = Vec::new();
+        let mut properties = HashMap::new();
+        parse_tag!(parser, "tileset", {
+            "image" => |attrs| {
+                images.push(Image::new(parser, attrs)?);
+                Ok(())
+            },
+            "tile" => |attrs| {
+                tiles.push(Tile::new(parser, attrs)?);
+                Ok(())
+            },
+            "properties" => |_| {
+                properties = parse_properties(parser)?;
+                Ok(())
+            },
+        });
+
+        Ok(Tileset {
+            first_gid: first_gid,
+            name: name,
+            tile_width: width,
+            tile_height: height,
+            spacing: spacing.unwrap_or(0),
+            margin: margin.unwrap_or(0),
+            tilecount: tilecount,
+            images: images,
+            tiles: tiles,
+            properties,
+        })
+    }
+}
diff --git a/src/util.rs b/src/util.rs
new file mode 100644
index 0000000000000000000000000000000000000000..61dcfd1c24bf24cdbebaba82fe17da8e93d048b4
--- /dev/null
+++ b/src/util.rs
@@ -0,0 +1,301 @@
+/// Loops through the attributes once and pulls out the ones we ask it to. It
+/// will check that the required ones are there. This could have been done with
+/// attrs.find but that would be inefficient.
+///
+/// This is probably a really terrible way to do this. It does cut down on lines
+/// though which is nice.
+macro_rules! get_attrs {
+    ($attrs:expr, optionals: [$(($oName:pat, $oVar:ident, $oMethod:expr)),* $(,)*],
+     required: [$(($name:pat, $var:ident, $method:expr)),* $(,)*], $err:expr) => {
+        {
+            $(let mut $oVar = None;)*
+            $(let mut $var = None;)*
+            for attr in $attrs.iter() {
+                match attr.name.local_name.as_ref() {
+                    $($oName => $oVar = $oMethod(attr.value.clone()),)*
+                    $($name => $var = $method(attr.value.clone()),)*
+                    _ => {}
+                }
+            }
+            if !(true $(&& $var.is_some())*) {
+                return Err($err);
+            }
+            (($($oVar),*), ($($var.unwrap()),*))
+        }
+    }
+}
+
+/// Goes through the children of the tag and will call the correct function for
+/// that child. Closes the tag
+///
+/// Not quite as bad.
+macro_rules! parse_tag {
+    ($parser:expr, $close_tag:expr, {$($open_tag:expr => $open_method:expr),* $(,)*}) => {
+        loop {
+            match $parser.next().map_err(TiledError::XmlDecodingError)? {
+                xml::reader::XmlEvent::StartElement {name, attributes, ..} => {
+                    if false {}
+                    $(else if name.local_name == $open_tag {
+                        match $open_method(attributes) {
+                            Ok(()) => {},
+                            Err(e) => return Err(e)
+                        };
+                    })*
+                }
+                xml::reader::XmlEvent::EndElement {name, ..} => {
+                    if name.local_name == $close_tag {
+                        break;
+                    }
+                }
+                xml::reader::XmlEvent::EndDocument => return Err(TiledError::PrematureEnd("Document ended before we expected.".to_string())),
+                _ => {}
+            }
+        }
+    }
+}
+
+use std::{
+    collections::HashMap,
+    fs::File,
+    io::{BufReader, Read},
+    path::Path,
+};
+
+pub(crate) use get_attrs;
+pub(crate) use parse_tag;
+use xml::{attribute::OwnedAttribute, reader::XmlEvent, EventReader};
+
+use crate::{
+    animation::Frame,
+    error::TiledError,
+    layers::{Chunk, LayerData, LayerTile},
+    map::Map,
+    tileset::Tileset,
+};
+
+pub(crate) fn parse_animation<R: Read>(
+    parser: &mut EventReader<R>,
+) -> Result<Vec<Frame>, TiledError> {
+    let mut animation = Vec::new();
+    parse_tag!(parser, "animation", {
+        "frame" => |attrs| {
+            animation.push(Frame::new(attrs)?);
+            Ok(())
+        },
+    });
+    Ok(animation)
+}
+
+pub(crate) fn parse_infinite_data<R: Read>(
+    parser: &mut EventReader<R>,
+    attrs: Vec<OwnedAttribute>,
+    width: u32,
+) -> Result<LayerData, TiledError> {
+    let ((e, c), ()) = get_attrs!(
+        attrs,
+        optionals: [
+            ("encoding", encoding, |v| Some(v)),
+            ("compression", compression, |v| Some(v)),
+        ],
+        required: [],
+        TiledError::MalformedAttributes("data must have an encoding and a compression".to_string())
+    );
+
+    let mut chunks = HashMap::<(i32, i32), Chunk>::new();
+    parse_tag!(parser, "data", {
+        "chunk" => |attrs| {
+            let chunk = Chunk::new(parser, attrs, e.clone(), c.clone())?;
+            chunks.insert((chunk.x, chunk.y), chunk);
+            Ok(())
+        }
+    });
+
+    Ok(LayerData::Infinite(chunks))
+}
+
+pub(crate) fn parse_data<R: Read>(
+    parser: &mut EventReader<R>,
+    attrs: Vec<OwnedAttribute>,
+    width: u32,
+) -> Result<LayerData, TiledError> {
+    let ((e, c), ()) = get_attrs!(
+        attrs,
+        optionals: [
+            ("encoding", encoding, |v| Some(v)),
+            ("compression", compression, |v| Some(v)),
+        ],
+        required: [],
+        TiledError::MalformedAttributes("data must have an encoding and a compression".to_string())
+    );
+
+    let tiles = parse_data_line(e, c, parser, width)?;
+
+    Ok(LayerData::Finite(tiles))
+}
+
+pub(crate) fn parse_data_line<R: Read>(
+    encoding: Option<String>,
+    compression: Option<String>,
+    parser: &mut EventReader<R>,
+    width: u32,
+) -> Result<Vec<Vec<LayerTile>>, TiledError> {
+    match (encoding, compression) {
+        (None, None) => {
+            return Err(TiledError::Other(
+                "XML format is currently not supported".to_string(),
+            ))
+        }
+        (Some(e), None) => match e.as_ref() {
+            "base64" => return parse_base64(parser).map(|v| convert_to_tile(&v, width)),
+            "csv" => return decode_csv(width, parser),
+            e => return Err(TiledError::Other(format!("Unknown encoding format {}", e))),
+        },
+        (Some(e), Some(c)) => match (e.as_ref(), c.as_ref()) {
+            ("base64", "zlib") => {
+                return parse_base64(parser)
+                    .and_then(decode_zlib)
+                    .map(|v| convert_to_tile(&v, width))
+            }
+            ("base64", "gzip") => {
+                return parse_base64(parser)
+                    .and_then(decode_gzip)
+                    .map(|v| convert_to_tile(&v, width))
+            }
+            #[cfg(feature = "zstd")]
+            ("base64", "zstd") => {
+                return parse_base64(parser)
+                    .and_then(decode_zstd)
+                    .map(|v| convert_to_tile(&v, width))
+            }
+            (e, c) => {
+                return Err(TiledError::Other(format!(
+                    "Unknown combination of {} encoding and {} compression",
+                    e, c
+                )))
+            }
+        },
+        _ => return Err(TiledError::Other("Missing encoding format".to_string())),
+    };
+}
+
+pub(crate) fn parse_base64<R: Read>(parser: &mut EventReader<R>) -> Result<Vec<u8>, TiledError> {
+    loop {
+        match parser.next().map_err(TiledError::XmlDecodingError)? {
+            XmlEvent::Characters(s) => {
+                return base64::decode(s.trim().as_bytes()).map_err(TiledError::Base64DecodingError)
+            }
+            XmlEvent::EndElement { name, .. } => {
+                if name.local_name == "data" {
+                    return Ok(Vec::new());
+                }
+            }
+            _ => {}
+        }
+    }
+}
+
+pub(crate) fn decode_zlib(data: Vec<u8>) -> Result<Vec<u8>, TiledError> {
+    use libflate::zlib::Decoder;
+    let mut zd =
+        Decoder::new(BufReader::new(&data[..])).map_err(|e| TiledError::DecompressingError(e))?;
+    let mut data = Vec::new();
+    match zd.read_to_end(&mut data) {
+        Ok(_v) => {}
+        Err(e) => return Err(TiledError::DecompressingError(e)),
+    }
+    Ok(data)
+}
+
+pub(crate) fn decode_gzip(data: Vec<u8>) -> Result<Vec<u8>, TiledError> {
+    use libflate::gzip::Decoder;
+    let mut zd =
+        Decoder::new(BufReader::new(&data[..])).map_err(|e| TiledError::DecompressingError(e))?;
+
+    let mut data = Vec::new();
+    zd.read_to_end(&mut data)
+        .map_err(|e| TiledError::DecompressingError(e))?;
+    Ok(data)
+}
+
+#[cfg(feature = "zstd")]
+pub(crate) fn decode_zstd(data: Vec<u8>) -> Result<Vec<u8>, TiledError> {
+    use std::io::Cursor;
+    use zstd::stream::read::Decoder;
+
+    let buff = Cursor::new(&data);
+    let mut zd = Decoder::with_buffer(buff).map_err(|e| TiledError::DecompressingError(e))?;
+
+    let mut data = Vec::new();
+    zd.read_to_end(&mut data)
+        .map_err(|e| TiledError::DecompressingError(e))?;
+    Ok(data)
+}
+
+pub(crate) fn decode_csv<R: Read>(
+    width: u32,
+    parser: &mut EventReader<R>,
+) -> Result<Vec<Vec<LayerTile>>, TiledError> {
+    loop {
+        match parser.next().map_err(TiledError::XmlDecodingError)? {
+            XmlEvent::Characters(s) => {
+                let mut tiles_it = s
+                    .split(&['\n', '\r', ','][0..])
+                    .filter(|v| v.trim() != "")
+                    .map(|v| v.parse().unwrap())
+                    .map(LayerTile::new)
+                    .peekable();
+                let mut rows = Vec::new();
+                while tiles_it.peek().is_some() {
+                    let row = tiles_it.by_ref().take(width as usize).collect();
+                    rows.push(row);
+                }
+                return Ok(rows);
+            }
+            XmlEvent::EndElement { name, .. } => {
+                if name.local_name == "data" {
+                    return Ok(Vec::new());
+                }
+            }
+            _ => {}
+        }
+    }
+}
+
+pub(crate) fn convert_to_tile(all: &Vec<u8>, width: u32) -> Vec<Vec<LayerTile>> {
+    let mut data = Vec::new();
+    for chunk in all.chunks((width * 4) as usize) {
+        let mut row = Vec::new();
+        for i in 0..width {
+            let start: usize = i as usize * 4;
+            let n = ((chunk[start + 3] as u32) << 24)
+                + ((chunk[start + 2] as u32) << 16)
+                + ((chunk[start + 1] as u32) << 8)
+                + chunk[start] as u32;
+            let n = LayerTile::new(n);
+            row.push(n);
+        }
+        data.push(row);
+    }
+    data
+}
+
+pub(crate) fn parse_impl<R: Read>(reader: R, map_path: Option<&Path>) -> Result<Map, TiledError> {
+    let mut parser = EventReader::new(reader);
+    loop {
+        match parser.next().map_err(TiledError::XmlDecodingError)? {
+            XmlEvent::StartElement {
+                name, attributes, ..
+            } => {
+                if name.local_name == "map" {
+                    return Map::new(&mut parser, attributes, map_path);
+                }
+            }
+            XmlEvent::EndDocument => {
+                return Err(TiledError::PrematureEnd(
+                    "Document ended before map was parsed".to_string(),
+                ))
+            }
+            _ => {}
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/lib.rs b/tests/lib.rs
index 80ab40b843c5d062166383073ea7ec0ba24701bf..92b1a284b368e0d65c51ee4724bf750a00383bc0 100644
--- a/tests/lib.rs
+++ b/tests/lib.rs
@@ -1,6 +1,9 @@
 use std::fs::File;
 use std::path::Path;
-use tiled::{parse, parse_file, parse_tileset, Map, PropertyValue, TiledError, LayerData};
+use tiled::{
+    error::TiledError, layers::LayerData, map::Map, parse, parse_file, parse_tileset,
+    properties::PropertyValue,
+};
 
 fn read_from_file(p: &Path) -> Result<Map, TiledError> {
     let file = File::open(p).unwrap();
@@ -20,7 +23,7 @@ fn test_gzip_and_zlib_encoded_and_raw_are_the_same() {
     assert_eq!(z, g);
     assert_eq!(z, r);
     assert_eq!(z, c);
-    
+
     if let LayerData::Finite(tiles) = &c.layers[0].tiles {
         assert_eq!(tiles.len(), 100);
         assert_eq!(tiles[0].len(), 100);
@@ -51,7 +54,7 @@ fn test_just_tileset() {
 
 #[test]
 fn test_infinite_tileset() {
-    let r = read_from_file_with_path(&Path::new("assets/tiled_base64_zlib_infinite.tmx")).unwrap();    
+    let r = read_from_file_with_path(&Path::new("assets/tiled_base64_zlib_infinite.tmx")).unwrap();
 
     if let LayerData::Infinite(chunks) = &r.layers[0].tiles {
         assert_eq!(chunks.len(), 4);
@@ -63,7 +66,6 @@ fn test_infinite_tileset() {
         assert_eq!(chunks[&(-32, 32)].height, 32);
     } else {
         assert!(false, "It is wrongly recognised as a finite map");
-
     }
 }
 
@@ -135,7 +137,7 @@ fn test_tileset_property() {
 #[test]
 fn test_flipped_gid() {
     let r = read_from_file_with_path(&Path::new("assets/tiled_flipped.tmx")).unwrap();
-    
+
     if let LayerData::Finite(tiles) = &r.layers[0].tiles {
         let t1 = tiles[0][0];
         let t2 = tiles[0][1];
@@ -159,7 +161,6 @@ fn test_flipped_gid() {
     } else {
         assert!(false, "It is wrongly recognised as an infinite map");
     }
-    
 }
 
 #[test]
@@ -173,4 +174,4 @@ fn test_ldk_export() {
     } else {
         assert!(false, "It is wrongly recognised as an infinite map");
     }
-}
\ No newline at end of file
+}