tileio.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. #include <stdio.h>
  2. #include <stdarg.h>
  3. #include "tile.h"
  4. #include "tilemap.h"
  5. #include "tileio.h"
  6. #include "silut.h"
  7. #include "fifi.h"
  8. #include "monolog.h"
  9. #include "bxml.h"
  10. #include "bxmlparser.h"
  11. #define TILEMAP_MAX_WIDTH 1024
  12. #define TILEMAP_MAX_HEIGHT 1024
  13. /* Tile flipping. */
  14. #define TMX_FLIPPED_HORIZONTALLY 0x80000000
  15. #define TMX_FLIPPED_VERTICALLY 0x40000000
  16. #define TMX_FLIPPED_DIAGONALLY 0x20000000
  17. #define TMX_FLIPPED_FILTER (~0xE0000000)
  18. Image * tileset_image_load(const char * filename) {
  19. return (Image*) fifi_loadsimple(
  20. (FifiSimpleLoader*)al_load_bitmap, filename, "map", NULL);
  21. }
  22. char * tileio_get_tile_property(Bxml * xtil, char * propname) {
  23. Bxml * proplist, * prop, * firstprop;
  24. firstprop = bxml_find_child_deep(xtil, "properties", "property", NULL);
  25. for (prop = firstprop; prop ;
  26. prop = bxml_find_next(prop->sibling, "property") ) {
  27. char * name = bxml_get_attribute(prop, "name");
  28. if ((name) && (strcmp(name, propname) == 0)) {
  29. return bxml_get_attribute(prop, "value");
  30. }
  31. }
  32. return NULL;
  33. }
  34. int * tileio_get_tile_property_int(Bxml * xtil, char * propname, int * ivalue) {
  35. char * value = tileio_get_tile_property(xtil, propname);
  36. if (!value) return NULL;
  37. (*ivalue) = atoi(value);
  38. return ivalue;
  39. }
  40. Tile * tile_load_anim_from_xml(Tile * tile, Bxml * xtil, Tileset * set) {
  41. long index;
  42. double duration;
  43. Bxml * xframe = NULL;
  44. Bxml * xanim = bxml_find_child(xtil, "animation");
  45. /* No animations, no problem. */
  46. if (!xanim) return tile;
  47. for (xframe = bxml_find_child(xanim, "frame"); xframe;
  48. xframe = bxml_find_next(xframe->sibling, "frame")) {
  49. if (!bxml_get_attribute_long(xframe, "tileid", &index)) {
  50. LOG_WARNING("Cannot parse tileid of animation frame. Frame ignored.");
  51. return tile;
  52. }
  53. if (!bxml_get_attribute_double(xframe, "duration", &duration)) {
  54. LOG_WARNING("Cannot parse duration of animation frame. Frame ignored.");
  55. return tile;
  56. }
  57. /* Tiled uses ms for durations, eruta s. */
  58. duration /= 1000.0;
  59. (void) set;
  60. // confusingly, here we don't have to substract the tile set's firstgid
  61. tile_add_animation_frame(tile, index, duration);
  62. }
  63. return tile;
  64. }
  65. Tile * tile_loadxml(Bxml * xtil, Tileset * set) {
  66. char * sflags = NULL;
  67. int ianim = 0, iwait = 0, iblend = 0, ibmask = 0;
  68. int ilight = 0, ilmask = 0;
  69. int ishadow = 0, ismask = 0;
  70. Bxml * firstprop;
  71. long id ;
  72. Tile * tile ;
  73. if (!bxml_get_attribute_long(xtil, "id", &id)) {
  74. LOG_ERROR("Cannot parse tile ID.");
  75. return NULL;
  76. }
  77. tile = tileset_get(set, id);
  78. if (!tile) {
  79. LOG_ERROR("Cannot get tile from tile set %d.", id);
  80. return NULL;
  81. }
  82. // Get and apply the properties of the tile...
  83. /* Here, sflags sometimes is null, I guess when there is a type without a type. */
  84. sflags = tileio_get_tile_property(xtil, "type");
  85. tile_property_(tile, sflags);
  86. tileio_get_tile_property_int(xtil, "anim" , &ianim);
  87. tileio_get_tile_property_int(xtil, "wait" , &iwait);
  88. tileio_get_tile_property_int(xtil, "blend" , &iblend);
  89. tileio_get_tile_property_int(xtil, "blendmask" , &ibmask);
  90. tileio_get_tile_property_int(xtil, "light" , &ilight);
  91. tileio_get_tile_property_int(xtil, "lightmask" , &ilmask);
  92. tileio_get_tile_property_int(xtil, "shadow" , &ishadow);
  93. tileio_get_tile_property_int(xtil, "shadowmask" , &ismask);
  94. /* Support classic style animations... */
  95. if(ianim) {
  96. tile_anim_(tile, ianim);
  97. if(iwait > 0) tile_wait_(tile, iwait);
  98. else tile_wait_(tile, 200);
  99. }
  100. /* Support tiled-style animations as well. A renderng time these
  101. * will take precedence over classic animations if available. */
  102. tile_load_anim_from_xml(tile, xtil, set);
  103. tile_blend_(tile, iblend);
  104. tile_blend_mask_(tile, ibmask);
  105. tile_light_(tile, ilight);
  106. tile_light_mask_(tile, ilmask);
  107. tile_shadow_(tile, ishadow);
  108. tile_shadow_mask_(tile, ismask);
  109. return tile;
  110. }
  111. Tileset * tileset_loadxml(Bxml * node) {
  112. Bxml * xima = NULL;
  113. Bxml * xtil = NULL;
  114. long firstgid = 1;
  115. Image * image;
  116. Tileset * set;
  117. char * iname;
  118. if (!bxml_get_attribute_long(node, "firstgid", &firstgid)) {
  119. LOG_ERROR("Could not parse firstgid attribute for tileset");
  120. return NULL;
  121. }
  122. xima = bxml_find_child(node, "image"); /* Image data is in image tag. */
  123. if(!xima) {
  124. LOG_ERROR("Could not parse image tag for tileset");
  125. return NULL;
  126. }
  127. iname = bxml_get_attribute(xima, "source");
  128. image = tileset_image_load(iname);
  129. LOG("Loaded tile set: %s, %p\n", iname, image);
  130. // xmlFree(iname);
  131. if(!image) {
  132. LOG_ERROR("Could not load tile set source: %s", iname);
  133. return NULL;
  134. }
  135. set = tileset_new(image, firstgid);
  136. if(!set) {
  137. LOG_ERROR("Could not allocate tile set %s.", iname);
  138. return NULL;
  139. }
  140. for (xtil = bxml_find_child(node, "tile"); xtil;
  141. xtil = bxml_find_next(xtil->sibling, "tile")) {
  142. tile_loadxml(xtil, set);
  143. }
  144. return set;
  145. }
  146. /* Look up table for layer names */
  147. Silut tileio_layernames[] = {
  148. { 0, "layer_0" },
  149. { 1, "layer_1" },
  150. { 2, "layer_2" },
  151. { 3, "layer_3" },
  152. SILUT_DONE
  153. };
  154. /* Look up table for object layer names */
  155. Silut tileio_objectnames[] = {
  156. { 0, "object_0" },
  157. { 1, "object_1" },
  158. { 2, "object_2" },
  159. { 3, "object_3" },
  160. SILUT_DONE
  161. };
  162. /* Look up table for data encoding names */
  163. Silut tileio_encodings[] = {
  164. { 0, "csv" },
  165. SILUT_DONE
  166. };
  167. /* Gets the next value from csv. Returns NULL when done. */
  168. char * csv_next(char * csv, unsigned long * value) {
  169. char * aid = NULL;
  170. if(!value) return NULL;
  171. if(sscanf(csv, " %lu , ", value)) {
  172. // a number could be scanned
  173. aid = strchr(csv, ',');
  174. return aid + 1;
  175. }
  176. // no number could be scanned, stop it there
  177. // (*value) = XML_BAD_VALUE;
  178. return NULL;
  179. }
  180. /** Calculates the Allegro draw flags for a tile.
  181. * Diagonal flipping is unsupported
  182. * */
  183. int tileio_allegro_flags_for(unsigned long tileindex) {
  184. int result = 0;
  185. if (tileindex & TMX_FLIPPED_HORIZONTALLY) {
  186. result |= ALLEGRO_FLIP_HORIZONTAL;
  187. }
  188. if (tileindex & TMX_FLIPPED_VERTICALLY) {
  189. result |= ALLEGRO_FLIP_VERTICAL;
  190. }
  191. if (tileindex & TMX_FLIPPED_DIAGONALLY) {
  192. LOG_WARNING("Diagonally flipped tiles are not supported by Eruta.\n");
  193. // Implement this in Allegro ???
  194. }
  195. return result;
  196. }
  197. /** Loads a single tile pane of the tile map from xml (tmx). */
  198. Tilepane * tilemap_loadpanexml(Tilemap * map, Bxml * xlayer, int count) {
  199. long w = 0 , h = 0;
  200. char * name, * csv, * senc;
  201. Silut * laid;
  202. Tilepane * pane;
  203. Bxml * xdata;
  204. int layer;
  205. int xindex, yindex, firstgid;
  206. (void) count;
  207. firstgid = tilemap_firstgid(map);
  208. if (firstgid < 0) {
  209. LOG_ERROR("Tile set should be set before loading tile panes.");
  210. return NULL;
  211. }
  212. if (!bxml_get_attribute_long(xlayer, "width", &w)) {
  213. LOG_ERROR("Could not parse width of layer.");
  214. return NULL;
  215. }
  216. if (!bxml_get_attribute_long(xlayer, "height", &h)) {
  217. LOG_ERROR("Could not parse height of layer.");
  218. return NULL;
  219. }
  220. if ((w<0) || (w>TILEMAP_MAX_WIDTH)) {
  221. LOG_ERROR("Width of layer too high or negative.");
  222. return NULL;
  223. }
  224. if ((h<0) || (h>TILEMAP_MAX_HEIGHT)) {
  225. LOG_ERROR("Height of layer too high or negative.");
  226. return NULL;
  227. }
  228. name = bxml_get_attribute(xlayer, "name");
  229. laid = silut_lsearchcstr(tileio_layernames, name);
  230. if(!laid) {
  231. LOG_ERROR("Unknown layer name: %s.\n"
  232. "Must be layer_0, layer_1, layer_2 or layer_3.\n", name);
  233. return NULL;
  234. }
  235. layer = laid->integer;
  236. pane = tilemap_panenew(map, layer, w, h);
  237. if(!pane) {
  238. LOG_ERROR("Could not create pane.\n");
  239. return NULL;
  240. }
  241. xdata = bxml_find_child(xlayer, "data");
  242. if(!xdata) {
  243. LOG_ERROR("Could not find pane data.\n");
  244. return NULL;
  245. }
  246. senc = bxml_get_attribute(xdata, "encoding");
  247. laid = silut_lsearchcstr(tileio_encodings, senc);
  248. if(!laid) {
  249. LOG_ERROR("Unknown encoding: %s.\n"
  250. "Must be csv.\n", name);
  251. // xmlFree(senc);
  252. return NULL;
  253. }
  254. // xmlFree(senc);
  255. // Get first text child of <data> and hope that it is the data itself.
  256. // Only accept csv for now...
  257. csv = bxml_get_text_under(xdata);
  258. // csv = (char *) BxmlGetContent(xdata);
  259. for(yindex = 0; yindex < h; yindex ++) {
  260. for(xindex = 0; xindex < w; xindex ++) {
  261. unsigned long tileindex = 0;
  262. int realindex;
  263. int drawflags;
  264. csv = csv_next(csv, &tileindex);
  265. if(!csv) {
  266. LOG_ERROR("Unexpected end of csv data");
  267. goto csv_done;
  268. }
  269. /* We read a tile index, set it.
  270. * TMX's tile indexes depend on the tile set's fristgid,
  271. * so just substract that.
  272. */
  273. drawflags = tileio_allegro_flags_for(tileindex);
  274. if (drawflags) {
  275. LOG("Tile with flags found: %d\n", tileindex, drawflags);
  276. tilemap_set_flags(map, layer, xindex, yindex, drawflags);
  277. }
  278. tileindex &= TMX_FLIPPED_FILTER;
  279. realindex = (int)(tileindex - firstgid);
  280. tilemap_setindex(map, layer, xindex, yindex, realindex);
  281. }
  282. }
  283. csv_done:
  284. return pane;
  285. }
  286. /** Loads the tile panes of the tile map from xml (tmx). */
  287. Tilemap * tilemap_loadpanesxml(Tilemap * map, Bxml * xlayer) {
  288. Bxml * index;
  289. int count;
  290. if (!xlayer) return NULL;
  291. for(index = xlayer, count = 0; index ;
  292. index = bxml_find_next(index->sibling, "layer"), count++) {
  293. tilemap_loadpanexml(map, index, count);
  294. }
  295. return map;
  296. }
  297. /** Loads a tile map from a tmx xml document
  298. */
  299. Tilemap * tilemap_loadxml(Bxml * xml, TilemapLoadExtra * extra) {
  300. Tilemap * result;
  301. Tileset * set;
  302. Bxml * root = NULL;
  303. Bxml * xset = NULL;
  304. Bxml * xlayer = NULL;
  305. long wide = 0, high = 0;
  306. long tilewide = 0, tilehigh = 0;
  307. (void) extra;
  308. root = xml;
  309. /* Get the root element node, which should be map. */
  310. if (strcmp((char *) root->name, "map")) {
  311. LOG_ERROR("Not a tile map file!");
  312. return NULL;
  313. }
  314. if (!bxml_get_attribute_long(root, "width", &wide)) {
  315. LOG_ERROR("Width not a number");
  316. return NULL;
  317. }
  318. if (!bxml_get_attribute_long(root, "height", &high)) {
  319. LOG_ERROR("Height not a number!");
  320. return NULL;
  321. }
  322. if ((wide < 1) || (high < 1)) {
  323. LOG_ERROR("Map dimensions too small: %d %d", high, wide);
  324. return NULL;
  325. }
  326. if ((wide > TILEMAP_MAX_WIDTH) || (high > TILEMAP_MAX_HEIGHT)) {
  327. LOG_ERROR("Map dimensions too big : %d %d, %Dx%d at most.",
  328. high, wide, TILEMAP_MAX_WIDTH, TILEMAP_MAX_HEIGHT);
  329. return NULL;
  330. }
  331. if (!bxml_get_attribute_long(root, "tilewidth", &tilewide)) {
  332. LOG_ERROR("Could not parse tile width");
  333. return NULL;
  334. }
  335. if (!bxml_get_attribute_long(root, "tileheight", &tilehigh)) {
  336. LOG_ERROR("Could not parse tile height!");
  337. return NULL;
  338. }
  339. if ((tilewide != 32) || (tilehigh != 32)) {
  340. LOG_ERROR("Eruta only supports 32x32 tiles!");
  341. return NULL;
  342. }
  343. /* TODO check for map type and maybe also background color? */
  344. // Use the first tile set (and that one only) as the map's tile set.
  345. xset = bxml_find_child(root, "tileset");
  346. if (!xset) {
  347. bxml_show_to(root, stderr, 0);
  348. LOG_ERROR("Tile set not found.");
  349. return NULL;
  350. }
  351. // Look for the layers as well
  352. xlayer = bxml_find_child(root, "layer");
  353. if (!xlayer) {
  354. LOG_ERROR("Layers not found.");
  355. return NULL;
  356. }
  357. // Load the tile set
  358. set = tileset_loadxml(xset);
  359. if(!set) {
  360. LOG_ERROR("Tile set not loaded!");
  361. return NULL;
  362. }
  363. // Create a new tile map with the loaded set.
  364. result = tilemap_new(set, wide, high, NULL);
  365. // load the layers
  366. if(!result) {
  367. LOG_ERROR("Out of memory when creating new tile map to load.");
  368. return NULL;
  369. }
  370. tilemap_loadpanesxml(result, xlayer);
  371. return result;
  372. }
  373. /**
  374. * Loads a tile map from a tmx file.
  375. */
  376. Tilemap * tilemap_loadtmx(const char * filename, TilemapLoadExtra * extra) {
  377. Tilemap * result = NULL;
  378. BxmlParser * parser = bxmlparser_new();
  379. Bxml * xml;
  380. /* Parse the file and get the DOM */
  381. xml = bxmlparser_parse_filename(parser, (char *) filename);
  382. if ((xml == NULL) || (bxmlparser_get_error(parser))) {
  383. LOG_ERROR("error: could not parse file %s: %s\n", filename, bxmlparser_get_error(parser));
  384. } else {
  385. result = tilemap_loadxml(xml, extra);
  386. }
  387. bxmlparser_free(parser);
  388. bxml_free(xml);
  389. return result;
  390. }
  391. /**
  392. * Loads a tile map.
  393. */
  394. Tilemap * tilemap_load(const char * filename, TilemapLoadExtra * extra) {
  395. Tilemap * result;
  396. result = tilemap_loadtmx(filename, extra);
  397. /* Set up blending if loaded OK. */
  398. if (result) {
  399. tilemap_init_blend(result);
  400. }
  401. return result;
  402. }
  403. void * tilemap_fifi_load(void * extra, const char * filename) {
  404. return tilemap_load(filename, extra);
  405. }