diff --git a/assets/tiled_base64_zlib.tmx b/assets/tiled_base64_zlib.tmx index b7a343d9cd3378fb3d1b9ef6f60afadd9a0389ad..706d1d36020a25787a76dc2793352e24faa49062 100644 --- a/assets/tiled_base64_zlib.tmx +++ b/assets/tiled_base64_zlib.tmx @@ -17,4 +17,16 @@ eJzt1zEKwzAMBVBdIScIJXPS+9+uDY1BuHbpULvLeyDIkEkfIXmLiK2qW6cYb7lqv+p41r1TcpmjZFLPyamVEXOcGSzpu66SixkZb41Xz9dO7fE+O4yR+703KmdyhExGK32v+1zv+rxLZDJO7vO5G3KvSyY1eYxT31Dlrm3t9AhZzNC6a+v3RsmhNzP83qdc8ttdHuO07thWLlt4E87w7S6wM+bo7e3ef8z1KRt5/N+388N88gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIHsArPIXTA== </data> </layer> + <objectgroup name="Object group" width="100" height="100"> + <object x="14" y="9" width="285" height="135"/> + <object x="329" y="217" width="102" height="109"> + <ellipse/> + </object> + <object x="314" y="376"> + <polyline points="0,0 -111,-63 -203,27 -205,-130 -78,-150 -6,-6"/> + </object> + <object x="479" y="84"> + <polygon points="0,0 139,128 -55,64 -37,-49 159,47 138,126"/> + </object> + </objectgroup> </map> diff --git a/src/lib.rs b/src/lib.rs index efdf0b179ef478b4a7a24d7f5615df65a7161b5d..1f38fd589860e8237fe9682fea08f66ce3739204 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -#![feature(globs, macro_rules)] +#![feature(globs, macro_rules, struct_variant)] extern crate flate2; extern crate xml; extern crate serialize; @@ -59,7 +59,7 @@ macro_rules! parse_tag { #[deriving(Show)] pub enum TiledError { - MissingAttributes(String), + MalformedAttributes(String), DecompressingError(IoError), DecodingError(FromBase64Error), Other(String) @@ -76,7 +76,7 @@ fn parse_properties<B: Buffer>(parser: &mut EventReader<B>) -> Result<Properties optionals: [], required: [("name", key, String, |v| Some(v)), ("value", value, String, |v| Some(v))], - MissingAttributes("property must have a name and a value".to_string())); + MalformedAttributes("property must have a name and a value".to_string())); p.insert(k, v); Ok(()) }); @@ -93,6 +93,7 @@ pub struct Map { tile_height: int, tilesets: Vec<Tileset>, layers: Vec<Layer>, + object_groups: Vec<ObjectGroup>, properties: Properties } @@ -107,11 +108,12 @@ impl Map { ("height", height, int, |v:String| from_str(v[])), ("tilewidth", tile_width, int, |v:String| from_str(v[])), ("tileheight", tile_height, int, |v:String| from_str(v[]))], - MissingAttributes("map must have a version, width and height with correct types".to_string())); + 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 properties = HashMap::new(); + let mut object_groups = Vec::new(); parse_tag!(parser, "map", "tileset" => |attrs| { tilesets.push(try!(Tileset::new(parser, attrs))); @@ -124,11 +126,15 @@ impl Map { "properties" => |_| { properties = try!(parse_properties(parser)); Ok(()) + }, + "objectgroup" => |attrs| { + object_groups.push(try!(ObjectGroup::new(parser, attrs))); + Ok(()) }); Ok(Map {version: v, orientation: o, width: w, height: h, tile_width: tw, tile_height: th, - tilesets: tilesets, layers: layers, + tilesets: tilesets, layers: layers, object_groups: object_groups, properties: properties}) } @@ -177,7 +183,7 @@ impl Tileset { optionals: [], required: [("firstgid", first_gid, uint, |v:String| from_str(v[])), ("name", name, String, |v| Some(v))], - MissingAttributes("tileset must have a firstgid and name with correct types".to_string())); + MalformedAttributes("tileset must have a firstgid and name with correct types".to_string())); let mut images = Vec::new(); parse_tag!(parser, "tileset", @@ -204,7 +210,7 @@ impl Image { required: [("source", source, String, |v| Some(v)), ("width", width, int, |v:String| from_str(v[])), ("height", height, int, |v:String| from_str(v[]))], - MissingAttributes("image must have a source, width and height with correct types".to_string())); + 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}) @@ -227,7 +233,7 @@ impl Layer { optionals: [("opacity", opacity, f32, |v:String| from_str(v[])), ("visible", visible, bool, |v:String| from_str(v[]).map(|x:int| x == 1))], required: [("name", name, String, |v| Some(v))], - MissingAttributes("layer must have a name".to_string())); + MalformedAttributes("layer must have a name".to_string())); let mut tiles = Vec::new(); let mut properties = HashMap::new(); parse_tag!(parser, "layer", @@ -244,13 +250,120 @@ impl Layer { } } +#[deriving(Show)] +pub struct ObjectGroup { + pub name: String, + pub objects: Vec<Object> +} + +impl ObjectGroup { + pub fn new<B: Buffer>(parser: &mut EventReader<B>, attrs: Vec<Attribute>) -> Result<ObjectGroup, TiledError> { + let ((), n) = get_attrs!( + attrs, + optionals: [], + required: [("name", name, String, |v| Some(v))], + MalformedAttributes("object groups must have a name".to_string())); + let mut objects = Vec::new(); + parse_tag!(parser, "objectgroup", + "object" => |attrs| { + objects.push(try!(Object::new(parser, attrs))); + Ok(()) + }); + Ok(ObjectGroup {name: n, objects: objects}) + } +} + +#[deriving(Show)] +pub enum Object { + Rect {x: int, y: int, width: uint, height: uint}, + Ellipse {x: int, y: int, width: uint, height: uint}, + Polyline {x: int, y: int, points: Vec<(int, int)>}, + Polygon {x: int, y: int, points: Vec<(int, int)>} +} + +impl Object { + pub fn new<B: Buffer>(parser: &mut EventReader<B>, attrs: Vec<Attribute>) -> Result<Object, TiledError> { + let ((w, h), (x, y)) = get_attrs!( + attrs, + optionals: [("width", width, uint, |v:String| from_str(v[])), + ("height", height, uint, |v:String| from_str(v[]))], + required: [("x", x, int, |v:String| from_str(v[])), + ("y", y, int, |v:String| from_str(v[]))], + MalformedAttributes("objects must have an x and a y number".to_string())); + let mut obj = None; + parse_tag!(parser, "object", + "ellipse" => |_| { + if w.is_none() || h.is_none() { + return Err(MalformedAttributes("An ellipse must have a width and height".to_string())); + } + let (w, h) = (w.unwrap(), h.unwrap()); + obj = Some(Ellipse {x: x, y: y, width: w as uint, height: h as uint}); + Ok(()) + }, + "polyline" => |attrs| { + obj = Some(try!(Object::new_polyline(x, y, attrs))); + Ok(()) + }, + "polygon" => |attrs| { + obj = Some(try!(Object::new_polygon(x, y, attrs))); + Ok(()) + }); + if obj.is_some() { + Ok(obj.unwrap()) + } else if w.is_some() && h.is_some() { + let w = w.unwrap(); + let h = h.unwrap(); + Ok(Rect {x: x, y: y, width: w as uint, height: h as uint}) + } else { + Err(MalformedAttributes("A rect must have a width and a height".to_string())) + } + } + + fn new_polyline(x: int, y: int, attrs: Vec<Attribute>) -> Result<Object, TiledError> { + let ((), s) = get_attrs!( + attrs, + optionals: [], + required: [("points", points, String, |v| Some(v))], + MalformedAttributes("A polyline must have points".to_string())); + let points = try!(Object::parse_points(s)); + Ok(Polyline {x: x, y: y, points: points}) + } + + fn new_polygon(x: int, y: int, attrs: Vec<Attribute>) -> Result<Object, TiledError> { + let ((), s) = get_attrs!( + attrs, + optionals: [], + required: [("points", points, String, |v| Some(v))], + MalformedAttributes("A polygon must have points".to_string())); + let points = try!(Object::parse_points(s)); + Ok(Polygon {x: x, y: y, points: points}) + } + + fn parse_points(s: String) -> Result<Vec<(int, int)>, TiledError> { + let pairs = s[].split(' '); + let mut points = Vec::new(); + for v in pairs.map(|p| p.splitn(1, ',')) { + let v: Vec<&str> = v.clone().collect(); + if v.len() != 2 { + return Err(MalformedAttributes("one of a polyline's points does not have an x and y coordinate".to_string())); + } + let (x, y) = (from_str(v[0]), from_str(v[1])); + if x.is_none() || y.is_none() { + return Err(MalformedAttributes("one of polyline's points does not have integer coordinates".to_string())); + } + points.push((x.unwrap(), y.unwrap())); + } + Ok(points) + } +} + fn parse_data<B: Buffer>(parser: &mut EventReader<B>, attrs: Vec<Attribute>, width: uint) -> Result<Vec<Vec<u32>>, TiledError> { let ((), (e, c)) = get_attrs!( attrs, optionals: [], required: [("encoding", encoding, String, |v| Some(v)), ("compression", compression, String, |v| Some(v))], - MissingAttributes("data must have an encoding and a compression".to_string())); + MalformedAttributes("data must have an encoding and a compression".to_string())); if !(e[] == "base64" && c[] == "zlib") { return Err(Other("Only base64 and zlib allowed for the moment".to_string())); }