Skip to content
Snippets Groups Projects
Commit 374e7c7b authored by Doug Reeves's avatar Doug Reeves
Browse files

When the tileset is in an external file, parse that file when given the path of the map file

parent 3b8a5479
No related branches found
No related tags found
No related merge requests found
This diff is collapsed.
<?xml version="1.0" encoding="UTF-8"?>
<tileset name="tilesheet" tilewidth="32" tileheight="32" tilecount="84">
<image source="tilesheet.png" width="448" height="192"/>
<tile id="1">
<properties>
<property name="a tile property" value="123"/>
</properties>
</tile>
</tileset>
...@@ -4,7 +4,9 @@ extern crate base64; ...@@ -4,7 +4,9 @@ extern crate base64;
use std::str::FromStr; use std::str::FromStr;
use std::collections::HashMap; use std::collections::HashMap;
use std::fs::File;
use std::io::{BufReader, Read, Error}; use std::io::{BufReader, Read, Error};
use std::path::Path;
use std::fmt; use std::fmt;
use xml::reader::{EventReader, Error as XmlError}; use xml::reader::{EventReader, Error as XmlError};
use xml::reader::XmlEvent; use xml::reader::XmlEvent;
...@@ -227,7 +229,7 @@ pub struct Map { ...@@ -227,7 +229,7 @@ pub struct Map {
} }
impl Map { impl Map {
fn new<R: Read>(parser: &mut EventReader<R>, attrs: Vec<OwnedAttribute>) -> Result<Map, TiledError> { fn new<R: Read>(parser: &mut EventReader<R>, attrs: Vec<OwnedAttribute>, map_path: Option<&Path>) -> Result<Map, TiledError> {
let (c, (v, o, w, h, tw, th)) = get_attrs!( let (c, (v, o, w, h, tw, th)) = get_attrs!(
attrs, attrs,
optionals: [("backgroundcolor", colour, |v:String| v.parse().ok())], optionals: [("backgroundcolor", colour, |v:String| v.parse().ok())],
...@@ -245,7 +247,7 @@ impl Map { ...@@ -245,7 +247,7 @@ impl Map {
let mut object_groups = Vec::new(); let mut object_groups = Vec::new();
parse_tag!(parser, "map", parse_tag!(parser, "map",
"tileset" => | attrs| { "tileset" => | attrs| {
tilesets.push(try!(Tileset::new(parser, attrs))); tilesets.push(try!(Tileset::new(parser, attrs, map_path)));
Ok(()) Ok(())
}, },
"layer" => |attrs| { "layer" => |attrs| {
...@@ -321,7 +323,12 @@ pub struct Tileset { ...@@ -321,7 +323,12 @@ pub struct Tileset {
} }
impl Tileset { impl Tileset {
fn new<R: Read>(parser: &mut EventReader<R>, attrs: Vec<OwnedAttribute>) -> Result<Tileset, TiledError> { 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_external(&attrs, map_path) })
}
fn new_internal<R: Read>(parser: &mut EventReader<R>, attrs: &Vec<OwnedAttribute>) -> Result<Tileset, TiledError> {
let ((s, m), (g, n, w, h)) = get_attrs!( let ((s, m), (g, n, w, h)) = get_attrs!(
attrs, attrs,
optionals: [("spacing", spacing, |v:String| v.parse().ok()), optionals: [("spacing", spacing, |v:String| v.parse().ok()),
...@@ -351,7 +358,62 @@ impl Tileset { ...@@ -351,7 +358,62 @@ impl Tileset {
margin: m.unwrap_or(0), margin: m.unwrap_or(0),
images: images, images: images,
tiles: tiles}) tiles: tiles})
} }
fn new_external(attrs: &Vec<OwnedAttribute>, map_path: Option<&Path>) -> Result<Tileset, TiledError> {
let ((), (g, 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()));
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)))?;
let mut tileset_parser = EventReader::new(file);
loop {
match try!(tileset_parser.next().map_err(TiledError::XmlDecodingError)) {
XmlEvent::StartElement {name, attributes, ..} => {
if name.local_name == "tileset" {
return Tileset::parse_external_tileset(g, &mut tileset_parser, &attributes)
}
}
XmlEvent::EndDocument => return Err(TiledError::PrematureEnd("Tileset Document ended before map was parsed".to_string())),
_ => {}
}
}
}
fn parse_external_tileset<R: Read>(g: u32, parser: &mut EventReader<R>, attrs: &Vec<OwnedAttribute>) -> Result<Tileset, TiledError> {
let ((s, m), (n, w, h)) = get_attrs!(
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()));
let mut images = Vec::new();
let mut tiles = Vec::new();
parse_tag!(parser, "tileset",
"image" => |attrs| {
images.push(try!(Image::new(parser, attrs)));
Ok(())
},
"tile" => |attrs| {
tiles.push(try!(Tile::new(parser, attrs)));
Ok(())
});
Ok(Tileset {first_gid: g,
name: n,
tile_width: w, tile_height: h,
spacing: s.unwrap_or(0),
margin: m.unwrap_or(0),
images: images,
tiles: tiles})
}
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
...@@ -710,15 +772,13 @@ fn convert_to_u32(all: &Vec<u8>, width: u32) -> Vec<Vec<u32>> { ...@@ -710,15 +772,13 @@ fn convert_to_u32(all: &Vec<u8>, width: u32) -> Vec<Vec<u32>> {
data data
} }
/// Parse a buffer hopefully containing the contents of a Tiled file and try to fn parse_impl<R: Read>(reader: R, map_path: Option<&Path>) -> Result<Map, TiledError> {
/// parse it.
pub fn parse<R: Read>(reader: R) -> Result<Map, TiledError> {
let mut parser = EventReader::new(reader); let mut parser = EventReader::new(reader);
loop { loop {
match try!(parser.next().map_err(TiledError::XmlDecodingError)) { match try!(parser.next().map_err(TiledError::XmlDecodingError)) {
XmlEvent::StartElement {name, attributes, ..} => { XmlEvent::StartElement {name, attributes, ..} => {
if name.local_name == "map" { if name.local_name == "map" {
return Map::new(&mut parser, attributes); return Map::new(&mut parser, attributes, map_path);
} }
} }
XmlEvent::EndDocument => return Err(TiledError::PrematureEnd("Document ended before map was parsed".to_string())), XmlEvent::EndDocument => return Err(TiledError::PrematureEnd("Document ended before map was parsed".to_string())),
...@@ -726,3 +786,17 @@ pub fn parse<R: Read>(reader: R) -> Result<Map, TiledError> { ...@@ -726,3 +786,17 @@ pub fn parse<R: Read>(reader: R) -> Result<Map, TiledError> {
} }
} }
} }
/// 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: &Path) -> Result<Map, TiledError> {
let file = File::open(path).map_err(|_| TiledError::Other(format!("Map file not found: {:?}", path)))?;
parse_impl(file, Some(path))
}
/// Parse a buffer hopefully containing the contents of a Tiled file and try to
/// parse it.
pub fn parse<R: Read>(reader: R) -> Result<Map, TiledError> {
parse_impl(reader, None)
}
...@@ -2,13 +2,17 @@ extern crate tiled; ...@@ -2,13 +2,17 @@ extern crate tiled;
use std::path::Path; use std::path::Path;
use std::fs::File; use std::fs::File;
use tiled::{Map, TiledError, parse}; use tiled::{Map, TiledError, parse, parse_file};
fn read_from_file(p: &Path) -> Result<Map, TiledError> { fn read_from_file(p: &Path) -> Result<Map, TiledError> {
let file = File::open(p).unwrap(); let file = File::open(p).unwrap();
return parse(file); return parse(file);
} }
fn read_from_file_with_path(p: &Path) -> Result<Map, TiledError> {
return parse_file(p);
}
#[test] #[test]
fn test_gzip_and_zlib_encoded_and_raw_are_the_same() { fn test_gzip_and_zlib_encoded_and_raw_are_the_same() {
let z = read_from_file(&Path::new("assets/tiled_base64_zlib.tmx")).unwrap(); let z = read_from_file(&Path::new("assets/tiled_base64_zlib.tmx")).unwrap();
...@@ -19,3 +23,10 @@ fn test_gzip_and_zlib_encoded_and_raw_are_the_same() { ...@@ -19,3 +23,10 @@ fn test_gzip_and_zlib_encoded_and_raw_are_the_same() {
assert_eq!(z, r); assert_eq!(z, r);
assert_eq!(z, c); assert_eq!(z, c);
} }
#[test]
fn test_external_tileset() {
let r = read_from_file(&Path::new("assets/tiled_base64.tmx")).unwrap();
let e = read_from_file_with_path(&Path::new("assets/tiled_base64_external.tmx")).unwrap();
assert_eq!(r, e);
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment