Newer
Older
use std::{
collections::HashMap,
fmt,
fs::File,
io::Read,
path::{Path, PathBuf},
str::FromStr,
};
use xml::{attribute::OwnedAttribute, reader::XmlEvent, EventReader};
use crate::{
error::{ParseTileError, TiledError},
layers::{ImageLayer, Layer},
objects::ObjectGroup,
/// All Tiled map files will be parsed into this. Holds all the layers and tilesets.
pub layers: Vec<Layer>,
pub image_layers: Vec<ImageLayer>,
pub object_groups: Vec<ObjectGroup>,
/// The background color of this map, if any.
pub background_color: Option<Color>,
/// Where this map was loaded from.
/// If fully embedded (loaded with path = `None`), this will return `None`.
pub source: Option<PathBuf>,
/// 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,
/// in which case this function may be required.
/// The path may be skipped if the map is fully embedded (Doesn't refer to external files).
pub fn parse_reader<R: Read>(reader: R, path: Option<&Path>) -> Result<Self, 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 Self::parse_xml(&mut parser, attributes, path);
}
}
XmlEvent::EndDocument => {
return Err(TiledError::PrematureEnd(
"Document ended before map was parsed".to_string(),
))
}
_ => {}
}
}
}
/// Parse a file hopefully containing a Tiled map and try to parse it. If the
/// file has an external tileset, the tileset file will be loaded using a path
/// relative to the map file's path.
pub fn parse_file(path: impl AsRef<Path>) -> Result<Self, TiledError> {
let file = File::open(path.as_ref())
.map_err(|_| TiledError::Other(format!("Map file not found: {:?}", path.as_ref())))?;
Self::parse_reader(file, Some(path.as_ref()))
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
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| {
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
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,
})
}
/// This function will return the correct Tileset given a GID.
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
}
}
/// Represents the way tiles are laid out in a map.
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
#[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"),
}
}
}