/* 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] * 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 #include #include #include #define MAX_ID 5000 #define STRING_PIECE 100 static int warnings, droppedStatements, badFormulas, droppedEffects, otherEffects; static int stateCount, skillCount, itemCount, enemyCount, troopCount, classCount, actorCount, gearCount, droppedLearnings, droppedGear; #define CLASS_LEVELS 100 /*** 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\t[0] = {0},\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, int offset) { 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 + offset); 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); } /* the entries of the table of skills. the items follow the skills in the same table, so the game has one place to look */ static int writeSkills(FILE *f, const kon_json_t *list, const char *kind, int offset) { int i, n = 0; memset(idSeen, 0, sizeof idSeen); 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, offset); n++; } 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\t[0] = {0},\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\t[0] = {0},\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); } /*** classes, items and actors ***/ static int classTraits; static void writeClasses(FILE *f, const char *name, const kon_json_t *list) { int i, k, p, high = highestId(list); memset(idSeen, 0, sizeof idSeen); for (i = 1; i < kon_jsonCount(list); i++) { const kon_json_t *c = kon_jsonAt(list, i); const kon_json_t *params = kon_jsonGet(c, "params"); int id; if (!c || c->type != KON_JSON_OBJECT) continue; id = tableId(c, i); if (id < 1) continue; for (p = 0; p < KON_PARAMS; p++) { const kon_json_t *row = kon_jsonAt(params, p); fprintf(f, "static const short %s_class%d_p%d[%d] = {", name, id, p, CLASS_LEVELS); for (k = 0; k < CLASS_LEVELS; k++) fprintf(f, "%s%d", k ? ", " : "", number(kon_jsonAt(row, k), 0, 32767, 0)); fputs("};\n", f); } fputs("\n", f); classTraits += kon_jsonCount(kon_jsonGet(c, "traits")); } fprintf(f, "const kon_class_t %s_classes[%d] = {\n\t[0] = {0},\n", name, high + 1); memset(idSeen, 0, sizeof idSeen); for (i = 1; i < kon_jsonCount(list); i++) { const kon_json_t *c = kon_jsonAt(list, i); const kon_json_t *exp = kon_jsonGet(c, "expParams"), *learn = kon_jsonGet(c, "learnings"); int id, n; if (!c || c->type != KON_JSON_OBJECT) continue; id = tableId(c, i); if (id < 1) continue; fprintf(f, "\t[%d] = {", id); cstring(f, text(kon_jsonGet(c, "name"))); fprintf(f, ", %d, {", CLASS_LEVELS - 1); for (p = 0; p < KON_PARAMS; p++) fprintf(f, "%s%s_class%d_p%d", p ? ", " : "", name, id, p); fprintf(f, "}, %d, %d, %d, %d, {", number(kon_jsonAt(exp, 0), 0, 100000, 30), number(kon_jsonAt(exp, 1), 0, 100000, 20), number(kon_jsonAt(exp, 2), 0, 100000, 30), number(kon_jsonAt(exp, 3), 0, 100000, 30)); n = 0; for (k = 0; k < kon_jsonCount(learn); k++) { const kon_json_t *l = kon_jsonAt(learn, k); if (n >= KON_CLASS_LEARNINGS) { droppedLearnings++; continue; } fprintf(f, "%s{%d, %d}", n ? ", " : "", number(kon_jsonGet(l, "level"), 1, 99, 1), number(kon_jsonGet(l, "skillId"), 0, MAX_ID, 0)); n++; } if (n == 0) fputs("{0, 0}", f); fprintf(f, "}, %d},\n", n); classCount++; } fputs("};\n\n", f); } /* items, weapons and armor in one table: the items by their id, the weapons after them, then the armor */ static int writeGear(FILE *f, const kon_json_t *list, int kind, int base, int skillBase) { int i, k, n = 0; memset(idSeen, 0, sizeof idSeen); for (i = 1; i < kon_jsonCount(list); i++) { const kon_json_t *it = kon_jsonAt(list, i); const kon_json_t *params = kon_jsonGet(it, "params"); int id, slot = 0, usable = 0; if (!it || it->type != KON_JSON_OBJECT) continue; id = tableId(it, i); if (id < 1) continue; if (kind == 0) { usable = number(kon_jsonGet(it, "scope"), 0, 100, 0) != 0 || kon_jsonCount(kon_jsonGet(it, "effects")) > 0; } else { slot = number(kon_jsonGet(it, "etypeId"), 1, 100, 1) - 1; if (slot >= KON_EQUIP_SLOTS) { droppedGear++; continue; } } fprintf(f, "\t[%d] = {", base + id); cstring(f, text(kon_jsonGet(it, "name"))); fprintf(f, ", %s, %d, %d, %d, %d, {", kind == 0 ? (kon_jsonInt(kon_jsonGet(it, "itypeId"), 1) == 2 ? "KON_ITEM_KEY" : "KON_ITEM_ITEM") : kind == 1 ? "KON_ITEM_WEAPON" : "KON_ITEM_ARMOR", number(kon_jsonGet(it, "price"), 0, 99999999, 0), kind == 0 && kon_jsonBool(kon_jsonGet(it, "consumable"), 0) ? 1 : 0, kind == 0 ? within(kon_jsonGet(it, "occasion"), 0, 3, 0) : 3, slot); for (k = 0; k < KON_PARAMS; k++) fprintf(f, "%s%d", k ? ", " : "", kind == 0 ? 0 : number(kon_jsonAt(params, k), -99999, 99999, 0)); fprintf(f, "}, %d},\n", usable ? skillBase + id : -1); n++; } return n; } static void writeActors(FILE *f, const char *name, const kon_json_t *list) { int i, k, high = highestId(list); fprintf(f, "const kon_rmActor_t %s_actors[%d] = {\n\t[0] = {0},\n", name, high + 1); memset(idSeen, 0, sizeof idSeen); for (i = 1; i < kon_jsonCount(list); i++) { const kon_json_t *a = kon_jsonAt(list, i); const kon_json_t *equips = kon_jsonGet(a, "equips"); int id; if (!a || a->type != KON_JSON_OBJECT) continue; id = tableId(a, i); if (id < 1) continue; fprintf(f, "\t[%d] = {", id); cstring(f, text(kon_jsonGet(a, "name"))); fputs(", ", f); cstring(f, text(kon_jsonGet(a, "nickname"))); fputs(", ", f); cstring(f, text(kon_jsonGet(a, "characterName"))); fputs(", ", f); cstring(f, text(kon_jsonGet(a, "faceName"))); fputs(", ", f); cstring(f, text(kon_jsonGet(a, "battlerName"))); fprintf(f, ", %d, %d, %d, %d, %d, {", number(kon_jsonGet(a, "characterIndex"), 0, 7, 0), number(kon_jsonGet(a, "faceIndex"), 0, 7, 0), number(kon_jsonGet(a, "classId"), 0, MAX_ID, 0), number(kon_jsonGet(a, "initialLevel"), 1, 99, 1), number(kon_jsonGet(a, "maxLevel"), 1, 99, 99)); for (k = 0; k < KON_EQUIP_SLOTS; k++) fprintf(f, "%s%d", k ? ", " : "", number(kon_jsonAt(equips, k), 0, MAX_ID, 0)); fputs("}},\n", f); actorCount++; } 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, *weapons, *armors, *enemies, *troops, *classes, *actors; int a, i, hs, hk, hi, hw, ha, he, ht, hc, hr, skillTotal, gearTotal; 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] \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"); weapons = load(folder, "Weapons.json"); armors = load(folder, "Armors.json"); enemies = load(folder, "Enemies.json"); troops = load(folder, "Troops.json"); classes = load(folder, "Classes.json"); actors = load(folder, "Actors.json"); if (!states && !skills && !items && !enemies && !troops && !classes && !actors) { fprintf(stderr, "none of the database files were found in %s\n", folder); return 1; } hs = highestId(states); hk = highestId(skills); hi = highestId(items); hw = highestId(weapons); ha = highestId(armors); he = highestId(enemies); ht = highestId(troops); hc = highestId(classes); hr = highestId(actors); skillTotal = hk + hi + 1; /* the skills by their id, then the items, hk + their id */ gearTotal = hi + hw + ha + 1; /* the items, then the weapons, then the armor */ 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 /* the skills, then the items as skills */\n#define %s_ITEM_SKILL_BASE %d /* an item's skill is its id and this */\n", upper, hs + 1, upper, skillTotal, upper, hk); fprintf(f, "#define %s_ITEM_IDS %d /* the items, the weapons, then the armor */\n#define %s_WEAPON_BASE %d /* a weapon's number is its id and this */\n#define %s_ARMOR_BASE %d\n", upper, gearTotal, upper, hi, upper, hi + hw); fprintf(f, "#define %s_CLASS_IDS %d\n#define %s_ACTOR_IDS %d\n#define %s_ENEMY_IDS %d\n#define %s_TROOP_IDS %d\n\n", upper, hc + 1, upper, hr + 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 const kon_item_t %s_items[%d];\nextern const kon_class_t %s_classes[%d];\nextern const kon_rmActor_t %s_actors[%d];\n", name, hs + 1, name, skillTotal, name, gearTotal, name, hc + 1, name, hr + 1); fprintf(f, "extern const kon_rmEnemy_t %s_enemies[%d];\nextern const kon_rmTroop_t %s_troops[%d];\n\n/* what the party module wants */\nextern const kon_database_t %s_database;\n\n", name, he + 1, name, ht + 1, name); 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 \n\n", name); if (states) writeStates(f, name, states); else fprintf(f, "const kon_state_t %s_states[1];\n\n", name); fprintf(f, "kon_skill_t %s_skills[%d] = {\n\t[0] = {0},\n", name, skillTotal); if (skills) skillCount = writeSkills(f, skills, "skill", 0); if (items) itemCount = writeSkills(f, items, "item", hk); fputs("};\n\n", f); 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); if (classes) writeClasses(f, name, classes); else fprintf(f, "const kon_class_t %s_classes[1];\n\n", name); fprintf(f, "const kon_item_t %s_items[%d] = {\n\t[0] = {0},\n", name, gearTotal); if (items) gearCount += writeGear(f, items, 0, 0, hk); if (weapons) gearCount += writeGear(f, weapons, 1, hi, hk); if (armors) gearCount += writeGear(f, armors, 2, hi + hw, hk); fputs("};\n\n", f); if (actors) writeActors(f, name, actors); else fprintf(f, "const kon_rmActor_t %s_actors[1];\n\n", name); fprintf(f, "const kon_database_t %s_database = {%s_items, %d, %s_classes, %d, %s_skills, %d, %s_states, %d};\n\n", name, name, gearTotal, name, hc + 1, name, skillTotal, name, hs + 1); 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}\n", name, upper, name); fclose(f); printf("%d states, %d skills, %d items, %d enemies, %d troops, %d classes, %d actors, %d items, weapons and armor\n", stateCount, skillCount, itemCount, enemyCount, troopCount, classCount, actorCount, gearCount); 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 (classTraits) printf("%d traits of classes are not converted (hit and evasion, equipment allowed, element and state rates)\n", classTraits); if (droppedLearnings) printf("%d learned skills did not fit (%d for each class)\n", droppedLearnings, KON_CLASS_LEARNINGS); if (droppedGear) printf("%d weapons and armor are for an equipment slot there is no room for\n", droppedGear); if (warnings > 25) printf("%d warnings, the first ones were printed\n", warnings); kon_jsonFree(states); kon_jsonFree(skills); kon_jsonFree(items); kon_jsonFree(weapons); kon_jsonFree(armors); kon_jsonFree(enemies); kon_jsonFree(troops); kon_jsonFree(classes); kon_jsonFree(actors); return 0; }