summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKonAki <konaki@anokonaki.org>2026-10-16 15:30:47 +0800
committerKonAki <konaki@anokonaki.org>2026-10-16 15:30:47 +0800
commitf56c963d2b5661da79abcbc68bce5b08db31d030 (patch)
tree4a17f73cbcf69cc7da954d2e57ccc28c03f60d1b
parentc4b53a0d1ba903dc94fe93d2fa50367bfbefa385 (diff)
add mzdata: database converter
-rw-r--r--tools/mzdata.c599
1 files changed, 599 insertions, 0 deletions
diff --git a/tools/mzdata.c b/tools/mzdata.c
new file mode 100644
index 0000000..637b06d
--- /dev/null
+++ b/tools/mzdata.c
@@ -0,0 +1,599 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * mzdata.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: mzdata [-n name] <data folder> <output folder>
+ * turns the database of an exported RPG Maker MV or MZ game into C for konrmdata.h and konbattle.h: states, skills, items, enemies and troops.
+ *
+ * name_data.h, name_data.c name_states, name_skills, name_items, name_enemies and name_troops, indexed by the game's own ids,
+ * and name_dataInit() which has to be called once before the skills and items are used
+ *
+ * only data is written, the damage formulas stay text that konbattle reads with its own small parser and never runs as
+ * JavaScript. what that parser cannot read is counted and listed, the skill then does nothing instead of something wrong
+ */
+
+#define KONJSON_IMPLEMENTATION
+#include "konjson.h"
+#include "konrmdata.h"
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define MAX_ID 5000
+#define STRING_PIECE 100
+
+static int warnings, droppedStatements, badFormulas, droppedEffects, otherEffects;
+static int stateCount, skillCount, itemCount, enemyCount, troopCount;
+
+/*** output ***/
+
+static void cstring(FILE *f, const char *s)
+{
+ size_t i, piece = 0;
+
+ if (!s || !*s) {
+ fputs("\"\"", f);
+ return;
+ }
+ fputc('"', f);
+ for (i = 0; s[i]; i++) {
+ unsigned char ch = (unsigned char)s[i];
+
+ if (piece >= STRING_PIECE) {
+ fputs("\"\n\t\"", f);
+ piece = 0;
+ }
+ if (ch == '"' || ch == '\\') fprintf(f, "\\%c", ch);
+ else if (ch == '\n') fputs("\\n", f);
+ else if (ch == '\t') fputs("\\t", f);
+ else if (ch >= 32 && ch < 127 && ch != '?') fputc(ch, f);
+ else fprintf(f, "\\%03o", ch);
+ piece++;
+ }
+ fputc('"', f);
+}
+
+static const char *text(const kon_json_t *j)
+{
+ const char *s = kon_jsonString(j, "");
+
+ return s ? s : "";
+}
+
+static int number(const kon_json_t *j, int low, int high, int fallback)
+{
+ int v = kon_jsonInt(j, fallback);
+
+ return v < low ? low : v > high ? high : v;
+}
+
+/* the value when it is inside the range, else the fallback */
+static int within(const kon_json_t *j, int low, int high, int fallback)
+{
+ int v = kon_jsonInt(j, fallback);
+
+ return v < low || v > high ? fallback : v;
+}
+
+/* an id that is in the tables, or 0. an id used twice is only taken the first time */
+static unsigned char idSeen[MAX_ID + 1];
+
+static int tableId(const kon_json_t *e, int index)
+{
+ int id = kon_jsonInt(kon_jsonGet(e, "id"), index);
+
+ if (id < 1 || id > MAX_ID || idSeen[id]) return 0;
+ idSeen[id] = 1;
+ return id;
+}
+
+/* a fraction as a percent, rounded */
+static int percent(double v)
+{
+ double p = v * 100.0;
+
+ if (!(p == p) || p > 100000.0) p = 100000.0;
+ if (p < -100000.0) p = -100000.0;
+ return (int)(p < 0 ? p - 0.5 : p + 0.5);
+}
+
+static int validName(const char *s)
+{
+ if (!isalpha((unsigned char)s[0]) && s[0] != '_') return 0;
+ for (; *s; s++) {
+ if (!isalnum((unsigned char)*s) && *s != '_') return 0;
+ }
+ return 1;
+}
+
+static int findData(char *out, size_t size, const char *folder)
+{
+ static const char *const where[] = {"", "/data", "/www/data"};
+ char path[1100];
+ FILE *f;
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ snprintf(path, sizeof path, "%s%s/System.json", folder, where[i]);
+ f = fopen(path, "rb");
+ if (f) {
+ fclose(f);
+ snprintf(out, size, "%s%s", folder, where[i]);
+ return 1;
+ }
+ }
+ fprintf(stderr, "no System.json in %s, %s/data or %s/www/data\n", folder, folder, folder);
+ return 0;
+}
+
+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;
+}
+
+static void warn(const char *what, int id, const char *name, const char *detail)
+{
+ if (warnings++ < 25) fprintf(stderr, "%s %d (%s): %s\n", what, id, name, detail);
+}
+
+/* the highest id of the entries that are objects */
+static int highestId(const kon_json_t *list)
+{
+ int i, high = 0;
+
+ for (i = 1; i < kon_jsonCount(list); i++) {
+ const kon_json_t *e = kon_jsonAt(list, i);
+
+ if (e && e->type == KON_JSON_OBJECT) {
+ int id = kon_jsonInt(kon_jsonGet(e, "id"), i);
+
+ if (id > high && id <= MAX_ID) high = id;
+ }
+ }
+ return high;
+}
+
+/*** states ***/
+
+static void writeStates(FILE *f, const char *name, const kon_json_t *list)
+{
+ int i, high = highestId(list), k;
+
+ memset(idSeen, 0, sizeof idSeen);
+
+ fprintf(f, "const kon_state_t %s_states[%d] = {\n", name, high + 1);
+ for (i = 1; i < kon_jsonCount(list); i++) {
+ const kon_json_t *s = kon_jsonAt(list, i);
+ const kon_json_t *traits;
+ double factor[KON_PARAMS];
+ int id, timing, restriction;
+
+ if (!s || s->type != KON_JSON_OBJECT) continue;
+ id = tableId(s, i);
+ if (id < 1) continue;
+ timing = within(kon_jsonGet(s, "autoRemovalTiming"), 0, 2, 0);
+ restriction = within(kon_jsonGet(s, "restriction"), 0, 4, 0);
+ for (k = 0; k < KON_PARAMS; k++) factor[k] = 1.0;
+ traits = kon_jsonGet(s, "traits");
+ for (k = 0; k < kon_jsonCount(traits); k++) {
+ const kon_json_t *t = kon_jsonAt(traits, k);
+ int data = number(kon_jsonGet(t, "dataId"), -1, 1000, -1);
+
+ if (number(kon_jsonGet(t, "code"), 0, 1000, 0) == 21 && data >= 0 && data < KON_PARAMS) factor[data] *= kon_jsonNumber(kon_jsonGet(t, "value"), 1.0);
+ }
+ fprintf(f, "\t[%d] = {", id);
+ cstring(f, text(kon_jsonGet(s, "name")));
+ fprintf(f, ", %d, %d, %d, %d, %d, %d, %d, {", id == 1, restriction, timing ? number(kon_jsonGet(s, "minTurns"), 0, 10000, 0) : 0, timing ? number(kon_jsonGet(s, "maxTurns"), 0, 10000, 0) : 0, timing, kon_jsonBool(kon_jsonGet(s, "removeAtBattleEnd"), 0) ? 1 : 0, kon_jsonBool(kon_jsonGet(s, "removeByDamage"), 0) ? number(kon_jsonGet(s, "chanceByDamage"), 0, 100, 100) : 0);
+ for (k = 0; k < KON_PARAMS; k++) fprintf(f, "%s%d", k ? ", " : "", percent(factor[k]) - 100);
+ fputs("}},\n", f);
+ stateCount++;
+ }
+ fputs("};\n\n", f);
+}
+
+/*** skills and items ***/
+
+static const char *const scopeNames[] = {"KON_SCOPE_NONE", "KON_SCOPE_ONE_ENEMY", "KON_SCOPE_ALL_ENEMIES", "KON_SCOPE_RANDOM_ENEMY", "KON_SCOPE_ONE_ALLY", "KON_SCOPE_ALL_ALLIES", "KON_SCOPE_ONE_ALLY_DEAD", "KON_SCOPE_ALL_ALLIES_DEAD", "KON_SCOPE_USER"};
+static const char *const damageNames[] = {"KON_DAMAGE_NONE", "KON_DAMAGE_HP", "KON_DAMAGE_MP", "KON_DAMAGE_HP_RECOVER", "KON_DAMAGE_MP_RECOVER", "KON_DAMAGE_HP_DRAIN", "KON_DAMAGE_MP_DRAIN"};
+static const char *const hitNames[] = {"KON_HIT_CERTAIN", "KON_HIT_PHYSICAL", "KON_HIT_MAGICAL"};
+
+/* a damage formula is an expression here. a skill that does something first (a.gainMp(5); a.atk * 4) keeps what it works out and
+ loses the rest, which is counted */
+static void formulaOf(const char *in, char *out, size_t size)
+{
+ char copy[512];
+ char *start, *semi, *end;
+
+ snprintf(copy, sizeof copy, "%s", in);
+ end = copy + strlen(copy);
+ while (end > copy && (isspace((unsigned char)end[-1]) || end[-1] == ';')) *--end = '\0';
+ semi = strrchr(copy, ';');
+ if (semi) {
+ droppedStatements++;
+ start = semi + 1;
+ } else {
+ start = copy;
+ }
+ while (*start && isspace((unsigned char)*start)) start++;
+ snprintf(out, size, "%s", *start ? start : "0");
+}
+
+static void writeSkill(FILE *f, const kon_json_t *s, int id, const char *kind)
+{
+ const kon_json_t *damage = kon_jsonGet(s, "damage"), *effects = kon_jsonGet(s, "effects");
+ char formula[512], extra[160];
+ const char *nm = text(kon_jsonGet(s, "name"));
+ int scope = within(kon_jsonGet(s, "scope"), 0, 100, 0), rscope = 0, random = 0;
+ int dtype = within(kon_jsonGet(damage, "type"), 0, 6, 0), element = kon_jsonInt(kon_jsonGet(damage, "elementId"), 0);
+ int add[KON_SKILL_STATES], addChance[KON_SKILL_STATES], remove[KON_SKILL_STATES], adds = 0, removes = 0, k;
+ kon_skill_t probe;
+
+ if (scope == 1) rscope = 1;
+ else if (scope == 2) rscope = 2;
+ else if (scope >= 3 && scope <= 6) {
+ rscope = 3;
+ random = scope - 2;
+ } else if (scope == 7) rscope = 4;
+ else if (scope == 8) rscope = 5;
+ else if (scope == 9) rscope = 6;
+ else if (scope == 10) rscope = 7;
+ else if (scope == 11) rscope = 8;
+
+ if (element < 0 || element >= KON_ELEMENTS) {
+ if (element > 0) warn(kind, id, nm, "element number past the 16 there is room for, it has no element");
+ element = 0;
+ }
+ formulaOf(text(kon_jsonGet(damage, "formula")), formula, sizeof formula);
+ if (dtype == 0) strcpy(formula, "");
+
+ for (k = 0; k < kon_jsonCount(effects); k++) {
+ const kon_json_t *e = kon_jsonAt(effects, k);
+ int code = number(kon_jsonGet(e, "code"), 0, 1000, 0), data = number(kon_jsonGet(e, "dataId"), 0, 100000, 0);
+ double v1 = kon_jsonNumber(kon_jsonGet(e, "value1"), 0), v2 = kon_jsonNumber(kon_jsonGet(e, "value2"), 0);
+
+ if (code == 21 && data > 0 && adds < KON_SKILL_STATES) {
+ add[adds] = data;
+ addChance[adds++] = percent(v1) < 0 ? 0 : percent(v1) > 100 ? 100 : percent(v1);
+ } else if (code == 22 && data > 0 && removes < KON_SKILL_STATES) {
+ remove[removes++] = data;
+ } else if ((code == 11 || code == 12) && dtype == 0 && !formula[0]) {
+ /* recovering hp or mp is a formula of its own when the skill has no damage */
+ dtype = code == 11 ? 3 : 4;
+ snprintf(formula, sizeof formula, "b.%s * %.4f + %d", code == 11 ? "mhp" : "mmp", v1 < 0 ? 0 : v1, (int)v2);
+ } else if (code == 21 || code == 22) {
+ droppedEffects++;
+ } else if (code != 21 && code != 22) {
+ otherEffects++;
+ }
+ }
+
+ memset(&probe, 0, sizeof probe);
+ probe.formulaText = formula;
+ if (formula[0] && !kon_skillCompile(&probe)) {
+ badFormulas++;
+ snprintf(extra, sizeof extra, "formula not understood: %.100s", formula);
+ warn(kind, id, nm, extra);
+ }
+
+ fprintf(f, "\t[%d] = {.name = ", id);
+ cstring(f, nm);
+ fprintf(f, ", .mpCost = %d, .tpCost = %d, .tpGain = %d, .scope = %s, .randomCount = %d, .speed = %d, .successRate = %d, .repeats = %d, .hitType = %s, .damageType = %s, .element = %d, .formulaText = ",
+ number(kon_jsonGet(s, "mpCost"), 0, 100000, 0), number(kon_jsonGet(s, "tpCost"), 0, 100000, 0), number(kon_jsonGet(s, "tpGain"), 0, 100000, 0), scopeNames[rscope], random,
+ number(kon_jsonGet(s, "speed"), -2000, 2000, 0), number(kon_jsonGet(s, "successRate"), 0, 100000, 100), number(kon_jsonGet(s, "repeats"), 1, 100, 1),
+ hitNames[within(kon_jsonGet(s, "hitType"), 0, 2, 0)], damageNames[dtype], element);
+ cstring(f, formula);
+ fprintf(f, ", .variance = %d, .critical = %d", number(kon_jsonGet(damage, "variance"), 0, 100, 0), kon_jsonBool(kon_jsonGet(damage, "critical"), 0) ? 1 : 0);
+ if (adds) {
+ fputs(", .addState = {", f);
+ for (k = 0; k < adds; k++) fprintf(f, "%s{%d, %d}", k ? ", " : "", add[k], addChance[k]);
+ fprintf(f, "}, .addStateCount = %d", adds);
+ }
+ if (removes) {
+ fputs(", .removeState = {", f);
+ for (k = 0; k < removes; k++) fprintf(f, "%s%d", k ? ", " : "", remove[k]);
+ fprintf(f, "}, .removeStateCount = %d", removes);
+ }
+ fputs("},\n", f);
+}
+
+static int writeSkills(FILE *f, const char *name, const char *table, const kon_json_t *list, const char *kind)
+{
+ int i, high = highestId(list), n = 0;
+
+ memset(idSeen, 0, sizeof idSeen);
+
+ fprintf(f, "kon_skill_t %s_%s[%d] = {\n", name, table, high + 1);
+ for (i = 1; i < kon_jsonCount(list); i++) {
+ const kon_json_t *s = kon_jsonAt(list, i);
+ int id;
+
+ if (!s || s->type != KON_JSON_OBJECT) continue;
+ id = tableId(s, i);
+ if (id < 1) continue;
+ writeSkill(f, s, id, kind);
+ n++;
+ }
+ fputs("};\n\n", f);
+ return n;
+}
+
+/*** enemies and troops ***/
+
+static void writeEnemies(FILE *f, const char *name, const kon_json_t *list)
+{
+ int i, high = highestId(list), k;
+
+ memset(idSeen, 0, sizeof idSeen);
+
+ for (i = 1; i < kon_jsonCount(list); i++) {
+ const kon_json_t *e = kon_jsonAt(list, i);
+ const kon_json_t *actions, *drops;
+ int id, n;
+
+ if (!e || e->type != KON_JSON_OBJECT) continue;
+ id = tableId(e, i);
+ if (id < 1) continue;
+ actions = kon_jsonGet(e, "actions");
+ n = kon_jsonCount(actions);
+ if (n > 0) {
+ fprintf(f, "static const kon_rmEnemyAction_t %s_enemy%d_actions[] = {\n", name, id);
+ for (k = 0; k < n; k++) {
+ const kon_json_t *a = kon_jsonAt(actions, k);
+
+ fprintf(f, "\t{%d, %d, %d, %d, %d},\n", number(kon_jsonGet(a, "skillId"), 0, MAX_ID, 0), number(kon_jsonGet(a, "conditionType"), 0, 6, 0), number(kon_jsonGet(a, "conditionParam1"), -100000, 100000, 0), number(kon_jsonGet(a, "conditionParam2"), -100000, 100000, 0), number(kon_jsonGet(a, "rating"), 0, 10, 5));
+ }
+ fputs("};\n\n", f);
+ }
+ drops = kon_jsonGet(e, "dropItems");
+ n = 0;
+ for (k = 0; k < kon_jsonCount(drops); k++) {
+ if (number(kon_jsonGet(kon_jsonAt(drops, k), "kind"), 0, 3, 0) > 0) n++;
+ }
+ if (n > 0) {
+ fprintf(f, "static const kon_rmDrop_t %s_enemy%d_drops[] = {\n", name, id);
+ for (k = 0; k < kon_jsonCount(drops); k++) {
+ const kon_json_t *d = kon_jsonAt(drops, k);
+ int kind = number(kon_jsonGet(d, "kind"), 0, 3, 0);
+
+ if (kind > 0) fprintf(f, "\t{%d, %d, %d},\n", kind, number(kon_jsonGet(d, "dataId"), 0, MAX_ID, 0), number(kon_jsonGet(d, "denominator"), 1, 100000, 1));
+ }
+ fputs("};\n\n", f);
+ }
+ }
+
+ memset(idSeen, 0, sizeof idSeen);
+ fprintf(f, "const kon_rmEnemy_t %s_enemies[%d] = {\n", name, high + 1);
+ for (i = 1; i < kon_jsonCount(list); i++) {
+ const kon_json_t *e = kon_jsonAt(list, i);
+ const kon_json_t *traits, *params;
+ double paramRate[KON_PARAMS], pdr = 1, mdr = 1, elementRate[KON_RM_ELEMENTS];
+ int x[10] = {0}, id, na, nd, drops = 0;
+
+ if (!e || e->type != KON_JSON_OBJECT) continue;
+ id = tableId(e, i);
+ if (id < 1) continue;
+ for (k = 0; k < KON_PARAMS; k++) paramRate[k] = 1.0;
+ for (k = 0; k < KON_RM_ELEMENTS; k++) elementRate[k] = 1.0;
+ traits = kon_jsonGet(e, "traits");
+ for (k = 0; k < kon_jsonCount(traits); k++) {
+ const kon_json_t *t = kon_jsonAt(traits, k);
+ int code = number(kon_jsonGet(t, "code"), 0, 1000, 0), data = number(kon_jsonGet(t, "dataId"), -1, 100000, -1);
+ double v = kon_jsonNumber(kon_jsonGet(t, "value"), 1.0);
+
+ if (code == 11 && data >= 0 && data < KON_RM_ELEMENTS) elementRate[data] *= v;
+ else if (code == 21 && data >= 0 && data < KON_PARAMS) paramRate[data] *= v;
+ else if (code == 22 && data >= 0 && data < 10) x[data] += percent(v);
+ else if (code == 23 && data == 6) pdr *= v;
+ else if (code == 23 && data == 7) mdr *= v;
+ }
+ params = kon_jsonGet(e, "params");
+ na = kon_jsonCount(kon_jsonGet(e, "actions"));
+ for (k = 0; k < kon_jsonCount(kon_jsonGet(e, "dropItems")); k++) {
+ if (number(kon_jsonGet(kon_jsonAt(kon_jsonGet(e, "dropItems"), k), "kind"), 0, 3, 0) > 0) drops++;
+ }
+ nd = drops;
+ fprintf(f, "\t[%d] = {", id);
+ cstring(f, text(kon_jsonGet(e, "name")));
+ fputs(", ", f);
+ cstring(f, text(kon_jsonGet(e, "battlerName")));
+ fprintf(f, ", %d, {", number(kon_jsonGet(e, "battlerHue"), 0, 360, 0));
+ for (k = 0; k < KON_PARAMS; k++) {
+ double base = kon_jsonNumber(kon_jsonAt(params, k), 0);
+ double v = base * paramRate[k];
+
+ fprintf(f, "%s%d", k ? ", " : "", v < 0 ? 0 : v > 99999999 ? 99999999 : (int)(v + 0.5));
+ }
+ fprintf(f, "}, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, {", number(kon_jsonGet(e, "exp"), 0, 99999999, 0), number(kon_jsonGet(e, "gold"), 0, 99999999, 0), x[0], x[1], x[2], x[3], x[4], x[5], percent(pdr), percent(mdr), x[7], x[8], x[9]);
+ for (k = 0; k < KON_RM_ELEMENTS; k++) fprintf(f, "%s%d", k ? ", " : "", percent(elementRate[k]));
+ fputs("}, ", f);
+ if (na > 0) fprintf(f, "%s_enemy%d_actions, %d, ", name, id, na);
+ else fputs("NULL, 0, ", f);
+ if (nd > 0) fprintf(f, "%s_enemy%d_drops, %d},\n", name, id, nd);
+ else fputs("NULL, 0},\n", f);
+ enemyCount++;
+ }
+ fputs("};\n\n", f);
+}
+
+static void writeTroops(FILE *f, const char *name, const kon_json_t *list)
+{
+ int i, high = highestId(list), k;
+
+ memset(idSeen, 0, sizeof idSeen);
+
+ for (i = 1; i < kon_jsonCount(list); i++) {
+ const kon_json_t *t = kon_jsonAt(list, i);
+ const kon_json_t *members, *pages;
+ int id, nm, np;
+
+ if (!t || t->type != KON_JSON_OBJECT) continue;
+ id = tableId(t, i);
+ if (id < 1) continue;
+ members = kon_jsonGet(t, "members");
+ nm = kon_jsonCount(members) < 8 ? kon_jsonCount(members) : 8;
+ if (nm > 0) {
+ fprintf(f, "static const kon_rmTroopMember_t %s_troop%d_members[] = {\n", name, id);
+ for (k = 0; k < nm; k++) {
+ const kon_json_t *m = kon_jsonAt(members, k);
+
+ fprintf(f, "\t{%d, %d, %d, %d},\n", number(kon_jsonGet(m, "enemyId"), 0, MAX_ID, 0), number(kon_jsonGet(m, "x"), -100000, 100000, 0), number(kon_jsonGet(m, "y"), -100000, 100000, 0), kon_jsonBool(kon_jsonGet(m, "hidden"), 0) ? 1 : 0);
+ }
+ fputs("};\n\n", f);
+ }
+ pages = kon_jsonGet(t, "pages");
+ np = kon_jsonCount(pages) < 100 ? kon_jsonCount(pages) : 100;
+ if (np > 0) {
+ fprintf(f, "static const kon_rmTroopPage_t %s_troop%d_pages[] = {\n", name, id);
+ for (k = 0; k < np; k++) {
+ const kon_json_t *p = kon_jsonAt(pages, k), *c = kon_jsonGet(p, "conditions");
+
+ fprintf(f, "\t{%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d},\n", kon_jsonBool(kon_jsonGet(c, "turnValid"), 0) ? 1 : 0, number(kon_jsonGet(c, "turnA"), 0, 100000, 0), number(kon_jsonGet(c, "turnB"), 0, 100000, 0), kon_jsonBool(kon_jsonGet(c, "turnEnding"), 0) ? 1 : 0,
+ kon_jsonBool(kon_jsonGet(c, "enemyValid"), 0) ? 1 : 0, number(kon_jsonGet(c, "enemyIndex"), 0, 100, 0), number(kon_jsonGet(c, "enemyHp"), 0, 100, 0),
+ kon_jsonBool(kon_jsonGet(c, "actorValid"), 0) ? 1 : 0, number(kon_jsonGet(c, "actorId"), 0, MAX_ID, 0), number(kon_jsonGet(c, "actorHp"), 0, 100, 0),
+ kon_jsonBool(kon_jsonGet(c, "switchValid"), 0) ? 1 : 0, number(kon_jsonGet(c, "switchId"), 0, 100000, 0), number(kon_jsonGet(p, "span"), 0, 2, 0));
+ }
+ fputs("};\n\n", f);
+ }
+ }
+
+ memset(idSeen, 0, sizeof idSeen);
+ fprintf(f, "const kon_rmTroop_t %s_troops[%d] = {\n", name, high + 1);
+ for (i = 1; i < kon_jsonCount(list); i++) {
+ const kon_json_t *t = kon_jsonAt(list, i);
+ int id, nm, np;
+
+ if (!t || t->type != KON_JSON_OBJECT) continue;
+ id = tableId(t, i);
+ if (id < 1) continue;
+ nm = kon_jsonCount(kon_jsonGet(t, "members")) < 8 ? kon_jsonCount(kon_jsonGet(t, "members")) : 8;
+ np = kon_jsonCount(kon_jsonGet(t, "pages")) < 100 ? kon_jsonCount(kon_jsonGet(t, "pages")) : 100;
+ fprintf(f, "\t[%d] = {", id);
+ cstring(f, text(kon_jsonGet(t, "name")));
+ if (nm > 0) fprintf(f, ", %s_troop%d_members, %d", name, id, nm);
+ else fputs(", NULL, 0", f);
+ if (np > 0) fprintf(f, ", %s_troop%d_pages, %d},\n", name, id, np);
+ else fputs(", NULL, 0},\n", f);
+ troopCount++;
+ }
+ fputs("};\n\n", f);
+}
+
+/*** main ***/
+
+int main(int argc, char **argv)
+{
+ char name[64] = "game", path[1100], upper[64], dataPath[1100];
+ const char *folder = NULL, *outFolder = NULL;
+ kon_json_t *states, *skills, *items, *enemies, *troops;
+ int a, i, hs = 0, hk = 0, hi = 0, he = 0, ht = 0;
+ FILE *f;
+
+ for (a = 1; a < argc; a++) {
+ if (!strcmp(argv[a], "-n") && a + 1 < argc) snprintf(name, sizeof name, "%s", argv[++a]);
+ else if (!folder) folder = argv[a];
+ else if (!outFolder) outFolder = argv[a];
+ }
+ if (!folder || !outFolder) {
+ fprintf(stderr, "usage: mzdata [-n name] <data folder> <output folder>\n");
+ return 2;
+ }
+ if (!validName(name)) {
+ fprintf(stderr, "the name has to be usable in C: letters, digits and _\n");
+ return 2;
+ }
+ if (!findData(dataPath, sizeof dataPath, folder)) return 1;
+ folder = dataPath;
+
+ states = load(folder, "States.json");
+ skills = load(folder, "Skills.json");
+ items = load(folder, "Items.json");
+ enemies = load(folder, "Enemies.json");
+ troops = load(folder, "Troops.json");
+ if (!states && !skills && !items && !enemies && !troops) {
+ fprintf(stderr, "none of the database files were found in %s\n", folder);
+ return 1;
+ }
+ hs = highestId(states);
+ hk = highestId(skills);
+ hi = highestId(items);
+ he = highestId(enemies);
+ ht = highestId(troops);
+
+ for (i = 0; name[i] && i < 63; i++) upper[i] = (char)toupper((unsigned char)name[i]);
+ upper[i] = '\0';
+
+ snprintf(path, sizeof path, "%s/%s_data.h", outFolder, name);
+ f = fopen(path, "wb");
+ if (!f) {
+ fprintf(stderr, "cannot write %s (does the output folder exist?)\n", path);
+ return 1;
+ }
+ fprintf(f, "/* written by mzdata, the database of the game */\n\n#ifndef %s_DATA_H\n#define %s_DATA_H\n\n#include \"konrmdata.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n", upper, upper);
+ fprintf(f, "#define %s_STATE_IDS %d\n#define %s_SKILL_IDS %d\n#define %s_ITEM_IDS %d\n#define %s_ENEMY_IDS %d\n#define %s_TROOP_IDS %d\n\n", upper, hs + 1, upper, hk + 1, upper, hi + 1, upper, he + 1, upper, ht + 1);
+ fprintf(f, "extern const kon_state_t %s_states[%d];\nextern kon_skill_t %s_skills[%d];\nextern kon_skill_t %s_items[%d];\nextern const kon_rmEnemy_t %s_enemies[%d];\nextern const kon_rmTroop_t %s_troops[%d];\n\n", name, hs + 1, name, hk + 1, name, hi + 1, name, he + 1, name, ht + 1);
+ fprintf(f, "/* compiles the formulas of the skills and items, once before they are used */\nvoid %s_dataInit(void);\n\n#ifdef __cplusplus\n}\n#endif\n#endif\n", name);
+ fclose(f);
+
+ snprintf(path, sizeof path, "%s/%s_data.c", outFolder, name);
+ f = fopen(path, "wb");
+ if (!f) {
+ fprintf(stderr, "cannot write %s\n", path);
+ return 1;
+ }
+ fprintf(f, "/* written by mzdata, the database of the game */\n\n#include \"%s_data.h\"\n\n#include <stddef.h>\n\n", name);
+ if (states) writeStates(f, name, states);
+ else fprintf(f, "const kon_state_t %s_states[1];\n\n", name);
+ if (skills) skillCount = writeSkills(f, name, "skills", skills, "skill");
+ else fprintf(f, "kon_skill_t %s_skills[1];\n\n", name);
+ if (items) itemCount = writeSkills(f, name, "items", items, "item");
+ else fprintf(f, "kon_skill_t %s_items[1];\n\n", name);
+ if (enemies) writeEnemies(f, name, enemies);
+ else fprintf(f, "const kon_rmEnemy_t %s_enemies[1];\n\n", name);
+ if (troops) writeTroops(f, name, troops);
+ else fprintf(f, "const kon_rmTroop_t %s_troops[1];\n\n", name);
+ fprintf(f, "void %s_dataInit(void)\n{\n\tint i;\n\n\tfor (i = 0; i < %s_SKILL_IDS; i++) kon_skillCompile(&%s_skills[i]);\n\tfor (i = 0; i < %s_ITEM_IDS; i++) kon_skillCompile(&%s_items[i]);\n}\n", name, upper, name, upper, name);
+ fclose(f);
+
+ printf("%d states, %d skills, %d items, %d enemies, %d troops\n", stateCount, skillCount, itemCount, enemyCount, troopCount);
+ if (droppedStatements) printf("%d formulas had something before the damage, which was dropped\n", droppedStatements);
+ if (badFormulas) printf("%d formulas the damage parser cannot read, those skills do nothing\n", badFormulas);
+ if (droppedEffects) printf("%d state effects did not fit (4 for each skill)\n", droppedEffects);
+ if (otherEffects) printf("%d other item and skill effects are not converted (growth, learning, common events and so on)\n", otherEffects);
+ if (warnings > 25) printf("%d warnings, the first ones were printed\n", warnings);
+ kon_jsonFree(states);
+ kon_jsonFree(skills);
+ kon_jsonFree(items);
+ kon_jsonFree(enemies);
+ kon_jsonFree(troops);
+ return 0;
+}