fixed scroll and added json2tmap tool
authorJohn Tsiombikas <nuclear@member.fsf.org>
Sat, 20 Jan 2024 06:00:49 +0000 (08:00 +0200)
committerJohn Tsiombikas <nuclear@member.fsf.org>
Sat, 20 Jan 2024 06:00:49 +0000 (08:00 +0200)
.gitignore
Makefile
src/part_simba.c
tools/json2tmap/Makefile [new file with mode: 0644]
tools/json2tmap/dynarr.c [new file with mode: 0644]
tools/json2tmap/dynarr.h [new file with mode: 0644]
tools/json2tmap/json.c [new file with mode: 0644]
tools/json2tmap/json.h [new file with mode: 0644]
tools/json2tmap/json2tmap.c [new file with mode: 0644]

index 0f77bed..09004c7 100644 (file)
@@ -6,6 +6,7 @@
 *.map
 *.z80
 tools/pngdump/pngdump
+tools/json2tmap/json2tmap
 core
 disasm
 .cache/
index d44e573..dff14a7 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -51,7 +51,7 @@ disasm: $(elf)
        $(OBJDUMP) -D $< >$@
 
 src/data-asm.o: src/data.s data/font8x8.img data/cellspr.img data/simbabg.tiles \
-       data/simbafg.tiles
+       data/simbafg.tiles data/simbafg.tmap
 src/z80prog-asm.o: src/z80prog.s $(z80bin)
 
 data/cellspr.img: data/cellspr.png
@@ -76,6 +76,10 @@ clean:
 cleandep:
        rm -f $(dep)
 
+.PHONY: cleandata
+cleandata:
+       rm -f data/*.img data/*.tiles data/*.tmap data/*.cmap
+
 .PHONY: run
 run: $(bin)
        mednafen $<
@@ -97,10 +101,20 @@ debug: $(bin)
 
 # ---- tools ----
 PNGDUMP = tools/pngdump/pngdump
+JSON2TMAP = tools/json2tmap/json2tmap
 
 $(PNGDUMP): tools/pngdump/main.c tools/pngdump/image.c tools/pngdump/quant.c
        $(MAKE) -C tools/pngdump
 
+$(JSON2TMAP): tools/json2tmap/json2tmap.c
+       $(MAKE) -C tools/json2tmap
+
+%.tmap: %.json $(JSON2TMAP)
+       $(JSON2TMAP) $< $@
+
+%.tiles: %-tiles.png $(PNGDUMP)
+       $(PNGDUMP) -o $@ -oc $(@:.tiles=.cmap) -T 8x8 $<
+
 %.tiles: %.png $(PNGDUMP)
        $(PNGDUMP) -o $@ -oc $(@:.tiles=.cmap) -om $(@:.tiles=.tmap) -T 8x8 -D $<
 
index ea6f3ae..a63834d 100644 (file)
@@ -67,18 +67,15 @@ void simba_start(void)
 
        /* set scrolling to whole screen */
        vdp_setreg(VDP_REG_MODE3, 0);
-       scroll = 0;
-       vdp_setup_addr(VDP_VRAM, 0xf000);       /* hscroll table is at f000, see vdp.S */
-       VDP_DATA = 0;
-       VDP_DATA = 0;
+       scroll = 256;
 }
 
 void simba_update(void)
 {
        if(bnstate & PAD_LEFT) {
-               if(scroll < 0) scroll++;
+               if(scroll > 256) scroll--;
        } else if(bnstate & PAD_RIGHT) {
-               if(scroll > -192) scroll--;
+               if(scroll < 704) scroll++;
        }
        dbgval[0] = scroll;
 }
@@ -86,6 +83,10 @@ void simba_update(void)
 void simba_vblank(void)
 {
        vdp_setup_addr(VDP_VRAM, 0xf000);       /* hscroll table is at f000, see vdp.S */
-       VDP_DATA = scroll;
-       VDP_DATA = scroll >> 1;
+       if(scroll < 512) {
+               VDP_DATA = 0;
+       } else {
+               VDP_DATA = 1024 - scroll;
+       }
+       VDP_DATA = 512 - (scroll >> 1);
 }
diff --git a/tools/json2tmap/Makefile b/tools/json2tmap/Makefile
new file mode 100644 (file)
index 0000000..edcd1d1
--- /dev/null
@@ -0,0 +1,11 @@
+obj = json2tmap.o json.o dynarr.o
+bin = json2tmap
+
+CFLAGS = -pedantic -Wall -g
+
+$(bin): $(obj)
+       $(CC) -o $@ $(obj) $(LDFLAGS)
+
+.PHONY: clean
+clean:
+       rm -f $(obj) $(bin)
diff --git a/tools/json2tmap/dynarr.c b/tools/json2tmap/dynarr.c
new file mode 100644 (file)
index 0000000..59bbf8c
--- /dev/null
@@ -0,0 +1,141 @@
+/* dynarr - dynamic resizable C array data structure
+ * author: John Tsiombikas <nuclear@member.fsf.org>
+ * license: public domain
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "dynarr.h"
+
+/* The array descriptor keeps auxilliary information needed to manipulate
+ * the dynamic array. It's allocated adjacent to the array buffer.
+ */
+struct arrdesc {
+       int nelem, szelem;
+       int max_elem;
+       int bufsz;      /* not including the descriptor */
+};
+
+#define DESC(x)                ((struct arrdesc*)((char*)(x) - sizeof(struct arrdesc)))
+
+void *dynarr_alloc(int elem, int szelem)
+{
+       struct arrdesc *desc;
+
+       if(!(desc = malloc(elem * szelem + sizeof *desc))) {
+               return 0;
+       }
+       desc->nelem = desc->max_elem = elem;
+       desc->szelem = szelem;
+       desc->bufsz = elem * szelem;
+       return (char*)desc + sizeof *desc;
+}
+
+void dynarr_free(void *da)
+{
+       if(da) {
+               free(DESC(da));
+       }
+}
+
+void *dynarr_resize(void *da, int elem)
+{
+       int newsz;
+       void *tmp;
+       struct arrdesc *desc;
+
+       if(!da) return 0;
+       desc = DESC(da);
+
+       newsz = desc->szelem * elem;
+
+       if(!(tmp = realloc(desc, newsz + sizeof *desc))) {
+               return 0;
+       }
+       desc = tmp;
+
+       desc->nelem = desc->max_elem = elem;
+       desc->bufsz = newsz;
+       return (char*)desc + sizeof *desc;
+}
+
+int dynarr_empty(void *da)
+{
+       return DESC(da)->nelem ? 0 : 1;
+}
+
+int dynarr_size(void *da)
+{
+       return DESC(da)->nelem;
+}
+
+
+void *dynarr_clear(void *da)
+{
+       return dynarr_resize(da, 0);
+}
+
+/* stack semantics */
+void *dynarr_push(void *da, void *item)
+{
+       struct arrdesc *desc;
+       int nelem;
+
+       desc = DESC(da);
+       nelem = desc->nelem;
+
+       if(nelem >= desc->max_elem) {
+               /* need to resize */
+               struct arrdesc *tmp;
+               int newsz = desc->max_elem ? desc->max_elem * 2 : 1;
+
+               if(!(tmp = dynarr_resize(da, newsz))) {
+                       fprintf(stderr, "failed to resize\n");
+                       return da;
+               }
+               da = tmp;
+               desc = DESC(da);
+               desc->nelem = nelem;
+       }
+
+       if(item) {
+               memcpy((char*)da + desc->nelem * desc->szelem, item, desc->szelem);
+       }
+       desc->nelem++;
+       return da;
+}
+
+void *dynarr_pop(void *da)
+{
+       struct arrdesc *desc;
+       int nelem;
+
+       desc = DESC(da);
+       nelem = desc->nelem;
+
+       if(!nelem) return da;
+
+       if(nelem <= desc->max_elem / 3) {
+               /* reclaim space */
+               struct arrdesc *tmp;
+               int newsz = desc->max_elem / 2;
+
+               if(!(tmp = dynarr_resize(da, newsz))) {
+                       fprintf(stderr, "failed to resize\n");
+                       return da;
+               }
+               da = tmp;
+               desc = DESC(da);
+               desc->nelem = nelem;
+       }
+       desc->nelem--;
+
+       return da;
+}
+
+void *dynarr_finalize(void *da)
+{
+       struct arrdesc *desc = DESC(da);
+       memmove(desc, da, desc->bufsz);
+       return desc;
+}
diff --git a/tools/json2tmap/dynarr.h b/tools/json2tmap/dynarr.h
new file mode 100644 (file)
index 0000000..619feeb
--- /dev/null
@@ -0,0 +1,90 @@
+/* dynarr - dynamic resizable C array data structure
+ * author: John Tsiombikas <nuclear@member.fsf.org>
+ * license: public domain
+ */
+#ifndef DYNARR_H_
+#define DYNARR_H_
+
+#define dynarr_alloc   g3dimpl_dynarr_alloc
+#define dynarr_free            g3dimpl_dynarr_free
+#define dynarr_resize  g3dimpl_dynarr_resize
+#define dynarr_empty   g3dimpl_dynarr_empty
+#define dynarr_size            g3dimpl_dynarr_size
+#define dynarr_clear   g3dimpl_dynarr_clear
+#define dynarr_push            g3dimpl_dynarr_push
+#define dynarr_pop             g3dimpl_dynarr_pop
+#define dynarr_finalize        g3dimpl_dynarr_finalize
+
+/* usage example:
+ * -------------
+ * int *arr = dynarr_alloc(0, sizeof *arr);
+ *
+ * int x = 10;
+ * arr = dynarr_push(arr, &x);
+ * x = 5;
+ * arr = dynarr_push(arr, &x);
+ * x = 42;
+ * arr = dynarr_push(arr, &x);
+ *
+ * for(i=0; i<dynarr_size(arr); i++) {
+ *     printf("%d\n", arr[i]);
+ *  }
+ *  dynarr_free(arr);
+ */
+
+void *dynarr_alloc(int elem, int szelem);
+void dynarr_free(void *da);
+void *dynarr_resize(void *da, int elem);
+
+/* dynarr_empty returns non-zero if the array is empty
+ * Complexity: O(1) */
+int dynarr_empty(void *da);
+/* dynarr_size returns the number of elements in the array
+ * Complexity: O(1) */
+int dynarr_size(void *da);
+
+void *dynarr_clear(void *da);
+
+/* stack semantics */
+void *dynarr_push(void *da, void *item);
+void *dynarr_pop(void *da);
+
+/* Finalize the array. No more resizing is possible after this call.
+ * Use free() instead of dynarr_free() to deallocate a finalized array.
+ * Returns pointer to the finalized array.
+ * dynarr_finalize can't fail.
+ * Complexity: O(n)
+ */
+void *dynarr_finalize(void *da);
+
+/* helper macros */
+#define DYNARR_RESIZE(da, n) \
+       do { (da) = dynarr_resize((da), (n)); } while(0)
+#define DYNARR_CLEAR(da) \
+       do { (da) = dynarr_clear(da); } while(0)
+#define DYNARR_PUSH(da, item) \
+       do { (da) = dynarr_push((da), (item)); } while(0)
+#define DYNARR_POP(da) \
+       do { (da) = dynarr_pop(da); } while(0)
+
+/* utility macros to push characters to a string. assumes and maintains
+ * the invariant that the last element is always a zero
+ */
+#define DYNARR_STRPUSH(da, c) \
+       do { \
+               char cnull = 0, ch = (char)(c); \
+               (da) = dynarr_pop(da); \
+               (da) = dynarr_push((da), &ch); \
+               (da) = dynarr_push((da), &cnull); \
+       } while(0)
+
+#define DYNARR_STRPOP(da) \
+       do { \
+               char cnull = 0; \
+               (da) = dynarr_pop(da); \
+               (da) = dynarr_pop(da); \
+               (da) = dynarr_push((da), &cnull); \
+       } while(0)
+
+
+#endif /* DYNARR_H_ */
diff --git a/tools/json2tmap/json.c b/tools/json2tmap/json.c
new file mode 100644 (file)
index 0000000..e38ea15
--- /dev/null
@@ -0,0 +1,635 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "dynarr.h"
+
+#if defined(_MSC_VER) || defined(__WATCOMC__)
+#include <malloc.h>
+#else
+#if !defined(__FreeBSD__) && !defined(__OpenBSD__)
+#include <alloca.h>
+#endif
+#endif
+
+#include "json.h"
+
+#ifdef _MSC_VER
+#define strncasecmp strnicmp
+#endif
+
+struct json_obj *json_alloc_obj(void)
+{
+       struct json_obj *obj;
+
+       if(!(obj = malloc(sizeof *obj))) {
+               fprintf(stderr, "json_alloc_obj: failed to allocate object\n");
+               return 0;
+       }
+       json_init_obj(obj);
+       return obj;
+}
+
+void json_free_obj(struct json_obj *obj)
+{
+       json_destroy_obj(obj);
+       free(obj);
+}
+
+void json_init_obj(struct json_obj *obj)
+{
+       memset(obj, 0, sizeof *obj);
+}
+
+void json_destroy_obj(struct json_obj *obj)
+{
+       int i;
+
+       for(i=0; i<obj->num_items; i++) {
+               json_destroy_item(obj->items + i);
+       }
+       free(obj->items);
+}
+
+struct json_arr *json_alloc_arr(void)
+{
+       struct json_arr *arr;
+
+       if(!(arr = malloc(sizeof *arr))) {
+               fprintf(stderr, "json_alloc_arr: failed to allocate array\n");
+               return 0;
+       }
+       json_init_arr(arr);
+       return arr;
+}
+
+void json_free_arr(struct json_arr *arr)
+{
+       json_destroy_arr(arr);
+       free(arr);
+}
+
+void json_init_arr(struct json_arr *arr)
+{
+       memset(arr, 0, sizeof *arr);
+}
+
+void json_destroy_arr(struct json_arr *arr)
+{
+       int i;
+
+       for(i=0; i<arr->size; i++) {
+               json_destroy_value(arr->val + i);
+       }
+       free(arr->val);
+}
+
+
+int json_item(struct json_item *item, const char *name)
+{
+       memset(item, 0, sizeof *item);
+
+       if(!(item->name = strdup(name))) {
+               fprintf(stderr, "json_item: failed to allocate name\n");
+               return -1;
+       }
+       return 0;
+}
+
+void json_destroy_item(struct json_item *item)
+{
+       free(item->name);
+       json_destroy_value(&item->val);
+}
+
+int json_value_str(struct json_value *jv, const char *str)
+{
+       jv->type = JSON_STR;
+
+       jv->str = 0;
+       if(str && !(jv->str = strdup(str))) {
+               fprintf(stderr, "json_value_str: failed to duplicate string\n");
+               return -1;
+       }
+       return 0;
+}
+
+void json_value_num(struct json_value *jv, double num)
+{
+       jv->type = JSON_NUM;
+       jv->num = num;
+}
+
+void json_value_bool(struct json_value *jv, int bval)
+{
+       jv->type = JSON_BOOL;
+       jv->boolean = bval;
+}
+
+void json_value_obj(struct json_value *jv, struct json_obj *obj)
+{
+       jv->type = JSON_OBJ;
+       if(obj) {
+               jv->obj = *obj;
+       } else {
+               json_init_obj(&jv->obj);
+       }
+}
+
+void json_value_arr(struct json_value *jv, struct json_arr *arr)
+{
+       jv->type = JSON_ARR;
+       if(arr) {
+               jv->arr = *arr;
+       } else {
+               json_init_arr(&jv->arr);
+       }
+}
+
+void json_destroy_value(struct json_value *jv)
+{
+       switch(jv->type) {
+       case JSON_STR:
+               free(jv->str);
+               break;
+
+       case JSON_OBJ:
+               json_destroy_obj(&jv->obj);
+               break;
+
+       case JSON_ARR:
+               json_destroy_arr(&jv->arr);
+               break;
+
+       default:
+               break;
+       }
+}
+
+int json_obj_append(struct json_obj *obj, const struct json_item *item)
+{
+       if(obj->num_items >= obj->max_items) {
+               int newsz = obj->max_items ? (obj->max_items << 1) : 8;
+               void *tmp = realloc(obj->items, newsz * sizeof *obj->items);
+               if(!tmp) {
+                       fprintf(stderr, "json_obj_append: failed to grow items array (%d)\n", newsz);
+                       return -1;
+               }
+               obj->items = tmp;
+               obj->max_items = newsz;
+       }
+
+       obj->items[obj->num_items++] = *item;
+       return 0;
+}
+
+int json_arr_append(struct json_arr *arr, const struct json_value *val)
+{
+       if(arr->size >= arr->maxsize) {
+               int newsz = arr->maxsize ? (arr->maxsize << 1) : 8;
+               void *tmp = realloc(arr->val, newsz * sizeof *arr->val);
+               if(!tmp) {
+                       fprintf(stderr, "json_arr_append: failed to grow array (%d)\n", newsz);
+                       return -1;
+               }
+               arr->val = tmp;
+               arr->maxsize = newsz;
+       }
+
+       arr->val[arr->size++] = *val;
+       return 0;
+}
+
+struct json_item *json_find_item(const struct json_obj *obj, const char *name)
+{
+       int i;
+       for(i=0; i<obj->num_items; i++) {
+               if(strcmp(obj->items[i].name, name) == 0) {
+                       return obj->items + i;
+               }
+       }
+       return 0;
+}
+
+/* lookup functions */
+
+struct json_value *json_lookup(struct json_obj *obj, const char *path)
+{
+       int len;
+       char *pelem;
+       const char *endp;
+       struct json_item *it = 0;
+
+       pelem = alloca(strlen(path) + 1);
+       while(*path) {
+               endp = path;
+               while(*endp && *endp != '.') endp++;
+               if(!(len = endp - path)) break;
+
+               memcpy(pelem, path, len);
+               pelem[len] = 0;
+
+               /* continue after the . or point at the terminator */
+               path = endp;
+               if(*path == '.') path++;
+
+               if(!(it = json_find_item(obj, pelem))) {
+                       return 0;
+               }
+
+               if(it->val.type != JSON_OBJ) {
+                       /* we hit a leaf. If the path continues we failed */
+                       if(*path) return 0;
+               }
+
+               /* item is an object, we can continue the lookup if necessary */
+               obj = &it->val.obj;
+       }
+
+       return it ? &it->val : 0;
+}
+
+const char *json_lookup_str(struct json_obj *obj, const char *path, const char *def)
+{
+       struct json_value *val;
+
+       if(!(val = json_lookup(obj, path)) || val->type != JSON_STR) {
+               return def;
+       }
+       return val->str;
+}
+
+double json_lookup_num(struct json_obj *obj, const char *path, double def)
+{
+       struct json_value *val;
+
+       if(!(val = json_lookup(obj, path)) || val->type != JSON_NUM) {
+               return def;
+       }
+       return val->num;
+}
+
+int json_lookup_bool(struct json_obj *obj, const char *path, int def)
+{
+       struct json_value *val;
+
+       if(!(val = json_lookup(obj, path)) || val->type != JSON_BOOL) {
+               return def;
+       }
+       return val->boolean;
+}
+
+struct json_obj *json_lookup_obj(struct json_obj *obj, const char *path, struct json_obj *def)
+{
+       struct json_value *val;
+
+       if(!(val = json_lookup(obj, path)) || val->type != JSON_OBJ) {
+               return def;
+       }
+       return &val->obj;
+}
+
+struct json_arr *json_lookup_arr(struct json_obj *obj, const char *path, struct json_arr *def)
+{
+       struct json_value *val;
+
+       if(!(val = json_lookup(obj, path)) || val->type != JSON_ARR) {
+               return def;
+       }
+       return &val->arr;
+}
+
+
+/* ---- parser ---- */
+
+#define MAX_TOKEN_SIZE 512
+struct parser {
+       char *text;
+       char nextc;
+       char *token;
+};
+
+enum { TOKEN_NUM, TOKEN_STR, TOKEN_BOOL };     /* plus all the single-char tokens */
+
+
+static int item(struct parser *p, struct json_item *it);
+static int array(struct parser *p, struct json_arr *arr);
+static int object(struct parser *p, struct json_obj *obj);
+
+
+static int next_char(struct parser *p)
+{
+       while(*p->text) {
+               p->nextc = *p->text++;
+               if(!isspace(p->nextc)) break;
+       }
+       return p->nextc;
+}
+
+#define SET_TOKEN(token, str, len) \
+       do { \
+               DYNARR_RESIZE(token, (len) + 1); \
+               memcpy(token, str, len); \
+               token[len] = 0; \
+       } while(0)
+
+static int next_token(struct parser *p)
+{
+       int len;
+       char *ptr, *s;
+
+       if(!p->nextc) return -1;
+
+       switch(p->nextc) {
+       case '{':
+       case '}':
+       case ',':
+       case '[':
+       case ']':
+       case ':':
+               SET_TOKEN(p->token, &p->nextc, 1);
+               next_char(p);
+               return p->token[0];
+
+       case '"':
+               DYNARR_CLEAR(p->token);
+               next_char(p);
+               while(p->nextc && p->nextc != '"') {
+                       DYNARR_STRPUSH(p->token, p->nextc);
+                       next_char(p);
+               }
+               next_char(p);
+               return TOKEN_STR;
+
+       default:
+               break;
+       }
+
+       s = p->text - 1;
+
+       strtod(s, &ptr);
+       if(ptr != s) {
+               len = ptr - s;
+               SET_TOKEN(p->token, s, len);
+               p->text = ptr;
+               next_char(p);
+               return TOKEN_NUM;
+       }
+
+       if(strncasecmp(s, "true", 4) == 0) {
+               SET_TOKEN(p->token, "true", 4);
+               p->text += 3;
+               next_char(p);
+               return TOKEN_BOOL;
+       }
+       if(strncasecmp(s, "false", 5) == 0) {
+               SET_TOKEN(p->token, "false", 5);
+               p->text += 4;
+               next_char(p);
+               return TOKEN_BOOL;
+       }
+
+       SET_TOKEN(p->token, &p->nextc, 1);
+       fprintf(stderr, "json_parse: unexpected character: %c\n", p->nextc);
+       return -1;
+}
+
+static int expect(struct parser *p, int tok)
+{
+       return next_token(p) == tok ? 1 : 0;
+}
+
+static const char *toktypestr(int tok)
+{
+       static char buf[] = "' '";
+       switch(tok) {
+       case TOKEN_NUM: return "number";
+       case TOKEN_STR: return "string";
+       case TOKEN_BOOL: return "boolean";
+       default:
+               break;
+       }
+       buf[1] = tok;
+       return buf;
+}
+
+#define EXPECT(p, x) \
+       do { \
+               if(!expect(p, x)) { \
+                       fprintf(stderr, "json_parse: expected: %s, found: %s\n", toktypestr(x), (p)->token); \
+                       return -1; \
+               } \
+       } while(0)
+
+static int value(struct parser *p, struct json_value *val)
+{
+       int toktype;
+       struct json_obj obj;
+       struct json_arr arr;
+
+       toktype = next_token(p);
+       switch(toktype) {
+       case TOKEN_STR:
+               if(json_value_str(val, p->token) == -1) {
+                       return -1;
+               }
+               break;
+
+       case TOKEN_NUM:
+               json_value_num(val, atof(p->token));
+               break;
+
+       case TOKEN_BOOL:
+               json_value_bool(val, *p->token == 't' ? 1 : 0);
+               break;
+
+       case '{':       /* object */
+               if(object(p, &obj) == -1) {
+                       return -1;
+               }
+               json_value_obj(val, &obj);
+               break;
+
+       case '[':       /* array */
+               if(array(p, &arr) == -1) {
+                       return -1;
+               }
+               json_value_arr(val, &arr);
+               break;
+
+       default:
+               fprintf(stderr, "json_parse: unexpected token %s while parsing item\n", p->text);
+               return -1;
+       }
+       return 0;
+}
+
+static int item(struct parser *p, struct json_item *it)
+{
+       EXPECT(p, TOKEN_STR);
+       if(json_item(it, p->token) == -1) {
+               return -1;
+       }
+       EXPECT(p, ':');
+       if(value(p, &it->val) == -1) {
+               free(it->name);
+               return -1;
+       }
+       return 0;
+}
+
+static int array(struct parser *p, struct json_arr *arr)
+{
+       struct json_value val;
+
+       json_init_arr(arr);
+
+       while(p->nextc != -1 && p->nextc != ']') {
+               if(value(p, &val) == -1) {
+                       fprintf(stderr, "json_parse: expected value in array\n");
+                       json_destroy_arr(arr);
+                       return -1;
+               }
+
+               if(json_arr_append(arr, &val) == -1) {
+                       json_destroy_value(&val);
+                       json_destroy_arr(arr);
+                       return -1;
+               }
+
+               if(p->nextc == ',') expect(p, ',');     /* eat up comma separator */
+       }
+       EXPECT(p, ']');
+       return 0;
+}
+
+static int object(struct parser *p, struct json_obj *obj)
+{
+       struct json_item it;
+
+       json_init_obj(obj);
+
+       while(p->nextc != -1 && p->nextc != '}') {
+               if(item(p, &it) == -1) {
+                       fprintf(stderr, "json_parse: expected item in object\n");
+                       json_destroy_obj(obj);
+                       return -1;
+               }
+
+               if(json_obj_append(obj, &it) == -1) {
+                       json_destroy_item(&it);
+                       json_destroy_obj(obj);
+                       return -1;
+               }
+
+               if(p->nextc == ',') expect(p, ',');     /* eat up comma separator */
+       }
+       EXPECT(p, '}');
+       return 0;
+}
+
+int json_parse(struct json_obj *root, const char *text)
+{
+       struct parser p;
+
+       if(!text || !*text) return -1;
+
+       p.nextc = *text;
+       p.text = (char*)(text + 1);
+       if(!(p.token = dynarr_alloc(0, 1))) {
+               fprintf(stderr, "json_parse: failed to allocate token dynamic array\n");
+               return -1;
+       }
+
+       EXPECT(&p, '{');
+       if(object(&p, root) == -1) {
+               dynarr_free(p.token);
+               return -1;
+       }
+       dynarr_free(p.token);
+       return 0;
+}
+
+
+static void putind(FILE *fp, int ind)
+{
+       int i;
+       for(i=0; i<ind; i++) {
+               fputs("  ", fp);
+       }
+}
+
+void json_print(FILE *fp, struct json_obj *root)
+{
+       json_print_obj(fp, root, 0);
+       fputc('\n', fp);
+}
+
+void json_print_obj(FILE *fp, struct json_obj *obj, int ind)
+{
+       int i;
+
+       fputs("{\n", fp);
+
+       for(i=0; i<obj->num_items; i++) {
+               putind(fp, ind + 1);
+               json_print_item(fp, obj->items + i, ind + 1);
+               if(i < obj->num_items - 1) {
+                       fputs(",\n", fp);
+               } else {
+                       fputc('\n', fp);
+               }
+       }
+
+       putind(fp, ind);
+       fputs("}", fp);
+}
+
+void json_print_arr(FILE *fp, struct json_arr *arr, int ind)
+{
+       int i;
+
+       fputs("[\n", fp);
+
+       for(i=0; i<arr->size; i++) {
+               putind(fp, ind + 1);
+               json_print_value(fp, arr->val + i, ind + 1);
+               if(i < arr->size - 1) {
+                       fputs(",\n", fp);
+               } else {
+                       fputc('\n', fp);
+               }
+       }
+
+       putind(fp, ind);
+       fputs("]", fp);
+}
+
+void json_print_item(FILE *fp, struct json_item *item, int ind)
+{
+       fprintf(fp, "\"%s\": ", item->name);
+       json_print_value(fp, &item->val, ind);
+}
+
+void json_print_value(FILE *fp, struct json_value *val, int ind)
+{
+       switch(val->type) {
+       case JSON_STR:
+               fprintf(fp, "\"%s\"", val->str);
+               break;
+       case JSON_NUM:
+               fprintf(fp, "%g", val->num);
+               break;
+       case JSON_BOOL:
+               fprintf(fp, "%s", val->boolean ? "true" : "false");
+               break;
+       case JSON_OBJ:
+               json_print_obj(fp, &val->obj, ind);
+               break;
+       case JSON_ARR:
+               json_print_arr(fp, &val->arr, ind);
+               break;
+       default:
+               fputs("<UNKNOWN>", fp);
+       }
+}
diff --git a/tools/json2tmap/json.h b/tools/json2tmap/json.h
new file mode 100644 (file)
index 0000000..705a82c
--- /dev/null
@@ -0,0 +1,105 @@
+#ifndef JSON_H_
+#define JSON_H_
+
+enum {
+       JSON_NULL,
+       JSON_STR,       /* "foo" */
+       JSON_NUM,       /* 3.141 */
+       JSON_BOOL,      /* true */
+       JSON_OBJ,       /* { ... } */
+       JSON_ARR        /* [ ... ] */
+};
+
+struct json_value;
+
+/* objects are collections of items { "foo": 1, "bar": 2 } */
+struct json_obj {
+       struct json_item *items;
+       int num_items, max_items;
+};
+
+/* arrays are collections of values [ "foo", "bar", 3.14, false, "xyzzy" ] */
+struct json_arr {
+       struct json_value *val;
+       int size, maxsize;
+};
+
+struct json_value {
+       int type;
+
+       char *str;                              /* JSON_STR */
+       double num;                             /* JSON_NUM */
+       int boolean;                    /* JSON_BOOL */
+       struct json_obj obj;    /* JSON_OBJ */
+       struct json_arr arr;    /* JSON_ARR */
+};
+
+/* items are key-value pairs "foo": "xyzzy" */
+struct json_item {
+       char *name;
+       struct json_value val;
+
+       struct json_item *next;
+};
+
+
+struct json_obj *json_alloc_obj(void);         /* malloc + json_init_obj */
+void json_free_obj(struct json_obj *obj);      /* json_destroy_obj + free */
+void json_init_obj(struct json_obj *obj);
+void json_destroy_obj(struct json_obj *obj);
+
+struct json_arr *json_alloc_arr(void);         /* malloc + json_init_arr */
+void json_free_arr(struct json_arr *arr);      /* json_destroy_arr + free */
+void json_init_arr(struct json_arr *arr);
+void json_destroy_arr(struct json_arr *arr);
+
+/* initialize item */
+int json_item(struct json_item *item, const char *name);
+void json_destroy_item(struct json_item *item);
+
+/* pointer values for str, obj, arr can be null */
+int json_value_str(struct json_value *jv, const char *str);
+void json_value_num(struct json_value *jv, double num);
+void json_value_bool(struct json_value *jv, int bval);
+void json_value_obj(struct json_value *jv, struct json_obj *obj);      /* shallow copy obj */
+void json_value_arr(struct json_value *jv, struct json_arr *arr);      /* shallow copy arr */
+
+void json_destroy_value(struct json_value *jv);
+
+/* item can be null, in which case only space is allocated
+ * if not null, contents are shallow-copied (moved), do not destroy or use
+ */
+int json_obj_append(struct json_obj *obj, const struct json_item *item);
+
+/* val can be null in which case only space is allocated
+ * if not null, contents are shallow-copied (moved), do not destroy or use
+ */
+int json_arr_append(struct json_arr *arr, const struct json_value *val);
+
+/* find named item in an object (direct descendant name only, not a path) */
+struct json_item *json_find_item(const struct json_obj *obj, const char *name);
+
+/* json_lookup returns 0 if the requested value is not found */
+struct json_value *json_lookup(struct json_obj *root, const char *path);
+
+/* typed lookup functions return their default argument if the requested value
+ * is not found, or if it has the wrong type.
+ */
+const char *json_lookup_str(struct json_obj *obj, const char *path, const char *def);
+double json_lookup_num(struct json_obj *obj, const char *path, double def);
+int json_lookup_bool(struct json_obj *obj, const char *path, int def);
+/* these next two are probably not very useful */
+struct json_obj *json_lookup_obj(struct json_obj *obj, const char *path, struct json_obj *def);
+struct json_arr *json_lookup_arr(struct json_obj *obj, const char *path, struct json_arr *def);
+
+
+int json_parse(struct json_obj *root, const char *text);
+
+/* mostly useful for debugging */
+void json_print(FILE *fp, struct json_obj *root);
+void json_print_obj(FILE *fp, struct json_obj *obj, int ind);
+void json_print_arr(FILE *fp, struct json_arr *arr, int ind);
+void json_print_item(FILE *fp, struct json_item *item, int ind);
+void json_print_value(FILE *fp, struct json_value *val, int ind);
+
+#endif /* JSON_H_ */
diff --git a/tools/json2tmap/json2tmap.c b/tools/json2tmap/json2tmap.c
new file mode 100644 (file)
index 0000000..a7f2a8e
--- /dev/null
@@ -0,0 +1,118 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include "json.h"
+
+int ispow2(unsigned int x);
+
+int main(int argc, char **argv)
+{
+       int i, x, y, xtiles, ytiles, idx, tileno;
+       long filesz;
+       FILE *fp;
+       struct json_obj json, *jtileobj;
+       struct json_item *jitem;
+       struct json_arr *jtiles;
+       char *buf;
+       uint16_t *tmap;
+
+       if(!argv[1] || !argv[2]) {
+               fprintf(stderr, "Usage: %s <json file> <tilemap file>\n", argv[0]);
+               return 1;
+       }
+
+       if(!(fp = fopen(argv[1], "rb"))) {
+               fprintf(stderr, "failed to open input file: %s\n", argv[1]);
+               return 1;
+       }
+       fseek(fp, 0, SEEK_END);
+       filesz = ftell(fp);
+       rewind(fp);
+
+       if(!(buf = malloc(filesz + 1))) {
+               fprintf(stderr, "failed to load file: %s\n", argv[1]);
+               return 1;
+       }
+       if(fread(buf, 1, filesz, fp) != filesz) {
+               fprintf(stderr, "unexpected EOF while reading %s\n", argv[1]);
+               return 1;
+       }
+       buf[filesz] = 0;
+       fclose(fp);
+
+       if(json_parse(&json, buf) == -1) {
+               return 1;
+       }
+
+       xtiles = (int)json_lookup_num(&json, "tileswide", -1);
+       ytiles = (int)json_lookup_num(&json, "tileshigh", -1);
+       x = (int)json_lookup_num(&json, "tilewidth", 8);
+       y = (int)json_lookup_num(&json, "tileheight", 8);
+       if(x != 8 || y != 8) {
+               fprintf(stderr, "invalid tile size: %dx%d\n", x, y);
+               return 1;
+       }
+       if(!ispow2(xtiles) || xtiles < 32 || xtiles > 128 || !ispow2(ytiles) ||
+                       ytiles < 32 || ytiles > 128) {
+               fprintf(stderr, "invalid tilemap size: %dx%d\n", xtiles, ytiles);
+               return 1;
+       }
+       if(!(jitem = json_find_item(&json, "layers")) || jitem->val.type != JSON_ARR) {
+               fprintf(stderr, "layers array missing\n");
+               return 1;
+       }
+       if(jitem->val.arr.size > 1) {
+               printf("multiple layers found, ignoring all but the first\n");
+               return 1;
+       }
+       if(!(jitem = json_find_item(&jitem->val.arr.val[0].obj, "tiles")) || jitem->val.type != JSON_ARR) {
+               fprintf(stderr, "tiles array missing\n");
+               return 1;
+       }
+       jtiles = &jitem->val.arr;
+
+       if(!(tmap = calloc(xtiles * ytiles, sizeof *tmap))) {
+               fprintf(stderr, "failed to allocate %dx%d tilemap\n", xtiles, ytiles);
+               return 1;
+       }
+
+       for(i=0; i<jtiles->size; i++) {
+               if(jtiles->val[i].type != JSON_OBJ) {
+                       fprintf(stderr, "invalid tiles array\n");
+                       return 1;
+               }
+               jtileobj = &jtiles->val[i].obj;
+
+               if((idx = json_lookup_num(jtileobj, "index", -1)) == -1) {
+                       continue;
+               }
+               x = json_lookup_num(jtileobj, "x", -1);
+               y = json_lookup_num(jtileobj, "y", -1);
+               if(x < 0 || y < 0) continue;
+               tileno = json_lookup_num(jtileobj, "tile", -1);
+
+               tmap[i] = (tileno << 8) | (tileno >> 8);
+               /* TODO flips */
+       }
+
+       if(!(fp = fopen(argv[2], "wb"))) {
+               fprintf(stderr, "failed to open output file: %s\n", argv[2]);
+               return 1;
+       }
+       fwrite(tmap, xtiles * ytiles, sizeof *tmap, fp);
+       fclose(fp);
+       return 0;
+}
+
+int ispow2(unsigned int x)
+{
+       int i, count = 0;
+
+       for(i=0; i<32; i++) {
+               if(x & 1) {
+                       if(++count > 1) return 0;
+               }
+               x >>= 1;
+       }
+       return 1;
+}