1use std::hash::{Hash, Hasher};
2use std::io::{BufReader, Read};
3use std::path::Path;
4
5use anyhow::*;
6use glam::{vec2, IVec2, UVec2, Vec4};
7use xml::attribute::OwnedAttribute;
8use xml::reader::{EventReader, XmlEvent};
9
10use crate::loader::TmxLoadContext;
11use crate::map::Map;
12
13use super::*;
14
15enum Data {
16 U8(Vec<u8>),
17 U32(Vec<u32>),
18}
19
20impl Data {
21 fn into_vec_u8(self) -> Vec<u8> {
22 match self {
23 Data::U8(v) => v,
24 Data::U32(_) => unimplemented!("u8 to u32 conversion is not needed"),
25 }
26 }
27
28 fn into_vec_u32(self) -> Vec<u32> {
29 match self {
30 Data::U8(v) => v
31 .chunks_exact(4)
32 .map(|chunk| {
33 (chunk[0] as u32)
34 | (chunk[1] as u32) << 8
35 | (chunk[2] as u32) << 16
36 | (chunk[3] as u32) << 24
37 })
38 .collect(),
39 Data::U32(v) => v,
40 }
41 }
42}
43
44impl Map {
45 pub(crate) fn load_from_xml_reader<R: Read + Send>(
46 env: TmxLoadContext<'_>,
47 mut reader: EventReader<R>,
48 ) -> Result<Self> {
49 loop {
50 if let XmlEvent::StartElement {
51 name, attributes, ..
52 } = reader.next()?
53 {
54 if name.local_name == "map" {
55 return Map::parse(env, attributes, &mut reader);
56 } else {
57 parse_empty(&mut reader)?;
58 }
59 }
60 }
61 }
62
63 fn parse<R: Read + Send>(
64 env: TmxLoadContext<'_>,
65 attributes: Vec<OwnedAttribute>,
66 reader: &mut EventReader<R>,
67 ) -> Result<Self> {
68 let mut result = Map {
69 properties: HashMap::new(),
70 tilesets: Vec::new(),
71 layers: Vec::new(),
72
73 width: 0,
74 height: 0,
75 tile_type: TileType::Ortho {
76 width: 0,
77 height: 0,
78 render_order: RenderOrder::RightDown,
79 },
80
81 background: [0; 4],
82 };
83
84 let mut render_order = RenderOrder::RightDown;
85 let mut tile_type = 0;
86 let mut tile_width = 0;
87 let mut tile_height = 0;
88 let mut stagger_y = false;
89 let mut stagger_i = true;
90 let mut hex_side_length = 0;
91
92 for a in attributes {
93 match a.name.local_name.as_ref() {
94 "width" => result.width = a.value.parse()?,
95 "height" => result.height = a.value.parse()?,
96 "tilewidth" => tile_width = a.value.parse()?,
97 "tileheight" => tile_height = a.value.parse()?,
98 "renderorder" => {
99 render_order = match a.value.as_ref() {
100 "right-down" => RenderOrder::RightDown,
101 "right-up" => RenderOrder::RightUp,
102 "left-down" => RenderOrder::LeftDown,
103 "left-up" => RenderOrder::LeftUp,
104 _ => bail!("invalid renderorder"),
105 }
106 }
107 "orientation" => {
108 tile_type = match a.value.as_ref() {
109 "orthogonal" => 0,
110 "isometric" => 1,
111 "staggered" => 2,
112 "hexagonal" => 3,
113 _ => bail!("invalid orientation"),
114 }
115 }
116 "backgroundcolor" => {
117 result.background = [1; 4];
118 }
119 "staggeraxis" => {
120 stagger_y = match a.value.as_ref() {
121 "x" => false,
122 "y" => true,
123 _ => bail!("invalid staggeraxis"),
124 }
125 }
126 "staggerindex" => {
127 stagger_i = match a.value.as_ref() {
128 "odd" => true,
129 "even" => false,
130 _ => bail!("invalid staggerindex"),
131 }
132 }
133 "hexsidelength" => hex_side_length = a.value.parse()?,
134 _ => (), }
136 }
137
138 result.tile_type = match tile_type {
139 0 => TileType::Ortho {
140 width: tile_width,
141 height: tile_height,
142 render_order,
143 },
144 1 => TileType::Isometric {
145 width: tile_width,
146 height: tile_height,
147 stagger: false,
148 stagger_odd: stagger_i,
149 stagger_y,
150 render_order,
151 },
152 2 => TileType::Isometric {
153 width: tile_width,
154 height: tile_height,
155 stagger: true,
156 stagger_odd: stagger_i,
157 stagger_y,
158 render_order,
159 },
160 3 => TileType::Hexagonal {
161 width: tile_width,
162 height: tile_width,
163 stagger_odd: stagger_i,
164 stagger_y,
165 side_length: hex_side_length,
166 render_order,
167 },
168 _ => unreachable!(),
169 };
170
171 while match reader.next()? {
172 XmlEvent::StartElement {
173 name, attributes, ..
174 } => {
175 match name.local_name.as_ref() {
176 "properties" => {
177 result.properties = parse_properties(reader)?;
178 }
179 "tileset" => {
180 result.tilesets.push(Arc::new(Tileset::parse(
181 env.clone(),
182 attributes,
183 reader,
184 )?));
185 }
186 "layer" => {
187 result.layers.push(Layer::parse_tiles(attributes, reader)?);
188 }
189 "objectgroup" => {
190 result = Layer::parse_objects(env.clone(), attributes, reader)?
191 .process(result)?;
192 }
193 "imagelayer" => {
194 result
195 .layers
196 .push(Layer::parse_image(env.clone(), attributes, reader)?);
197 }
198 "group" => {
199 result
200 .layers
201 .push(Layer::parse_group(env.clone(), attributes, reader)?);
202 }
203 _ => parse_empty(reader)?, }
205
206 true
207 }
208 XmlEvent::EndElement { .. } => false,
209 _ => true,
210 } {
211 continue;
212 }
213
214 Ok(result)
215 }
216}
217
218impl Tileset {
219 fn parse<R: Read + Send>(
221 env: TmxLoadContext<'_>,
222 attributes: Vec<OwnedAttribute>,
223 reader: &mut EventReader<R>,
224 ) -> Result<Self> {
225 let mut result = Tileset {
226 first_gid: 0,
227 source: "embedded#".to_string(),
228 tiles: Vec::new(),
229 image: None,
230 tile_size: Vec2::ZERO,
231 };
232
233 let mut found_source = false;
234
235 for a in attributes.iter() {
236 match a.name.local_name.as_ref() {
237 "firstgid" => {
238 result.first_gid = a.value.parse()?;
239 }
240 "name" => {
241 result.source = format!("embedded#{}", a.value.clone());
242 }
243 "source" => {
244 found_source = true;
245 let source_path = Path::new(a.value.as_str());
246 let file_name = env.file_path(source_path);
247 let sub_env = env.file_directory(source_path);
248 let file = env.load_file(source_path)?;
249 let file = BufReader::new(file.as_slice());
250 let mut reader = EventReader::new(file);
251 loop {
252 if let XmlEvent::StartElement {
253 name, attributes, ..
254 } = reader.next()?
255 {
256 if name.local_name == "tileset" {
257 result =
258 Tileset::parse_tsx(result, sub_env, attributes, &mut reader)?;
259 result.source = format!("{}", file_name.display());
260 break;
261 } else {
262 parse_empty(&mut reader)?;
263 }
264 }
265 }
266 }
267 _ => (),
268 }
269 }
270
271 if found_source {
272 parse_empty(reader)?;
276 Ok(result)
277 } else {
278 Tileset::parse_tsx(result, env, attributes, reader)
279 }
280 }
281
282 fn parse_tsx<R: Read + Send>(
284 mut tileset: Tileset,
285 env: TmxLoadContext<'_>,
286 attributes: Vec<OwnedAttribute>,
287 reader: &mut EventReader<R>,
288 ) -> Result<Tileset> {
289 let mut tile_width = 0;
290 let mut tile_height = 0;
291 let mut spacing = 0;
292 let mut margin = 0;
293 let mut tile_count: Option<u32> = None;
294 let mut columns: Option<i32> = None;
295
296 for a in attributes.iter() {
297 match a.name.local_name.as_ref() {
298 "tilewidth" => tile_width = a.value.parse()?,
299 "tileheight" => tile_height = a.value.parse()?,
300 "spacing" => spacing = a.value.parse()?,
301 "margin" => margin = a.value.parse()?,
302 "tilecount" => tile_count = Some(a.value.parse()?),
303 "columns" => columns = Some(a.value.parse()?),
304 _ => (),
305 }
306 }
307
308 tileset.tile_size.x = tile_width as f32;
309 tileset.tile_size.y = tile_height as f32;
310
311 while match reader.next()? {
312 XmlEvent::StartElement {
313 name, attributes, ..
314 } => {
315 match name.local_name.as_ref() {
316 "image" => {
317 let columns = columns;
318 let spacing = spacing;
319 let margin = margin;
320 let tile_width = tile_width;
321 let mut tiles_added = 0;
322 let image = parse_image(env.clone(), attributes, reader)?;
323 tileset.image = Some(image.clone());
324
325 let (width, height) = (image.width(), image.height());
326 let (width, height) = (width as i32, height as i32);
327 let columns = columns.unwrap_or_else(|| {
328 let mut space = width - margin * 2;
329 let mut cols = 0;
330 while space >= tile_width {
331 space -= tile_width + spacing;
332 space -= spacing;
333 cols += 1;
334 }
335 cols
336 });
337 let rows = {
338 let mut space = height - margin * 2;
339 let mut rows = 0;
340 while space >= tile_height {
341 space -= tile_height + spacing;
342 rows += 1;
343 }
344 rows
345 };
346
347 for y in 0..rows {
348 for x in 0..columns {
349 if tile_count.map_or(true, |tc| tiles_added < tc) {
350 let u = (margin + x * tile_width + x * spacing) as f32
351 / width as f32;
352 let v = (margin + y * tile_height + y * spacing) as f32
353 / height as f32;
354 let w = tile_width as f32 / width as f32;
355 let h = tile_height as f32 / height as f32;
356
357 tileset.tiles.push(Some(Tile {
358 image: Some(image.clone()),
359 top_left: Vec2::new(u, v),
360 bottom_right: Vec2::new(u + w, v + h),
361 width: tile_width,
362 height: tile_height,
363 animation: Vec::new(),
364 properties: HashMap::new(),
365 object_group: Vec::new(),
366 }));
367
368 tiles_added += 1;
369 } else {
370 break;
371 }
372 }
373 }
374 }
375 "tile" => {
376 let (id, tile) = Tile::parse(env.clone(), attributes, reader)?;
377
378 if id < tileset.tiles.len() {
379 if tileset.tiles[id].is_none() {
380 tileset.tiles[id].replace(tile);
381 } else {
382 tileset.tiles[id].as_mut().unwrap().join(tile);
384 }
385 } else {
386 while id > tileset.tiles.len() {
387 tileset.tiles.push(None);
388 }
389 tileset.tiles.push(Some(tile));
390 }
391 }
392 _ => parse_empty(reader)?, }
394
395 true
396 }
397 XmlEvent::EndElement { .. } => false,
398 _ => true,
399 } {
400 continue;
401 }
402
403 Ok(tileset)
404 }
405}
406
407impl Tile {
408 fn join(&mut self, mut new_data: Tile) {
409 self.properties = new_data.properties;
410 self.animation = new_data.animation;
411 if new_data.image.is_some() {
412 self.top_left = new_data.top_left;
413 self.bottom_right = new_data.bottom_right;
414 self.image = new_data.image;
415 }
416 self.object_group.append(&mut new_data.object_group);
417 }
418
419 fn parse<R: Read + Send>(
420 env: TmxLoadContext<'_>,
421 attributes: Vec<OwnedAttribute>,
422 reader: &mut EventReader<R>,
423 ) -> Result<(usize, Tile)> {
424 let mut id = 0;
425
426 for a in attributes.iter() {
427 if a.name.local_name == "id" {
428 id = a.value.parse()?
429 }
430 }
431
432 let mut result = Tile {
433 image: None,
434 top_left: Vec2::new(0.0, 0.0),
435 bottom_right: Vec2::new(1.0, 1.0),
436 width: 0,
437 height: 0,
438 animation: Vec::new(),
439 properties: HashMap::new(),
440 object_group: Vec::new(),
441 };
442
443 while match reader.next()? {
444 XmlEvent::StartElement {
445 name, attributes, ..
446 } => {
447 match name.local_name.as_ref() {
448 "properties" => {
449 result.properties = parse_properties(reader)?;
450 }
451 "image" => {
452 let image = parse_image(env.clone(), attributes, reader)?;
453 result.width = image.width() as i32;
454 result.height = image.height() as i32;
455 result.image = Some(image);
456 }
457 "animation" => {
458 result.animation = parse_animation(reader)?;
459 }
460 "objectgroup" => {
461 let group = Layer::parse_objects(env.clone(), attributes, reader)?;
462 if let Layer::ObjectLayer {
463 mut objects,
464 offset,
465 ..
466 } = group
467 {
468 assert_eq!(offset, IVec2::ZERO);
469 result.object_group.append(&mut objects);
470 }
471 }
472 _ => parse_empty(reader)?, }
474
475 true
476 }
477 XmlEvent::EndElement { .. } => false,
478 _ => true,
479 } {
480 continue;
481 }
482
483 Ok((id, result))
484 }
485}
486
487impl Layer {
488 fn parse_tiles<R: Read + Send>(
489 attributes: Vec<OwnedAttribute>,
490 reader: &mut EventReader<R>,
491 ) -> Result<Self> {
492 let mut position = IVec2::ZERO;
493 let mut size = UVec2::ZERO;
494 let mut color = Vec4::new(1.0, 1.0, 1.0, 1.0);
495 let mut visible = true;
496 let mut offset = IVec2::ZERO;
497 let mut parallax = Vec2::new(1.0, 1.0);
498 let mut data = Vec::new();
499
500 for a in attributes {
501 match a.name.local_name.as_ref() {
502 "x" => position.x = a.value.parse()?,
503 "y" => position.y = a.value.parse()?,
504 "width" => size.x = a.value.parse()?,
505 "height" => size.y = a.value.parse()?,
506 "offsetx" => offset.x = a.value.parse()?,
507 "offsety" => offset.y = a.value.parse()?,
508 "parallaxx" => parallax.x = a.value.parse()?,
509 "parallaxy" => parallax.y = a.value.parse()?,
510 "opacity" => color.w *= a.value.parse::<f32>()?,
511 "tintcolor" => color *= parse_color_vec4(a.value.as_str())?,
512 "visible" => visible = a.value == "true",
513 _ => (), }
515 }
516
517 while match reader.next()? {
518 XmlEvent::StartElement {
519 name, attributes, ..
520 } => {
521 match name.local_name.as_ref() {
522 "data" => data = parse_data(attributes, reader)?.into_vec_u32(),
523 _ => parse_empty(reader)?, }
525
526 true
527 }
528 XmlEvent::EndElement { .. } => false,
529 _ => true,
530 } {}
531
532 Ok(Layer::TileLayer {
533 position,
534 size,
535 color,
536 visible,
537 offset,
538 parallax,
539 data,
540 })
541 }
542
543 fn parse_objects<R: Read + Send>(
544 env: TmxLoadContext<'_>,
545 attributes: Vec<OwnedAttribute>,
546 reader: &mut EventReader<R>,
547 ) -> Result<Self> {
548 let mut offset = IVec2::ZERO;
549 let mut parallax = Vec2::new(1.0, 1.0);
550 let mut color = Vec4::new(1.0, 1.0, 1.0, 1.0);
551 let mut visible = true;
552 let mut draworder_index = false;
553 let mut objects = Vec::new();
554
555 for a in attributes {
556 match a.name.local_name.as_ref() {
557 "offsetx" => offset.x = a.value.parse()?,
558 "offsety" => offset.y = a.value.parse()?,
559 "parallaxx" => parallax.x = a.value.parse()?,
560 "parallaxy" => parallax.y = a.value.parse()?,
561 "opacity" => color.w *= a.value.parse::<f32>()?,
562 "tintcolor" => color *= parse_color_vec4(a.value.as_str())?,
563 "visible" => visible = a.value == "true",
564 "draworder" => draworder_index = a.value == "index",
565 _ => (), }
567 }
568
569 while match reader.next()? {
570 XmlEvent::StartElement {
571 name, attributes, ..
572 } => {
573 match name.local_name.as_ref() {
574 "object" => {
575 objects.push(Object::parse(env.clone(), attributes, reader)?);
576 }
577 _ => parse_empty(reader)?, }
579
580 true
581 }
582 XmlEvent::EndElement { .. } => false,
583 _ => true,
584 } {
585 continue;
586 }
587
588 Ok(Layer::ObjectLayer {
589 offset,
590 parallax,
591 color,
592 visible,
593 draworder_index,
594 objects,
595 })
596 }
597
598 fn process(mut self, mut map: Map) -> Result<Map> {
599 match &mut self {
606 Layer::ObjectLayer { objects, .. } => {
607 for object in objects.iter_mut() {
608 if let Some(&Property::File(ref tileset_source)) =
609 object.properties.get("__include_tileset__")
610 {
611 let mut found = false;
612 for tileset in map.tilesets.iter() {
613 if tileset.source == tileset_source.as_ref() {
614 object.tile = object.tile.map(|t| tileset.first_gid + t);
615 found = true;
616 }
617 }
618
619 if !found {
620 println!("Can't find the tileset back in the map!!");
624 println!(
625 "Tilesets in map: {:#?}",
626 map.tilesets
627 .iter()
628 .map(|ts| ts.source.as_str())
629 .collect::<Vec<_>>()
630 );
631 println!("Tileset in template: {}", tileset_source);
632
633 todo!("Tilesets referenced in templates must also exist in the map for now.");
634
635 }
637 }
638 }
639 }
640 &mut _ => unreachable!(),
641 }
642
643 map.layers.push(self);
644
645 Ok(map)
646 }
647
648 fn parse_image<R: Read + Send>(
649 env: TmxLoadContext<'_>,
650 attributes: Vec<OwnedAttribute>,
651 reader: &mut EventReader<R>,
652 ) -> Result<Self> {
653 let mut image = Err(anyhow!("no image found"));
654
655 let mut offset = IVec2::ZERO;
656 let mut parallax = Vec2::new(1.0, 1.0);
657 let mut color = Vec4::new(1.0, 1.0, 1.0, 1.0);
658 let mut visible: bool = true;
659
660 for a in attributes {
661 match a.name.local_name.as_ref() {
662 "offsetx" => offset.x = a.value.parse()?,
663 "offsety" => offset.y = a.value.parse()?,
664 "parallaxx" => parallax.x = a.value.parse()?,
665 "parallaxy" => parallax.y = a.value.parse()?,
666 "opacity" => color.w *= a.value.parse::<f32>()?,
667 "tintcolor" => color *= parse_color_vec4(a.value.as_str())?,
668 "visible" => visible = a.value == "true",
669 _ => (), }
671 }
672
673 while match reader.next()? {
674 XmlEvent::StartElement {
675 name, attributes, ..
676 } => {
677 match name.local_name.as_ref() {
678 "image" => {
679 image = parse_image(env.clone(), attributes, reader);
680 }
681 _ => parse_empty(reader)?, }
683
684 true
685 }
686 XmlEvent::EndElement { .. } => false,
687 _ => true,
688 } {
689 continue;
690 }
691
692 image.map(|image| Layer::ImageLayer {
693 image,
694 color,
695 visible,
696 offset,
697 parallax,
698 })
699 }
700
701 fn parse_group<'a, R: Read + Send>(
702 env: TmxLoadContext<'a>,
703 attributes: Vec<OwnedAttribute>,
704 reader: &'a mut EventReader<R>,
705 ) -> Result<Self> {
706 let mut offset = IVec2::ZERO;
707 let mut parallax = Vec2::new(1.0, 1.0);
708 let mut color = Vec4::new(1.0, 1.0, 1.0, 1.0);
709 for a in attributes {
712 match a.name.local_name.as_ref() {
713 "offsetx" => offset.x = a.value.parse()?,
714 "offsety" => offset.y = a.value.parse()?,
715 "parallaxx" => parallax.x = a.value.parse()?,
716 "parallaxy" => parallax.y = a.value.parse()?,
717 "opacity" => color.w *= a.value.parse::<f32>()?,
718 "tintcolor" => color *= parse_color_vec4(a.value.as_str())?,
719 _ => (), }
722 }
723
724 let mut layers = Vec::new();
725
726 while match reader.next()? {
727 XmlEvent::StartElement {
728 name, attributes, ..
729 } => {
730 match name.local_name.as_ref() {
731 "layer" => {
732 layers.push(Layer::parse_tiles(attributes, reader)?);
733 }
734 "objectgroup" => {
735 layers.push(Layer::parse_objects(env.clone(), attributes, reader)?);
736 }
737 "imagelayer" => {
738 layers.push(Layer::parse_image(env.clone(), attributes, reader)?);
739 }
740 "group" => {
741 layers.push(Layer::parse_group(env.clone(), attributes, reader)?);
742 }
743 _ => parse_empty(reader)?, }
745
746 true
747 }
748 XmlEvent::EndElement { .. } => false,
749 _ => true,
750 } {
751 continue;
752 }
753
754 for l in layers.iter_mut() {
755 l.add_offset(offset.x, offset.y);
756 l.mul_parallax(parallax.x, parallax.y);
757 l.mul_color(color);
758 }
759 Ok(Layer::Group { layers })
760 }
761}
762
763impl Object {
764 fn parse<'a, R: Read + Send>(
765 env: TmxLoadContext<'a>,
766 attributes: Vec<OwnedAttribute>,
767 reader: &'a mut EventReader<R>,
768 ) -> Result<Object> {
769 let mut result = Object {
770 id: 0,
771 properties: HashMap::new(),
772 tile: None,
773 shape: Shape {
774 points: Vec::new(),
775 closed: false,
776 },
777 name: String::from(""),
778 ty: String::from(""),
779 x: 0.0,
780 y: 0.0,
781 width: 0.0,
782 height: 0.0,
783 rotation: 0.0,
784 visible: true,
785 };
786
787 for a in attributes.iter() {
789 if a.name.local_name == "template" {
790 let sub_env = env.file_directory(Path::new(a.value.as_str()));
791
792 let file = env.load_file(Path::new(a.value.as_str()).to_path_buf())?;
793 let file = BufReader::new(file.as_slice());
794 let mut reader = EventReader::new(file);
795
796 loop {
797 if let XmlEvent::StartElement { name, .. } = reader.next()? {
798 if name.local_name == "template" {
799 result = Object::parse_template(sub_env.clone(), &mut reader)?;
800 break;
801 } else {
802 parse_empty(&mut reader)?;
803 }
804 }
805 }
806 }
807 }
808
809 for a in attributes.iter() {
811 match a.name.local_name.as_ref() {
812 "id" => result.id = a.value.parse()?,
813 "gid" => result.tile = Some(a.value.parse()?),
814 "name" => result.name = a.value.clone(),
815 "type" => result.ty = a.value.clone(),
816 "x" => result.x = a.value.parse()?,
817 "y" => result.y = a.value.parse()?,
818 "width" => result.width = a.value.parse()?,
819 "height" => result.height = a.value.parse()?,
820 "rotation" => result.rotation = a.value.parse()?,
821 "visible" => result.visible = a.value == "true",
822 _ => (),
823 }
824 }
825
826 result.shape = Shape {
827 points: vec![
828 vec2(0.0, 0.0),
829 vec2(result.width, 0.0),
830 vec2(result.width, result.height),
831 vec2(0.0, result.height),
832 ],
833 closed: true,
834 };
835
836 while match reader.next()? {
837 XmlEvent::StartElement {
838 name, attributes, ..
839 } => {
840 match name.local_name.as_ref() {
841 "properties" => {
842 for (k, v) in parse_properties(reader)?.into_iter() {
843 result.properties.insert(k, v);
844 }
845 }
846 "polyline" | "polygon" => {
847 let points: Result<Vec<Vec2>> = attributes
848 .iter()
849 .filter(|a| a.name.local_name == "points")
850 .flat_map(|a| a.value.split(' '))
851 .map(|pt| {
852 let mut i = pt.split(',').map(|x| x.parse::<f32>());
853 let x = i.next();
854 let y = i.next();
855 match (x, y) {
856 (Some(Ok(x)), Some(Ok(y))) => Ok(Vec2::new(x, y)),
857 _ => Err(anyhow!("invalid point")),
858 }
859 })
860 .fold(Ok(Vec::new()), |vec, result| match vec {
861 Ok(mut vec) => {
862 vec.push(result?);
863 Ok(vec)
864 }
865 Err(e) => Err(e),
866 });
867
868 result.shape = Shape {
869 points: points?,
870 closed: name.local_name == "polygon",
871 };
872 parse_empty(reader)?;
873 }
874 "ellipse" => {
875 let offset = vec2(result.width * 0.5, result.height * 0.5);
876 result.shape = Shape {
877 points: (0..16)
878 .into_iter()
879 .map(|i| {
880 let a = i as f32 * std::f32::consts::PI / 8.0;
881 offset
882 + vec2(
883 a.cos() * result.width * 0.5,
884 a.sin() * result.height * 0.5,
885 )
886 })
887 .collect(),
888 closed: true,
889 };
890 parse_empty(reader)?;
891 }
892 "point" => {
893 result.shape = Shape {
894 points: vec![vec2(0.0, 0.0)],
895 closed: false,
896 };
897 parse_empty(reader)?;
898 }
899 _ => parse_empty(reader)?, }
901
902 true
903 }
904 XmlEvent::EndElement { .. } => false,
905 _ => true,
906 } {}
907
908 Ok(result)
909 }
910
911 fn parse_template<R: Read + Send>(
912 env: TmxLoadContext<'_>,
913 reader: &mut EventReader<R>,
914 ) -> Result<Object> {
915 let mut tileset = Err(anyhow!("tileset not found"));
916 let mut object = Err(anyhow!("object not found"));
917
918 while match reader.next()? {
919 XmlEvent::StartElement {
920 name, attributes, ..
921 } => {
922 match name.local_name.as_ref() {
923 "tileset" => {
924 let mut first_gid = 0;
925 let mut source = "".to_string();
926 for a in attributes.iter() {
927 match a.name.local_name.as_ref() {
928 "firstgid" => first_gid = a.value.parse()?,
929 "source" => {
930 source = format!(
931 "{}",
932 env.file_path(Path::new(a.value.as_str())).display()
933 );
934 }
935 _ => (),
936 }
937 }
938
939 tileset = Ok((first_gid, source));
940 parse_empty(reader)?;
941 }
942 "object" => {
943 object = Object::parse(env.clone(), attributes, reader);
944 }
945 _ => parse_empty(reader)?, }
947
948 true
949 }
950 XmlEvent::EndElement { .. } => false,
951 _ => true,
952 } {
953 continue;
954 }
955
956 let mut object = object?;
957 if object.tile.is_some() {
958 let (first_gid, source) = tileset?;
959 object.tile = object.tile.map(|t| t - first_gid);
960 object
961 .properties
962 .insert("__include_tileset__".to_string(), Property::File(source));
963 }
964 Ok(object)
965 }
966}
967
968fn parse_image<R: Read + Send>(
969 env: TmxLoadContext<'_>,
970 attributes: Vec<OwnedAttribute>,
971 reader: &mut EventReader<R>,
972) -> Result<texture::Texture> {
973 let mut source: Option<String> = None;
974 let mut width: Option<u32> = None;
976 let mut height: Option<u32> = None;
977 let mut data: Option<Vec<u8>> = None;
978 for a in attributes.iter() {
981 match a.name.local_name.as_ref() {
982 "source" => source = Some(a.value.clone()),
983 "width" => width = Some(a.value.parse()?),
985 "height" => height = Some(a.value.parse()?),
986 _ => (),
988 }
989 }
990
991 while match reader.next()? {
992 XmlEvent::StartElement {
993 name, attributes, ..
994 } => {
995 match name.local_name.as_ref() {
996 "data" => data = Some(parse_data(attributes, reader)?.into_vec_u8()),
997 _ => parse_empty(reader)?, }
999
1000 true
1001 }
1002 XmlEvent::EndElement { .. } => false,
1003 _ => true,
1004 } {
1005 continue;
1006 }
1007
1008 let mut image = if let Some(source) = source.as_ref() {
1009 Texture::from_path(env.file_path(Path::new(source)))
1010 } else if let Some(data) = data {
1011 let mut h = std::collections::hash_map::DefaultHasher::default();
1012 data.hash(&mut h);
1013 Texture::from_bytes(data.as_slice(), format!("embedded#{}", h.finish()))?
1014 } else {
1015 bail!("invalid image")
1016 };
1017
1018 if let (Some(width), Some(height)) = (width, height) {
1019 image = image.resize(width, height)?;
1020 }
1021 Ok(image)
1022}
1023
1024fn parse_data<R: Read + Send>(
1025 attributes: Vec<OwnedAttribute>,
1026 reader: &mut EventReader<R>,
1027) -> Result<Data> {
1028 let mut decode_csv = false;
1029 let mut decode_base64 = false;
1030 let mut decompress_z = false;
1031 let mut decompress_g = false;
1032
1033 for a in attributes.iter() {
1034 match a.name.local_name.as_ref() {
1035 "encoding" => match a.value.as_ref() {
1036 "csv" => decode_csv = true,
1037 "base64" => decode_base64 = true,
1038 _ => (),
1039 },
1040 "compression" => match a.value.as_ref() {
1041 "zlib" => decompress_z = true,
1042 "glib" => decompress_g = true,
1043 _ => (),
1044 },
1045 _ => (),
1046 }
1047 }
1048
1049 let mut result = Data::U32(Vec::new());
1050
1051 while match reader.next()? {
1052 XmlEvent::StartElement { .. } => {
1053 parse_empty(reader)?;
1054 true
1055 }
1056 XmlEvent::Characters(s) => {
1057 if decode_csv {
1058 result = Data::U32(
1059 s.split(',')
1060 .filter(|v| v.trim() != "")
1061 .map(|v| v.replace('\r', "").parse().unwrap_or(0))
1062 .collect(),
1063 );
1064 } else if decode_base64 {
1065 let bytes = base64::decode(s.trim().as_bytes())?;
1066
1067 let bytes = if decompress_z {
1068 let mut zd = libflate::zlib::Decoder::new(BufReader::new(&bytes[..]))?;
1069 let mut bytes = Vec::new();
1070 zd.read_to_end(&mut bytes)?;
1071
1072 bytes
1073 } else if decompress_g {
1074 let mut zd = libflate::gzip::Decoder::new(BufReader::new(&bytes[..]))?;
1075 let mut bytes = Vec::new();
1076 zd.read_to_end(&mut bytes)?;
1077
1078 bytes
1079 } else {
1080 bytes
1081 };
1082
1083 result = Data::U8(bytes)
1084 } else {
1085 bail!("<tile> based data is not supported");
1086 }
1087
1088 true
1089 }
1090 XmlEvent::EndElement { .. } => false,
1091 _ => true,
1092 } {
1093 continue;
1094 }
1095
1096 Ok(result)
1097}
1098
1099fn parse_properties<R: Read + Send>(
1100 reader: &mut EventReader<R>,
1101) -> Result<HashMap<String, Property>> {
1102 let mut result = HashMap::new();
1103
1104 while match reader.next()? {
1105 XmlEvent::StartElement {
1106 name, attributes, ..
1107 } => {
1108 match name.local_name.as_ref() {
1109 "property" => {
1110 let (k, v) = parse_property(attributes, reader)?;
1111 result.insert(k, v);
1112 }
1113 _ => parse_empty(reader)?, }
1115
1116 true
1117 }
1118 XmlEvent::EndElement { .. } => false,
1119 _ => true,
1120 } {
1121 continue;
1122 }
1123
1124 Ok(result)
1125}
1126
1127fn parse_property<R: Read + Send>(
1128 attributes: Vec<OwnedAttribute>,
1129 reader: &mut EventReader<R>,
1130) -> Result<(String, Property)> {
1131 let mut key = String::from("");
1132 let mut value = Property::Int(0);
1133 let mut ty = 0;
1134
1135 for a in attributes {
1136 match a.name.local_name.as_ref() {
1137 "name" => key = a.value.clone(),
1138 "type" => {
1139 ty = match a.value.as_ref() {
1140 "string" => 0,
1141 "int" => 1,
1142 "float" => 2,
1143 "bool" => 3,
1144 "color" => 4,
1145 "file" => 5,
1146 _ => bail!("invalid property type"),
1147 }
1148 }
1149 "value" => {
1150 value = match ty {
1151 0 => Property::String(a.value.clone()),
1152 1 => Property::Int(a.value.parse()?),
1153 2 => Property::Float(a.value.parse()?),
1154 3 => Property::Bool(a.value == "true"),
1155 4 => Property::Color(parse_color(a.value.as_str())?),
1156 5 => Property::File(a.value.clone()),
1157 _ => unreachable!(),
1158 }
1159 }
1160 _ => (), }
1162 }
1163
1164 parse_empty(reader)?;
1165
1166 Ok((key, value))
1167}
1168
1169fn parse_animation<R: Read + Send>(reader: &mut EventReader<R>) -> Result<Vec<Frame>> {
1170 let mut result = Vec::new();
1171
1172 while match reader.next()? {
1173 XmlEvent::StartElement {
1174 name, attributes, ..
1175 } => {
1176 match name.local_name.as_ref() {
1177 "frame" => result.push(parse_frame(attributes, reader)?),
1178 _ => parse_empty(reader)?, }
1180
1181 true
1182 }
1183 XmlEvent::EndElement { .. } => false,
1184 _ => true,
1185 } {
1186 continue;
1187 }
1188
1189 Ok(result)
1190}
1191
1192fn parse_frame<R: Read + Send>(
1193 attributes: Vec<OwnedAttribute>,
1194 reader: &mut EventReader<R>,
1195) -> Result<Frame> {
1196 let mut frame = Frame {
1197 tile: 0,
1198 duration: 0,
1199 };
1200
1201 for a in attributes {
1202 match a.name.local_name.as_ref() {
1203 "tileid" => frame.tile = a.value.parse()?,
1204 "duration" => frame.duration = a.value.parse()?,
1205 _ => (), }
1207 }
1208
1209 parse_empty(reader)?;
1210
1211 Ok(frame)
1212}
1213
1214fn parse_empty<R: Read + Send>(reader: &mut EventReader<R>) -> Result<()> {
1215 while match reader.next()? {
1216 XmlEvent::StartElement { .. } => {
1217 parse_empty(reader)?;
1218 true
1219 }
1220 XmlEvent::EndElement { .. } => false,
1221 _ => true,
1222 } {
1223 continue;
1224 }
1225 Ok(())
1226}
1227
1228fn parse_color(text: &str) -> Result<[u8; 4]> {
1229 let lowercase: Vec<char> = text
1230 .chars()
1231 .filter(|&c| c != '#')
1232 .map(|c| c.to_ascii_lowercase())
1233 .collect();
1234 let mut result = [255u8; 4];
1235
1236 let nibble = |c| match c {
1237 '0' => 0,
1238 '1' => 1,
1239 '2' => 2,
1240 '3' => 3,
1241 '4' => 4,
1242 '5' => 5,
1243 '6' => 6,
1244 '7' => 7,
1245 '8' => 8,
1246 '9' => 9,
1247 'a' => 10,
1248 'b' => 11,
1249 'c' => 12,
1250 'd' => 13,
1251 'e' => 14,
1252 'f' => 15,
1253 _ => 0,
1254 };
1255
1256 match lowercase.len() {
1257 6 => {
1258 for (i, e) in lowercase.chunks_exact(2).enumerate() {
1259 result[i + 1] = nibble(e[0]) << 4 | nibble(e[1]);
1260 }
1261 Ok(result)
1262 }
1263
1264 8 => {
1265 for (i, e) in lowercase.chunks_exact(2).enumerate() {
1266 result[i] = nibble(e[0]) << 4 | nibble(e[1]);
1267 }
1268 Ok(result)
1269 }
1270
1271 _ => bail!("invalid color"),
1272 }
1273}
1274
1275fn parse_color_vec4(text: &str) -> Result<Vec4> {
1276 let [a, r, g, b] = parse_color(text)?;
1277 Ok(Vec4::new(r as f32, g as f32, b as f32, a as f32) * (1.0 / 255.0))
1278}