diff options
| -rw-r--r-- | tools/mzscript.c | 851 |
1 files changed, 851 insertions, 0 deletions
diff --git a/tools/mzscript.c b/tools/mzscript.c new file mode 100644 index 0000000..53fcbff --- /dev/null +++ b/tools/mzscript.c @@ -0,0 +1,851 @@ +/* SPDX-License-Identifier: MIT */ +/* + * mzscript.c + * + * Copyright (c) 2026 KonAki + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * usage: mzscript <data folder> <output folder> + * turns the events of an exported RPG Maker MV or MZ game into konscript text. + * + * script.kons every common event (label common_N), troop page (tN pM) and map event page (mN eN pN) as one script + * speakers.tsv what the numbers after say mean: number, face file, face index, name, window background, window position + * + * what konscript has itself becomes konscript. everything else becomes host commands, where the host number is the + * RPG Maker command code and the parameters are b, c, d (the first three numbers) and a text with all of them split by + * tabs, in the order RPG Maker keeps them. a choice, loop or battle result is turned into labels and jumps. + * conditions and battle results that konscript can't test are answered by the host in scratch switch and scratch variable + */ + +#define KONJSON_IMPLEMENTATION +#include "konjson.h" + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/*** output ***/ + +typedef struct buf { + char *p; + size_t n, cap; +} buf_t; + +static void put(buf_t *b, const char *fmt, ...) +{ + va_list ap; + int need; + + for (;;) { + va_start(ap, fmt); + need = vsnprintf(b->p ? b->p + b->n : NULL, b->cap - b->n, fmt, ap); + va_end(ap); + if (need >= 0 && b->p && (size_t)need < b->cap - b->n) { + b->n += (size_t)need; + return; + } + b->cap = b->cap ? b->cap * 2 : 1 << 16; + b->p = realloc(b->p, b->cap); + if (!b->p) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + } +} + +#define MAX_LINE 3800 + +static void putString(buf_t *b, const char *s) +{ + size_t i, n = strlen(s); + + if (n > MAX_LINE) n = MAX_LINE; + put(b, "\""); + for (i = 0; i < n; i++) { + unsigned char c = (unsigned char)s[i]; + + if (c == '"') put(b, "\\\""); + else if (c == '\\') put(b, "\\\\"); + else if (c == '\n') put(b, "\\n"); + else if (c == '\t') put(b, "\\t"); + else if (c < 0x20) put(b, " "); + else put(b, "%c", c); + } + put(b, "\""); +} + +/*** counting ***/ + +typedef struct tally { + int code; + int n; +} tally_t; + +static tally_t hosts[256]; +static int hostKinds; +static int structured, hostLines, warnings; + +static void countHost(int code) +{ + int i; + + hostLines++; + for (i = 0; i < hostKinds; i++) { + if (hosts[i].code == code) { + hosts[i].n++; + return; + } + } + if (hostKinds < 256) { + hosts[hostKinds].code = code; + hosts[hostKinds].n = 1; + hostKinds++; + } +} + +static void warn(const char *scope, const char *what) +{ + if (warnings++ < 20) fprintf(stderr, "%s: %s\n", scope, what); +} + +/*** speakers ***/ + +typedef struct speaker { + char face[96]; + int index, background, position; + char name[96]; +} speaker_t; + +static speaker_t *speakers; +static int speakerCount, speakerCap; + +static int speakerFor(const char *face, int index, const char *name, int background, int position) +{ + int i; + + if (!face[0] && !name[0] && background == 0 && position == 2) return -1; + for (i = 0; i < speakerCount; i++) { + speaker_t *s = &speakers[i]; + + if (!strcmp(s->face, face) && s->index == index && !strcmp(s->name, name) && s->background == background && s->position == position) return i; + } + if (speakerCount == speakerCap) { + speakerCap = speakerCap ? speakerCap * 2 : 64; + speakers = realloc(speakers, (size_t)speakerCap * sizeof *speakers); + if (!speakers) exit(1); + } + snprintf(speakers[speakerCount].face, sizeof speakers[0].face, "%s", face); + snprintf(speakers[speakerCount].name, sizeof speakers[0].name, "%s", name); + speakers[speakerCount].index = index; + speakers[speakerCount].background = background; + speakers[speakerCount].position = position; + return speakerCount++; +} + +/*** parameters ***/ + +#define MAX_TOKENS 4096 + +typedef struct tokens { + char **item; + int count; + int numbers[3], numberCount; + int hasString; +} tokens_t; + +static void addToken(tokens_t *t, const char *text, int isNumber, double number) +{ + char *copy; + size_t i; + + if (t->count >= MAX_TOKENS) return; + copy = malloc(strlen(text) + 1); + if (!copy) exit(1); + strcpy(copy, text); + for (i = 0; copy[i]; i++) { + if (copy[i] == '\t') copy[i] = ' '; + } + t->item[t->count++] = copy; + if (isNumber) { + if (t->numberCount < 3) t->numbers[t->numberCount++] = number > 2147483647.0 ? 2147483647 : number < -2147483647.0 ? -2147483647 : (int)number; + } else { + t->hasString = 1; + } +} + +static void flatten(tokens_t *t, const kon_json_t *j) +{ + int i; + char text[64]; + + if (!j) return; + switch (j->type) { + case KON_JSON_NUMBER: + if (j->number == (double)(long long)j->number) snprintf(text, sizeof text, "%lld", (long long)j->number); + else snprintf(text, sizeof text, "%.6g", j->number); + addToken(t, text, 1, j->number); + break; + case KON_JSON_BOOL: + addToken(t, j->number ? "1" : "0", 1, j->number); + break; + case KON_JSON_STRING: + addToken(t, j->string, 0, 0); + break; + case KON_JSON_NULL: + addToken(t, "", 0, 0); + break; + default: + for (i = 0; i < j->count; i++) flatten(t, &j->items[i]); + break; + } +} + +static void freeTokens(tokens_t *t) +{ + int i; + + for (i = 0; i < t->count; i++) free(t->item[i]); + free(t->item); +} + +static void host(buf_t *out, int code, const kon_json_t *parameters) +{ + tokens_t t; + int i; + size_t len = 0; + + memset(&t, 0, sizeof t); + t.item = calloc(MAX_TOKENS, sizeof *t.item); + if (!t.item) exit(1); + flatten(&t, parameters); + countHost(code); + + put(out, "\thost %d", code); + for (i = 0; i < t.numberCount; i++) put(out, " %d", t.numbers[i]); + if (t.hasString || t.count > 3) { + char *joined; + + for (i = 0; i < t.count; i++) len += strlen(t.item[i]) + 1; + joined = malloc(len + 1); + if (!joined) exit(1); + joined[0] = '\0'; + for (i = 0; i < t.count; i++) { + if (i) strcat(joined, "\t"); + strcat(joined, t.item[i]); + } + if (t.numberCount < 3) { + while (t.numberCount < 3) { + put(out, " 0"); + t.numberCount++; + } + } + put(out, " "); + putString(out, joined); + free(joined); + } + put(out, "\n"); + freeTokens(&t); +} + +/*** one event list ***/ + +typedef struct frame { + int kind, indent, id, hasElse, branch, count; +} frame_t; + +enum { F_COND, F_CHOICE, F_LOOP, F_BATTLE }; + +typedef struct context { + buf_t *out; + const char *scope; + int counter; + int scratchSwitch, scratchVar; + char labelNames[512][64]; + char labelDone[512]; + int labelCount; + frame_t stack[48]; + int depth; +} context_t; + +static const char *labelName(context_t *c, char tag, int id, int n, char *out) +{ + if (tag == 'e') snprintf(out, 40, "%s_%d", c->scope, id); + else snprintf(out, 40, "%s_%d%c%d", c->scope, id, tag, n); + return out; +} + +static int commandCode(const kon_json_t *list, int i) +{ + return kon_jsonInt(kon_jsonGet(kon_jsonAt(list, i), "code"), 0); +} + +static int commandIndent(const kon_json_t *list, int i) +{ + return kon_jsonInt(kon_jsonGet(kon_jsonAt(list, i), "indent"), 0); +} + +static const kon_json_t *commandParameters(const kon_json_t *list, int i) +{ + return kon_jsonGet(kon_jsonAt(list, i), "parameters"); +} + +static int nextAtIndent(const kon_json_t *list, int from, int indent) +{ + int n = kon_jsonCount(list), j; + + for (j = from + 1; j < n; j++) { + if (commandIndent(list, j) == indent) return j; + if (commandIndent(list, j) < indent) return -1; + } + return -1; +} + +static int hasElse(const kon_json_t *list, int from, int indent) +{ + int j = from; + + for (;;) { + j = nextAtIndent(list, j, indent); + if (j < 0) return 0; + if (commandCode(list, j) == 411) return 1; + if (commandCode(list, j) == 412) return 0; + } +} + +/* a call with nothing after it but the ends of blocks can be a jump, so a common event that calls itself doesn't pile up */ +static int tailCall(const kon_json_t *list, int i) +{ + int n = kon_jsonCount(list), j; + + for (j = i + 1; j < n; j++) { + int code = commandCode(list, j); + + if (code != 0 && code != 412 && code != 404) return 0; + } + return 1; +} + +static void scriptLabels(context_t *c, const kon_json_t *list) +{ + int i, n = kon_jsonCount(list); + + c->labelCount = 0; + memset(c->labelDone, 0, sizeof c->labelDone); + for (i = 0; i < n; i++) { + if (commandCode(list, i) == 118 && c->labelCount < 512) { + snprintf(c->labelNames[c->labelCount], sizeof c->labelNames[0], "%s", kon_jsonString(kon_jsonAt(commandParameters(list, i), 0), "")); + c->labelCount++; + } + } +} + +static int labelIndex(context_t *c, const char *name) +{ + int i; + + for (i = 0; i < c->labelCount; i++) { + if (!strcmp(c->labelNames[i], name)) return i; + } + return -1; +} + +static frame_t *top(context_t *c, int indent) +{ + if (c->depth && c->stack[c->depth - 1].indent == indent) return &c->stack[c->depth - 1]; + return NULL; +} + +static frame_t *push(context_t *c, int kind, int indent) +{ + frame_t *f = &c->stack[c->depth < 47 ? c->depth++ : 47]; + + memset(f, 0, sizeof *f); + f->kind = kind; + f->indent = indent; + f->id = ++c->counter; + return f; +} + +static const char *compare[] = {"==", ">=", "<=", ">", "<", "!="}; + +static void condition(context_t *c, const kon_json_t *p, const char *elseLabel) +{ + buf_t *out = c->out; + int type = kon_jsonInt(kon_jsonAt(p, 0), -1); + + if (type == 0) { + put(out, "\tif s%d %s else %s\n", kon_jsonInt(kon_jsonAt(p, 1), 0), kon_jsonInt(kon_jsonAt(p, 2), 0) == 0 ? "on" : "off", elseLabel); + structured++; + return; + } + if (type == 1) { + int cmp = kon_jsonInt(kon_jsonAt(p, 4), 0); + int operandVar = kon_jsonInt(kon_jsonAt(p, 2), 0) == 1; + double v = kon_jsonNumber(kon_jsonAt(p, 3), 0); + + if (cmp >= 0 && cmp <= 5 && (operandVar || (v > -200000000 && v < 200000000))) { + put(out, "\tif v%d %s ", kon_jsonInt(kon_jsonAt(p, 1), 0), compare[cmp]); + if (operandVar) put(out, "v%d", kon_jsonInt(kon_jsonAt(p, 3), 0)); + else put(out, "%d", (int)v); + put(out, " else %s\n", elseLabel); + structured++; + return; + } + } + host(out, 111, p); + put(out, "\tif s%d on else %s\n", c->scratchSwitch, elseLabel); +} + +static void variables(context_t *c, const kon_json_t *p, const kon_json_t *whole) +{ + static const char *const ops[] = {"=", "+=", "-=", "*=", "/=", "%="}; + int first = kon_jsonInt(kon_jsonAt(p, 0), 0), last = kon_jsonInt(kon_jsonAt(p, 1), 0); + int op = kon_jsonInt(kon_jsonAt(p, 2), 0), type = kon_jsonInt(kon_jsonAt(p, 3), 0); + double v = kon_jsonNumber(kon_jsonAt(p, 4), 0); + int id; + + if (op >= 0 && op <= 5 && last >= first && last - first < 1000 && ((type == 0 && v > -200000000 && v < 200000000) || type == 1)) { + for (id = first; id <= last; id++) { + put(c->out, "\tset v%d %s ", id, ops[op]); + if (type == 1) put(c->out, "v%d\n", (int)v); + else put(c->out, "%d\n", (int)v); + } + structured++; + return; + } + host(c->out, 122, whole); +} + +static void textBlock(const kon_json_t *list, int *i, int continuation, tokens_t *lines) +{ + int n = kon_jsonCount(list); + + while (*i + 1 < n && commandCode(list, *i + 1) == continuation) { + (*i)++; + addToken(lines, kon_jsonString(kon_jsonAt(commandParameters(list, *i), 0), ""), 0, 0); + } +} + +static char *joinLines(tokens_t *t, const char *first) +{ + size_t len = strlen(first) + 1; + char *s; + int i; + + for (i = 0; i < t->count; i++) len += strlen(t->item[i]) + 1; + s = malloc(len + 1); + if (!s) exit(1); + strcpy(s, first); + for (i = 0; i < t->count; i++) { + if (i || first[0]) strcat(s, "\n"); + strcat(s, t->item[i]); + } + return s; +} + +static void convert(context_t *c, const kon_json_t *list) +{ + buf_t *out = c->out; + int n = kon_jsonCount(list), i; + char a[40], b[40]; + + c->depth = 0; + scriptLabels(c, list); + + for (i = 0; i < n; i++) { + int code = commandCode(list, i), indent = commandIndent(list, i); + const kon_json_t *p = commandParameters(list, i); + frame_t *f; + + switch (code) { + case 0: + break; + case 101: { + tokens_t lines; + char *text; + int who; + + memset(&lines, 0, sizeof lines); + lines.item = calloc(MAX_TOKENS, sizeof *lines.item); + if (!lines.item) exit(1); + textBlock(list, &i, 401, &lines); + who = speakerFor(kon_jsonString(kon_jsonAt(p, 0), ""), kon_jsonInt(kon_jsonAt(p, 1), 0), kon_jsonString(kon_jsonAt(p, 4), ""), kon_jsonInt(kon_jsonAt(p, 2), 0), kon_jsonInt(kon_jsonAt(p, 3), 2)); + text = joinLines(&lines, ""); + if (who < 0) put(out, "\tsay - "); + else put(out, "\tsay %d ", who); + putString(out, text); + put(out, "\n"); + free(text); + freeTokens(&lines); + structured++; + break; + } + case 102: { + const kon_json_t *choices = kon_jsonAt(p, 0); + int k, count = kon_jsonCount(choices); + + f = push(c, F_CHOICE, indent); + f->count = count > 16 ? 16 : count; + if (count > 16) warn(c->scope, "more than 16 choices"); + put(out, "\tmenu\n"); + for (k = 0; k < f->count; k++) { + put(out, "\toption "); + putString(out, kon_jsonString(kon_jsonAt(choices, k), "")); + put(out, " %s\n", labelName(c, 'o', f->id, k, a)); + } + structured++; + break; + } + case 402: + f = top(c, indent); + if (!f || f->kind != F_CHOICE) { + warn(c->scope, "choice branch without choices"); + break; + } + if (f->branch) put(out, "\tjump %s\n", labelName(c, 'e', f->id, 0, a)); + f->branch++; + put(out, "%s:\n", labelName(c, 'o', f->id, kon_jsonInt(kon_jsonAt(p, 0), 0), a)); + break; + case 403: + f = top(c, indent); + if (!f || f->kind != F_CHOICE) break; + put(out, "\tjump %s\n", labelName(c, 'e', f->id, 0, a)); + put(out, "%s_%dcancel:\n", c->scope, f->id); + warnings += 0; + break; + case 404: + f = top(c, indent); + if (!f || f->kind != F_CHOICE) break; + put(out, "%s:\n", labelName(c, 'e', f->id, 0, a)); + c->depth--; + break; + case 111: + f = push(c, F_COND, indent); + f->hasElse = hasElse(list, i, indent); + condition(c, p, f->hasElse ? labelName(c, 'n', f->id, 0, a) : labelName(c, 'e', f->id, 0, b)); + break; + case 411: + f = top(c, indent); + if (!f || f->kind != F_COND) break; + put(out, "\tjump %s\n", labelName(c, 'e', f->id, 0, a)); + put(out, "%s:\n", labelName(c, 'n', f->id, 0, a)); + break; + case 412: + f = top(c, indent); + if (!f || f->kind != F_COND) break; + put(out, "%s:\n", labelName(c, 'e', f->id, 0, a)); + c->depth--; + break; + case 112: + f = push(c, F_LOOP, indent); + put(out, "%s:\n", labelName(c, 't', f->id, 0, a)); + break; + case 413: + f = top(c, indent); + if (!f || f->kind != F_LOOP) break; + put(out, "\tjump %s\n", labelName(c, 't', f->id, 0, a)); + put(out, "%s:\n", labelName(c, 'e', f->id, 0, a)); + c->depth--; + break; + case 113: { + int k; + + for (k = c->depth - 1; k >= 0 && c->stack[k].kind != F_LOOP; k--) {} + if (k < 0) { + warn(c->scope, "break loop outside a loop"); + break; + } + put(out, "\tjump %s\n", labelName(c, 'e', c->stack[k].id, 0, a)); + break; + } + case 115: + put(out, "\treturn\n"); + break; + case 117: + put(out, "\t%s common_%d\n", tailCall(list, i) ? "jump" : "call", kon_jsonInt(kon_jsonAt(p, 0), 0)); + structured++; + break; + case 118: { + int k = labelIndex(c, kon_jsonString(kon_jsonAt(p, 0), "")); + + if (k >= 0 && !c->labelDone[k]) { + c->labelDone[k] = 1; + put(out, "%s_L%d:\n", c->scope, k); + } + break; + } + case 119: { + int k = labelIndex(c, kon_jsonString(kon_jsonAt(p, 0), "")); + + if (k < 0) { + put(out, "\t# jump to a label that is not there\n"); + break; + } + put(out, "\tjump %s_L%d\n", c->scope, k); + structured++; + break; + } + case 121: { + int id, first = kon_jsonInt(kon_jsonAt(p, 0), 0), last = kon_jsonInt(kon_jsonAt(p, 1), 0); + + if (last - first > 2000) last = first + 2000; + for (id = first; id <= last; id++) put(out, "\t%s s%d\n", kon_jsonInt(kon_jsonAt(p, 2), 0) == 0 ? "on" : "off", id); + structured++; + break; + } + case 122: + variables(c, p, p); + break; + case 108: { + tokens_t lines; + int k; + + memset(&lines, 0, sizeof lines); + lines.item = calloc(MAX_TOKENS, sizeof *lines.item); + if (!lines.item) exit(1); + addToken(&lines, kon_jsonString(kon_jsonAt(p, 0), ""), 0, 0); + textBlock(list, &i, 408, &lines); + for (k = 0; k < lines.count; k++) { + char *s; + + for (s = lines.item[k]; *s; s++) { + if ((unsigned char)*s < 0x20) *s = ' '; + } + if (strlen(lines.item[k]) > 400) lines.item[k][400] = '\0'; + put(out, "\t# %s\n", lines.item[k]); + } + freeTokens(&lines); + break; + } + case 230: + put(out, "\twait %d\n", (int)(kon_jsonNumber(kon_jsonAt(p, 0), 0) * 1000.0 / 60.0 + 0.5)); + structured++; + break; + case 301: { + int next = nextAtIndent(list, i, indent); + + host(out, 301, p); + if (next >= 0 && (commandCode(list, next) == 601 || commandCode(list, next) == 602 || commandCode(list, next) == 603)) push(c, F_BATTLE, indent); + break; + } + case 601: + case 602: + case 603: + f = top(c, indent); + if (!f || f->kind != F_BATTLE) break; + if (f->branch) { + put(out, "\tjump %s\n", labelName(c, 'e', f->id, 0, a)); + put(out, "%s:\n", labelName(c, 'n', f->id, f->branch - 1, a)); + } + put(out, "\tif v%d == %d else %s\n", c->scratchVar, code - 601, labelName(c, 'n', f->id, f->branch, a)); + f->branch++; + break; + case 604: + f = top(c, indent); + if (!f || f->kind != F_BATTLE) break; + put(out, "\tjump %s\n", labelName(c, 'e', f->id, 0, a)); + put(out, "%s:\n", labelName(c, 'n', f->id, f->branch - 1, a)); + put(out, "%s:\n", labelName(c, 'e', f->id, 0, a)); + c->depth--; + break; + case 105: + case 355: { + tokens_t lines; + char *text; + int number = 0; + + memset(&lines, 0, sizeof lines); + lines.item = calloc(MAX_TOKENS, sizeof *lines.item); + if (!lines.item) exit(1); + textBlock(list, &i, code == 105 ? 405 : 655, &lines); + text = joinLines(&lines, code == 355 ? kon_jsonString(kon_jsonAt(p, 0), "") : ""); + if (code == 105) number = kon_jsonInt(kon_jsonAt(p, 0), 2); + countHost(code); + put(out, "\thost %d %d 0 0 ", code, number); + putString(out, text); + put(out, "\n"); + free(text); + freeTokens(&lines); + break; + } + case 205: + host(out, 205, p); + while (i + 1 < n && commandCode(list, i + 1) == 505) i++; + break; + case 357: + host(out, 357, p); + while (i + 1 < n && commandCode(list, i + 1) == 657) i++; + break; + case 401: + case 405: + case 408: + case 505: + case 655: + case 657: + break; + default: + host(out, code, p); + break; + } + } + while (c->depth) { + warn(c->scope, "a block was left open"); + c->depth--; + } +} + +/*** files ***/ + +static kon_json_t *load(const char *folder, const char *name) +{ + char path[1024], error[120]; + kon_json_t *j; + + snprintf(path, sizeof path, "%s/%s", folder, name); + j = kon_jsonParseFile(path, error, sizeof error); + if (!j && strncmp(error, "cannot open", 11)) fprintf(stderr, "%s: %s\n", path, error); + return j; +} + +int main(int argc, char **argv) +{ + buf_t out; + context_t c; + kon_json_t *system, *json; + char path[1024], scope[40]; + int id, k, p, maxCommon = 0, entries = 0; + FILE *f; + + if (argc < 3) { + fprintf(stderr, "usage: mzscript <data folder> <output folder>\n"); + return 2; + } + memset(&out, 0, sizeof out); + memset(&c, 0, sizeof c); + c.out = &out; + + system = load(argv[1], "System.json"); + if (!system) return 1; + c.scratchSwitch = kon_jsonCount(kon_jsonGet(system, "switches")) + 1; + c.scratchVar = kon_jsonCount(kon_jsonGet(system, "variables")) + 1; + kon_jsonFree(system); + put(&out, "# scratch switch s%d and scratch variable v%d are the host's\n\n", c.scratchSwitch, c.scratchVar); + + json = load(argv[1], "CommonEvents.json"); + if (json) { + maxCommon = kon_jsonCount(json) - 1; + for (id = 1; id <= maxCommon; id++) { + const kon_json_t *event = kon_jsonAt(json, id); + + snprintf(scope, sizeof scope, "c%d", id); + c.scope = scope; + c.counter = 0; + put(&out, "common_%d:\n", id); + if (event && event->type == KON_JSON_OBJECT) convert(&c, kon_jsonGet(event, "list")); + put(&out, "\treturn\n\n"); + entries++; + } + kon_jsonFree(json); + } + + json = load(argv[1], "Troops.json"); + if (json) { + for (id = 1; id < kon_jsonCount(json); id++) { + const kon_json_t *pages = kon_jsonGet(kon_jsonAt(json, id), "pages"); + + for (p = 0; p < kon_jsonCount(pages); p++) { + snprintf(scope, sizeof scope, "t%dp%d", id, p + 1); + c.scope = scope; + c.counter = 0; + put(&out, "%s:\n", scope); + convert(&c, kon_jsonGet(kon_jsonAt(pages, p), "list")); + put(&out, "\treturn\n\n"); + entries++; + } + } + kon_jsonFree(json); + } + + for (id = 1; id < 2000; id++) { + char name[32]; + const kon_json_t *events; + + snprintf(name, sizeof name, "Map%03d.json", id); + json = load(argv[1], name); + if (!json) continue; + events = kon_jsonGet(json, "events"); + for (k = 1; k < kon_jsonCount(events); k++) { + const kon_json_t *event = kon_jsonAt(events, k), *pages = kon_jsonGet(event, "pages"); + + if (!event || event->type != KON_JSON_OBJECT) continue; + for (p = 0; p < kon_jsonCount(pages); p++) { + snprintf(scope, sizeof scope, "m%de%dp%d", id, kon_jsonInt(kon_jsonGet(event, "id"), k), p + 1); + c.scope = scope; + c.counter = 0; + put(&out, "%s:\n", scope); + convert(&c, kon_jsonGet(kon_jsonAt(pages, p), "list")); + put(&out, "\treturn\n\n"); + entries++; + } + } + kon_jsonFree(json); + } + + snprintf(path, sizeof path, "%s/script.kons", argv[2]); + f = fopen(path, "wb"); + if (!f) { + fprintf(stderr, "cannot write %s\n", path); + return 1; + } + fwrite(out.p, 1, out.n, f); + fclose(f); + free(out.p); + + snprintf(path, sizeof path, "%s/speakers.tsv", argv[2]); + f = fopen(path, "wb"); + if (!f) { + fprintf(stderr, "cannot write %s\n", path); + return 1; + } + for (k = 0; k < speakerCount; k++) fprintf(f, "%d\t%s\t%d\t%s\t%d\t%d\n", k, speakers[k].face, speakers[k].index, speakers[k].name, speakers[k].background, speakers[k].position); + fclose(f); + + printf("%d scripts, %d speakers\n", entries, speakerCount); + printf("%d commands became konscript, %d became host commands", structured, hostLines); + printf(" (%d kinds)\n", hostKinds); + for (k = 0; k < hostKinds; k++) { + int best = k, j; + tally_t t; + + for (j = k + 1; j < hostKinds; j++) { + if (hosts[j].n > hosts[best].n) best = j; + } + t = hosts[k]; + hosts[k] = hosts[best]; + hosts[best] = t; + if (k < 15) printf(" %6d host %d\n", hosts[k].n, hosts[k].code); + } + if (warnings) printf("%d warnings\n", warnings); + free(speakers); + return 0; +} |
