Newer
Older

Doug Reeves
committed
use std::fs::File;
use std::io::{BufReader, Error, Read};

Doug Reeves
committed
use std::path::Path;
use xml::reader::XmlEvent;
use xml::reader::{Error as XmlError, EventReader};
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.
($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())*) {
}
(($($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.
($parser:expr, $close_tag:expr, {$($open_tag:expr => $open_method:expr),* $(,)*}) => {
XmlEvent::StartElement {name, attributes, ..} => {
match $open_method(attributes) {
Ok(()) => {},
Err(e) => return Err(e)
};
})*
XmlEvent::EndDocument => return Err(TiledError::PrematureEnd("Document ended before we expected.".to_string())),
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
type Err = ParseTileError;
fn from_str(s: &str) -> Result<Colour, ParseTileError> {
let s = if s.starts_with("#") { &s[1..] } else { s };
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)
/// A attribute was missing, had the wrong type of wasn't formated
/// correctly.
/// An error occured when decompressing using the
Base64DecodingError(base64::DecodeError),
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),
StringValue(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)),
_ => 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> {
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());
/// All Tiled files will be parsed into this. Holds all the layers and tilesets
#[derive(Debug, PartialEq, Clone)]
pub version: String,
pub orientation: Orientation,
pub width: u32,
pub height: u32,
pub tile_width: u32,
pub tile_height: u32,
pub tilesets: Vec<Tileset>,
pub layers: Vec<Layer>,
pub object_groups: Vec<ObjectGroup>,
pub properties: Properties,
pub background_colour: Option<Colour>,
fn new<R: Read>(
parser: &mut EventReader<R>,
attrs: Vec<OwnedAttribute>,
map_path: Option<&Path>,
) -> Result<Map, TiledError> {
optionals: [
("backgroundcolor", colour, |v:String| v.parse().ok()),
],
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();
parse_tag!(parser, "map", {
"tileset" => | attrs| {
Ok(())
},
"layer" => |attrs| {
layer_index += 1;
Ok(())
},
"imagelayer" => |attrs| {
image_layers.push(ImageLayer::new(parser, attrs, layer_index)?);
layer_index += 1;
Ok(())
},
"properties" => |_| {
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,
})
pub fn get_tileset_by_gid(&self, gid: u32) -> Option<&Tileset> {
let mut maximum_ts = None;
for tileset in self.tilesets.iter() {
if tileset.first_gid as i32 > maximum_gid && tileset.first_gid <= gid {
maximum_ts = Some(tileset);
}
}
maximum_ts
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
type Err = ParseTileError;
fn from_str(s: &str) -> Result<Orientation, ParseTileError> {
"orthogonal" => Ok(Orientation::Orthogonal),
"isometric" => Ok(Orientation::Isometric),
"staggered" => Ok(Orientation::Staggered),
"hexagonal" => Ok(Orientation::Hexagonal),
_ => Err(ParseTileError::OrientationError),
pub tile_width: u32,
pub tile_height: u32,
pub spacing: u32,
pub margin: u32,
/// The Tiled spec says that a tileset can have mutliple images so a `Vec`
pub images: Vec<Image>,
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))

Doug Reeves
committed
}
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!(
optionals: [
("spacing", spacing, |v:String| v.parse().ok()),
("margin", margin, |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 tiles = Vec::new();
let mut properties = HashMap::new();
parse_tag!(parser, "tileset", {
"image" => |attrs| {
"properties" => |_| {
properties = parse_properties(parser)?;
Ok(())
},
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,

Doug Reeves
committed
}
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())
);

Doug Reeves
committed
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> {

Doug Reeves
committed
let mut tileset_parser = EventReader::new(file);
loop {
match tileset_parser
.next()
.map_err(TiledError::XmlDecodingError)?
{
XmlEvent::StartElement {
name, attributes, ..
} => {

Doug Reeves
committed
if name.local_name == "tileset" {
return Tileset::parse_external_tileset(
first_gid,
&mut tileset_parser,
&attributes,
);

Doug Reeves
committed
}
}
XmlEvent::EndDocument => {
return Err(TiledError::PrematureEnd(
"Tileset Document ended before map was parsed".to_string(),
))
}

Doug Reeves
committed
_ => {}
}
}
}
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!(

Doug Reeves
committed
attrs,
optionals: [
("spacing", spacing, |v:String| v.parse().ok()),
("margin", margin, |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())
);

Doug Reeves
committed
let mut images = Vec::new();
let mut tiles = Vec::new();
let mut properties = HashMap::new();
parse_tag!(parser, "tileset", {
"image" => |attrs| {
Ok(())
},
"tile" => |attrs| {
"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),
images: images,
tiles: tiles,

Doug Reeves
committed
}
pub struct Tile {
pub id: u32,
pub images: Vec<Image>,
pub properties: Properties,
pub objectgroup: Option<ObjectGroup>,
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 objectgroup = None;
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
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)]
fn new<R: Read>(
parser: &mut EventReader<R>,
attrs: Vec<OwnedAttribute>,
) -> Result<Image, TiledError> {
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,
})
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
/// 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 name: String,
pub opacity: f32,
pub visible: bool,
/// 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 properties: Properties,
pub layer_index: u32,
fn new<R: Read>(
parser: &mut EventReader<R>,
attrs: Vec<OwnedAttribute>,
width: u32,
layer_index: u32,
) -> Result<Layer, TiledError> {
optionals: [
("opacity", opacity, |v:String| v.parse().ok()),
("visible", visible, |v:String| v.parse().ok().map(|x:i32| x == 1)),
],
required: [
("name", name, |v| Some(v)),
],
TiledError::MalformedAttributes("layer must have a name".to_string())
);
parse_tag!(parser, "layer", {
"data" => |attrs| {
Ok(())
},
"properties" => |_| {
Ok(())
},
});
Ok(Layer {
name: n,
opacity: o.unwrap_or(1.0),
visible: v.unwrap_or(true),
tiles: tiles,
properties: properties,
layer_index,
})
#[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,
fn new<R: Read>(
parser: &mut EventReader<R>,
attrs: Vec<OwnedAttribute>,
layer_index: u32,
) -> Result<ImageLayer, TiledError> {
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,
#[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>,
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!(
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 properties = HashMap::new();
parse_tag!(parser, "objectgroup", {
"object" => |attrs| {
});
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,
#[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)> },
#[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 visible: bool,
pub shape: ObjectShape,
pub properties: Properties,
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!(
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()),
("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 w = w.unwrap_or(0f32);
let h = h.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();
"ellipse" => |_| {
shape = Some(ObjectShape::Ellipse {
width: w,
height: h,
});
Ok(())
},
"polyline" => |attrs| {
Ok(())
},
"polygon" => |attrs| {
Ok(())
},
"point" => |_| {
shape = Some(Object::new_point(x, y)?);
Ok(())
},
"properties" => |_| {
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(),
x: x,
y: y,
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())
);
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())
);
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> {
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());
return Err(TiledError::MalformedAttributes(
"one of polyline's points does not have i32eger coordinates".to_string(),
));
}
points.push((x.unwrap(), y.unwrap()));
}
Ok(points)
}
}
}
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| {