diff options
| author | KonAki <konaki@anokonaki.org> | 2026-10-16 15:30:47 +0800 |
|---|---|---|
| committer | KonAki <konaki@anokonaki.org> | 2026-10-16 15:30:47 +0800 |
| commit | f56c963d2b5661da79abcbc68bce5b08db31d030 (patch) | |
| tree | 4a17f73cbcf69cc7da954d2e57ccc28c03f60d1b | |
| parent | c4b53a0d1ba903dc94fe93d2fa50367bfbefa385 (diff) | |
add mzdata: database converter
| -rw-r--r-- | tools/mzdata.c | 599 |
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; +} |
