Skip to content
Snippets Groups Projects
Commit 2ec9c563 authored by Matthew Hall's avatar Matthew Hall Committed by GitHub
Browse files

Merge pull request #24 from guodman/master

Load Tilesets From External Files
parents e002da4a 9c2c3ae9
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;
use std::str::FromStr;
use std::collections::HashMap;
use std::fs::File;
use std::io::{BufReader, Read, Error};
use std::path::Path;
use std::fmt;
use xml::reader::{EventReader, Error as XmlError};
use xml::reader::XmlEvent;
......@@ -227,7 +229,7 @@ pub struct 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!(
attrs,
optionals: [("backgroundcolor", colour, |v:String| v.parse().ok())],
......@@ -245,7 +247,7 @@ impl Map {
let mut object_groups = Vec::new();
parse_tag!(parser, "map",
"tileset" => | attrs| {
tilesets.push(try!(Tileset::new(parser, attrs)));
tilesets.push(try!(Tileset::new(parser, attrs, map_path)));
Ok(())
},
"layer" => |attrs| {
......@@ -321,8 +323,13 @@ pub struct Tileset {
}
impl Tileset {
fn new<R: Read>(parser: &mut EventReader<R>, attrs: Vec<OwnedAttribute>) -> Result<Tileset, TiledError> {
let ((s, m), (g, n, w, h)) = get_attrs!(
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_reference(&attrs, map_path) })
}
fn new_internal<R: Read>(parser: &mut EventReader<R>, attrs: &Vec<OwnedAttribute>) -> Result<Tileset, TiledError> {
let ((spacing, margin), (first_gid, name, width, height)) = get_attrs!(
attrs,
optionals: [("spacing", spacing, |v:String| v.parse().ok()),
("margin", margin, |v:String| v.parse().ok())],
......@@ -344,14 +351,73 @@ impl Tileset {
Ok(())
});
Ok(Tileset {first_gid: g,
name: n,
tile_width: w, tile_height: h,
spacing: s.unwrap_or(0),
margin: m.unwrap_or(0),
Ok(Tileset {first_gid: first_gid,
name: name,
tile_width: width, tile_height: height,
spacing: spacing.unwrap_or(0),
margin: margin.unwrap_or(0),
images: images,
tiles: tiles})
}
}
fn new_reference(attrs: &Vec<OwnedAttribute>, map_path: Option<&Path>) -> Result<Tileset, TiledError> {
let ((), (first_gid, 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)))?;
Tileset::new_external(file, first_gid)
}
fn new_external<R: Read>(file: R, first_gid: u32) -> Result<Tileset, TiledError> {
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(first_gid, &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>(first_gid: u32, parser: &mut EventReader<R>, attrs: &Vec<OwnedAttribute>) -> Result<Tileset, TiledError> {
let ((spacing, margin), (name, width, height)) = 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: first_gid,
name: name,
tile_width: width, tile_height: height,
spacing: spacing.unwrap_or(0),
margin: margin.unwrap_or(0),
images: images,
tiles: tiles})
}
}
#[derive(Debug, PartialEq, Eq)]
......@@ -710,15 +776,13 @@ fn convert_to_u32(all: &Vec<u8>, width: u32) -> Vec<Vec<u32>> {
data
}
/// 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> {
fn parse_impl<R: Read>(reader: R, map_path: Option<&Path>) -> Result<Map, TiledError> {
let mut parser = EventReader::new(reader);
loop {
match try!(parser.next().map_err(TiledError::XmlDecodingError)) {
XmlEvent::StartElement {name, attributes, ..} => {
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())),
......@@ -726,3 +790,26 @@ 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)
}
/// Parse a buffer hopefully containing the contents of a Tiled tileset.
///
/// External tilesets do not have a firstgid attribute. That lives in the
/// map. You must pass in `first_gid`. If you do not need to use gids for anything,
/// passing in 1 will work fine.
pub fn parse_tileset<R: Read>(reader: R, first_gid: u32) -> Result<Tileset, TiledError> {
Tileset::new_external(reader, first_gid)
}
......@@ -2,13 +2,17 @@ extern crate tiled;
use std::path::Path;
use std::fs::File;
use tiled::{Map, TiledError, parse};
use tiled::{Map, TiledError, parse, parse_file, parse_tileset};
fn read_from_file(p: &Path) -> Result<Map, TiledError> {
let file = File::open(p).unwrap();
return parse(file);
}
fn read_from_file_with_path(p: &Path) -> Result<Map, TiledError> {
return parse_file(p);
}
#[test]
fn test_gzip_and_zlib_encoded_and_raw_are_the_same() {
let z = read_from_file(&Path::new("assets/tiled_base64_zlib.tmx")).unwrap();
......@@ -19,3 +23,17 @@ fn test_gzip_and_zlib_encoded_and_raw_are_the_same() {
assert_eq!(z, r);
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);
}
#[test]
fn test_just_tileset() {
let r = read_from_file(&Path::new("assets/tiled_base64.tmx")).unwrap();
let t = parse_tileset(File::open(Path::new("assets/tilesheet.tsx")).unwrap(), 1).unwrap();
assert_eq!(r.tilesets[0], t);
}
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