LibTMJ  1.0.0
A library for loading JSON Tiled maps
map.c
Go to the documentation of this file.
1 #include <string.h>
2 
3 #include <jansson.h>
4 
5 #include "log.h"
6 #include "tileset.h"
7 #include "tmj.h"
8 
13 Property* unpack_properties(json_t* properties) {
14  if (properties == NULL) {
15  return NULL;
16  }
17 
18  if (!json_is_array(properties)) {
19  logmsg(TMJ_LOG_ERR, "'properties' must be an array");
20 
21  return NULL;
22  }
23 
24  json_error_t error;
25 
26  size_t property_count = json_array_size(properties);
27 
28  Property* ret = calloc(property_count, sizeof(Property));
29 
30  if (ret == NULL) {
31  logmsg(TMJ_LOG_ERR, "Unable to unpack properties, the system is out of memory");
32 
33  return NULL;
34  }
35 
36  json_t* value;
37 
38  size_t idx = 0;
39  json_t* property = NULL;
40 
41  json_array_foreach(properties, idx, property) {
42  int unpk = json_unpack_ex(property,
43  &error,
44  0,
45  "{s:s, s?s, s?s, s:o}",
46  "name",
47  &ret[idx].name,
48  "type",
49  &ret[idx].type,
50  "propertytype",
51  &ret[idx].propertytype,
52  "value",
53  &value);
54 
55  if (unpk == -1) {
56  logmsg(TMJ_LOG_ERR, "Unable to unpack properties, %s at line %d column %d", error.text, error.line, error.column);
57 
58  free(ret);
59 
60  return NULL;
61  }
62 
63  // note: string is default type, so missing field means string
64  if (ret[idx].type == NULL || strcmp(ret[idx].type, "string") == 0) {
65  unpk = json_unpack_ex(value, &error, 0, "s", &ret[idx].value_string);
66 
67  if (unpk == -1) {
68  logmsg(TMJ_LOG_ERR, "Unable to unpack string value from property, %s at line %d column %d", error.text, error.line, error.column);
69 
70  free(ret);
71 
72  return NULL;
73  }
74  }
75 
76  if (strcmp(ret[idx].type, "int") == 0) {
77  unpk = json_unpack_ex(value, &error, 0, "i", &ret[idx].value_int);
78 
79  if (unpk == -1) {
80  logmsg(TMJ_LOG_ERR, "Unable to unpack integer value from property, %s at line %d column %d", error.text, error.line, error.column);
81 
82  free(ret);
83 
84  return NULL;
85  }
86  }
87 
88  if (strcmp(ret[idx].type, "float") == 0) {
89  unpk = json_unpack_ex(value, &error, 0, "i", &ret[idx].value_float);
90 
91  if (unpk == -1) {
92  logmsg(TMJ_LOG_ERR, "Unable to unpack float value from property, %s at line %d column %d", error.text, error.line, error.column);
93 
94  free(ret);
95 
96  return NULL;
97  }
98  }
99 
100  if (strcmp(ret[idx].type, "bool") == 0) {
101  unpk = json_unpack_ex(value, &error, 0, "b", &ret[idx].value_bool);
102 
103  if (unpk == -1) {
104  logmsg(TMJ_LOG_ERR, "Unable to unpack bool value from property, %s at line %d column %d", error.text, error.line, error.column);
105 
106  free(ret);
107 
108  return NULL;
109  }
110  }
111 
112  if (strcmp(ret[idx].type, "color") == 0) {
113  unpk = json_unpack_ex(value, &error, 0, "s", &ret[idx].value_color);
114 
115  if (unpk == -1) {
116  logmsg(TMJ_LOG_ERR, "Unable to unpack color value from property, %s at line %d column %d", error.text, error.line, error.column);
117 
118  free(ret);
119 
120  return NULL;
121  }
122  }
123 
124  if (strcmp(ret[idx].type, "file") == 0) {
125  unpk = json_unpack_ex(value, &error, 0, "s", &ret[idx].value_file);
126 
127  if (unpk == -1) {
128  logmsg(TMJ_LOG_ERR, "Unable to unpack file value from property, %s at line %d column %d", error.text, error.line, error.column);
129 
130  free(ret);
131 
132  return NULL;
133  }
134  }
135 
136  if (strcmp(ret[idx].type, "object") == 0) {
137  unpk = json_unpack_ex(value, &error, 0, "i", &ret[idx].value_object);
138 
139  if (unpk == -1) {
140  logmsg(TMJ_LOG_ERR, "Unable to unpack object value from property, %s at line %d column %d", error.text, error.line, error.column);
141 
142  free(ret);
143 
144  return NULL;
145  }
146  }
147  }
148 
149  return ret;
150 }
151 
155 Point* unpack_points(json_t* points) {
156  if (points == NULL) {
157  return NULL;
158  }
159 
160  if (!json_is_array(points)) {
161  logmsg(TMJ_LOG_ERR, "'polygon' or 'polyline' must be an array");
162 
163  return NULL;
164  }
165 
166  json_error_t error;
167 
168  size_t point_count = json_array_size(points);
169 
170  Point* ret = calloc(point_count, sizeof(Point));
171 
172  if (ret == NULL) {
173  logmsg(TMJ_LOG_ERR, "Unable to unpack points, the system is out of memory");
174 
175  return NULL;
176  }
177 
178  size_t idx;
179  json_t* point;
180 
181  json_array_foreach(points, idx, point) {
182  int unpk = json_unpack_ex(point, &error, 0, "{s:F, s:F}", "x", &ret[idx].x, "y", &ret[idx].y);
183 
184  if (unpk == -1) {
185  logmsg(TMJ_LOG_ERR, "Unable to unpack points, %s at line %d column %d", error.text, error.line, error.column);
186 
187  free(ret);
188 
189  return NULL;
190  }
191  }
192 
193  return ret;
194 }
195 
199 Text* unpack_text(json_t* text) {
200  if (text == NULL) {
201  return NULL;
202  }
203 
204  json_error_t error;
205 
206  Text* ret = calloc(1, sizeof(Text));
207 
208  if (ret == NULL) {
209  logmsg(TMJ_LOG_ERR, "Unable to unpack text, the system is out of memory");
210 
211  return NULL;
212  }
213 
214  int unpk = json_unpack_ex(text,
215  &error,
216  0,
217  "{"
218  "s?b, s?b, s?b, s?b, s?b, s?b,"
219  "s?s, s?s?, s?s, s:s, s?s,"
220  "s?i,"
221  "}",
222  "bold",
223  &ret->bold,
224  "italic",
225  &ret->italic,
226  "kerning",
227  &ret->kerning,
228  "strikeout",
229  &ret->strikeout,
230  "underline",
231  &ret->underline,
232  "wrap",
233  &ret->wrap,
234  "color",
235  &ret->color,
236  "fontfamily",
237  &ret->fontfamily,
238  "halign",
239  &ret->halign,
240  "text",
241  &ret->text,
242  "valign",
243  &ret->valign,
244  "pixelsize",
245  &ret->pixelsize);
246  if (unpk == -1) {
247  logmsg(TMJ_LOG_ERR, "Unable to unpack text, %s at line %d column %d", error.text, error.line, error.column);
248 
249  free(ret);
250 
251  return NULL;
252  }
253 
254  return ret;
255 }
256 
257 Object* unpack_objects(json_t* objects) {
258  if (objects == NULL) {
259  return NULL;
260  }
261 
262  if (!json_is_array(objects)) {
263  logmsg(TMJ_LOG_ERR, "'objects' must be an array");
264  }
265 
266  json_error_t error;
267 
268  size_t object_count = json_array_size(objects);
269 
270  Object* ret = calloc(object_count, sizeof(Object));
271 
272  if (ret == NULL) {
273  logmsg(TMJ_LOG_ERR, "Unable to unpack objects, the system is out of memory");
274 
275  return NULL;
276  }
277 
278  size_t idx = 0;
279  json_t* object = NULL;
280 
281  json_array_foreach(objects, idx, object) {
282  json_t* properties = NULL;
283  json_t* text = NULL;
284  json_t* polygon = NULL;
285  json_t* polyline = NULL;
286 
287  // Unpack scalar values
288  int unpk = json_unpack_ex(object,
289  &error,
290  0,
291  "{"
292  "s?b, s?b, s:b,"
293  "s:s, s?s, s?s,"
294  "s?i, s:i,"
295  "s:F, s:F, s:F, s:F, s:F,"
296  "s?o"
297  "}",
298  "ellipse",
299  &ret[idx].ellipse,
300  "point",
301  &ret[idx].point,
302  "visible",
303  &ret[idx].visible,
304  "name",
305  &ret[idx].name,
306  "template",
307  &ret[idx].template,
308  "type",
309  &ret[idx].type,
310  "gid",
311  &ret[idx].gid,
312  "id",
313  &ret[idx].id,
314  "height",
315  &ret[idx].height,
316  "rotation",
317  &ret[idx].rotation,
318  "width",
319  &ret[idx].width,
320  "x",
321  &ret[idx].x,
322  "y",
323  &ret[idx].y,
324  "properties",
325  &properties,
326  "text",
327  &text,
328  "polygon",
329  &polygon,
330  "polyline",
331  &polyline);
332 
333  if (unpk == -1) {
334  logmsg(TMJ_LOG_ERR, "Unable to unpack object, %s at line %d column %d", error.text, error.line, error.column);
335 
336  return NULL;
337  }
338 
339  // Unpack properties
340  if (properties != NULL) {
341  if (!json_is_array(properties)) {
342  logmsg(TMJ_LOG_ERR, "'properties' must be an array");
343 
344  goto fail_properties;
345  }
346 
347  ret[idx].properties = unpack_properties(properties);
348 
349  if (ret[idx].properties == NULL) {
350  logmsg(TMJ_LOG_ERR, "Unable to unpack object properties");
351 
352  goto fail_properties;
353  }
354 
355  ret[idx].property_count = json_array_size(properties);
356  }
357 
358  // Unpack text
359  if (text != NULL) {
360  ret[idx].text = unpack_text(text);
361 
362  if (ret[idx].text == NULL) {
363  logmsg(TMJ_LOG_ERR, "Unable to unpack object text");
364 
365  goto fail_text;
366  }
367  }
368 
369  // If this object is any of the below items, we don't need to unpack anything else
370  if (ret[idx].ellipse || ret[idx].point || ret[idx].gid != 0 || ret[idx].text != NULL) {
371  continue;
372  }
373 
374  // Unpack Polygon
375  if (polygon != NULL) {
376  ret[idx].polygon = unpack_points(polygon);
377 
378  if (ret[idx].polygon == NULL) {
379  goto fail_polygon;
380  }
381 
382  ret[idx].polygon_point_count = json_array_size(polygon);
383 
384  // No need to unpack a polyline if this object was a polygon
385  continue;
386  }
387 
388  // Unpack Polyline
389  if (polyline != NULL) {
390  ret[idx].polyline = unpack_points(polyline);
391 
392  if (ret[idx].polyline == NULL) {
393  goto fail_polyline;
394  }
395 
396  ret[idx].polyline_point_count = json_array_size(polyline);
397  }
398  }
399 
400  return ret;
401 
402 fail_polygon:
403 fail_polyline:
404  for (size_t i = 0; i < object_count; i++) {
405  free(ret[i].text);
406  }
407 
408 fail_text:
409  for (size_t i = 0; i < object_count; i++) {
410  free(ret[i].properties);
411  }
412 
413 fail_properties:
414  free(ret);
415 
416  return NULL;
417 }
418 
423 void free_objects(Object* objects, size_t object_count) {
424  for (size_t i = 0; i < object_count; i++) {
425  // We don't bother freeing polyline, because polygon and polyline are a union
426  free(objects[i].polygon);
427  free(objects[i].text);
428  free(objects[i].properties);
429  }
430 
431  free(objects);
432 }
433 
434 Chunk* unpack_chunks(json_t* chunks) {
435  if (chunks == NULL) {
436  return NULL;
437  }
438 
439  if (!json_is_array(chunks)) {
440  logmsg(TMJ_LOG_ERR, "Could not unpack layer chunks, 'chunks' must be an array");
441 
442  return NULL;
443  }
444 
445  json_error_t error;
446 
447  size_t chunk_count = json_array_size(chunks);
448 
449  Chunk* ret = calloc(chunk_count, sizeof(Chunk));
450 
451  if (ret == NULL) {
452  logmsg(TMJ_LOG_ERR, "Unable to unpack chunks, the system is out of memory");
453 
454  return NULL;
455  }
456 
457  size_t idx;
458  json_t* chunk;
459 
460  json_array_foreach(chunks, idx, chunk) {
461  json_t* data = NULL;
462 
463  int unpk = json_unpack_ex(chunk,
464  &error,
465  0,
466  "{"
467  "s:i, s:i, s:i, s:i,"
468  "s:o,"
469  "}",
470  "height",
471  &ret[idx].height,
472  "width",
473  &ret[idx].width,
474  "x",
475  &ret[idx].x,
476  "y",
477  &ret[idx].y,
478  "data",
479  &data);
480 
481  if (unpk == -1) {
482  logmsg(TMJ_LOG_ERR, "Unable to unpack chunk, %s at line %d column %d", error.text, error.line, error.column);
483 
484  goto fail_chunk;
485  }
486 
487  if (json_is_string(data)) {
488  unpk = json_unpack_ex(data, &error, 0, "s", &ret[idx].data_str);
489 
490  if (unpk == -1) {
491  logmsg(TMJ_LOG_ERR, "Unable to unpack chunk data, %s at line %d column %d", error.text, error.line, error.column);
492 
493  goto fail_chunk;
494  }
495 
496  ret[idx].data_is_str = true;
497  } else if (json_is_array(data)) {
498  size_t datum_count = json_array_size(data);
499 
500  ret[idx].data_uint = calloc(datum_count, sizeof(unsigned int));
501 
502  if (ret[idx].data_uint == NULL) {
503  logmsg(TMJ_LOG_ERR, "Unable to unpack chunk data, the system is out memory");
504 
505  goto fail_chunk;
506  }
507 
508  size_t idx2;
509  json_t* datum;
510 
511  json_array_foreach(data, idx2, datum) {
512  unpk = json_unpack_ex(datum, &error, 0, "i", &ret[idx].data_uint[idx2]);
513 
514  if (unpk == -1) {
515  logmsg(TMJ_LOG_ERR, "Unable to unpack chunk datum, %s at line %d column %d", error.text, error.line, error.column);
516 
517  goto fail_data;
518  }
519  }
520  } else {
521  logmsg(TMJ_LOG_ERR, "Unable to unpack chunk, chunk data must be a string or an array of uint");
522 
523  goto fail_chunk;
524  }
525  }
526 
527  return ret;
528 
529 fail_data:
530  for (size_t i = 0; i < chunk_count; i++) {
531  if (!ret[i].data_is_str) {
532  free(ret[i].data_uint);
533  }
534  }
535 
536 fail_chunk:
537  free(ret);
538 
539  return NULL;
540 }
541 
542 // Helper function for freeing chunks, since they contain dynamically-allocated arrays
543 void free_chunks(Chunk* chunks, size_t chunk_count) {
544  for (size_t i = 0; i < chunk_count; i++) {
545  if (!chunks[i].data_is_str) {
546  free(chunks[i].data_uint);
547  }
548  }
549 
550  free(chunks);
551 }
552 
556 Layer* unpack_layers(json_t* layers) {
557  if (!json_is_array(layers)) {
558  logmsg(TMJ_LOG_ERR, "Could not unpack layer, 'layers' must be an array");
559 
560  return NULL;
561  }
562 
563  size_t layer_count = json_array_size(layers);
564 
565  if (layer_count < 1) {
566  logmsg(TMJ_LOG_ERR, "Unable to load layers, 'layers' array must have at least one element");
567 
568  return NULL;
569  }
570 
571  Layer* ret = calloc(layer_count, sizeof(Layer));
572 
573  if (ret == NULL) {
574  logmsg(TMJ_LOG_ERR, "Unable to load layers, the system is out of memory");
575 
576  return NULL;
577  }
578 
579  size_t idx;
580  json_t* layer;
581  json_error_t error;
582 
583  json_array_foreach(layers, idx, layer) {
584  // Unpack id so we can log errors with it
585  int unpk = json_unpack_ex(layer, &error, 0, "{s:i}", "id", &ret[idx].id);
586 
587  if (unpk == -1) {
588  logmsg(TMJ_LOG_ERR, "Could not unpack layer ID, %s at line %d column %d", error.text, error.line, error.column);
589  }
590 
591  logmsg(TMJ_LOG_DEBUG, "Loading layer[%d]", ret[idx].id);
592 
593  // Unpack type
594  unpk = json_unpack_ex(layer, &error, 0, "{s:s}", "type", &ret[idx].type);
595 
596  if (unpk == -1) {
597  logmsg(TMJ_LOG_ERR, "Could not unpack layer[%d], %s at line %d column %d", ret[idx].id, error.text, error.line, error.column);
598 
599  goto fail_layer;
600  }
601 
602  // Unpack scalar values
603  unpk = json_unpack_ex(layer,
604  &error,
605  0,
606  "{"
607  "s?b, s:b,"
608  "s?s, s:s, s?s,"
609  "s?i, s?i, s:i, s:i,"
610  "s?F, s?F, s:F, s?F, s?F"
611  "}",
612  "locked",
613  &ret[idx].locked,
614  "visible",
615  &ret[idx].visible,
616  "class",
617  &ret[idx].class,
618  "name",
619  &ret[idx].name,
620  "tintcolor",
621  &ret[idx].tintcolor,
622  "startx",
623  &ret[idx].startx,
624  "starty",
625  &ret[idx].starty,
626  "x",
627  &ret[idx].x,
628  "y",
629  &ret[idx].y,
630  "offsetx",
631  &ret[idx].offsetx,
632  "offsety",
633  &ret[idx].offsety,
634  "opacity",
635  &ret[idx].opacity,
636  "parallaxx",
637  &ret[idx].parallaxx,
638  "parallaxy",
639  &ret[idx].parallaxy);
640 
641  if (unpk == -1) {
642  logmsg(TMJ_LOG_ERR, "Could not unpack layer[%d], %s at line %d column %d", ret[idx].id, error.text, error.line, error.column);
643 
644  goto fail_layer;
645  }
646 
647  // Unpack conditional scalar values
648  if (strcmp(ret[idx].type, "imagelayer") == 0) {
649  unpk = json_unpack_ex(layer,
650  &error,
651  0,
652  "{"
653  "s:b, s:b,"
654  "s:s, s?s"
655  "}",
656  "repeatx",
657  &ret[idx].repeatx,
658  "repeaty",
659  &ret[idx].repeaty,
660  "image",
661  &ret[idx].image,
662  "transparentcolor",
663  &ret[idx].transparentcolor);
664 
665  if (unpk == -1) {
666  logmsg(TMJ_LOG_ERR, "Could not unpack layer[%d], %s at line %d column %d", ret[idx].id, error.text, error.line, error.column);
667 
668  goto fail_layer;
669  }
670  } else if (strcmp(ret[idx].type, "tilelayer") == 0) {
671  unpk = json_unpack_ex(layer,
672  &error,
673  0,
674  "{"
675  "s?s, s?s,"
676  "s:i, s:i,"
677  "}",
678  "compression",
679  &ret[idx].compression,
680  "encoding",
681  &ret[idx].encoding,
682  "height",
683  &ret[idx].height,
684  "width",
685  &ret[idx].width);
686 
687  if (unpk == -1) {
688  logmsg(TMJ_LOG_ERR, "Could not unpack layer[%d], %s at line %d column %d", ret[idx].id, error.text, error.line, error.column);
689 
690  goto fail_layer;
691  }
692  } else if (strcmp(ret[idx].type, "objectgroup") == 0) {
693  unpk = json_unpack_ex(layer, &error, 0, "{s?s}", "draworder", &ret[idx].draworder);
694 
695  if (unpk == -1) {
696  logmsg(TMJ_LOG_ERR, "Could not unpack layer[%d], %s at line %d column %d", ret[idx].id, error.text, error.line, error.column);
697 
698  goto fail_layer;
699  }
700  }
701 
702  // Unpack data
703  if (strcmp(ret[idx].type, "tilelayer") == 0) {
704  json_t* data = NULL;
705 
706  unpk = json_unpack_ex(layer, &error, 0, "{s:o}", "data", &data);
707 
708  if (unpk == -1) {
709  logmsg(TMJ_LOG_ERR, "Could not unpack layer[%d], %s at line %d column %d", ret[idx].id, error.text, error.line, error.column);
710 
711  goto fail_layer;
712  }
713 
714  if (json_is_string(data)) {
715  ret[idx].data_is_str = true;
716 
717  unpk = json_unpack_ex(layer, &error, 0, "{s:s}", "data", &ret[idx].data_str);
718 
719  if (unpk == -1) {
720  logmsg(TMJ_LOG_ERR, "Could not unpack layer[%d], %s at line %d column %d", ret[idx].id, error.text, error.line, error.column);
721 
722  goto fail_layer;
723  }
724  } else if (json_is_array(data)) {
725  ret[idx].data_is_str = false;
726 
727  ret[idx].data_count = json_array_size(data);
728 
729  ret[idx].data_uint = calloc(ret[idx].data_count, sizeof(unsigned int));
730 
731  if (ret[idx].data_uint == NULL) {
732  logmsg(TMJ_LOG_ERR, "Unable to unpack layer[%d]->data, the system is out of memory", ret[idx].id);
733 
734  goto fail_layer;
735  }
736 
737  json_t* datum;
738 
739  size_t idx2;
740 
741  json_array_foreach(data, idx2, datum) {
742  unpk = json_unpack_ex(datum, &error, 0, "i", &ret[idx].data_uint[idx2]);
743 
744  if (unpk == -1) {
745  goto fail_data;
746  }
747  }
748  } else {
749  logmsg(TMJ_LOG_ERR, "Unable to unpack layer[%d]->data, data must be a string or an array", ret[idx].id);
750 
751  goto fail_layer;
752  }
753  }
754 
755  // Unpack properties
756  json_t* properties = NULL;
757 
758  unpk = json_unpack_ex(layer, &error, 0, "{s?o}", "properties", &properties);
759 
760  if (unpk == -1) {
761  goto fail_data;
762  }
763 
764  if (properties != NULL) {
765  ret[idx].properties = unpack_properties(properties);
766 
767  if (ret[idx].properties == NULL) {
768  logmsg(TMJ_LOG_ERR, "Unable to unpack layer[%d]->properties", ret[idx].id);
769 
770  goto fail_data;
771  }
772  }
773 
774  // Unpack chunks
775  if (strcmp(ret[idx].type, "tilelayer") == 0) {
776  json_t* chunks = NULL;
777 
778  unpk = json_unpack_ex(layer, &error, 0, "{s?o}", "chunks", &chunks);
779 
780  if (unpk == -1) {
781  goto fail_properties;
782  }
783 
784  if (chunks != NULL) {
785  ret[idx].chunks = unpack_chunks(chunks);
786 
787  if (ret[idx].chunks == NULL) {
788  logmsg(TMJ_LOG_ERR, "Unable to unpack layer[%d]->chunks", ret[idx].id);
789 
790  goto fail_properties;
791  }
792  }
793  }
794 
795  // Unpack objects
796  if (strcmp(ret[idx].type, "objectgroup") == 0) {
797  json_t* objects = NULL;
798 
799  unpk = json_unpack_ex(layer, &error, 0, "{s:o}", "objects", &objects);
800 
801  if (unpk == -1) {
802  goto fail_chunks;
803  }
804 
805  if (objects != NULL) {
806  ret[idx].objects = unpack_objects(objects);
807 
808  if (ret[idx].objects == NULL) {
809  logmsg(TMJ_LOG_ERR, "Unable to unpack layer[%d]->objects", ret[idx].id);
810 
811  goto fail_chunks;
812  }
813 
814  ret[idx].object_count = json_array_size(objects);
815  }
816  }
817 
818  // Unpack nested layers
819  if (strcmp(ret[idx].type, "group") == 0) {
820  json_t* nested_layers = NULL;
821 
822  unpk = json_unpack_ex(layer, &error, 0, "{s:o}", "layers", &nested_layers);
823 
824  if (unpk == -1) {
825  goto fail_objects;
826  }
827 
828  if (json_is_array(nested_layers) && json_array_size(nested_layers) > 0) {
829  ret[idx].layers = unpack_layers(nested_layers);
830 
831  if (ret[idx].layers == NULL) {
832  logmsg(TMJ_LOG_ERR, "Unable to unpack layer[%d]->layers", ret[idx].id);
833 
834  goto fail_objects;
835  }
836  }
837  }
838  }
839 
840  return ret;
841 
842 fail_objects:
843  for (size_t i = 0; i < layer_count; i++) {
844  free_objects(ret[i].objects, ret[i].object_count);
845  }
846 fail_chunks:
847  for (size_t i = 0; i < layer_count; i++) {
848  free_chunks(ret[i].chunks, ret[i].chunk_count);
849  }
850 
851 fail_properties:
852  for (size_t i = 0; i < layer_count; i++) {
853  free(ret[i].properties);
854  }
855 
856 fail_data:
857  for (size_t i = 0; i < layer_count; i++) {
858  if (!ret[i].data_is_str) {
859  free(ret[i].data_uint);
860  }
861  }
862 
863 fail_layer:
864  free(ret);
865 
866  return NULL;
867 }
868 
874 void layers_free(Layer* layers, size_t layer_count) {
875  for (size_t i = 0; i < layer_count; i++) {
876  free_objects(layers[i].objects, layers[i].object_count);
877  free_chunks(layers[i].chunks, layers[i].chunk_count);
878  free(layers[i].properties);
879  free(layers[i].data_uint);
880 
881  layers_free(layers[i].layers, layers[i].layer_count);
882  }
883 
884  free(layers);
885 }
886 
887 Map* map_load_json(json_t* root, const char* path) {
888  json_error_t error;
889 
890  Map* map = calloc(1, sizeof(Map));
891 
892  if (map == NULL) {
893  logmsg(TMJ_LOG_ERR, "Could not load map '%s', the system is out of memory", path);
894 
895  goto fail_map;
896  }
897 
898  map->root = root;
899 
900  // Verify type (i.e, check that this is a map and not a tileset or something)
901  int unpk = json_unpack_ex(root, &error, 0, "{s:s}", "type", &map->type);
902 
903  if (unpk == -1) {
904  logmsg(TMJ_LOG_ERR, "Unable to unpack map[%s]->type, %s at line %d, column %d", path, error.text, error.line, error.column);
905 
906  goto fail_map;
907  }
908 
909  if (strcmp(map->type, "map") != 0) {
910  logmsg(TMJ_LOG_ERR, "File at path '%s' is of type '%s' and is not a map file", path, map->type);
911 
912  goto fail_map;
913  }
914 
915  json_t* tilesets = NULL;
916  json_t* layers = NULL;
917  json_t* properties = NULL;
918 
919  // Unpack scalar values
920  unpk = json_unpack_ex(root,
921  &error,
922  0,
923  "{"
924  "s:b,"
925  "s?s, s?s, s:s, s:s, s:s, s:s,"
926  "s:i, s:i, s:i, s:i, s:i, s:i, s:i,"
927  "s?F, s?F,"
928  "s:o, s:o, s?o"
929  "}",
930  "infinite",
931  &map->infinite,
932  "backgroundcolor",
933  &map->backgroundcolor,
934  "class",
935  &map->class,
936  "orientation",
937  &map->orientation,
938  "renderorder",
939  &map->renderorder,
940  "tiledversion",
941  &map->tiledversion,
942  "version",
943  &map->version,
944  "compressionlevel",
945  &map->compressionlevel,
946  "height",
947  &map->height,
948  "nextlayerid",
949  &map->nextlayerid,
950  "nextobjectid",
951  &map->nextobjectid,
952  "tileheight",
953  &map->tileheight,
954  "tilewidth",
955  &map->tilewidth,
956  "width",
957  &map->width,
958  "parallaxoriginx",
959  &map->parallaxoriginx,
960  "parallaxoriginy",
961  &map->parallaxoriginy,
962  "tilesets",
963  &tilesets,
964  "layers",
965  &layers,
966  "properties",
967  &properties);
968 
969  if (unpk == -1) {
970  logmsg(TMJ_LOG_ERR, "Could not unpack map[%s], %s at line %d, column %d", path, error.text, error.line, error.column);
971 
972  goto fail_map;
973  }
974 
975  // Unpack conditional scalar values
976  if (strcmp(map->orientation, "staggered") == 0 || strcmp(map->orientation, "hexagonal") == 0) {
977  unpk = json_unpack_ex(root, &error, 0, "{s:s, s:s}", "staggeraxis", &map->staggeraxis, "staggerindex", &map->staggerindex);
978 
979  if (unpk == -1) {
980  logmsg(TMJ_LOG_ERR, "Could not unpack map[%s], %s at line %d, column %d", path, error.text, error.line, error.column);
981 
982  goto fail_map;
983  }
984  }
985 
986  if (strcmp(map->orientation, "hexagonal") == 0) {
987  unpk = json_unpack_ex(root, &error, 0, "{s:i}", "hexsidelength", &map->hexsidelength);
988 
989  if (unpk == -1) {
990  logmsg(TMJ_LOG_ERR, "Unable to unpack map[%s], %s at line %d, column %d", path, error.text, error.line, error.column);
991 
992  goto fail_map;
993  }
994  }
995 
996  // Unpack properties
997  if (properties != NULL) {
998  map->properties = unpack_properties(properties);
999 
1000  if (map->properties == NULL) {
1001  logmsg(TMJ_LOG_ERR, "Unable to unpack map[%s]->properties", path);
1002 
1003  goto fail_map;
1004  }
1005  }
1006 
1007  // Unpack layers
1008  map->layers = unpack_layers(layers);
1009 
1010  if (map->layers == NULL) {
1011  logmsg(TMJ_LOG_ERR, "Unable to unpack map[%s]->layers", path);
1012 
1013  goto fail_properties;
1014  }
1015 
1016  map->layer_count = json_array_size(layers);
1017 
1018  // Unpack tilesets
1019  if (!json_is_array(tilesets)) {
1020  logmsg(TMJ_LOG_ERR, "Unable to unpack map[%s]->tilesets, tilesets must be an array of Tilesets", path);
1021 
1022  goto fail_layers;
1023  }
1024 
1025  size_t tileset_count = json_array_size(tilesets);
1026 
1027  map->tilesets = calloc(tileset_count, sizeof(Tileset));
1028 
1029  if (map->tilesets == NULL) {
1030  logmsg(TMJ_LOG_ERR, "Unable to unpack map[%s]->tilesets, the system is out of memory", path);
1031 
1032  goto fail_layers;
1033  }
1034 
1035  size_t idx;
1036  json_t* tileset = NULL;
1037 
1038  json_array_foreach(tilesets, idx, tileset) {
1039  char* source = NULL;
1040  int firstgid = 0;
1041 
1042  unpk = json_unpack_ex(tileset, &error, 0, "{s?s, s?i}", "source", &source, "firstgid", &firstgid);
1043 
1044  if (unpk == -1) {
1045  logmsg(TMJ_LOG_ERR, "Unable to unpack map[%s]->tilesets, %s at line %d column %d", path, error.text, error.line, error.column);
1046 
1047  free(map->tilesets);
1048 
1049  goto fail_layers;
1050  }
1051 
1052  // The tileset is not included in the map object, save the firstgid and source
1053  if (source) {
1054  map->tilesets[idx].firstgid = firstgid;
1055  map->tilesets[idx].source = source;
1056  }
1057  // The tileset is embedded in the map, unpack it
1058  else {
1059  if (unpack_tileset(tileset, &map->tilesets[idx]) != 0) {
1060  logmsg(TMJ_LOG_ERR, "Unable to unpack map[%s]->tilesets, could not unpack embedded tileset", path);
1061 
1062  goto fail_tilesets;
1063  }
1064  }
1065 
1066  map->tileset_count = tileset_count;
1067  }
1068 
1069  return map;
1070 
1071 fail_tilesets:
1072  tilesets_free(map->tilesets, map->tileset_count);
1073 
1074 fail_layers:
1075  layers_free(map->layers, map->layer_count);
1076 
1077 fail_properties:
1078  free(map->properties);
1079 
1080 fail_map:
1081  json_decref(root);
1082 
1083  free(map);
1084 
1085  return NULL;
1086 }
1087 
1088 Map* tmj_map_loadf(const char* path, bool check_extension) {
1089  char* ext = strrchr(path, '.');
1090 
1091  if (check_extension) {
1092  if (ext == NULL) {
1093  logmsg(TMJ_LOG_ERR, "Map filename '%s' has no extension", path);
1094 
1095  return NULL;
1096  }
1097 
1098  if (strcmp(ext, ".tmj") != 0 && strcmp(ext, ".json") != 0) {
1099  logmsg(TMJ_LOG_ERR, "Map filename '%s' has unknown extension, '%s'", path, ext);
1100  logmsg(TMJ_LOG_ERR, "Map filename '%s' must have '.tmj' or '.json' extension to be loaded", path);
1101 
1102  return NULL;
1103  }
1104  }
1105 
1106  logmsg(TMJ_LOG_DEBUG, "Loading JSON map file %s", path);
1107 
1108  json_error_t error;
1109  json_t* root = json_load_file(path, JSON_REJECT_DUPLICATES, &error);
1110 
1111  if (root == NULL) {
1112  logmsg(TMJ_LOG_ERR, "Could not load map %s, %s at line %d column %d", path, error.text, error.line, error.column);
1113 
1114  return NULL;
1115  }
1116 
1117  return map_load_json(root, path);
1118 }
1119 
1120 Map* tmj_map_load(const char* map, const char* name) {
1121  json_error_t error;
1122 
1123  json_t* root = json_loads(map, JSON_REJECT_DUPLICATES, &error);
1124 
1125  if (root == NULL) {
1126  logmsg(TMJ_LOG_ERR, "Could not load map %s, %s at line %d column %d", name, error.text, error.line, error.column);
1127 
1128  return NULL;
1129  }
1130 
1131  return map_load_json(root, name);
1132 }
1133 
1134 void tmj_map_free(Map* map) {
1135  if (!map) {
1136  return;
1137  }
1138 
1139  tilesets_free(map->tilesets, map->tileset_count);
1140  layers_free(map->layers, map->layer_count);
1141  free(map->properties);
1142 
1143  json_decref(map->root);
1144 
1145  free(map);
1146 }
void logmsg(tmj_log_priority priority, char *msg,...)
Processes log messages and passes them to the active logging callback, if there is one.
Definition: log.c:23
Map * tmj_map_load(const char *map, const char *name)
Loads the Tiled map from the given JSON object string.
Definition: map.c:1120
Map * tmj_map_loadf(const char *path, bool check_extension)
Loads the Tiled map from the file at the given path.
Definition: map.c:1088
void tmj_map_free(Map *map)
Frees the memory associated with the given map.
Definition: map.c:1134
@ TMJ_LOG_ERR
Definition: tmj.h:500
@ TMJ_LOG_DEBUG
Definition: tmj.h:497
Chunk * unpack_chunks(json_t *chunks)
Definition: map.c:434
Object * unpack_objects(json_t *objects)
Definition: map.c:257
Point * unpack_points(json_t *points)
Unpacks an array of points.
Definition: map.c:155
Map * map_load_json(json_t *root, const char *path)
Definition: map.c:887
Property * unpack_properties(json_t *properties)
Definition: map.c:13
void free_chunks(Chunk *chunks, size_t chunk_count)
Definition: map.c:543
Text * unpack_text(json_t *text)
Unpacks a text object.
Definition: map.c:199
void free_objects(Object *objects, size_t object_count)
Helper function to free Objects.
Definition: map.c:423
Layer * unpack_layers(json_t *layers)
Loads map layers recursively.
Definition: map.c:556
void layers_free(Layer *layers, size_t layer_count)
Helper function for freeing layer tree associated with a map.
Definition: map.c:874
https://doc.mapeditor.org/en/stable/reference/json-map-format/#chunk
Definition: tmj.h:43
unsigned int * data_uint
Definition: tmj.h:54
bool data_is_str
Definition: tmj.h:49
https://doc.mapeditor.org/en/stable/reference/json-map-format/#layer
Definition: tmj.h:127
unsigned int * data_uint
Definition: tmj.h:146
Property * properties
Definition: tmj.h:174
Object * objects
Definition: tmj.h:171
size_t data_count
Definition: tmj.h:144
Chunk * chunks
Definition: tmj.h:165
bool data_is_str
Definition: tmj.h:143
struct Layer * layers
Definition: tmj.h:168
size_t object_count
Definition: tmj.h:170
https://doc.mapeditor.org/en/stable/reference/json-map-format/#json-map-format
Definition: tmj.h:355
int nextlayerid
Definition: tmj.h:377
int height
Definition: tmj.h:375
char * class
Definition: tmj.h:365
char * backgroundcolor
Definition: tmj.h:364
char * staggeraxis
Definition: tmj.h:368
char * renderorder
Definition: tmj.h:367
char * staggerindex
Definition: tmj.h:369
Property * properties
Definition: tmj.h:391
int hexsidelength
Definition: tmj.h:376
double parallaxoriginy
Definition: tmj.h:384
json_t * root
The root object returned by jansson after parsing.
Definition: tmj.h:360
double parallaxoriginx
Definition: tmj.h:383
char * type
Definition: tmj.h:371
Layer * layers
Definition: tmj.h:388
int compressionlevel
Definition: tmj.h:374
char * version
Definition: tmj.h:372
char * orientation
Definition: tmj.h:366
size_t tileset_count
Definition: tmj.h:393
int width
Definition: tmj.h:381
bool infinite
Definition: tmj.h:362
int tileheight
Definition: tmj.h:379
char * tiledversion
Definition: tmj.h:370
size_t layer_count
Definition: tmj.h:387
int tilewidth
Definition: tmj.h:380
Tileset * tilesets
Definition: tmj.h:394
int nextobjectid
Definition: tmj.h:378
https://doc.mapeditor.org/en/stable/reference/json-map-format/#object
Definition: tmj.h:89
size_t property_count
Definition: tmj.h:118
Point * polygon
Definition: tmj.h:114
Text * text
Definition: tmj.h:121
size_t polygon_point_count
Definition: tmj.h:110
size_t polyline_point_count
Definition: tmj.h:111
Point * polyline
Definition: tmj.h:115
Property * properties
Definition: tmj.h:119
https://doc.mapeditor.org/en/stable/reference/json-map-format/#point
Definition: tmj.h:61
https://doc.mapeditor.org/en/stable/reference/json-map-format/#property
Definition: tmj.h:24
https://doc.mapeditor.org/en/stable/reference/json-map-format/#text
Definition: tmj.h:69
bool wrap
Definition: tmj.h:75
bool underline
Definition: tmj.h:74
char * fontfamily
Definition: tmj.h:78
bool bold
Definition: tmj.h:70
bool strikeout
Definition: tmj.h:73
char * color
Definition: tmj.h:77
bool kerning
Definition: tmj.h:72
char * valign
Definition: tmj.h:81
char * halign
Definition: tmj.h:79
bool italic
Definition: tmj.h:71
char * text
Definition: tmj.h:80
int pixelsize
Definition: tmj.h:83
https://doc.mapeditor.org/en/stable/reference/json-map-format/#tileset
Definition: tmj.h:303
char * source
Definition: tmj.h:316
int firstgid
Definition: tmj.h:324
int unpack_tileset(json_t *tileset, Tileset *ret)
Definition: tileset.c:13
void tilesets_free(Tileset *tilesets, size_t tileset_count)
Helper function for freeing tilesets embedded in maps.
Definition: tileset.c:550
The libtmj API.