summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKonAki <konaki@anokonaki.org>2026-10-14 12:16:31 +0800
committerKonAki <konaki@anokonaki.org>2026-10-14 12:16:31 +0800
commitee1aa5715693f7a8bd4886641dea0f6900a30eaa (patch)
treeac29a783fafa5a469f438d0fb1b51d01667e2bb3
parent68565896993ac42769b02eb809c883a7a5726e00 (diff)
add mzscript and kons2c, converters write C directly through konscriptc
-rw-r--r--tools/kons2c.c216
-rw-r--r--tools/konscriptc.h284
-rw-r--r--tools/mzscript.c129
3 files changed, 392 insertions, 237 deletions
diff --git a/tools/kons2c.c b/tools/kons2c.c
index 8cacbc8..2a53520 100644
--- a/tools/kons2c.c
+++ b/tools/kons2c.c
@@ -25,31 +25,16 @@
/*
* usage: kons2c [-n name] [-split rows] <script.kons> <output prefix>
- * turns konscript text into C. any converter can write konscript text, this is the one place that writes C from it.
- *
- * the text goes through the real parser first, so every jump lands on a label that is there, and nothing is written if it
- * doesn't parse. what comes out is only constant data: a list of kon_cmd_t, a sorted list of labels and a find function.
- * strings are written as literals where everything but plain printable characters is an octal escape, so what is in the
- * script can't be anything but text to the compiler. there is no pointer arithmetic, no generated logic and nothing to overrun
- *
- * writes prefix.h and prefix.c, and with -split also prefix_0.inc, prefix_1.inc and so on that prefix.c includes inside
- * the one array, so a big script is several files for the editor and one list for the program
+ * konscript text to C, for scripts written by hand. the converters write C themselves through konscriptc.h
*/
-#include "konscript.h"
+#define KONSCRIPTC_IMPLEMENTATION
+#include "konscriptc.h"
-#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-static const char *const opNames[] = {
- "KON_OP_END", "KON_OP_SAY", "KON_OP_MENU", "KON_OP_OPTION", "KON_OP_JUMP", "KON_OP_SWITCH", "KON_OP_VAR",
- "KON_OP_IFSWITCH", "KON_OP_IFVAR", "KON_OP_WAIT", "KON_OP_HOST", "KON_OP_CALL", "KON_OP_RETURN"
-};
-
-/*** reading ***/
-
static char *readFile(const char *path, size_t *size)
{
FILE *f = fopen(path, "rb");
@@ -72,86 +57,14 @@ static char *readFile(const char *path, size_t *size)
return text;
}
-static int compareLabels(const void *a, const void *b)
-{
- return strcmp(((const kon_scriptLabel_t *)a)->name, ((const kon_scriptLabel_t *)b)->name);
-}
-
-/*** writing ***/
-
-static int putString(FILE *f, const char *s)
-{
- size_t i, column = 0;
-
- if (!s) return fputs("NULL", f) >= 0;
- fputc('"', f);
- for (i = 0; s[i]; i++) {
- unsigned char c = (unsigned char)s[i];
-
- if (column >= 100) {
- fputs("\"\n\t\t\"", f);
- column = 0;
- }
- if (c == '"' || c == '\\' || c == '?') {
- fprintf(f, "\\%c", c);
- column += 2;
- } else if (c == '\n' || c == '\t') {
- fputs(c == '\n' ? "\\n" : "\\t", f);
- column += 2;
- } else if (c >= 0x20 && c < 0x7F) {
- fputc(c, f);
- column++;
- } else {
- fprintf(f, "\\%03o", c);
- column += 4;
- }
- }
- fputc('"', f);
- return 1;
-}
-
-static void putRow(FILE *f, const kon_cmd_t *c)
-{
- fprintf(f, "\t{%s, %d, %d, %d, %d, ", opNames[c->op], c->a, c->b, c->c, c->d);
- putString(f, c->text);
- fputs("},\n", f);
-}
-
-static void identifier(char *out, size_t size, const char *path)
-{
- const char *base = strrchr(path, '/');
- size_t i, n = 0;
-
- base = base ? base + 1 : path;
- for (i = 0; base[i] && base[i] != '.' && n + 1 < size; i++) out[n++] = isalnum((unsigned char)base[i]) ? (char)tolower((unsigned char)base[i]) : '_';
- out[n] = '\0';
- if (!n || isdigit((unsigned char)out[0])) {
- memmove(out + 1, out, n + 1 < size ? n + 1 : size - 1);
- out[0] = 'k';
- }
-}
-
-static FILE *create(const char *prefix, const char *suffix)
-{
- char path[1024];
- FILE *f;
-
- if (snprintf(path, sizeof path, "%s%s", prefix, suffix) >= (int)sizeof path) return NULL;
- f = fopen(path, "wb");
- if (!f) fprintf(stderr, "cannot write %s\n", path);
- return f;
-}
-
int main(int argc, char **argv)
{
- char name[64] = "", upper[64];
+ char name[64] = "", error[200];
const char *input = NULL, *prefix = NULL;
- int split = 0, i, rows, lines = 1;
- size_t size, k;
+ int split = 0, i;
+ size_t size;
char *source;
- kon_scriptBuild_t b;
- kon_cmd_t *cmds;
- FILE *f;
+ konsc_result_t result;
for (i = 1; i < argc; i++) {
if (!strcmp(argv[i], "-n") && i + 1 < argc) {
@@ -164,125 +77,22 @@ int main(int argc, char **argv)
prefix = argv[i];
}
}
- if (!input || !prefix || split < 0) {
+ if (!input || !prefix) {
fprintf(stderr, "usage: kons2c [-n name] [-split rows] <script.kons> <output prefix>\n");
return 2;
}
- if (!name[0]) identifier(name, sizeof name, prefix);
- for (k = 0; name[k]; k++) {
- if (!isalnum((unsigned char)name[k]) && name[k] != '_') {
- fprintf(stderr, "the name has to be letters, digits and _\n");
- return 2;
- }
- upper[k] = (char)toupper((unsigned char)name[k]);
- }
- upper[k] = '\0';
- if (isdigit((unsigned char)name[0])) {
- fprintf(stderr, "the name can't start with a digit\n");
- return 2;
- }
-
+ if (!name[0]) konsc_name(name, sizeof name, prefix);
source = readFile(input, &size);
if (!source) {
fprintf(stderr, "cannot read %s\n", input);
return 1;
}
- for (k = 0; k < size; k++) {
- if (source[k] == '\n') lines++;
- }
-
- memset(&b, 0, sizeof b);
- b.maxCmds = lines + 2;
- b.maxLabels = lines + 2;
- b.poolSize = (int)(size + 16);
- b.hashSize = 16;
- while (b.hashSize < 2 * b.maxLabels) b.hashSize *= 2;
- cmds = malloc((size_t)b.maxCmds * sizeof *cmds);
- b.cmds = cmds;
- b.pool = malloc((size_t)b.poolSize);
- b.labels = malloc((size_t)b.maxLabels * sizeof *b.labels);
- b.hash = malloc((size_t)b.hashSize * sizeof *b.hash);
- if (size > 2000000000u || !cmds || !b.pool || !b.labels || !b.hash) {
- fprintf(stderr, "out of memory\n");
- return 1;
- }
- if (!kon_scriptParse(&b, source)) {
- fprintf(stderr, "%s:%d: %s\n", input, b.errorLine, b.error);
+ if (konsc_write(source, size, prefix, name, split, &result, error, sizeof error) != 0) {
+ fprintf(stderr, "%s: %s\n", input, error);
+ free(source);
return 1;
}
-
- /* a label at the very end would point past the list, so the list gets an end of its own to land on */
- cmds[b.count].op = KON_OP_END;
- cmds[b.count].a = cmds[b.count].b = cmds[b.count].c = cmds[b.count].d = 0;
- cmds[b.count].text = NULL;
- rows = b.count + 1;
- qsort(b.labels, (size_t)b.labelCount, sizeof *b.labels, compareLabels);
-
- if (split) {
- int part, first;
-
- for (part = 0, first = 0; first < rows; part++, first += split) {
- char suffix[32];
- int last = first + split < rows ? first + split : rows;
-
- snprintf(suffix, sizeof suffix, "_%d.inc", part);
- f = create(prefix, suffix);
- if (!f) return 1;
- fprintf(f, "/* part %d of %s, rows %d to %d, included by %s.c */\n", part, name, first, last - 1, name);
- for (i = first; i < last; i++) putRow(f, &cmds[i]);
- if (fclose(f) != 0) return 1;
- }
- }
-
- f = create(prefix, ".c");
- if (!f) return 1;
- {
- const char *base = strrchr(prefix, '/');
-
- fprintf(f, "/* made by kons2c from %s, don't edit */\n\n", strrchr(input, '/') ? strrchr(input, '/') + 1 : input);
- fprintf(f, "#include \"%s.h\"\n\n#include <string.h>\n\n", base ? base + 1 : prefix);
- }
- fprintf(f, "const int %s_count = %d;\n\n", name, rows);
- fprintf(f, "const kon_cmd_t %s_cmds[%d] = {\n", name, rows);
- if (split) {
- const char *base = strrchr(prefix, '/');
- int part, first;
-
- for (part = 0, first = 0; first < rows; part++, first += split) fprintf(f, "#include \"%s_%d.inc\"\n", base ? base + 1 : prefix, part);
- } else {
- for (i = 0; i < rows; i++) putRow(f, &cmds[i]);
- }
- fprintf(f, "};\n\n");
- fprintf(f, "const int %s_labelCount = %d;\n\n", name, b.labelCount);
- fprintf(f, "const kon_scriptLabel_t %s_labels[%d] = {\n", name, b.labelCount > 0 ? b.labelCount : 1);
- for (i = 0; i < b.labelCount; i++) {
- fprintf(f, "\t{");
- putString(f, b.labels[i].name);
- fprintf(f, ", %d},\n", b.labels[i].at);
- }
- if (b.labelCount == 0) fprintf(f, "\t{\"\", 0}\n");
- fprintf(f, "};\n\n");
- fprintf(f, "int %s_find(const char *label)\n{\n\tint low = 0, high = %s_labelCount - 1;\n\n", name, name);
- fprintf(f, "\twhile (low <= high) {\n\t\tint mid = low + (high - low) / 2;\n\t\tint order = strcmp(label, %s_labels[mid].name);\n\n", name);
- fprintf(f, "\t\tif (order == 0) return %s_labels[mid].at;\n\t\tif (order < 0) high = mid - 1;\n\t\telse low = mid + 1;\n\t}\n\treturn -1;\n}\n", name);
- if (fclose(f) != 0) return 1;
-
- f = create(prefix, ".h");
- if (!f) return 1;
- fprintf(f, "/* made by kons2c, don't edit */\n\n#ifndef %s_H\n#define %s_H\n\n#include \"konscript.h\"\n\n", upper, upper);
- fprintf(f, "#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n");
- fprintf(f, "#define %s_COUNT %d\n\n", upper, rows);
- fprintf(f, "extern const int %s_count;\nextern const kon_cmd_t %s_cmds[%d];\n", name, name, rows);
- fprintf(f, "extern const int %s_labelCount;\nextern const kon_scriptLabel_t %s_labels[%d];\n\n", name, name, b.labelCount > 0 ? b.labelCount : 1);
- fprintf(f, "/* where a label is, or -1 */\nint %s_find(const char *label);\n\n", name);
- fprintf(f, "#ifdef __cplusplus\n}\n#endif\n#endif\n");
- if (fclose(f) != 0) return 1;
-
- printf("%s: %d commands, %d labels%s\n", name, rows, b.labelCount, split ? ", split" : "");
+ printf("%s: %d commands, %d labels%s\n", name, result.commands, result.labels, split ? ", split" : "");
free(source);
- free(cmds);
- free(b.pool);
- free(b.labels);
- free(b.hash);
return 0;
}
diff --git a/tools/konscriptc.h b/tools/konscriptc.h
new file mode 100644
index 0000000..7996b56
--- /dev/null
+++ b/tools/konscriptc.h
@@ -0,0 +1,284 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * konscriptc.h
+ *
+ * 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.
+ */
+
+/*
+ * what every converter uses to write konscript as C, so there is one place that does it and one place to check.
+ * in one .c file: #define KONSCRIPTC_IMPLEMENTATION before including it
+ *
+ * the text goes through the real parser first, so every jump lands on a label that is there, and nothing is written if it
+ * doesn't parse. what comes out is only constant data: a list of kon_cmd_t, a sorted list of labels and a find function.
+ * strings are written as literals where everything but plain printable characters is an octal escape, so what is in the
+ * script can't be anything but text to the compiler. there is no pointer arithmetic, no generated logic and nothing to overrun
+ *
+ * writes prefix.h and prefix.c, and with split also prefix_0.inc, prefix_1.inc and so on that prefix.c includes inside
+ * the one array, so a big script is several files for the editor and one list for the program
+ */
+
+#ifndef KONSCRIPTC_H
+#define KONSCRIPTC_H
+
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct konsc_result {
+ int commands, labels;
+} konsc_result_t;
+
+/* name is the start of every name in the files, letters digits and _, not starting with a digit.
+ split is how many commands go in each .inc, 0 for none. 0 if it worked, -1 and a message in error if not */
+int konsc_write(const char *source, size_t size, const char *prefix, const char *name, int split, konsc_result_t *result, char *error, int errorSize);
+
+/* a C string literal of s, or NULL. long ones are cut into pieces the compiler joins */
+void konsc_string(FILE *f, const char *s);
+
+/* a name for the files made from the end of a path, "out/my game.c" becomes my_game */
+void konsc_name(char *out, size_t size, const char *path);
+
+#ifdef __cplusplus
+}
+#endif
+
+#ifdef KONSCRIPTC_IMPLEMENTATION
+
+#include "konscript.h"
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+static const char *const konsc__ops[] = {
+ "KON_OP_END", "KON_OP_SAY", "KON_OP_MENU", "KON_OP_OPTION", "KON_OP_JUMP", "KON_OP_SWITCH", "KON_OP_VAR",
+ "KON_OP_IFSWITCH", "KON_OP_IFVAR", "KON_OP_WAIT", "KON_OP_HOST", "KON_OP_CALL", "KON_OP_RETURN"
+};
+
+static int konsc__compareLabels(const void *a, const void *b)
+{
+ return strcmp(((const kon_scriptLabel_t *)a)->name, ((const kon_scriptLabel_t *)b)->name);
+}
+
+void konsc_string(FILE *f, const char *s)
+{
+ size_t i, column = 0;
+
+ if (!s) {
+ fputs("NULL", f);
+ return;
+ }
+ fputc('"', f);
+ for (i = 0; s[i]; i++) {
+ unsigned char c = (unsigned char)s[i];
+
+ if (column >= 100) {
+ fputs("\"\n\t\t\"", f);
+ column = 0;
+ }
+ if (c == '"' || c == '\\' || c == '?') {
+ fprintf(f, "\\%c", c);
+ column += 2;
+ } else if (c == '\n' || c == '\t') {
+ fputs(c == '\n' ? "\\n" : "\\t", f);
+ column += 2;
+ } else if (c >= 0x20 && c < 0x7F) {
+ fputc(c, f);
+ column++;
+ } else {
+ fprintf(f, "\\%03o", c);
+ column += 4;
+ }
+ }
+ fputc('"', f);
+}
+
+void konsc_name(char *out, size_t size, const char *path)
+{
+ const char *base = strrchr(path, '/');
+ size_t i, n = 0;
+
+ base = base ? base + 1 : path;
+ for (i = 0; base[i] && base[i] != '.' && n + 1 < size; i++) out[n++] = isalnum((unsigned char)base[i]) ? (char)tolower((unsigned char)base[i]) : '_';
+ out[n] = '\0';
+ if (!n || isdigit((unsigned char)out[0])) {
+ memmove(out + 1, out, n + 1 < size ? n + 1 : size - 1);
+ out[0] = 'k';
+ }
+}
+
+static void konsc__row(FILE *f, const kon_cmd_t *c)
+{
+ fprintf(f, "\t{%s, %d, %d, %d, %d, ", konsc__ops[c->op], c->a, c->b, c->c, c->d);
+ konsc_string(f, c->text);
+ fputs("},\n", f);
+}
+
+static int konsc__fail(char *error, int errorSize, const char *what, const char *detail)
+{
+ if (error && errorSize > 0) snprintf(error, (size_t)errorSize, "%s%s", what, detail ? detail : "");
+ return -1;
+}
+
+static FILE *konsc__create(const char *prefix, const char *suffix, char *error, int errorSize)
+{
+ char path[1024];
+ FILE *f;
+
+ if (snprintf(path, sizeof path, "%s%s", prefix, suffix) >= (int)sizeof path) {
+ konsc__fail(error, errorSize, "path too long", NULL);
+ return NULL;
+ }
+ f = fopen(path, "wb");
+ if (!f) konsc__fail(error, errorSize, "cannot write ", path);
+ return f;
+}
+
+int konsc_write(const char *source, size_t size, const char *prefix, const char *name, int split, konsc_result_t *result, char *error, int errorSize)
+{
+ char upper[64];
+ const char *base = strrchr(prefix, '/');
+ kon_scriptBuild_t b;
+ kon_cmd_t *cmds;
+ size_t k;
+ int i, rows, lines = 1, status = -1;
+ FILE *f;
+
+ base = base ? base + 1 : prefix;
+ if (!name[0] || strlen(name) >= sizeof upper || isdigit((unsigned char)name[0])) return konsc__fail(error, errorSize, "the name has to be letters, digits and _ and not start with a digit", NULL);
+ for (k = 0; name[k]; k++) {
+ if (!isalnum((unsigned char)name[k]) && name[k] != '_') return konsc__fail(error, errorSize, "the name has to be letters, digits and _ and not start with a digit", NULL);
+ upper[k] = (char)toupper((unsigned char)name[k]);
+ }
+ upper[k] = '\0';
+ if (split < 0 || size > 1000000000u) return konsc__fail(error, errorSize, "bad split or a script that is too big", NULL);
+
+ for (k = 0; k < size; k++) {
+ if (source[k] == '\n') lines++;
+ }
+ memset(&b, 0, sizeof b);
+ b.maxCmds = lines + 2;
+ b.maxLabels = lines + 2;
+ b.poolSize = (int)size + 16;
+ b.hashSize = 16;
+ while (b.hashSize < 2 * b.maxLabels) b.hashSize *= 2;
+ cmds = malloc((size_t)b.maxCmds * sizeof *cmds);
+ b.cmds = cmds;
+ b.pool = malloc((size_t)b.poolSize);
+ b.labels = malloc((size_t)b.maxLabels * sizeof *b.labels);
+ b.hash = malloc((size_t)b.hashSize * sizeof *b.hash);
+ if (!cmds || !b.pool || !b.labels || !b.hash) {
+ konsc__fail(error, errorSize, "out of memory", NULL);
+ goto done;
+ }
+ if (!kon_scriptParse(&b, source)) {
+ if (error && errorSize > 0) snprintf(error, (size_t)errorSize, "line %d: %s", b.errorLine, b.error);
+ goto done;
+ }
+
+ /* a label at the very end would point past the list, so the list gets an end of its own to land on */
+ cmds[b.count].op = KON_OP_END;
+ cmds[b.count].a = cmds[b.count].b = cmds[b.count].c = cmds[b.count].d = 0;
+ cmds[b.count].text = NULL;
+ rows = b.count + 1;
+ qsort(b.labels, (size_t)b.labelCount, sizeof *b.labels, konsc__compareLabels);
+
+ if (split) {
+ int part, first;
+
+ for (part = 0, first = 0; first < rows; part++, first += split) {
+ char suffix[32];
+ int last = first + split < rows ? first + split : rows;
+
+ snprintf(suffix, sizeof suffix, "_%d.inc", part);
+ f = konsc__create(prefix, suffix, error, errorSize);
+ if (!f) goto done;
+ fprintf(f, "/* part %d of %s, commands %d to %d, included by %s.c */\n", part, name, first, last - 1, base);
+ for (i = first; i < last; i++) konsc__row(f, &cmds[i]);
+ if (fclose(f) != 0) {
+ konsc__fail(error, errorSize, "write failed", NULL);
+ goto done;
+ }
+ }
+ }
+
+ f = konsc__create(prefix, ".c", error, errorSize);
+ if (!f) goto done;
+ fprintf(f, "/* made by konscriptc, don't edit */\n\n#include \"%s.h\"\n\n#include <string.h>\n\n", base);
+ fprintf(f, "const int %s_count = %d;\n\n", name, rows);
+ fprintf(f, "const kon_cmd_t %s_cmds[%d] = {\n", name, rows);
+ if (split) {
+ int part, first;
+
+ for (part = 0, first = 0; first < rows; part++, first += split) fprintf(f, "#include \"%s_%d.inc\"\n", base, part);
+ } else {
+ for (i = 0; i < rows; i++) konsc__row(f, &cmds[i]);
+ }
+ fprintf(f, "};\n\n");
+ fprintf(f, "const int %s_labelCount = %d;\n\n", name, b.labelCount);
+ fprintf(f, "const kon_scriptLabel_t %s_labels[%d] = {\n", name, b.labelCount > 0 ? b.labelCount : 1);
+ for (i = 0; i < b.labelCount; i++) {
+ fputs("\t{", f);
+ konsc_string(f, b.labels[i].name);
+ fprintf(f, ", %d},\n", b.labels[i].at);
+ }
+ if (b.labelCount == 0) fputs("\t{\"\", 0}\n", f);
+ fputs("};\n\n", f);
+ fprintf(f, "int %s_find(const char *label)\n{\n\tint low = 0, high = %s_labelCount - 1;\n\n", name, name);
+ fprintf(f, "\twhile (low <= high) {\n\t\tint mid = low + (high - low) / 2;\n\t\tint order = strcmp(label, %s_labels[mid].name);\n\n", name);
+ fprintf(f, "\t\tif (order == 0) return %s_labels[mid].at;\n\t\tif (order < 0) high = mid - 1;\n\t\telse low = mid + 1;\n\t}\n\treturn -1;\n}\n", name);
+ if (fclose(f) != 0) {
+ konsc__fail(error, errorSize, "write failed", NULL);
+ goto done;
+ }
+
+ f = konsc__create(prefix, ".h", error, errorSize);
+ if (!f) goto done;
+ fprintf(f, "/* made by konscriptc, don't edit */\n\n#ifndef %s_H\n#define %s_H\n\n#include \"konscript.h\"\n\n", upper, upper);
+ fputs("#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n", f);
+ fprintf(f, "#define %s_COUNT %d\n\n", upper, rows);
+ fprintf(f, "extern const int %s_count;\nextern const kon_cmd_t %s_cmds[%d];\n", name, name, rows);
+ fprintf(f, "extern const int %s_labelCount;\nextern const kon_scriptLabel_t %s_labels[%d];\n\n", name, name, b.labelCount > 0 ? b.labelCount : 1);
+ fprintf(f, "/* where a label is, or -1 */\nint %s_find(const char *label);\n\n", name);
+ fputs("#ifdef __cplusplus\n}\n#endif\n#endif\n", f);
+ if (fclose(f) != 0) {
+ konsc__fail(error, errorSize, "write failed", NULL);
+ goto done;
+ }
+
+ if (result) {
+ result->commands = rows;
+ result->labels = b.labelCount;
+ }
+ status = 0;
+done:
+ free(cmds);
+ free(b.pool);
+ free(b.labels);
+ free(b.hash);
+ return status;
+}
+
+#endif /* end of KONSCRIPTC_IMPLEMENTATION */
+#endif
diff --git a/tools/mzscript.c b/tools/mzscript.c
index f9371f6..077efb2 100644
--- a/tools/mzscript.c
+++ b/tools/mzscript.c
@@ -24,21 +24,26 @@
*/
/*
- * usage: mzscript <data folder> <output folder>
- * turns the events of an exported RPG Maker MV or MZ game into konscript text.
+ * usage: mzscript [-n name] [-split rows] [-text] <data folder> <output folder>
+ * turns the events of an exported RPG Maker MV or MZ game into C, ready to build with the engine.
*
- * 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
+ * name.h, name.c every common event (label common_N), troop page (tN pM) and map event page (mN eN pN) as one list of
+ * kon_cmd_t, see konscriptc.h for how it is written and checked. with -split the list is also name_0.inc and so on
+ * name_speakers.c what the numbers after say mean: face file, face index, name, window background, window position,
+ * and the scratch switch and variable the host answers conditions and battle results in
+ * name.kons only with -text, the same script as konscript text for reading
*
* 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
+ * tabs, in the order RPG Maker keeps them. a choice, loop or battle result is turned into labels and jumps
*/
#define KONJSON_IMPLEMENTATION
#include "konjson.h"
+#define KONSCRIPTC_IMPLEMENTATION
+#include "konscriptc.h"
+#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
@@ -731,31 +736,88 @@ static kon_json_t *load(const char *folder, const char *name)
return j;
}
+static int writeSpeakers(const char *prefix, const char *name, int scratchSwitch, int scratchVar)
+{
+ char path[1100];
+ const char *base = strrchr(prefix, '/');
+ char upper[64];
+ FILE *f;
+ int k;
+
+ base = base ? base + 1 : prefix;
+ for (k = 0; name[k] && k < 63; k++) upper[k] = (char)toupper((unsigned char)name[k]);
+ upper[k] = '\0';
+ snprintf(path, sizeof path, "%s_speakers.h", prefix);
+ f = fopen(path, "wb");
+ if (!f) {
+ fprintf(stderr, "cannot write %s\n", path);
+ return 1;
+ }
+ fprintf(f, "/* made by mzscript, don't edit */\n\n#ifndef %s_SPEAKERS_H\n#define %s_SPEAKERS_H\n\n", upper, upper);
+ fprintf(f, "#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n");
+ fprintf(f, "/* the host answers conditions and battle results in this switch and variable */\n");
+ fprintf(f, "#define %s_SCRATCH_SWITCH %d\n#define %s_SCRATCH_VARIABLE %d\n\n", upper, scratchSwitch, upper, scratchVar);
+ fprintf(f, "typedef struct %s_speaker {\n\tconst char *face;\n\tint faceIndex;\n\tconst char *name;\n\tint background, position;\n} %s_speaker_t;\n\n", name, name);
+ fprintf(f, "extern const int %s_speakerCount;\nextern const %s_speaker_t %s_speakers[%d];\n\n", name, name, name, speakerCount > 0 ? speakerCount : 1);
+ fprintf(f, "#ifdef __cplusplus\n}\n#endif\n#endif\n");
+ if (fclose(f) != 0) return 1;
+
+ snprintf(path, sizeof path, "%s_speakers.c", prefix);
+ f = fopen(path, "wb");
+ if (!f) {
+ fprintf(stderr, "cannot write %s\n", path);
+ return 1;
+ }
+ fprintf(f, "/* made by mzscript, don't edit */\n\n#include \"%s_speakers.h\"\n\n", base);
+ fprintf(f, "const int %s_speakerCount = %d;\n\n", name, speakerCount);
+ fprintf(f, "const %s_speaker_t %s_speakers[%d] = {\n", name, name, speakerCount > 0 ? speakerCount : 1);
+ for (k = 0; k < speakerCount; k++) {
+ fputs("\t{", f);
+ konsc_string(f, speakers[k].face);
+ fprintf(f, ", %d, ", speakers[k].index);
+ konsc_string(f, speakers[k].name);
+ fprintf(f, ", %d, %d},\n", speakers[k].background, speakers[k].position);
+ }
+ if (!speakerCount) fputs("\t{\"\", 0, \"\", 0, 0}\n", f);
+ fputs("};\n", f);
+ return fclose(f) != 0;
+}
+
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");
+ char scope[40], name[64] = "game", prefix[1024], error[200];
+ const char *folder = NULL, *outFolder = NULL;
+ int id, k, p, a, maxCommon = 0, entries = 0, split = 0, text = 0;
+ konsc_result_t result;
+
+ for (a = 1; a < argc; a++) {
+ if (!strcmp(argv[a], "-n") && a + 1 < argc) snprintf(name, sizeof name, "%s", argv[++a]);
+ else if (!strcmp(argv[a], "-split") && a + 1 < argc) split = atoi(argv[++a]);
+ else if (!strcmp(argv[a], "-text")) text = 1;
+ else if (!folder) folder = argv[a];
+ else if (!outFolder) outFolder = argv[a];
+ }
+ if (!folder || !outFolder) {
+ fprintf(stderr, "usage: mzscript [-n name] [-split rows] [-text] <data folder> <output folder>\n");
return 2;
}
+ snprintf(prefix, sizeof prefix, "%s/%s", outFolder, name);
+
memset(&out, 0, sizeof out);
memset(&c, 0, sizeof c);
c.out = &out;
- system = load(argv[1], "System.json");
+ system = load(folder, "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");
+ json = load(folder, "CommonEvents.json");
if (json) {
maxCommon = kon_jsonCount(json) - 1;
c.maxCommon = maxCommon;
@@ -773,7 +835,7 @@ int main(int argc, char **argv)
kon_jsonFree(json);
}
- json = load(argv[1], "Troops.json");
+ json = load(folder, "Troops.json");
if (json) {
for (id = 1; id < kon_jsonCount(json); id++) {
const kon_json_t *pages = kon_jsonGet(kon_jsonAt(json, id), "pages");
@@ -792,11 +854,11 @@ int main(int argc, char **argv)
}
for (id = 1; id < 2000; id++) {
- char name[32];
+ char file[32];
const kon_json_t *events;
- snprintf(name, sizeof name, "Map%03d.json", id);
- json = load(argv[1], name);
+ snprintf(file, sizeof file, "Map%03d.json", id);
+ json = load(folder, file);
if (!json) continue;
events = kon_jsonGet(json, "events");
for (k = 1; k < kon_jsonCount(events); k++) {
@@ -816,26 +878,25 @@ int main(int argc, char **argv)
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);
+ if (text) {
+ char path[1100];
+ FILE *f;
- snprintf(path, sizeof path, "%s/speakers.tsv", argv[2]);
- f = fopen(path, "wb");
- if (!f) {
- fprintf(stderr, "cannot write %s\n", path);
+ snprintf(path, sizeof path, "%s.kons", prefix);
+ f = fopen(path, "wb");
+ if (!f || fwrite(out.p, 1, out.n, f) != out.n || fclose(f) != 0) {
+ fprintf(stderr, "cannot write %s\n", path);
+ return 1;
+ }
+ }
+ if (konsc_write(out.p, out.n, prefix, name, split, &result, error, sizeof error) != 0) {
+ fprintf(stderr, "the script could not be written: %s (use -text to see it)\n", error);
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);
+ free(out.p);
+ if (writeSpeakers(prefix, name, c.scratchSwitch, c.scratchVar)) return 1;
- printf("%d scripts, %d speakers\n", entries, speakerCount);
+ printf("%d scripts, %d commands, %d labels, %d speakers\n", entries, result.commands, result.labels, speakerCount);
printf("%d commands became konscript, %d became host commands", structured, hostLines);
printf(" (%d kinds)\n", hostKinds);
for (k = 0; k < hostKinds; k++) {