From 186ecd90d33a3ef5aad0de36bd8d5dedbc7bb4ac Mon Sep 17 00:00:00 2001 From: rl-starbound <47905753+rl-starbound@users.noreply.github.com> Date: Mon, 16 Jan 2023 09:37:35 -0500 Subject: [PATCH] Add support for pre-1.0 Tiled JSON formats While attempting to parse Starbound dungeons in Tiled JSON format, I encountered a number of parse errors. These errors appear to be due to pre-1.0 differences in the Tiled JSON map and tileset formats. As far as I can tell, most Starbound dungeon files were created and/or last edited with Tiled 0.15.2. * Map object `properties` were dictionaries rather than lists, with the key being the property name and the value always a string. * Tileset `tiles` were dictionaries rather than lists, with the key being the tile id (integer cast as a string) and the value being a dictionary containing the remainder of the tile definition. * Some map and tileset properties did not exist, or were optional. This patch introduces minimal changes to the code to allow the Starbound files to be parsed. There may be other legacy quirks, but these are the only ones I've noticed in these files. --- pytiled_parser/parsers/json/properties.py | 22 +++++++++++++--------- pytiled_parser/parsers/json/tiled_map.py | 6 +++--- pytiled_parser/parsers/json/tileset.py | 16 +++++++++++++--- 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/pytiled_parser/parsers/json/properties.py b/pytiled_parser/parsers/json/properties.py index 4e9896f..62b2e17 100644 --- a/pytiled_parser/parsers/json/properties.py +++ b/pytiled_parser/parsers/json/properties.py @@ -27,7 +27,7 @@ def parse(raw_properties: List[RawProperty]) -> Properties: """Parse a list of `RawProperty` objects into `Properties`. Args: - raw_properties: The list of `RawProperty` objects to parse. + raw_properties: The list or dict of `RawProperty` objects to parse. The dict type is supported for parsing legacy Tiled dungeon files. Returns: Properties: The parsed `Property` objects. @@ -36,13 +36,17 @@ def parse(raw_properties: List[RawProperty]) -> Properties: final: Properties = {} value: Property - for raw_property in raw_properties: - if raw_property["type"] == "file": - value = Path(cast(str, raw_property["value"])) - elif raw_property["type"] == "color": - value = parse_color(cast(str, raw_property["value"])) - else: - value = raw_property["value"] - final[raw_property["name"]] = value + if isinstance(raw_properties, dict): + for name, value in raw_properties.items(): + final[name] = value + else: + for raw_property in raw_properties: + if raw_property["type"] == "file": + value = Path(cast(str, raw_property["value"])) + elif raw_property["type"] == "color": + value = parse_color(cast(str, raw_property["value"])) + else: + value = raw_property["value"] + final[raw_property["name"]] = value return final diff --git a/pytiled_parser/parsers/json/tiled_map.py b/pytiled_parser/parsers/json/tiled_map.py index b01050f..a5113f5 100644 --- a/pytiled_parser/parsers/json/tiled_map.py +++ b/pytiled_parser/parsers/json/tiled_map.py @@ -112,14 +112,14 @@ def parse(file: Path) -> TiledMap: # `map` is a built-in function map_ = TiledMap( map_file=file, - infinite=raw_tiled_map["infinite"], + infinite=raw_tiled_map.get("infinite", False), layers=[parse_layer(layer_, parent_dir) for layer_ in raw_tiled_map["layers"]], map_size=Size(raw_tiled_map["width"], raw_tiled_map["height"]), - next_layer_id=raw_tiled_map["nextlayerid"], + next_layer_id=raw_tiled_map.get("nextlayerid"), next_object_id=raw_tiled_map["nextobjectid"], orientation=raw_tiled_map["orientation"], render_order=raw_tiled_map["renderorder"], - tiled_version=raw_tiled_map["tiledversion"], + tiled_version=raw_tiled_map.get("tiledversion", ""), tile_size=Size(raw_tiled_map["tilewidth"], raw_tiled_map["tileheight"]), tilesets=tilesets, version=version, diff --git a/pytiled_parser/parsers/json/tileset.py b/pytiled_parser/parsers/json/tileset.py index fc74ee6..cbbc51d 100644 --- a/pytiled_parser/parsers/json/tileset.py +++ b/pytiled_parser/parsers/json/tileset.py @@ -249,7 +249,7 @@ def parse( tile_count=raw_tileset["tilecount"], tile_width=raw_tileset["tilewidth"], tile_height=raw_tileset["tileheight"], - columns=raw_tileset["columns"], + columns=raw_tileset.get("columns", 1), spacing=raw_tileset["spacing"], margin=raw_tileset["margin"], firstgid=firstgid, @@ -304,8 +304,18 @@ def parse( if raw_tileset.get("tiles") is not None: tiles = {} - for raw_tile in raw_tileset["tiles"]: - tiles[raw_tile["id"]] = _parse_tile(raw_tile, external_path=external_path) + if isinstance(raw_tileset["tiles"], dict): + for raw_tile_id, raw_tile in raw_tileset["tiles"].items(): + assert raw_tile.get("id") is None + raw_tile["id"] = int(raw_tile_id) + tiles[raw_tile["id"]] = _parse_tile( + raw_tile, external_path=external_path + ) + else: + for raw_tile in raw_tileset["tiles"]: + tiles[raw_tile["id"]] = _parse_tile( + raw_tile, external_path=external_path + ) tileset.tiles = tiles if raw_tileset.get("wangsets") is not None: