summary refs log tree commit diff
path: root/utl/json2map/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'utl/json2map/main.c')
-rw-r--r--utl/json2map/main.c356
1 files changed, 356 insertions, 0 deletions
diff --git a/utl/json2map/main.c b/utl/json2map/main.c
new file mode 100644
index 0000000..3bc7ba4
--- /dev/null
+++ b/utl/json2map/main.c
@@ -0,0 +1,356 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include "cJSON.h"
+#include "common.h"
+
+struct tilemap_T {
+	unsigned width, height;
+	int *tilemap;
+};
+
+#define PACKED __attribute__((__packed__))
+
+struct PACKED sets {
+	uint32_t tilesets_count;
+	uint32_t wang_count;
+};
+
+struct PACKED map {
+	uint32_t width;
+	uint32_t height;
+	uint32_t layers;
+	uint8_t tiles[];
+};
+
+struct tilemap_T make_tilemap(cJSON const *const layer, int const wangStart, int const wangSize) {
+	cJSON const *width_obj = cJSON_GetObjectItemCaseSensitive(layer, "width");
+	cJSON const *height_obj = cJSON_GetObjectItemCaseSensitive(layer, "height");
+	if (!cJSON_IsNumber(width_obj) || !cJSON_IsNumber(height_obj)) {
+		fputs("missing tile layer size\n", stderr);
+		return (struct tilemap_T) {0, 0, NULL};
+	}
+	int const width = width_obj->valueint;
+	int const height = height_obj->valueint;
+	cJSON const *data = cJSON_GetObjectItemCaseSensitive(layer, "data");
+	int const size = cJSON_GetArraySize(data);
+	if (width * height != size) {
+		fputs("tile data size mismatch", stderr);
+		return (struct tilemap_T) {0, 0, NULL};
+	}
+	struct tilemap_T in = {.tilemap = NULL, .width = width, .height = height};
+	in.tilemap = malloc(size * sizeof (int));
+	cJSON const *tile = NULL;
+	size_t i = 0;
+	cJSON_ArrayForEach (tile, data) {
+		in.tilemap[i] = tile->valueint;
+		i++;
+	}
+	struct tilemap_T out = {.tilemap = NULL, .width = width - 1, .height = height - 1};
+	out.tilemap = malloc(out.width * out.height * sizeof (int));
+	printf("=== tilemap (%d tiles) ===\n", size);
+	for (int y = 0; y < out.height; y++) {
+		for (int x = 0; x < out.width; x++) {
+			int a = in.tilemap[x + y * in.width];
+			if (a == 0) {
+				out.tilemap[x + y * out.width] = 0;
+			} else if (a < wangStart) {
+				out.tilemap[x + y * out.width] = a - 1;
+			} else if (a > wangStart + wangSize) {
+				out.tilemap[x + y * out.width] = a - wangSize - 1;
+			} else {
+				int b = (in.tilemap[x + y * in.width] - wangStart) >> 4;
+				int c = (in.tilemap[(x + 1) + y * in.width] - wangStart) >> 4;
+				int d = (in.tilemap[x + (y + 1) * in.width] - wangStart) >> 4;
+				int e = (in.tilemap[(x + 1) + (y + 1) * in.width] - wangStart) >> 4;
+				int f = b == c && b == d && b == e;
+				if (f) {
+					out.tilemap[x + y * out.width] = b + 0xf0;
+				} else {
+					out.tilemap[x + y * out.width] = 0;
+				}
+			}
+		}
+	}
+	free(in.tilemap);
+	return out;
+}
+
+size_t safe_write(void const *restrict buf, size_t size, FILE *restrict stream) {
+	if (stream == NULL) {
+		return 0;
+	}
+	return fwrite(buf, 1, size, stream);
+}
+
+int main(int argc, char **argv) {
+	if (argc != 2 && argc != 3) {
+		return EXIT_FAILURE;
+	}
+	struct blob file = load_file(argv[1]);
+	cJSON *json = cJSON_ParseWithLength(file.data, file.size);
+	free(file.data);
+	
+	FILE *outfile = NULL;
+	if (argc == 3) {
+		outfile = fopen(argv[2], "wb");
+	}
+	
+	int status = EXIT_SUCCESS;
+	if (json == NULL) {
+		puts("null");
+		status = EXIT_FAILURE;
+		goto end;
+	}
+	cJSON const *tilewidth = cJSON_GetObjectItemCaseSensitive(json, "tilewidth");
+	cJSON const *tileheight = cJSON_GetObjectItemCaseSensitive(json, "tileheight");
+	if (!cJSON_IsNumber(tilewidth) || !cJSON_IsNumber(tileheight) || tilewidth->valueint != 8 || tileheight->valueint != 8) {
+		status = EXIT_FAILURE;
+		goto end;
+	}
+
+	cJSON const *tilesets = cJSON_GetObjectItemCaseSensitive(json, "tilesets");
+	cJSON const *tileset = NULL;
+	int wangStart = 0;
+	int wangSize = 0;
+
+	char *wang_tileset = NULL;
+	uint32_t wang_height;
+	char *tilesets_data = NULL;
+	size_t tilesets_size = 0;
+	struct sets counts = {0, 0};
+	
+	cJSON_ArrayForEach(tileset, tilesets) {
+		printf("=== tileset (%% tiles) ===\n");
+		cJSON const *wangs = cJSON_GetObjectItemCaseSensitive(tileset, "wangsets");
+		if (wangs != NULL) {
+			cJSON const *wang = NULL;
+			cJSON_ArrayForEach(wang, wangs) {
+				puts("=== wang ===");
+				cJSON const *type = cJSON_GetObjectItemCaseSensitive(wang, "type");
+				if (strcmp("corner", type->valuestring) == 0) {
+					if (wangStart != 0) {
+						fputs("two or more corner sets\n", stderr);
+						status = EXIT_FAILURE;
+						goto end;
+					}
+					wangStart = cJSON_GetObjectItemCaseSensitive(tileset, "firstgid")->valueint;
+					wangSize = cJSON_GetObjectItemCaseSensitive(tileset, "tilecount")->valueint;
+					wang_tileset = cJSON_GetObjectItemCaseSensitive(tileset, "image")->valuestring;
+					switch (cJSON_GetObjectItemCaseSensitive(tileset, "imagewidth")->valueint) {
+						case 32:
+							wang_height = cJSON_GetObjectItemCaseSensitive(tileset, "imageheight")->valueint / 4;
+							break;
+						case 128:
+							wang_height = cJSON_GetObjectItemCaseSensitive(tileset, "imageheight")->valueint;
+							break;
+						default:
+							goto end;
+					}
+					printf("=== wang %u-%u ===\n", wangStart, wangStart + wangSize);
+					counts.wang_count++;
+					goto wang_continue;
+				}
+			}
+		}
+		cJSON const *image = cJSON_GetObjectItemCaseSensitive(tileset, "image");
+		{
+			char const *const str = image->valuestring;
+			size_t start = 0, length = strlen(str);
+			for (size_t i = 0; i < length; i++) {
+				if (str[i] == '/') {
+					start = i + 1;
+				}
+			}
+			length -= start;
+			for (size_t i = length; i > 0; i--) {
+				if (str[start + i] == '.') {
+					length = i;
+					break;
+				}
+			}
+			//printf("%.*s\n", (int) length, str + start);
+			uint32_t height = cJSON_GetObjectItemCaseSensitive(tileset, "imageheight")->valueint;
+			tilesets_data = realloc(tilesets_data, tilesets_size + length + sizeof (uint32_t) + 1);
+			memcpy(tilesets_data + tilesets_size, &height, sizeof (uint32_t));
+			memcpy(tilesets_data + tilesets_size + sizeof (uint32_t), str + start, length);
+			tilesets_data[tilesets_size + sizeof (uint32_t) + length] = 0;
+			puts(tilesets_data + tilesets_size + sizeof (uint32_t));
+			tilesets_size += length + sizeof (uint32_t) + 1;
+			counts.tilesets_count++;
+		}
+		puts(image->valuestring);
+		#if 0
+		char *str = cJSON_Print(tileset);
+		puts(str);
+		free(str);
+		#endif
+		wang_continue:;
+	}
+	if (counts.wang_count == 1) {
+		char const *const str = wang_tileset;
+		size_t start = 0, length = strlen(str);
+		for (size_t i = 0; i < length; i++) {
+			if (str[i] == '/') {
+				start = i + 1;
+			}
+		}
+		length -= start;
+		for (size_t i = length; i > 0; i--) {
+			if (str[start + i] == '.') {
+				length = i;
+				break;
+			}
+		}
+		//printf("%.*s\n", (int) length, str + start);
+		wang_tileset = malloc(length + 1);
+		memcpy(wang_tileset, str + start, length);
+		wang_tileset[length] = 0;
+		puts(wang_tileset);
+	}
+
+	cJSON const *width_obj = cJSON_GetObjectItemCaseSensitive(json, "width");
+	cJSON const *height_obj = cJSON_GetObjectItemCaseSensitive(json, "height");
+	if (!cJSON_IsNumber(width_obj) || !cJSON_IsNumber(height_obj)) {
+		fputs("missing tilemap size\n", stderr);
+		return 1;
+	}
+	struct map map = {
+		.width = width_obj->valueint - 1,
+		.height = height_obj->valueint - 1,
+		.layers = 0,
+	};
+	uint8_t *tile_data = NULL;
+	float *parallaxes = NULL;
+	
+	cJSON const *layers = cJSON_GetObjectItemCaseSensitive(json, "layers");
+	cJSON const *layer = NULL;
+	cJSON_ArrayForEach (layer, layers) {
+		cJSON const *type_obj = cJSON_GetObjectItemCaseSensitive(layer, "type");
+		if (!cJSON_IsString(type_obj)) {
+			status = EXIT_FAILURE;
+			goto end;
+		}
+		char const *type = type_obj->valuestring;
+		puts(type);
+		if (strcmp(type, "group") == 0) {
+			// group layer
+			cJSON const *sublayers = cJSON_GetObjectItemCaseSensitive(layer, "layers");
+			cJSON const *sublayer = NULL;
+			struct tilemap_T layert = {.tilemap = NULL, .width = 0, .height = 0};
+			cJSON_ArrayForEach (sublayer, sublayers) {
+				struct tilemap_T tilemap = make_tilemap(sublayer, wangStart, wangSize);
+				if (tilemap.tilemap == NULL) {
+					status = EXIT_FAILURE;
+					goto end;
+				}
+				if (layert.tilemap == NULL) {
+					layert.width = tilemap.width;
+					layert.height = tilemap.height;
+					layert.tilemap = malloc(layert.width * layert.height * sizeof (int));
+					memcpy(layert.tilemap, tilemap.tilemap, layert.width * layert.height * sizeof (int));
+				} else {
+					for (size_t i = 0; i < layert.width * layert.height; i++) {
+						if (tilemap.tilemap[i] != 0) {
+							layert.tilemap[i] = tilemap.tilemap[i];
+						}
+					}
+				}
+				free(tilemap.tilemap);
+			}
+			for (int y = 0; y < layert.height; y++) {
+				for (int x = 0; x < layert.width; x++) {
+					printf("%2x,", layert.tilemap[x + y * layert.width]);
+				}
+				fputc('\n', stdout);
+			}
+
+			size_t const layer_size = map.width * map.height;
+			tile_data = realloc(tile_data, layer_size * (map.layers + 1));
+			for (size_t i = 0; i < layer_size; i++) {
+				tile_data[layer_size * map.layers + i] = layert.tilemap[i];
+			}
+			
+			parallaxes = realloc(parallaxes, sizeof (float) * 2 * (map.layers + 1));
+			float px = 1.0f, py = 1.0f;
+			cJSON const *parallax;
+			parallax = cJSON_GetObjectItemCaseSensitive(layer, "parallaxx");
+			if (cJSON_IsNumber(parallax)) {
+				px = parallax->valuedouble;
+			}
+			parallax = cJSON_GetObjectItemCaseSensitive(layer, "parallaxy");
+			if (cJSON_IsNumber(parallax)) {
+				py = parallax->valuedouble;
+			}
+			printf("parallax: %fx%f\n", px, py);
+			parallaxes[map.layers * 2 + 0] = px;
+			parallaxes[map.layers * 2 + 1] = py;
+			
+			map.layers++;
+			free(layert.tilemap);
+		} else if (strcmp(type, "tilelayer") == 0) {
+			// tile layer
+			struct tilemap_T tilemap = make_tilemap(layer, wangStart, wangSize);
+			if (tilemap.tilemap == NULL) {
+				status = EXIT_FAILURE;
+				goto end;
+			}
+			for (int y = 0; y < tilemap.height; y++) {
+				for (int x = 0; x < tilemap.width; x++) {
+					printf("%2x,", tilemap.tilemap[x + y * tilemap.width]);
+				}
+				fputc('\n', stdout);
+			}
+
+			size_t const layer_size = map.width * map.height;
+			tile_data = realloc(tile_data, layer_size * (map.layers + 1));
+			for (size_t i = 0; i < layer_size; i++) {
+				tile_data[layer_size * map.layers + i] = tilemap.tilemap[i];
+			}
+			
+			parallaxes = realloc(parallaxes, sizeof (float) * 2 * (map.layers + 1));
+			float px = 1.0f, py = 1.0f;
+			cJSON const *parallax;
+			parallax = cJSON_GetObjectItemCaseSensitive(layer, "parallaxx");
+			if (cJSON_IsNumber(parallax)) {
+				px = parallax->valuedouble;
+			}
+			parallax = cJSON_GetObjectItemCaseSensitive(layer, "parallaxy");
+			if (cJSON_IsNumber(parallax)) {
+				py = parallax->valuedouble;
+			}
+			printf("parallax: %fx%f\n", px, py);
+			parallaxes[map.layers * 2 + 0] = px;
+			parallaxes[map.layers * 2 + 1] = py;
+			
+			map.layers++;
+			free(tilemap.tilemap);
+			#if 0
+			char *str = cJSON_Print(layer);
+			puts(str);
+			free(str);
+			#endif
+		} else {
+			// ??? layer
+		}
+	}
+	
+	fwrite(&counts, 1, sizeof (counts), outfile);
+	fwrite(tilesets_data, 1, tilesets_size, outfile);
+	if (wang_tileset != NULL) {
+		fwrite(&wang_height, 1, sizeof (uint32_t), outfile);
+		fwrite(wang_tileset, 1, strlen(wang_tileset) + 1, outfile);
+	}
+	fwrite(&map, 1, sizeof (map), outfile);
+	fwrite(tile_data, 1, map.width * map.height * map.layers, outfile);
+	fwrite(parallaxes, 1, sizeof (float) * 2 * map.layers, outfile);
+	
+	free(tile_data);
+	end:
+	if (outfile != NULL) {
+		fclose(outfile);
+	}
+	cJSON_Delete(json);
+	return status;
+}