Mercurial > molko
changeset 25:0d5ecefcccd3
molko-map: reimplement using JSON as it is supported by tiled, closes #2448 @4h
author | David Demelier <markand@malikania.fr> |
---|---|
date | Fri, 10 Jan 2020 20:08:26 +0100 |
parents | 4a06503641eb |
children | 65398df5fb5c |
files | .hgignore INSTALL.md Makefile src/main.c tools/molko-map.c |
diffstat | 5 files changed, 226 insertions(+), 311 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgignore Fri Jan 10 13:30:01 2020 +0100 +++ b/.hgignore Fri Jan 10 20:08:26 2020 +0100 @@ -9,11 +9,12 @@ \.DS_Store$ # object files. +\.d$ +\.o$ ^libmolko\.a$ ^molko$ ^tests/test-color$ -\.d$ -\.o$ +^tools/molko-map$ # doxygen stuff. ^doxygen/html$
--- a/INSTALL.md Fri Jan 10 13:30:01 2020 +0100 +++ b/INSTALL.md Fri Jan 10 20:08:26 2020 +0100 @@ -8,7 +8,7 @@ - C99 compliant compiler, - POSIX system (make, ar, shell), -- [Expat][], XML parsing library, +- [Jansson][], JSON parsing library, - [SDL2][], Multimedia library, - [SDL2_image][], Image loading addon for SDL2, - [SDL2_ttf][], Fonts addon for SDL2, @@ -46,7 +46,7 @@ Note: replace `x86_64` with `i686` if you have a deprecated system. -[Expat][]: https://libexpat.github.io +[Jansson][]: http://www.digip.org/jansson [SDL2]: http://libsdl.org [SDL2_image]: https://www.libsdl.org/projects/SDL_image [SDL2_ttf]: https://www.libsdl.org/projects/SDL_ttf
--- a/Makefile Fri Jan 10 13:30:01 2020 +0100 +++ b/Makefile Fri Jan 10 20:08:26 2020 +0100 @@ -38,8 +38,8 @@ SDL_CFLAGS= `pkg-config --cflags sdl2 SDL2_image SDL2_mixer SDL2_ttf` SDL_LDFLAGS= `pkg-config --libs sdl2 SDL2_image SDL2_mixer SDL2_ttf` -EXPAT_CFLAGS= `pkg-config --cflags expat` -EXPAT_LDFLAGS= `pkg-config --libs expat` +JANSSON_CFLAGS= `pkg-config --cflags jansson` +JANSSON_LDFLAGS=`pkg-config --libs jansson` TESTS= tests/test-color.c TESTS_INCS= -I extern/libgreatest -I src ${SDL_CFLAGS} @@ -78,7 +78,7 @@ tools: ${TOOLS_OBJS} tools/molko-map: tools/molko-map.c - ${CC} -o $@ $< ${CFLAGS} ${EXPAT_CFLAGS} ${EXPAT_LDFLAGS} + ${CC} -o $@ $< ${CFLAGS} ${JANSSON_CFLAGS} ${JANSSON_LDFLAGS} doxygen: doxygen doxygen/Doxyfile
--- a/src/main.c Fri Jan 10 13:30:01 2020 +0100 +++ b/src/main.c Fri Jan 10 20:08:26 2020 +0100 @@ -41,7 +41,7 @@ sys_init(); window_init("Molko's Adventure", 1280, 720); - if (!(font = font_openf("assets/fonts/DejaVuSerifCondensed.ttf", 12))) + if (!(font = font_openf("assets/fonts/DejaVuSansCondensed.ttf", 12))) exit(1); struct message welcome = {
--- a/tools/molko-map.c Fri Jan 10 13:30:01 2020 +0100 +++ b/tools/molko-map.c Fri Jan 10 20:08:26 2020 +0100 @@ -1,5 +1,5 @@ /* - * molko-map.c -- convert tiled .tmx into custom files + * molko-map.c -- convert tiled tiled JSON files into custom files * * Copyright (c) 2020 David Demelier <markand@malikania.fr> * @@ -21,303 +21,10 @@ #include <stdbool.h> #include <stdio.h> #include <stdlib.h> +#include <stdnoreturn.h> #include <string.h> -#include <expat.h> - -enum state { - STATE_ROOT, /* no tag read yet */ - STATE_MAP, /* top <map> */ - STATE_MAP_PROPERTIES, /* <map><properties> */ - STATE_TILESET, /* <tileset> */ - STATE_TILESET_IMAGE, /* <tileset><image> */ - STATE_LAYER, /* <layer> */ - STATE_LAYER_DATA, /* <layer><data> */ -}; - -enum layer { - LAYER_NONE, - LAYER_BACKGROUND, - LAYER_FOREGROUND -}; - -struct context { - enum state state; - enum layer layer; -}; - -static const int layer_indices[] = { - [LAYER_NONE] = -1, - [LAYER_BACKGROUND] = 0, - [LAYER_FOREGROUND] = 1 -}; - -static void on_data(struct context *, const char *, const char **); -static void on_image(struct context *, const char *, const char **); -static void on_layer(struct context *, const char *, const char **); -static void on_map(struct context *, const char *, const char **); -static void on_properties(struct context *, const char *, const char **); -static void on_property(struct context *, const char *, const char **); -static void on_tile(struct context *, const char *, const char **); -static void on_tileset(struct context *, const char *, const char **); -static void die(const char *, ...); - -static const struct { - const char *name; - void (*begin)(struct context *, const char *, const char **); - void (*end)(struct context *, const char *); -} handlers[] = { - { "data", on_data, NULL }, - { "image", on_image, NULL }, - { "layer", on_layer, NULL }, - { "map", on_map, NULL }, - { "properties", on_properties, NULL }, - { "property", on_property, NULL }, - { "tile", on_tile, NULL }, - { "tileset", on_tileset, NULL }, - { NULL, NULL, NULL }, -}; - -static bool -is_known_map_property(const char *name) -{ - static const char *known[] = { - "title", - NULL - }; - - for (size_t i = 0; known[i]; ++i) - if (strcmp(known[i], name) == 0) - return true; - - return false; -} - -static void -write_map_property(const char **attrs) -{ - /* - * This is how <map><properties> are defined: - * - * 0 1 2 3 - * <property name="PROPERTY NAME" value="PROPERTY VALUE"> - */ - for (size_t i = 0; attrs[i]; i += 4) { - if (is_known_map_property(attrs[i + 1])) - printf("%s|%s\n", attrs[i + 1], attrs[i + 3]); - else - die("unsupported map property: %s\n", attrs[i + 1]); - } -} - -static const char * -find_attr(const char **attrs, const char *key) -{ - for (size_t i = 0; attrs[i]; i += 2) - if (strcmp(attrs[i], key) == 0) - return attrs[i + 1]; - - return NULL; -} - -static void -on_map(struct context *ctx, const char *name, const char **attrs) -{ - (void)ctx; - (void)name; - (void)attrs; - - ctx->state = STATE_MAP; -} - -static void -on_properties(struct context *ctx, const char *name, const char **attrs) -{ - (void)name; - (void)attrs; - - /* - * <properties> exist in different parent tags - */ - switch (ctx->state) { - case STATE_MAP: - ctx->state = STATE_MAP_PROPERTIES; - break; - default: - fprintf(stderr, "warning: unsupported <properties>\n"); - break; - } -} - -static void -on_property(struct context *ctx, const char *name, const char **attrs) -{ - (void)name; - (void)attrs; - - switch (ctx->state) { - case STATE_MAP_PROPERTIES: - write_map_property(attrs); - break; - default: - break; - } -} - -static void -on_layer(struct context *ctx, const char *name, const char **attrs) -{ - (void)name; - - /* - * The layer is defined like this: - * - * Values of attrs: - * 0 1 IGNORED - * <layer name="background" width="10" height="10"> - */ - const char *layername = find_attr(attrs, "name"); - - if (!layername) - die("layer is missing 'name' attribute\n"); - - if (strcmp(layername, "background") == 0) - ctx->layer = LAYER_BACKGROUND; - else - die("invalid '%s' value for 'name' property\n", layername); - - ctx->state = STATE_LAYER; -} - -static void -on_data(struct context *ctx, const char *name, const char **attrs) -{ - (void)name; - (void)attrs; - - if (ctx->state != STATE_LAYER) - die("reading <data> outside a <layer> node\n"); - - ctx->state = STATE_LAYER_DATA; -} - -static void -on_tile(struct context *ctx, const char *name, const char **attrs) -{ - (void)name; - - /* TODO: add more layer */ - if (ctx->state != STATE_LAYER_DATA) - die("reading <tile> outside a <data> node\n"); - - /* - * The tile is defined as following: - * - * Values of attrs: - * 0 1 - * <tile gid="1" /> - */ - const char *gid = find_attr(attrs, "gid"); - - if (!gid) - die("missing 'gid' attribute in <tile> node\n"); - - printf("tile|%d|%s\n", layer_indices[ctx->layer], gid); -} - -static void -on_tileset(struct context *ctx, const char *name, const char **attrs) -{ - (void)ctx; - (void)name; - (void)attrs; - - ctx->state = STATE_TILESET; -} - -static void -on_image(struct context *ctx, const char *name, const char **attrs) -{ - (void)name; - - if (ctx->state != STATE_TILESET) - die("reading <image> outside a <tileset> node\n"); - - /** - * The tile is defined as following: - * - * Values of attrs: - * 0 1 - * <image source="name.png" ... /> - */ - const char *src = find_attr(attrs, "source"); - - if (!src) - die("missing 'source' attribute in <image> node\n"); - - printf("tileset|%s\n", src); -} - -static void -on_start_element(struct context *ctx, const char *name, const char **attrs) -{ - assert(ctx); - assert(name); - assert(attrs); - - size_t i; - - for (i = 0; handlers[i].name; ++i) { - if (strcmp(name, handlers[i].name) == 0) { - handlers[i].begin(ctx, name, attrs); - break; - } - } - - if (!handlers[i].name) - fprintf(stderr, "unsupported <%s> tag\n", name); -} - -static void -on_end_element(void *data, const char *name) -{ - (void)data; - (void)name; -#if 0 - size_t i; - - for (i = 0; handlers[i].name; ++i) { - if (strcmp(name, handlers[i].name) == 0) { - handlers[i].end(ctx, name, attrs); - break; - } - } - - if (!handlers[i].name) - fprintf(stderr, "unsupported <%s> tag\n", name); -#endif -} - -static void -run(XML_Parser parser) -{ - char buffer[BUFSIZ]; - size_t nbread; - struct context ctx = {0}; - - XML_SetUserData(parser, &ctx); - - while ((nbread = fread(buffer, 1, sizeof (buffer), stdin)) > 0) { - bool done = nbread < sizeof (buffer); - - if (XML_Parse(parser, buffer, nbread, done) == XML_STATUS_ERROR) { - fprintf(stderr, "%lu: %s\n", - XML_GetCurrentLineNumber(parser), - XML_ErrorString(XML_GetErrorCode(parser))); - exit(1); - } - } -} +#include <jansson.h> static void die(const char *fmt, ...) @@ -332,16 +39,223 @@ exit(1); } +static bool +is_layer(const char *name) +{ + return strcmp(name, "background") == 0 || + strcmp(name, "foreground") == 0 || + strcmp(name, "objects") == 0; +} + +static void +write_properties(const json_t *props) +{ + size_t index; + json_t *prop, *name, *type, *value; + + if (!json_is_array(props)) + return; + + json_array_foreach(props, index, prop) { + name = json_object_get(prop, "name"); + type = json_object_get(prop, "type"); + value = json_object_get(prop, "value"); + + if (!json_is_object(prop) || + !json_is_string(name) || + !json_is_string(type) || + !json_is_string(value)) + die("invalid property\n"); + + printf("%s|%s\n", json_string_value(name), + json_string_value(value)); + } +} + +static void +write_metadata(const json_t *document) +{ + json_t *height = json_object_get(document, "height"); + json_t *width = json_object_get(document, "width"); + + if (!height || !json_is_integer(height)) + die("missing 'height' property\n"); + if (!width || !json_is_integer(width)) + die("missing 'width' property\n"); + + printf("width|%lld\n", json_integer_value(width)); + printf("height|%lld\n", json_integer_value(height)); +} + +static void +write_object_property(int id, const json_t *property) +{ + assert(json_is_object(property)); + + json_t *name = json_object_get(property, "name"); + json_t *type = json_object_get(property, "type"); + json_t *value = json_object_get(property, "value"); + + if (!name || !json_is_string(name)) + die("invalid 'name' property in object"); + if (!type || !json_is_string(type)) + die("invalid 'type' property in object"); + if (!value || !json_is_string(value)) + die("invalid 'value' property in object"); + + printf("object-property|%d|%s|%s\n", + id, + json_string_value(name), + json_string_value(value) + ); +} + +static void +write_object(const json_t *object) +{ + assert(json_is_object(object)); + + json_t *id = json_object_get(object, "id"); + json_t *x = json_object_get(object, "x"); + json_t *y = json_object_get(object, "y"); + json_t *width = json_object_get(object, "width"); + json_t *height = json_object_get(object, "height"); + json_t *type = json_object_get(object, "type"); + json_t *props = json_object_get(object, "properties"); + + if (!id || !json_is_integer(id)) + die("invalid 'id' property in object\n"); + if (!x || !json_is_real(x)) + die("invalid 'x' property in object\n"); + if (!y || !json_is_real(y)) + die("invalid 'y' property in object\n"); + if (!width || !json_is_real(width)) + die("invalid 'width' property in object\n"); + if (!height || !json_is_real(height)) + die("invalid 'height' property in object\n"); + if (!type || !json_is_string(type)) + die("invalid 'type' property in object\n"); + + /* In tiled, those properties are float but we only use ints in MA */ + printf("object|%lld|%s|%d|%d|%d|%d\n", + json_integer_value(id), + json_string_value(type), + (int)json_real_value(x), + (int)json_real_value(y), + (int)json_real_value(width), + (int)json_real_value(height) + ); + + if (json_is_array(props)) { + json_t *prop; + size_t index; + + json_array_foreach(props, index, prop) { + if (!json_is_object(prop)) + die("invalid property in object\n"); + + write_object_property(json_integer_value(id), prop); + } + } +} + +static void +write_layer(const json_t *layer) +{ + json_t *objects = json_object_get(layer, "objects"); + json_t *data = json_object_get(layer, "data"); + json_t *name = json_object_get(layer, "name"); + json_t *tile, *object; + size_t index; + + if (!name || !json_is_string(name)) + die("invalid 'name' property in layer"); + if (!is_layer(json_string_value(name))) + die("invalid 'name' layer: %s\n", json_string_value(name)); + + printf("layer|%s\n", json_string_value(name)); + + /* Only foreground/background have 'data' property */ + if (json_is_array(data)) { + json_array_foreach(data, index, tile) { + if (!json_is_integer(tile)) + die("invalid 'data' property in layer\n"); + + printf("%lld\n", json_integer_value(tile)); + } + } + + /* Only objects has 'objects' property */ + if (json_is_array(objects)) { + json_array_foreach(objects, index, object) { + if (!json_is_object(object)) + die("invalid 'objects' property in layer\n"); + + write_object(object); + } + } +} + +static void +write_layers(const json_t *layers) +{ + size_t index; + json_t *layer; + + if (!layers) + return; + + json_array_foreach(layers, index, layer) { + if (!json_is_object(layer)) + die("layer is not an object\n"); + + write_layer(layer); + } +} + +static void +write_tileset(const json_t *tileset) +{ + json_t *image = json_object_get(tileset, "image"); + + if (!image || !json_is_string(image)) + die("invalid 'image' property in tileset"); + + printf("tileset|%s\n", json_string_value(image)); +} + +static void +write_tilesets(const json_t *tilesets) +{ + json_t *tileset; + size_t index; + + if (!json_is_array(tilesets)) + die("invalid 'tilesets' property"); + + json_array_foreach(tilesets, index, tileset) { + if (!json_is_object(tileset)) + die("invalid tileset"); + + write_tileset(tileset); + } +} + int main(void) { - XML_Parser parser; + json_t *document; + json_error_t error; + + document = json_loadf(stdin, 0, &error); + + if (!document) + die("%d:%d: %s\n", error.line, error.column, error.text); - parser = XML_ParserCreate(NULL); - XML_SetElementHandler(parser, - (XML_StartElementHandler)on_start_element, on_end_element); + write_properties(json_object_get(document, "properties")); + write_metadata(document); + write_layers(json_object_get(document, "layers")); + write_tilesets(json_object_get(document, "tilesets")); - run(parser); - - XML_ParserFree(parser); + json_decref(document); }