LibTMJ 1.4.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) {
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 size_t 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 // Unpack data
707 if (strcmp(ret[idx].type, "tilelayer") == 0) {
708 json_t* data = NULL;
709
710 unpk = json_unpack_ex(layer, &error, 0, "{s:o}", "data", &data);
711
712 if (unpk == -1) {
713 logmsg(TMJ_LOG_ERR, "Could not unpack layer[%d], %s at line %d column %d", ret[idx].id, error.text, error.line, error.column);
714
715 goto fail_layer;
716 }
717
718 if (json_is_string(data)) {
719 ret[idx].data_is_str = true;
720
721 unpk = json_unpack_ex(layer, &error, 0, "{s:s}", "data", &ret[idx].data_str);
722
723 if (unpk == -1) {
724 logmsg(TMJ_LOG_ERR, "Could not unpack layer[%d], %s at line %d column %d", ret[idx].id, error.text, error.line, error.column);
725
726 goto fail_layer;
727 }
728 } else if (json_is_array(data)) {
729 ret[idx].data_is_str = false;
730
731 ret[idx].data_count = json_array_size(data);
732
733 ret[idx].data_uint = calloc(ret[idx].data_count, sizeof(unsigned int));
734
735 if (ret[idx].data_uint == NULL) {
736 logmsg(TMJ_LOG_ERR, "Unable to unpack layer[%d]->data, the system is out of memory", ret[idx].id);
737
738 goto fail_layer;
739 }
740
741 json_t* datum;
742
743 size_t idx2;
744
745 json_array_foreach(data, idx2, datum) {
746 unpk = json_unpack_ex(datum, &error, 0, "i", &ret[idx].data_uint[idx2]);
747
748 if (unpk == -1) {
749 goto fail_data;
750 }
751 }
752 } else {
753 logmsg(TMJ_LOG_ERR, "Unable to unpack layer[%d]->data, data must be a string or an array", ret[idx].id);
754
755 goto fail_layer;
756 }
757 }
758
759 // Unpack properties
760 json_t* properties = NULL;
761
762 unpk = json_unpack_ex(layer, &error, 0, "{s?o}", "properties", &properties);
763
764 if (unpk == -1) {
765 goto fail_data;
766 }
767
768 if (properties != NULL) {
769 ret[idx].properties = unpack_properties(properties);
770
771 if (ret[idx].properties == NULL) {
772 logmsg(TMJ_LOG_ERR, "Unable to unpack layer[%d]->properties", ret[idx].id);
773
774 goto fail_data;
775 }
776 }
777
778 // Unpack chunks
779 if (strcmp(ret[idx].type, "tilelayer") == 0) {
780 json_t* chunks = NULL;
781
782 unpk = json_unpack_ex(layer, &error, 0, "{s?o}", "chunks", &chunks);
783
784 if (unpk == -1) {
785 goto fail_properties;
786 }
787
788 if (chunks != NULL) {
789 ret[idx].chunks = unpack_chunks(chunks);
790
791 if (ret[idx].chunks == NULL) {
792 logmsg(TMJ_LOG_ERR, "Unable to unpack layer[%d]->chunks", ret[idx].id);
793
794 goto fail_properties;
795 }
796 }
797 }
798
799 // Unpack objects
800 if (strcmp(ret[idx].type, "objectgroup") == 0) {
801 json_t* objects = NULL;
802
803 unpk = json_unpack_ex(layer, &error, 0, "{s:o}", "objects", &objects);
804
805 if (unpk == -1) {
806 goto fail_chunks;
807 }
808
809 if (objects != NULL) {
810 ret[idx].objects = unpack_objects(objects);
811
812 if (ret[idx].objects == NULL) {
813 logmsg(TMJ_LOG_ERR, "Unable to unpack layer[%d]->objects", ret[idx].id);
814
815 goto fail_chunks;
816 }
817
818 ret[idx].object_count = json_array_size(objects);
819 }
820 }
821
822 // Unpack nested layers
823 if (strcmp(ret[idx].type, "group") == 0) {
824 json_t* nested_layers = NULL;
825
826 unpk = json_unpack_ex(layer, &error, 0, "{s:o}", "layers", &nested_layers);
827
828 if (unpk == -1) {
829 goto fail_objects;
830 }
831
832 if (json_is_array(nested_layers) && json_array_size(nested_layers) > 0) {
833 ret[idx].layers = unpack_layers(nested_layers);
834
835 if (ret[idx].layers == NULL) {
836 logmsg(TMJ_LOG_ERR, "Unable to unpack layer[%d]->layers", ret[idx].id);
837
838 goto fail_objects;
839 }
840 }
841 }
842 }
843
844 return ret;
845
846fail_objects:
847 for (size_t i = 0; i < layer_count; i++) {
848 free_objects(ret[i].objects, ret[i].object_count);
849 }
850fail_chunks:
851 for (size_t i = 0; i < layer_count; i++) {
852 free_chunks(ret[i].chunks, ret[i].chunk_count);
853 }
854
855fail_properties:
856 for (size_t i = 0; i < layer_count; i++) {
857 free(ret[i].properties);
858 }
859
860fail_data:
861 for (size_t i = 0; i < layer_count; i++) {
862 if (!ret[i].data_is_str) {
863 free(ret[i].data_uint);
864 }
865 }
866
867fail_layer:
868 free(ret);
869
870 return NULL;
871}
872
878void layers_free(Layer* layers, size_t layer_count) {
879 for (size_t i = 0; i < layer_count; i++) {
880 free_objects(layers[i].objects, layers[i].object_count);
881 free_chunks(layers[i].chunks, layers[i].chunk_count);
882 free(layers[i].properties);
883 if (!layers[i].data_is_str) {
884 free(layers[i].data_uint);
885 }
886
887 layers_free(layers[i].layers, layers[i].layer_count);
888 }
889
890 free(layers);
891}
892
893Map* map_load_json(json_t* root, const char* path) {
894 json_error_t error;
895
896 Map* map = calloc(1, sizeof(Map));
897
898 if (map == NULL) {
899 logmsg(TMJ_LOG_ERR, "Could not load map '%s', the system is out of memory", path);
900
901 goto fail_map;
902 }
903
904 map->root = root;
905
906 // Verify type (i.e, check that this is a map and not a tileset or something)
907 int unpk = json_unpack_ex(root, &error, 0, "{s:s}", "type", &map->type);
908
909 if (unpk == -1) {
910 logmsg(TMJ_LOG_ERR, "Unable to unpack map[%s]->type, %s at line %d, column %d", path, error.text, error.line, error.column);
911
912 goto fail_map;
913 }
914
915 if (strcmp(map->type, "map") != 0) {
916 logmsg(TMJ_LOG_ERR, "File at path '%s' is of type '%s' and is not a map file", path, map->type);
917
918 goto fail_map;
919 }
920
921 json_t* tilesets = NULL;
922 json_t* layers = NULL;
923 json_t* properties = NULL;
924
925 // Unpack scalar values
926 unpk = json_unpack_ex(root,
927 &error,
928 0,
929 "{"
930 "s:b,"
931 "s?s, s?s, s:s, s:s, s:s, s:s,"
932 "s:i, s:i, s:i, s:i, s:i, s:i, s:i,"
933 "s?F, s?F,"
934 "s:o, s:o, s?o"
935 "}",
936 "infinite",
937 &map->infinite,
938 "backgroundcolor",
939 &map->backgroundcolor,
940 "class",
941 &map->class,
942 "orientation",
943 &map->orientation,
944 "renderorder",
945 &map->renderorder,
946 "tiledversion",
947 &map->tiledversion,
948 "version",
949 &map->version,
950 "compressionlevel",
951 &map->compressionlevel,
952 "height",
953 &map->height,
954 "nextlayerid",
955 &map->nextlayerid,
956 "nextobjectid",
957 &map->nextobjectid,
958 "tileheight",
959 &map->tileheight,
960 "tilewidth",
961 &map->tilewidth,
962 "width",
963 &map->width,
964 "parallaxoriginx",
965 &map->parallaxoriginx,
966 "parallaxoriginy",
967 &map->parallaxoriginy,
968 "tilesets",
969 &tilesets,
970 "layers",
971 &layers,
972 "properties",
973 &properties);
974
975 if (unpk == -1) {
976 logmsg(TMJ_LOG_ERR, "Could not unpack map[%s], %s at line %d, column %d", path, error.text, error.line, error.column);
977
978 goto fail_map;
979 }
980
981 // Unpack conditional scalar values
982 if (strcmp(map->orientation, "staggered") == 0 || strcmp(map->orientation, "hexagonal") == 0) {
983 unpk = json_unpack_ex(root, &error, 0, "{s:s, s:s}", "staggeraxis", &map->staggeraxis, "staggerindex", &map->staggerindex);
984
985 if (unpk == -1) {
986 logmsg(TMJ_LOG_ERR, "Could not unpack map[%s], %s at line %d, column %d", path, error.text, error.line, error.column);
987
988 goto fail_map;
989 }
990 }
991
992 if (strcmp(map->orientation, "hexagonal") == 0) {
993 unpk = json_unpack_ex(root, &error, 0, "{s:i}", "hexsidelength", &map->hexsidelength);
994
995 if (unpk == -1) {
996 logmsg(TMJ_LOG_ERR, "Unable to unpack map[%s], %s at line %d, column %d", path, error.text, error.line, error.column);
997
998 goto fail_map;
999 }
1000 }
1001
1002 // Unpack properties
1003 if (properties != NULL) {
1004 map->properties = unpack_properties(properties);
1005
1006 if (map->properties == NULL) {
1007 logmsg(TMJ_LOG_ERR, "Unable to unpack map[%s]->properties", path);
1008
1009 goto fail_map;
1010 }
1011 }
1012
1013 // Unpack layers
1014 map->layers = unpack_layers(layers);
1015
1016 if (map->layers == NULL) {
1017 logmsg(TMJ_LOG_ERR, "Unable to unpack map[%s]->layers", path);
1018
1019 goto fail_properties;
1020 }
1021
1022 map->layer_count = json_array_size(layers);
1023
1024 // Unpack tilesets
1025 if (!json_is_array(tilesets)) {
1026 logmsg(TMJ_LOG_ERR, "Unable to unpack map[%s]->tilesets, tilesets must be an array of Tilesets", path);
1027
1028 goto fail_layers;
1029 }
1030
1031 size_t tileset_count = json_array_size(tilesets);
1032
1033 map->tilesets = calloc(tileset_count, sizeof(Tileset));
1034
1035 if (map->tilesets == NULL) {
1036 logmsg(TMJ_LOG_ERR, "Unable to unpack map[%s]->tilesets, the system is out of memory", path);
1037
1038 goto fail_layers;
1039 }
1040
1041 size_t idx;
1042 json_t* tileset = NULL;
1043
1044 json_array_foreach(tilesets, idx, tileset) {
1045 char* source = NULL;
1046 int firstgid = 0;
1047
1048 unpk = json_unpack_ex(tileset, &error, 0, "{s?s, s?i}", "source", &source, "firstgid", &firstgid);
1049
1050 if (unpk == -1) {
1051 logmsg(TMJ_LOG_ERR, "Unable to unpack map[%s]->tilesets, %s at line %d column %d", path, error.text, error.line, error.column);
1052
1053 free(map->tilesets);
1054
1055 goto fail_layers;
1056 }
1057
1058 // The tileset is not included in the map object, save the firstgid and source
1059 if (source) {
1060 map->tilesets[idx].firstgid = firstgid;
1061 map->tilesets[idx].source = source;
1062 }
1063 // The tileset is embedded in the map, unpack it
1064 else {
1065 if (unpack_tileset(tileset, &map->tilesets[idx]) != 0) {
1066 logmsg(TMJ_LOG_ERR, "Unable to unpack map[%s]->tilesets, could not unpack embedded tileset", path);
1067
1068 goto fail_tilesets;
1069 }
1070 }
1071
1072 map->tileset_count = tileset_count;
1073 }
1074
1075 return map;
1076
1077fail_tilesets:
1079
1080fail_layers:
1081 layers_free(map->layers, map->layer_count);
1082
1083fail_properties:
1084 free(map->properties);
1085
1086fail_map:
1087 json_decref(root);
1088
1089 free(map);
1090
1091 return NULL;
1092}
1093
1094Map* tmj_map_loadf(const char* path, bool check_extension) {
1095 char* ext = strrchr(path, '.');
1096
1097 if (check_extension) {
1098 if (ext == NULL) {
1099 logmsg(TMJ_LOG_ERR, "Map filename '%s' has no extension", path);
1100
1101 return NULL;
1102 }
1103
1104 if (strcmp(ext, ".tmj") != 0 && strcmp(ext, ".json") != 0) {
1105 logmsg(TMJ_LOG_ERR, "Map filename '%s' has unknown extension, '%s'", path, ext);
1106 logmsg(TMJ_LOG_ERR, "Map filename '%s' must have '.tmj' or '.json' extension to be loaded", path);
1107
1108 return NULL;
1109 }
1110 }
1111
1112 logmsg(TMJ_LOG_DEBUG, "Loading JSON map file %s", path);
1113
1114 json_error_t error;
1115 json_t* root = json_load_file(path, JSON_REJECT_DUPLICATES, &error);
1116
1117 if (root == NULL) {
1118 logmsg(TMJ_LOG_ERR, "Could not load map %s, %s at line %d column %d", path, error.text, error.line, error.column);
1119
1120 return NULL;
1121 }
1122
1123 return map_load_json(root, path);
1124}
1125
1126Map* tmj_map_load(const char* map, const char* name) {
1127 json_error_t error;
1128
1129 json_t* root = json_loads(map, JSON_REJECT_DUPLICATES, &error);
1130
1131 if (root == NULL) {
1132 logmsg(TMJ_LOG_ERR, "Could not load map %s, %s at line %d column %d", name, error.text, error.line, error.column);
1133
1134 return NULL;
1135 }
1136
1137 return map_load_json(root, name);
1138}
1139
1140void tmj_map_free(Map* map) {
1141 if (!map) {
1142 return;
1143 }
1144
1146 layers_free(map->layers, map->layer_count);
1147 free(map->properties);
1148
1149 json_decref(map->root);
1150
1151 free(map);
1152}
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:1094
void tmj_map_free(Map *map)
Frees the memory associated with the given map.
Definition map.c:1140
Map * tmj_map_load(const char *map, const char *name)
Loads the Tiled map from the given JSON object string.
Definition map.c:1126
@ 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:893
Text * unpack_text(json_t *text)
Unpacks a text object.
Definition map.c:199
Chunk * unpack_chunks(json_t *chunks)
Definition map.c:433
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
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:878
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.