From 01f38e3f67f0e0f9956ca26554f3d667d425495b Mon Sep 17 00:00:00 2001
From: Matt Hall <matthew@quickbeam.me.uk>
Date: Sun, 2 May 2021 12:41:16 +0100
Subject: [PATCH] Handle csv data without newlines

Closes #99. ldtk seems to output tiled files with no newlines in the csv
data. Tiled itself does always seem to put the newlines in however, it
can open the ldtk files so we should too.
---
 Cargo.toml                  |  2 +-
 assets/ldk_tiled_export.tmx |  8 ++++++++
 src/lib.rs                  | 26 ++++++++++++--------------
 tests/lib.rs                | 30 ++++++++++++++++++++++++++++--
 4 files changed, 49 insertions(+), 17 deletions(-)
 create mode 100644 assets/ldk_tiled_export.tmx

diff --git a/Cargo.toml b/Cargo.toml
index 57c3509..5ae5d76 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 
 name = "tiled"
-version = "0.9.4"
+version = "0.9.5"
 description = "A rust crate for loading in maps created by the Tiled editor"
 repository = "https://github.com/mattyhall/rs-tiled.git"
 # documentation = "http://rust-ci.org/mattyhall/rs-tiled/doc/tiled/"
diff --git a/assets/ldk_tiled_export.tmx b/assets/ldk_tiled_export.tmx
new file mode 100644
index 0000000..b14ed13
--- /dev/null
+++ b/assets/ldk_tiled_export.tmx
@@ -0,0 +1,8 @@
+<map version="1.4" tiledversion="1.4.2" orientation="orthogonal" renderorder="right-down" compressionlevel="0" width="8" height="8" tilewidth="32" tileheight="32" infinite="0" backgroundcolor="#696A79" nextlayerid="2" nextobjectid="1">
+    <tileset firstgid="1" name="Tilesheet" tilewidth="32" tileheight="32" tilecount="84" columns="14" objectalignment="topleft" margin="0" spacing="0">
+        <image source="tilesheet.png" width="448" height="192"/>
+    </tileset>
+    <layer id="1" name="Tiles" width="8" height="8" opacity="1">
+        <data encoding="csv">0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,47,1,1,0,0,0,0,47,47,0,0,0,0,0,0,47,47,0,0,0,0,0,0,0,0,47,0,0,0,0,0,0,0,0,0,0,0,0</data>
+    </layer>
+</map>
\ No newline at end of file
diff --git a/src/lib.rs b/src/lib.rs
index 9f3490c..33364a3 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1140,7 +1140,7 @@ fn parse_data_line<R: Read>(
         }
         (Some(e), None) => match e.as_ref() {
             "base64" => return parse_base64(parser).map(|v| convert_to_tile(&v, width)),
-            "csv" => return decode_csv(parser),
+            "csv" => return decode_csv(width, parser),
             e => return Err(TiledError::Other(format!("Unknown encoding format {}", e))),
         },
         (Some(e), Some(c)) => match (e.as_ref(), c.as_ref()) {
@@ -1224,22 +1224,20 @@ fn decode_zstd(data: Vec<u8>) -> Result<Vec<u8>, TiledError> {
     Ok(data)
 }
 
-fn decode_csv<R: Read>(parser: &mut EventReader<R>) -> Result<Vec<Vec<LayerTile>>, TiledError> {
+fn decode_csv<R: Read>(width: u32, parser: &mut EventReader<R>) -> Result<Vec<Vec<LayerTile>>, TiledError> {
     loop {
         match parser.next().map_err(TiledError::XmlDecodingError)? {
             XmlEvent::Characters(s) => {
-                let mut rows: Vec<Vec<LayerTile>> = Vec::new();
-                for row in s.split('\n') {
-                    if row.trim() == "" {
-                        continue;
-                    }
-                    rows.push(
-                        row.split(',')
-                            .filter(|v| v.trim() != "")
-                            .map(|v| v.replace('\r', "").parse().unwrap())
-                            .map(|id| LayerTile::new(id))
-                            .collect(),
-                    );
+                let mut tiles_it = s
+                    .split(&['\n', '\r', ','][0..])
+                    .filter(|v| v.trim() != "")
+                    .map(|v| v.parse().unwrap())
+                    .map(LayerTile::new)
+                    .peekable();
+                let mut rows = Vec::new();
+                while tiles_it.peek().is_some() {
+                    let row = tiles_it.by_ref().take(width as usize).collect();
+                    rows.push(row);
                 }
                 return Ok(rows);
             }
diff --git a/tests/lib.rs b/tests/lib.rs
index 957fa8e..80ab40b 100644
--- a/tests/lib.rs
+++ b/tests/lib.rs
@@ -20,6 +20,19 @@ fn test_gzip_and_zlib_encoded_and_raw_are_the_same() {
     assert_eq!(z, g);
     assert_eq!(z, r);
     assert_eq!(z, c);
+    
+    if let LayerData::Finite(tiles) = &c.layers[0].tiles {
+        assert_eq!(tiles.len(), 100);
+        assert_eq!(tiles[0].len(), 100);
+        assert_eq!(tiles[99].len(), 100);
+        assert_eq!(tiles[0][0].gid, 35);
+        assert_eq!(tiles[1][0].gid, 17);
+        assert_eq!(tiles[2][0].gid, 0);
+        assert_eq!(tiles[2][1].gid, 17);
+        assert!(tiles[99].iter().map(|t| t.gid).all(|g| g == 0));
+    } else {
+        assert!(false, "It is wrongly recognised as an infinite map");
+    }
 }
 
 #[test]
@@ -49,7 +62,7 @@ fn test_infinite_tileset() {
         assert_eq!(chunks[&(0, 32)].height, 32);
         assert_eq!(chunks[&(-32, 32)].height, 32);
     } else {
-        assert!(false, "It is wrongly recognized as a finite map");
+        assert!(false, "It is wrongly recognised as a finite map");
 
     }
 }
@@ -144,7 +157,20 @@ fn test_flipped_gid() {
         assert!(!t4.flip_h);
         assert!(!t4.flip_v);
     } else {
-        assert!(false, "It is wrongly recognized as an infinite map");
+        assert!(false, "It is wrongly recognised as an infinite map");
     }
     
 }
+
+#[test]
+fn test_ldk_export() {
+    let r = read_from_file_with_path(&Path::new("assets/ldk_tiled_export.tmx")).unwrap();
+    if let LayerData::Finite(tiles) = &r.layers[0].tiles {
+        assert_eq!(tiles.len(), 8);
+        assert_eq!(tiles[0].len(), 8);
+        assert_eq!(tiles[0][0].gid, 0);
+        assert_eq!(tiles[1][0].gid, 1);
+    } else {
+        assert!(false, "It is wrongly recognised as an infinite map");
+    }
+}
\ No newline at end of file
-- 
GitLab