diff --git a/src/animation.rs b/src/animation.rs index 1ffe8802ac24a87e8a05f30c2bb30c4e587bf796..bf0d5364d7ba4126829672e3697380526b3ce87c 100644 --- a/src/animation.rs +++ b/src/animation.rs @@ -1,6 +1,6 @@ use xml::attribute::OwnedAttribute; -use crate::{error::TiledError, util::get_attrs}; +use crate::{error::TiledError, util::{get_attrs, XmlEventResult, parse_tag}}; #[derive(Debug, PartialEq, Clone)] pub struct Frame { @@ -25,3 +25,17 @@ impl Frame { }) } } + + +pub(crate) fn parse_animation( + parser: &mut impl Iterator<Item = XmlEventResult>, +) -> Result<Vec<Frame>, TiledError> { + let mut animation = Vec::new(); + parse_tag!(parser, "animation", { + "frame" => |attrs| { + animation.push(Frame::new(attrs)?); + Ok(()) + }, + }); + Ok(animation) +} \ No newline at end of file diff --git a/src/layers/tile/util.rs b/src/layers/tile/util.rs index 8937a318919bdffb72b2605c84dd9b61525b93d1..fde41f23412a8d5979e9087e8b6ff3e825b3bfc3 100644 --- a/src/layers/tile/util.rs +++ b/src/layers/tile/util.rs @@ -1,4 +1,4 @@ -use std::io::{BufReader, Read}; +use std::{convert::TryInto, io::Read}; use xml::reader::XmlEvent; @@ -11,47 +11,25 @@ pub(crate) fn parse_data_line( tilesets: &[MapTilesetGid], ) -> Result<Vec<Option<LayerTileData>>, TiledError> { match (encoding.as_deref(), compression.as_deref()) { - (Some("base64"), None) => { - return parse_base64(parser).map(|v| convert_to_tiles(&v, tilesets)) - } - (Some("csv"), None) => return decode_csv(parser, tilesets), - (Some(_), None) => { - return Err(TiledError::InvalidEncodingFormat { - encoding, - compression, - }) - } - (Some(e), Some(c)) => match (e, c) { - ("base64", "zlib") => { - return parse_base64(parser) - .and_then(decode_zlib) - .map(|v| convert_to_tiles(&v, tilesets)) - } - ("base64", "gzip") => { - return parse_base64(parser) - .and_then(decode_gzip) - .map(|v| convert_to_tiles(&v, tilesets)) - } - #[cfg(feature = "zstd")] - ("base64", "zstd") => { - return parse_base64(parser) - .and_then(decode_zstd) - .map(|v| convert_to_tiles(&v, tilesets)) - } - _ => { - return Err(TiledError::InvalidEncodingFormat { - encoding, - compression, - }) - } - }, - _ => { - return Err(TiledError::InvalidEncodingFormat { - encoding, - compression, - }) - } - }; + (Some("csv"), None) => decode_csv(parser, tilesets), + + (Some("base64"), None) => parse_base64(parser).map(|v| convert_to_tiles(&v, tilesets)), + (Some("base64"), Some("zlib")) => parse_base64(parser) + .and_then(|data| process_decoder(libflate::zlib::Decoder::new(&data[..]))) + .map(|v| convert_to_tiles(&v, tilesets)), + (Some("base64"), Some("gzip")) => parse_base64(parser) + .and_then(|data| process_decoder(libflate::gzip::Decoder::new(&data[..]))) + .map(|v| convert_to_tiles(&v, tilesets)), + #[cfg(feature = "zstd")] + (Some("base64"), Some("zstd")) => parse_base64(parser) + .and_then(|data| process_decoder(zstd::stream::read::Decoder::with_buffer(&data[..]))) + .map(|v| convert_to_tiles(&v, tilesets)), + + _ => Err(TiledError::InvalidEncodingFormat { + encoding, + compression, + }), + } } fn parse_base64(parser: &mut impl Iterator<Item = XmlEventResult>) -> Result<Vec<u8>, TiledError> { @@ -60,10 +38,8 @@ fn parse_base64(parser: &mut impl Iterator<Item = XmlEventResult>) -> Result<Vec 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()); - } + XmlEvent::EndElement { name, .. } if name.local_name == "data" => { + return Ok(Vec::new()); } _ => {} } @@ -71,40 +47,14 @@ fn parse_base64(parser: &mut impl Iterator<Item = XmlEventResult>) -> Result<Vec Err(TiledError::PrematureEnd("Ran out of XML data".to_owned())) } -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) -} - -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 process_decoder(decoder: std::io::Result<impl Read>) -> Result<Vec<u8>, TiledError> { + decoder + .and_then(|mut decoder| { + let mut data = Vec::new(); + decoder.read_to_end(&mut data)?; + Ok(data) + }) + .map_err(|e| TiledError::DecompressingError(e)) } fn decode_csv( @@ -121,10 +71,8 @@ fn decode_csv( .collect(); return Ok(tiles); } - XmlEvent::EndElement { name, .. } => { - if name.local_name == "data" { - return Ok(Vec::new()); - } + XmlEvent::EndElement { name, .. } if name.local_name == "data" => { + return Ok(Vec::new()); } _ => {} } @@ -132,14 +80,11 @@ fn decode_csv( Err(TiledError::PrematureEnd("Ran out of XML data".to_owned())) } -fn convert_to_tiles(all: &Vec<u8>, tilesets: &[MapTilesetGid]) -> Vec<Option<LayerTileData>> { - let mut data = Vec::new(); - for chunk in all.chunks_exact(4) { - let n = chunk[0] as u32 - + ((chunk[1] as u32) << 8) - + ((chunk[2] as u32) << 16) - + ((chunk[3] as u32) << 24); - data.push(LayerTileData::from_bits(n, tilesets)); - } - data +fn convert_to_tiles(data: &[u8], tilesets: &[MapTilesetGid]) -> Vec<Option<LayerTileData>> { + data.chunks_exact(4) + .map(|chunk| { + let bits = u32::from_le_bytes(chunk.try_into().unwrap()); + LayerTileData::from_bits(bits, tilesets) + }) + .collect() } diff --git a/src/objects.rs b/src/objects.rs index 5c4e1fb92366be7163a2069a6240850f3d678ebf..07d6c34621d76e46e79949cd5d8dd9407f9915e7 100644 --- a/src/objects.rs +++ b/src/objects.rs @@ -42,11 +42,12 @@ impl ObjectData { attrs: Vec<OwnedAttribute>, tilesets: Option<&[MapTilesetGid]>, ) -> Result<ObjectData, TiledError> { - let ((id, bits, n, t, w, h, v, r), (x, y)) = get_attrs!( + let ((id, tile, 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()), + ("gid", tile, |v:String| v.parse().ok() + .and_then(|bits| LayerTileData::from_bits(bits, tilesets?))), ("name", name, |v:String| v.parse().ok()), ("type", obj_type, |v:String| v.parse().ok()), ("width", width, |v:String| v.parse().ok()), @@ -60,22 +61,21 @@ impl ObjectData { ], 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 visible = v.unwrap_or(true); + let width = w.unwrap_or(0f32); + let height = h.unwrap_or(0f32); + let rotation = r.unwrap_or(0f32); let id = id.unwrap_or(0u32); - let tile = bits.and_then(|bits| LayerTileData::from_bits(bits, tilesets?)); - let n = n.unwrap_or(String::new()); - let t = t.unwrap_or(String::new()); + let name = n.unwrap_or_else(|| String::new()); + let obj_type = t.unwrap_or_else(|| String::new()); let mut shape = None; let mut properties = HashMap::new(); parse_tag!(parser, "object", { "ellipse" => |_| { shape = Some(ObjectShape::Ellipse { - width: w, - height: h, + width, + height, }); Ok(()) }, @@ -88,7 +88,7 @@ impl ObjectData { Ok(()) }, "point" => |_| { - shape = Some(ObjectData::new_point(x, y)?); + shape = Some(ObjectShape::Point(x, y)); Ok(()) }, "properties" => |_| { @@ -97,22 +97,19 @@ impl ObjectData { }, }); - let shape = shape.unwrap_or(ObjectShape::Rect { - width: w, - height: h, - }); + let shape = shape.unwrap_or(ObjectShape::Rect { width, height }); Ok(ObjectData { id, tile, - name: n.clone(), - obj_type: t.clone(), - width: w, - height: h, + name, + obj_type, + width, + height, x, y, - rotation: r, - visible: v, + rotation, + visible, shape, properties, }) @@ -130,7 +127,7 @@ impl ObjectData { TiledError::MalformedAttributes("A polyline must have points".to_string()) ); let points = ObjectData::parse_points(s)?; - Ok(ObjectShape::Polyline { points: points }) + Ok(ObjectShape::Polyline { points }) } fn new_polygon(attrs: Vec<OwnedAttribute>) -> Result<ObjectShape, TiledError> { @@ -146,29 +143,27 @@ impl ObjectData { 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) + pairs + .map(|point| point.split(',')) + .map(|components| { + let v: Vec<&str> = components.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()); + match (x, y) { + (Some(x), Some(y)) => Ok((x, y)), + _ => Err(TiledError::MalformedAttributes( + "one of polyline's points does not have i32eger coordinates".to_string(), + )), + } + }) + .collect() } } @@ -176,7 +171,7 @@ pub type Object<'map> = MapWrapper<'map, ObjectData>; impl<'map> Object<'map> { /// Returns the tile that the object is using as image, if any. - pub fn get_tile<'res: 'map>(&self) -> Option<LayerTile<'map>> { + pub fn get_tile(&self) -> Option<LayerTile<'map>> { self.data() .tile .map(|tile| LayerTile::from_data(&tile, self.map())) diff --git a/src/tile.rs b/src/tile.rs index 0493fb508c21eaae75f294ee7749f87fc4a31ddb..ffa1ef6b52454eeb765133ec105cee8c6eaf5478 100644 --- a/src/tile.rs +++ b/src/tile.rs @@ -3,12 +3,12 @@ use std::{collections::HashMap, path::Path}; use xml::attribute::OwnedAttribute; use crate::{ - animation::Frame, + animation::{Frame, parse_animation}, error::TiledError, image::Image, layers::ObjectLayerData, properties::{parse_properties, Properties}, - util::{get_attrs, parse_animation, parse_tag, XmlEventResult}, + util::{get_attrs, parse_tag, XmlEventResult}, }; pub type TileId = u32; diff --git a/src/tileset.rs b/src/tileset.rs index c660333a97c72a96703f3e88ddce79035e3247f8..46c13e9577c1cf56e5cef98912fbb5f3bbaac4e1 100644 --- a/src/tileset.rs +++ b/src/tileset.rs @@ -93,14 +93,12 @@ impl Tileset { { XmlEvent::StartElement { name, attributes, .. - } => { - if name.local_name == "tileset" { - return Self::parse_external_tileset( - &mut tileset_parser.into_iter(), - &attributes, - path, - ); - } + } if name.local_name == "tileset" => { + return Self::parse_external_tileset( + &mut tileset_parser.into_iter(), + &attributes, + path, + ); } XmlEvent::EndDocument => { return Err(TiledError::PrematureEnd( diff --git a/src/util.rs b/src/util.rs index 0eafe24679acfe2c7b8f421f52b3ab2c059dc274..e9d5127c327e0a62de37c8eab2bac3c8ac2b9537 100644 --- a/src/util.rs +++ b/src/util.rs @@ -55,21 +55,7 @@ macro_rules! parse_tag { pub(crate) use get_attrs; pub(crate) use parse_tag; -use crate::{animation::Frame, error::TiledError, Gid, MapTilesetGid}; - -// TODO: Move to animation module -pub(crate) fn parse_animation( - parser: &mut impl Iterator<Item = XmlEventResult>, -) -> Result<Vec<Frame>, TiledError> { - let mut animation = Vec::new(); - parse_tag!(parser, "animation", { - "frame" => |attrs| { - animation.push(Frame::new(attrs)?); - Ok(()) - }, - }); - Ok(animation) -} +use crate::{Gid, MapTilesetGid}; pub(crate) type XmlEventResult = xml::reader::Result<xml::reader::XmlEvent>;