Newer
Older
DungeonShooting / DungeonShooting_Godot / addons / vnen.tiled_importer / tiled_xml_to_dict.gd
  1. # The MIT License (MIT)
  2. #
  3. # Copyright (c) 2018 George Marques
  4. #
  5. # Permission is hereby granted, free of charge, to any person obtaining a copy
  6. # of this software and associated documentation files (the "Software"), to deal
  7. # in the Software without restriction, including without limitation the rights
  8. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. # copies of the Software, and to permit persons to whom the Software is
  10. # furnished to do so, subject to the following conditions:
  11. #
  12. # The above copyright notice and this permission notice shall be included in all
  13. # copies or substantial portions of the Software.
  14. #
  15. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  21. # SOFTWARE.
  22.  
  23. tool
  24. extends Reference
  25.  
  26. # Reads a TMX file from a path and return a Dictionary with the same structure
  27. # as the JSON map format
  28. # Returns an error code if failed
  29. func read_tmx(path):
  30. var parser = XMLParser.new()
  31. var err = parser.open(path)
  32. if err != OK:
  33. printerr("Error opening TMX file '%s'." % [path])
  34. return err
  35.  
  36. while parser.get_node_type() != XMLParser.NODE_ELEMENT:
  37. err = parser.read()
  38. if err != OK:
  39. printerr("Error parsing TMX file '%s' (around line %d)." % [path, parser.get_current_line()])
  40. return err
  41.  
  42. if parser.get_node_name().to_lower() != "map":
  43. printerr("Error parsing TMX file '%s'. Expected 'map' element.")
  44. return ERR_INVALID_DATA
  45.  
  46. var data = attributes_to_dict(parser)
  47. if not "infinite" in data:
  48. data.infinite = false
  49. data.type = "map"
  50. data.tilesets = []
  51. data.layers = []
  52.  
  53. err = parser.read()
  54. if err != OK:
  55. printerr("Error parsing TMX file '%s' (around line %d)." % [path, parser.get_current_line()])
  56. return err
  57.  
  58. while err == OK:
  59. if parser.get_node_type() == XMLParser.NODE_ELEMENT_END:
  60. if parser.get_node_name() == "map":
  61. break
  62. elif parser.get_node_type() == XMLParser.NODE_ELEMENT:
  63. if parser.get_node_name() == "tileset":
  64. # Empty element means external tileset
  65. if not parser.is_empty():
  66. var tileset = parse_tileset(parser)
  67. if typeof(tileset) != TYPE_DICTIONARY:
  68. # Error happened
  69. return err
  70. data.tilesets.push_back(tileset)
  71. else:
  72. var tileset_data = attributes_to_dict(parser)
  73. if not "source" in tileset_data:
  74. printerr("Error parsing TMX file '%s'. Missing tileset source (around line %d)." % [path, parser.get_current_line()])
  75. return ERR_INVALID_DATA
  76. data.tilesets.push_back(tileset_data)
  77.  
  78. elif parser.get_node_name() == "layer":
  79. var layer = parse_tile_layer(parser, data.infinite)
  80. if typeof(layer) != TYPE_DICTIONARY:
  81. printerr("Error parsing TMX file '%s'. Invalid tile layer data (around line %d)." % [path, parser.get_current_line()])
  82. return ERR_INVALID_DATA
  83. data.layers.push_back(layer)
  84.  
  85. elif parser.get_node_name() == "imagelayer":
  86. var layer = parse_image_layer(parser)
  87. if typeof(layer) != TYPE_DICTIONARY:
  88. printerr("Error parsing TMX file '%s'. Invalid image layer data (around line %d)." % [path, parser.get_current_line()])
  89. return ERR_INVALID_DATA
  90. data.layers.push_back(layer)
  91.  
  92. elif parser.get_node_name() == "objectgroup":
  93. var layer = parse_object_layer(parser)
  94. if typeof(layer) != TYPE_DICTIONARY:
  95. printerr("Error parsing TMX file '%s'. Invalid object layer data (around line %d)." % [path, parser.get_current_line()])
  96. return ERR_INVALID_DATA
  97. data.layers.push_back(layer)
  98.  
  99. elif parser.get_node_name() == "group":
  100. var layer = parse_group_layer(parser, data.infinite)
  101. if typeof(layer) != TYPE_DICTIONARY:
  102. printerr("Error parsing TMX file '%s'. Invalid group layer data (around line %d)." % [path, parser.get_current_line()])
  103. return ERR_INVALID_DATA
  104. data.layers.push_back(layer)
  105.  
  106. elif parser.get_node_name() == "properties":
  107. var prop_data = parse_properties(parser)
  108. if typeof(prop_data) == TYPE_STRING:
  109. return prop_data
  110.  
  111. data.properties = prop_data.properties
  112. data.propertytypes = prop_data.propertytypes
  113.  
  114. err = parser.read()
  115.  
  116. return data
  117.  
  118. # Reads a TSX and return a tileset dictionary
  119. # Returns an error code if fails
  120. func read_tsx(path):
  121. var parser = XMLParser.new()
  122. var err = parser.open(path)
  123. if err != OK:
  124. printerr("Error opening TSX file '%s'." % [path])
  125. return err
  126.  
  127. while parser.get_node_type() != XMLParser.NODE_ELEMENT:
  128. err = parser.read()
  129. if err != OK:
  130. printerr("Error parsing TSX file '%s' (around line %d)." % [path, parser.get_current_line()])
  131. return err
  132.  
  133. if parser.get_node_name().to_lower() != "tileset":
  134. printerr("Error parsing TMX file '%s'. Expected 'map' element.")
  135. return ERR_INVALID_DATA
  136.  
  137. var tileset = parse_tileset(parser)
  138.  
  139. return tileset
  140.  
  141. # Parses a tileset element from the XML and return a dictionary
  142. # Return an error code if fails
  143. func parse_tileset(parser):
  144. var err = OK
  145. var data = attributes_to_dict(parser)
  146. data.tiles = {}
  147.  
  148. err = parser.read()
  149. while err == OK:
  150. if parser.get_node_type() == XMLParser.NODE_ELEMENT_END:
  151. if parser.get_node_name() == "tileset":
  152. break
  153.  
  154. elif parser.get_node_type() == XMLParser.NODE_ELEMENT:
  155. if parser.get_node_name() == "tile":
  156. var attr = attributes_to_dict(parser)
  157. var tile_data = parse_tile_data(parser)
  158. if typeof(tile_data) != TYPE_DICTIONARY:
  159. # Error happened
  160. return tile_data
  161. if "properties" in tile_data and "propertytypes" in tile_data:
  162. if not "tileproperties" in data:
  163. data.tileproperties = {}
  164. data.tilepropertytypes = {}
  165. data.tileproperties[str(attr.id)] = tile_data.properties
  166. data.tilepropertytypes[str(attr.id)] = tile_data.propertytypes
  167. tile_data.erase("tileproperties")
  168. tile_data.erase("tilepropertytypes")
  169. data.tiles[str(attr.id)] = tile_data
  170.  
  171. elif parser.get_node_name() == "image":
  172. var attr = attributes_to_dict(parser)
  173. if not "source" in attr:
  174. printerr("Error loading image tag. No source attribute found (around line %d)." % [parser.get_current_line()])
  175. return ERR_INVALID_DATA
  176. data.image = attr.source
  177. if "width" in attr:
  178. data.imagewidth = attr.width
  179. if "height" in attr:
  180. data.imageheight = attr.height
  181.  
  182. elif parser.get_node_name() == "properties":
  183. var prop_data = parse_properties(parser)
  184. if typeof(prop_data) != TYPE_DICTIONARY:
  185. # Error happened
  186. return prop_data
  187.  
  188. data.properties = prop_data.properties
  189. data.propertytypes = prop_data.propertytypes
  190.  
  191. err = parser.read()
  192.  
  193. return data
  194.  
  195.  
  196. # Parses the data of a single tile from the XML and return a dictionary
  197. # Returns an error code if fails
  198. func parse_tile_data(parser):
  199. var err = OK
  200. var data = {}
  201. var obj_group = {}
  202. if parser.is_empty():
  203. return data
  204.  
  205. err = parser.read()
  206. while err == OK:
  207.  
  208. if parser.get_node_type() == XMLParser.NODE_ELEMENT_END:
  209. if parser.get_node_name() == "tile":
  210. return data
  211. elif parser.get_node_name() == "objectgroup":
  212. data.objectgroup = obj_group
  213.  
  214. elif parser.get_node_type() == XMLParser.NODE_ELEMENT:
  215. if parser.get_node_name() == "image":
  216. # If there are multiple images in one tile we only use the last one.
  217. var attr = attributes_to_dict(parser)
  218. if not "source" in attr:
  219. printerr("Error loading image tag. No source attribute found (around line %d)." % [parser.get_current_line()])
  220. return ERR_INVALID_DATA
  221. data.image = attr.source
  222. data.imagewidth = attr.width
  223. data.imageheight = attr.height
  224.  
  225. elif parser.get_node_name() == "objectgroup":
  226. obj_group = attributes_to_dict(parser)
  227. for attr in ["width", "height", "offsetx", "offsety"]:
  228. if not attr in obj_group:
  229. data[attr] = 0
  230. if not "opacity" in data:
  231. data.opacity = 1
  232. if not "visible" in data:
  233. data.visible = true
  234. if parser.is_empty():
  235. data.objectgroup = obj_group
  236.  
  237. elif parser.get_node_name() == "object":
  238. if not "objects" in obj_group:
  239. obj_group.objects = []
  240. var obj = parse_object(parser)
  241. if typeof(obj) != TYPE_DICTIONARY:
  242. # Error happened
  243. return obj
  244. obj_group.objects.push_back(obj)
  245.  
  246. elif parser.get_node_name() == "properties":
  247. var prop_data = parse_properties(parser)
  248. data["properties"] = prop_data.properties
  249. data["propertytypes"] = prop_data.propertytypes
  250. elif parser.get_node_name() == "animation":
  251. var frame_list = []
  252. var err2 = parser.read()
  253. while err2 == OK:
  254. if parser.get_node_type() == XMLParser.NODE_ELEMENT:
  255. if parser.get_node_name() == "frame":
  256. var frame = {"tileid": 0, "duration": 0}
  257. for i in parser.get_attribute_count():
  258. if parser.get_attribute_name(i) == "tileid":
  259. frame["tileid"] = parser.get_attribute_value(i)
  260. if parser.get_attribute_name(i) == "duration":
  261. frame["duration"] = parser.get_attribute_value(i)
  262. frame_list.push_back(frame)
  263. elif parser.get_node_type() == XMLParser.NODE_ELEMENT_END:
  264. if parser.get_node_name() == "animation":
  265. break
  266. err2 = parser.read()
  267. data["animation"] = frame_list
  268.  
  269. err = parser.read()
  270.  
  271. return data
  272.  
  273. # Parses the data of a single object from the XML and return a dictionary
  274. # Returns an error code if fails
  275. static func parse_object(parser):
  276. var err = OK
  277. var data = attributes_to_dict(parser)
  278.  
  279. if not parser.is_empty():
  280. err = parser.read()
  281. while err == OK:
  282. if parser.get_node_type() == XMLParser.NODE_ELEMENT_END:
  283. if parser.get_node_name() == "object":
  284. break
  285.  
  286. elif parser.get_node_type() == XMLParser.NODE_ELEMENT:
  287. if parser.get_node_name() == "properties":
  288. var prop_data = parse_properties(parser)
  289. data["properties"] = prop_data.properties
  290. data["propertytypes"] = prop_data.propertytypes
  291.  
  292. elif parser.get_node_name() == "point":
  293. data.point = true
  294.  
  295. elif parser.get_node_name() == "ellipse":
  296. data.ellipse = true
  297.  
  298. elif parser.get_node_name() == "polygon" or parser.get_node_name() == "polyline":
  299. var points = []
  300. var points_raw = parser.get_named_attribute_value("points").split(" ", false, 0)
  301.  
  302. for pr in points_raw:
  303. points.push_back({
  304. "x": float(pr.split(",")[0]),
  305. "y": float(pr.split(",")[1]),
  306. })
  307.  
  308. data[parser.get_node_name()] = points
  309.  
  310. err = parser.read()
  311.  
  312. return data
  313.  
  314.  
  315. # Parses a tile layer from the XML and return a dictionary
  316. # Returns an error code if fails
  317. func parse_tile_layer(parser, infinite):
  318. var err = OK
  319. var data = attributes_to_dict(parser)
  320. data.type = "tilelayer"
  321. if not "x" in data:
  322. data.x = 0
  323. if not "y" in data:
  324. data.y = 0
  325. if infinite:
  326. data.chunks = []
  327. else:
  328. data.data = []
  329.  
  330. var current_chunk = null
  331. var encoding = ""
  332.  
  333. if not parser.is_empty():
  334. err = parser.read()
  335.  
  336. while err == OK:
  337. if parser.get_node_type() == XMLParser.NODE_ELEMENT_END:
  338. if parser.get_node_name() == "layer":
  339. break
  340. elif parser.get_node_name() == "chunk":
  341. data.chunks.push_back(current_chunk)
  342. current_chunk = null
  343.  
  344. elif parser.get_node_type() == XMLParser.NODE_ELEMENT:
  345. if parser.get_node_name() == "data":
  346. var attr = attributes_to_dict(parser)
  347.  
  348. if "compression" in attr:
  349. data.compression = attr.compression
  350.  
  351. if "encoding" in attr:
  352. encoding = attr.encoding
  353. if attr.encoding != "csv":
  354. data.encoding = attr.encoding
  355.  
  356. if not infinite:
  357. err = parser.read()
  358. if err != OK:
  359. return err
  360.  
  361. if attr.encoding != "csv":
  362. data.data = parser.get_node_data().strip_edges()
  363. else:
  364. var csv = parser.get_node_data().split(",", false)
  365.  
  366. for v in csv:
  367. data.data.push_back(int(v.strip_edges()))
  368.  
  369. elif parser.get_node_name() == "tile":
  370. var gid = int(parser.get_named_attribute_value_safe("gid"))
  371. if infinite:
  372. current_chunk.data.push_back(gid)
  373. else:
  374. data.data.push_back(gid)
  375.  
  376. elif parser.get_node_name() == "chunk":
  377. current_chunk = attributes_to_dict(parser)
  378. current_chunk.data = []
  379. if encoding != "":
  380. err = parser.read()
  381. if err != OK:
  382. return err
  383. if encoding != "csv":
  384. current_chunk.data = parser.get_node_data().strip_edges()
  385. else:
  386. var csv = parser.get_node_data().split(",", false)
  387. for v in csv:
  388. current_chunk.data.push_back(int(v.strip_edges()))
  389.  
  390. elif parser.get_node_name() == "properties":
  391. var prop_data = parse_properties(parser)
  392. if typeof(prop_data) == TYPE_STRING:
  393. return prop_data
  394.  
  395. data.properties = prop_data.properties
  396. data.propertytypes = prop_data.propertytypes
  397.  
  398. err = parser.read()
  399.  
  400. return data
  401.  
  402. # Parses an object layer from the XML and return a dictionary
  403. # Returns an error code if fails
  404. func parse_object_layer(parser):
  405. var err = OK
  406. var data = attributes_to_dict(parser)
  407. data.type = "objectgroup"
  408. data.objects = []
  409.  
  410. if not parser.is_empty():
  411. err = parser.read()
  412. while err == OK:
  413. if parser.get_node_type() == XMLParser.NODE_ELEMENT_END:
  414. if parser.get_node_name() == "objectgroup":
  415. break
  416. if parser.get_node_type() == XMLParser.NODE_ELEMENT:
  417. if parser.get_node_name() == "object":
  418. data.objects.push_back(parse_object(parser))
  419. elif parser.get_node_name() == "properties":
  420. var prop_data = parse_properties(parser)
  421. if typeof(prop_data) != TYPE_DICTIONARY:
  422. # Error happened
  423. return prop_data
  424. data.properties = prop_data.properties
  425. data.propertytypes = prop_data.propertytypes
  426.  
  427. err = parser.read()
  428.  
  429. return data
  430.  
  431. # Parses an image layer from the XML and return a dictionary
  432. # Returns an error code if fails
  433. func parse_image_layer(parser):
  434. var err = OK
  435. var data = attributes_to_dict(parser)
  436. data.type = "imagelayer"
  437. data.image = ""
  438.  
  439. if not parser.is_empty():
  440. err = parser.read()
  441.  
  442. while err == OK:
  443. if parser.get_node_type() == XMLParser.NODE_ELEMENT_END:
  444. if parser.get_node_name().to_lower() == "imagelayer":
  445. break
  446. elif parser.get_node_type() == XMLParser.NODE_ELEMENT:
  447. if parser.get_node_name().to_lower() == "image":
  448. var image = attributes_to_dict(parser)
  449. if not image.has("source"):
  450. printerr("Missing source attribute in imagelayer (around line %d)." % [parser.get_current_line()])
  451. return ERR_INVALID_DATA
  452. data.image = image.source
  453.  
  454. elif parser.get_node_name() == "properties":
  455. var prop_data = parse_properties(parser)
  456. if typeof(prop_data) != TYPE_DICTIONARY:
  457. # Error happened
  458. return prop_data
  459. data.properties = prop_data.properties
  460. data.propertytypes = prop_data.propertytypes
  461.  
  462. err = parser.read()
  463.  
  464. return data
  465.  
  466. # Parses a group layer from the XML and return a dictionary
  467. # Returns an error code if fails
  468. func parse_group_layer(parser, infinite):
  469. var err = OK
  470. var result = attributes_to_dict(parser)
  471. result.type = "group"
  472. result.layers = []
  473.  
  474. if not parser.is_empty():
  475. err = parser.read()
  476.  
  477. while err == OK:
  478. if parser.get_node_type() == XMLParser.NODE_ELEMENT_END:
  479. if parser.get_node_name().to_lower() == "group":
  480. break
  481. elif parser.get_node_type() == XMLParser.NODE_ELEMENT:
  482. if parser.get_node_name() == "layer":
  483. var layer = parse_tile_layer(parser, infinite)
  484. if typeof(layer) != TYPE_DICTIONARY:
  485. printerr("Error parsing TMX file. Invalid tile layer data (around line %d)." % [parser.get_current_line()])
  486. return ERR_INVALID_DATA
  487. result.layers.push_back(layer)
  488.  
  489. elif parser.get_node_name() == "imagelayer":
  490. var layer = parse_image_layer(parser)
  491. if typeof(layer) != TYPE_DICTIONARY:
  492. printerr("Error parsing TMX file. Invalid image layer data (around line %d)." % [parser.get_current_line()])
  493. return ERR_INVALID_DATA
  494. result.layers.push_back(layer)
  495.  
  496. elif parser.get_node_name() == "objectgroup":
  497. var layer = parse_object_layer(parser)
  498. if typeof(layer) != TYPE_DICTIONARY:
  499. printerr("Error parsing TMX file. Invalid object layer data (around line %d)." % [parser.get_current_line()])
  500. return ERR_INVALID_DATA
  501. result.layers.push_back(layer)
  502.  
  503. elif parser.get_node_name() == "group":
  504. var layer = parse_group_layer(parser, infinite)
  505. if typeof(layer) != TYPE_DICTIONARY:
  506. printerr("Error parsing TMX file. Invalid group layer data (around line %d)." % [parser.get_current_line()])
  507. return ERR_INVALID_DATA
  508. result.layers.push_back(layer)
  509.  
  510. elif parser.get_node_name() == "properties":
  511. var prop_data = parse_properties(parser)
  512. if typeof(prop_data) == TYPE_STRING:
  513. return prop_data
  514.  
  515. result.properties = prop_data.properties
  516. result.propertytypes = prop_data.propertytypes
  517.  
  518. err = parser.read()
  519. return result
  520.  
  521. # Parses properties data from the XML and return a dictionary
  522. # Returns an error code if fails
  523. static func parse_properties(parser):
  524. var err = OK
  525. var data = {
  526. "properties": {},
  527. "propertytypes": {},
  528. }
  529.  
  530. if not parser.is_empty():
  531. err = parser.read()
  532.  
  533. while err == OK:
  534. if parser.get_node_type() == XMLParser.NODE_ELEMENT_END:
  535. if parser.get_node_name() == "properties":
  536. break
  537. elif parser.get_node_type() == XMLParser.NODE_ELEMENT:
  538. if parser.get_node_name() == "property":
  539. var prop_data = attributes_to_dict(parser)
  540. if not (prop_data.has("name") and prop_data.has("value")):
  541. printerr("Missing information in custom properties (around line %d)." % [parser.get_current_line()])
  542. return ERR_INVALID_DATA
  543.  
  544. data.properties[prop_data.name] = prop_data.value
  545. if prop_data.has("type"):
  546. data.propertytypes[prop_data.name] = prop_data.type
  547. else:
  548. data.propertytypes[prop_data.name] = "string"
  549.  
  550. err = parser.read()
  551.  
  552. return data
  553.  
  554. # Reads the attributes of the current element and return them as a dictionary
  555. static func attributes_to_dict(parser):
  556. var data = {}
  557. for i in range(parser.get_attribute_count()):
  558. var attr = parser.get_attribute_name(i)
  559. var val = parser.get_attribute_value(i)
  560. if val.is_valid_integer():
  561. val = int(val)
  562. elif val.is_valid_float():
  563. val = float(val)
  564. elif val == "true":
  565. val = true
  566. elif val == "false":
  567. val = false
  568. data[attr] = val
  569. return data