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