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