Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make path a requirement for parsing tilesets #160

Merged
merged 6 commits into from
Feb 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 0 additions & 10 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,6 @@ pub enum TiledError {
Base64DecodingError(base64::DecodeError),
XmlDecodingError(xml::reader::Error),
PrematureEnd(String),
/// Tried to parse external data of an object without a file location,
/// e.g. by using Map::parse_reader.
SourceRequired {
object_to_parse: String,
},
/// The path given is invalid because it isn't contained in any folder.
PathIsNotFile,
CouldNotOpenFile {
Expand Down Expand Up @@ -49,11 +44,6 @@ impl fmt::Display for TiledError {
TiledError::Base64DecodingError(e) => write!(fmt, "{}", e),
TiledError::XmlDecodingError(e) => write!(fmt, "{}", e),
TiledError::PrematureEnd(e) => write!(fmt, "{}", e),
TiledError::SourceRequired {
ref object_to_parse,
} => {
write!(fmt, "Tried to parse external {} without a file location, e.g. by using Map::parse_reader.", object_to_parse)
}
TiledError::PathIsNotFile => {
write!(
fmt,
Expand Down
2 changes: 1 addition & 1 deletion src/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ impl Map {
match res.result_type {
EmbeddedParseResultType::ExternalReference { tileset_path } => {
let file = File::open(&tileset_path).map_err(|err| TiledError::CouldNotOpenFile{path: tileset_path.clone(), err })?;
let tileset = cache.get_or_try_insert_tileset_with(tileset_path.clone(), || Tileset::new_external(file, Some(&tileset_path)))?;
let tileset = cache.get_or_try_insert_tileset_with(tileset_path.clone(), || Tileset::parse_reader(file, &tileset_path))?;
tilesets.push(MapTilesetGid{first_gid: res.first_gid, tileset});
}
EmbeddedParseResultType::Embedded { tileset } => {
Expand Down
4 changes: 2 additions & 2 deletions src/tile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ impl Tile {
pub(crate) fn new(
parser: &mut impl Iterator<Item = XmlEventResult>,
attrs: Vec<OwnedAttribute>,
path_relative_to: Option<&Path>,
path_relative_to: &Path,
) -> Result<(TileId, Tile), TiledError> {
let ((tile_type, probability), id) = get_attrs!(
attrs,
Expand All @@ -47,7 +47,7 @@ impl Tile {
let mut animation = None;
parse_tag!(parser, "tile", {
"image" => |attrs| {
image = Some(Image::new(parser, attrs, path_relative_to.ok_or(TiledError::SourceRequired{object_to_parse:"Image".to_owned()})?)?);
image = Some(Image::new(parser, attrs, path_relative_to)?);
Ok(())
},
"properties" => |_| {
Expand Down
91 changes: 46 additions & 45 deletions src/tileset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,6 @@ pub struct Tileset {

/// The custom properties of the tileset.
pub properties: Properties,

/// Where this tileset was loaded from.
/// If fully embedded, this will return `None`.
pub source: Option<PathBuf>,
bjorn marked this conversation as resolved.
Show resolved Hide resolved
}

pub(crate) enum EmbeddedParseResultType {
Expand All @@ -62,30 +58,29 @@ struct TilesetProperties {
name: String,
tile_width: u32,
tile_height: u32,
/// The path all non-absolute paths are relative to.
path_relative_to: Option<PathBuf>,
source: Option<PathBuf>,
}

impl Tileset {
/// Parse a buffer hopefully containing the contents of a Tiled tileset.
pub fn parse<R: Read>(reader: R) -> Result<Self, TiledError> {
Tileset::new_external(reader, None)
}

/// Parse a buffer hopefully containing the contents of a Tiled tileset.
pub fn parse_with_path<R: Read>(reader: R, path: impl AsRef<Path>) -> Result<Self, TiledError> {
Tileset::new_external(reader, Some(path.as_ref()))
}

pub fn get_tile(&self, id: u32) -> Option<&Tile> {
self.tiles.get(&id)
}
/// The root all non-absolute paths contained within the tileset are relative to.
root_path: PathBuf,
}

impl Tileset {
pub(crate) fn new_external<R: Read>(file: R, path: Option<&Path>) -> Result<Self, TiledError> {
let mut tileset_parser = EventReader::new(file);
/// Parses a tileset out of a reader hopefully containing the contents of a Tiled tileset.
/// Uses the `path` parameter as the root for any relative paths found in the tileset.
///
/// ## Example
/// ```
/// use std::fs::File;
/// use std::path::PathBuf;
/// use std::io::BufReader;
/// use tiled::Tileset;
///
/// let path = "assets/tilesheet.tsx";
/// let reader = BufReader::new(File::open(path).unwrap());
/// let tileset = Tileset::parse_reader(reader, path).unwrap();
///
/// assert_eq!(tileset.image.unwrap().source, PathBuf::from("assets/tilesheet.png"));
/// ```
pub fn parse_reader<R: Read>(reader: R, path: impl AsRef<Path>) -> Result<Self, TiledError> {
let mut tileset_parser = EventReader::new(reader);
loop {
match tileset_parser
.next()
Expand All @@ -97,7 +92,7 @@ impl Tileset {
return Self::parse_external_tileset(
&mut tileset_parser.into_iter(),
&attributes,
path,
path.as_ref(),
);
}
XmlEvent::EndDocument => {
Expand All @@ -110,26 +105,31 @@ impl Tileset {
}
}

/// Gets the tile with the specified ID from the tileset.
pub fn get_tile(&self, id: u32) -> Option<&Tile> {
self.tiles.get(&id)
}
}

impl Tileset {
pub(crate) fn parse_xml_in_map(
parser: &mut impl Iterator<Item = XmlEventResult>,
attrs: Vec<OwnedAttribute>,
map_path: &Path,
) -> Result<EmbeddedParseResult, TiledError> {
let path_relative_to = map_path.parent();
Tileset::parse_xml_embedded(parser, &attrs, path_relative_to).or_else(|err| {
Tileset::parse_xml_embedded(parser, &attrs, map_path).or_else(|err| {
if matches!(err, TiledError::MalformedAttributes(_)) {
Tileset::parse_xml_reference(&attrs, path_relative_to)
Tileset::parse_xml_reference(&attrs, map_path)
} else {
Err(err)
}
})
}

/// Returns both the tileset and its first gid in the corresponding map.
fn parse_xml_embedded(
parser: &mut impl Iterator<Item = XmlEventResult>,
attrs: &Vec<OwnedAttribute>,
path_relative_to: Option<&Path>,
map_path: &Path,
) -> Result<EmbeddedParseResult, TiledError> {
let ((spacing, margin, columns, name), (tilecount, first_gid, tile_width, tile_height)) = get_attrs!(
attrs,
Expand All @@ -148,18 +148,22 @@ impl Tileset {
TiledError::MalformedAttributes("tileset must have a firstgid, name tile width and height with correct types".to_string())
);

let root_path = map_path
.parent()
.ok_or(TiledError::PathIsNotFile)?
.to_owned();

Self::finish_parsing_xml(
parser,
TilesetProperties {
spacing,
margin,
name: name.unwrap_or_default(),
path_relative_to: path_relative_to.map(Path::to_owned),
root_path,
columns,
tilecount,
tile_height,
tile_width,
source: None,
},
)
.map(|tileset| EmbeddedParseResult {
Expand All @@ -170,7 +174,7 @@ impl Tileset {

fn parse_xml_reference(
attrs: &Vec<OwnedAttribute>,
path_relative_to: Option<&Path>,
map_path: &Path,
) -> Result<EmbeddedParseResult, TiledError> {
let ((), (first_gid, source)) = get_attrs!(
attrs,
Expand All @@ -182,10 +186,9 @@ impl Tileset {
TiledError::MalformedAttributes("Tileset reference must have a firstgid and source with correct types".to_string())
);

let tileset_path = path_relative_to
.ok_or(TiledError::SourceRequired {
object_to_parse: "Tileset".to_string(),
})?
let tileset_path = map_path
.parent()
.ok_or(TiledError::PathIsNotFile)?
.join(source);

Ok(EmbeddedParseResult {
Expand All @@ -197,7 +200,7 @@ impl Tileset {
fn parse_external_tileset(
parser: &mut impl Iterator<Item = XmlEventResult>,
attrs: &Vec<OwnedAttribute>,
path: Option<&Path>,
path: &Path,
) -> Result<Tileset, TiledError> {
let ((spacing, margin, columns, name), (tilecount, tile_width, tile_height)) = get_attrs!(
attrs,
Expand All @@ -215,20 +218,19 @@ impl Tileset {
TiledError::MalformedAttributes("tileset must have a name, tile width and height with correct types".to_string())
);

let source_path = path.and_then(|p| p.parent().map(Path::to_owned));
let root_path = path.parent().ok_or(TiledError::PathIsNotFile)?.to_owned();
aleokdev marked this conversation as resolved.
Show resolved Hide resolved

Self::finish_parsing_xml(
parser,
TilesetProperties {
spacing,
margin,
name: name.unwrap_or_default(),
path_relative_to: source_path,
root_path,
columns,
tilecount,
tile_height,
tile_width,
source: path.map(Path::to_owned),
},
)
}
Expand All @@ -243,15 +245,15 @@ impl Tileset {

parse_tag!(parser, "tileset", {
"image" => |attrs| {
image = Some(Image::new(parser, attrs, prop.path_relative_to.as_ref().ok_or(TiledError::SourceRequired{object_to_parse: "Image".to_string()})?)?);
image = Some(Image::new(parser, attrs, &prop.root_path)?);
Ok(())
},
"properties" => |_| {
properties = parse_properties(parser)?;
Ok(())
},
"tile" => |attrs| {
let (id, tile) = Tile::new(parser, attrs, prop.path_relative_to.as_ref().and_then(|p| Some(p.as_path())))?;
let (id, tile) = Tile::new(parser, attrs, &prop.root_path)?;
tiles.insert(id, tile);
Ok(())
},
Expand Down Expand Up @@ -284,7 +286,6 @@ impl Tileset {
image,
tiles,
properties,
source: prop.source,
})
}

Expand Down